From 0b605bd1c67f67534882d64fb97ae11953c6b9d4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Eriksson?= Date: Fri, 6 Sep 2024 21:27:38 +0200 Subject: [PATCH 01/37] tsparser: initial object storage implementation --- proto/encore/parser/meta/v1/meta.proto | 6 + proto/encore/runtime/v1/infra.proto | 22 ++++ tsparser/src/legacymeta/mod.rs | 31 ++++- tsparser/src/parser/resources/infra/mod.rs | 1 + .../src/parser/resources/infra/objects.rs | 113 ++++++++++++++++++ tsparser/src/parser/resources/mod.rs | 5 + tsparser/src/parser/usageparser/mod.rs | 1 + 7 files changed, 178 insertions(+), 1 deletion(-) create mode 100644 tsparser/src/parser/resources/infra/objects.rs diff --git a/proto/encore/parser/meta/v1/meta.proto b/proto/encore/parser/meta/v1/meta.proto index 7dc571fc75..29bb335901 100644 --- a/proto/encore/parser/meta/v1/meta.proto +++ b/proto/encore/parser/meta/v1/meta.proto @@ -24,6 +24,7 @@ message Data { repeated SQLDatabase sql_databases = 14; repeated Gateway gateways = 15; Lang language = 16; + repeated Bucket buckets = 17; } // Lang describes the language an application is written in. @@ -58,6 +59,7 @@ message Service { repeated DBMigration migrations = 4; repeated string databases = 5; // databases this service connects to bool has_config = 6; // true if the service has uses config + repeated string buckets = 7; // buckets this service uses } message Selector { @@ -318,6 +320,10 @@ message DBMigration { string description = 3; // descriptive name } +message Bucket { + string name = 1; + optional string doc = 2; +} message PubSubTopic { string name = 1; // The pub sub topic name (unique per application) diff --git a/proto/encore/runtime/v1/infra.proto b/proto/encore/runtime/v1/infra.proto index 9490ca44ef..9c0691f5bf 100644 --- a/proto/encore/runtime/v1/infra.proto +++ b/proto/encore/runtime/v1/infra.proto @@ -21,6 +21,7 @@ message Infrastructure { repeated PubSubCluster pubsub_clusters = 3; repeated RedisCluster redis_clusters = 4; repeated AppSecret app_secrets = 5; + repeated BucketCluster bucket_clusters = 6; } } @@ -305,6 +306,27 @@ message PubSubSubscription { } } +message BucketCluster { + // The unique resource id for this cluster. + string rid = 1; + + repeated Bucket buckets = 2; + + string region = 3; + string endpoint = 4; +} + +message Bucket { + // The unique resource id for this bucket. + string rid = 1; + + // The encore name of the bucket. + string encore_name = 2; + + // The cloud name of the bucket. + string cloud_name = 3; +} + message Gateway { // The unique id for this resource. string rid = 1; diff --git a/tsparser/src/legacymeta/mod.rs b/tsparser/src/legacymeta/mod.rs index 4691443ded..c6dbb36187 100644 --- a/tsparser/src/legacymeta/mod.rs +++ b/tsparser/src/legacymeta/mod.rs @@ -11,7 +11,7 @@ use crate::parser::parser::{ParseContext, ParseResult, Service}; use crate::parser::resourceparser::bind::{Bind, BindKind}; use crate::parser::resources::apis::{authhandler, gateway}; use crate::parser::resources::infra::cron::CronJobSchedule; -use crate::parser::resources::infra::{cron, pubsub_subscription, pubsub_topic, sqldb}; +use crate::parser::resources::infra::{cron, pubsub_subscription, pubsub_topic, sqldb, objects}; use crate::parser::resources::Resource; use crate::parser::types::ObjectId; use crate::parser::usageparser::Usage; @@ -72,6 +72,7 @@ impl<'a> MetaBuilder<'a> { rel_path, rpcs: vec![], // filled in later databases: vec![], // filled in later + buckets: vec![], // filled in later has_config: false, // TODO change when config is supported // We no longer care about migrations in a service, so just set @@ -198,6 +199,10 @@ impl<'a> MetaBuilder<'a> { self.data.sql_databases.push(self.sql_database(db)?); } + Resource::Bucket(bkt) => { + self.data.buckets.push(self.bucket(bkt)); + } + Resource::PubSubTopic(topic) => { let idx = self.data.pubsub_topics.len(); let top = self.pubsub_topic(topic)?; @@ -371,6 +376,22 @@ impl<'a> MetaBuilder<'a> { let idx = svc_index.get(&svc.name).unwrap(); self.data.svcs[*idx].databases.push(access.db.name.clone()); } + + Usage::AccessBucket(access) => { + let Some(svc) = self.service_for_range(&access.range) else { + HANDLER.with(|h| { + h.span_err( + access.range.to_span(), + "unable to determine which service is accessing this bucket", + ) + }); + continue; + }; + + let idx = svc_index.get(&svc.name).unwrap(); + self.data.svcs[*idx].buckets.push(access.bucket.name.clone()); + } + Usage::CallEndpoint(call) => { let src_service = self .service_for_range(&call.range) @@ -524,6 +545,13 @@ impl<'a> MetaBuilder<'a> { }) } + fn bucket(&self, bkt: &objects::Bucket) -> v1::Bucket { + v1::Bucket { + name: bkt.name.clone(), + doc: bkt.doc.clone(), + } + } + /// Compute the relative path from the app root. /// It reports an error if the path is not under the app root. fn rel_path<'b>(&self, path: &'b Path) -> Result<&'b Path> { @@ -608,6 +636,7 @@ fn new_meta() -> v1::Data { experiments: vec![], metrics: vec![], sql_databases: vec![], + buckets: vec![], gateways: vec![], language: v1::Lang::Typescript as i32, } diff --git a/tsparser/src/parser/resources/infra/mod.rs b/tsparser/src/parser/resources/infra/mod.rs index 6864ebd220..854e6e1fd4 100644 --- a/tsparser/src/parser/resources/infra/mod.rs +++ b/tsparser/src/parser/resources/infra/mod.rs @@ -3,3 +3,4 @@ pub mod pubsub_subscription; pub mod pubsub_topic; pub mod secret; pub mod sqldb; +pub mod objects; diff --git a/tsparser/src/parser/resources/infra/objects.rs b/tsparser/src/parser/resources/infra/objects.rs new file mode 100644 index 0000000000..1eb486803a --- /dev/null +++ b/tsparser/src/parser/resources/infra/objects.rs @@ -0,0 +1,113 @@ +use anyhow::Result; +use litparser_derive::LitParser; +use swc_common::errors::HANDLER; +use swc_ecma_ast as ast; +use swc_common::sync::Lrc; + +use crate::parser::resourceparser::bind::ResourceOrPath; +use crate::parser::resourceparser::bind::{BindData, BindKind}; +use crate::parser::resourceparser::paths::PkgPath; +use crate::parser::resourceparser::resource_parser::ResourceParser; +use crate::parser::resources::parseutil::{iter_references, TrackedNames}; +use crate::parser::resources::parseutil::{NamedClassResourceOptionalConfig, NamedStaticMethod}; +use crate::parser::resources::Resource; +use crate::parser::resources::ResourcePath; +use crate::parser::usageparser::{ResolveUsageData, Usage, UsageExprKind}; +use crate::parser::Range; + +#[derive(Debug, Clone)] +pub struct Bucket { + pub name: String, + pub doc: Option, +} + +#[derive(LitParser, Default)] +struct DecodedBucketConfig {} + +pub const OBJECTS_PARSER: ResourceParser = ResourceParser { + name: "objects", + interesting_pkgs: &[PkgPath("encore.dev/storage/objects")], + + run: |pass| { + let names = TrackedNames::new(&[("encore.dev/storage/objects", "Bucket")]); + + let module = pass.module.clone(); + { + type Res = NamedClassResourceOptionalConfig; + for r in iter_references::(&module, &names) { + let r = r?; + + // Not yet used. + let _cfg = r.config.unwrap_or_default(); + + let object = match &r.bind_name { + None => None, + Some(id) => pass + .type_checker + .resolve_obj(pass.module.clone(), &ast::Expr::Ident(id.clone())), + }; + + let resource = Resource::Bucket(Lrc::new(Bucket { + name: r.resource_name, + doc: r.doc_comment, + })); + pass.add_resource(resource.clone()); + pass.add_bind(BindData { + range: r.range, + resource: ResourceOrPath::Resource(resource), + object, + kind: BindKind::Create, + ident: r.bind_name, + }); + } + } + + { + for r in iter_references::(&module, &names) { + let r = r?; + let object = match &r.bind_name { + None => None, + Some(id) => pass + .type_checker + .resolve_obj(pass.module.clone(), &ast::Expr::Ident(id.clone())), + }; + + pass.add_bind(BindData { + range: r.range, + resource: ResourceOrPath::Path(ResourcePath::Bucket { + name: r.resource_name, + }), + object, + kind: BindKind::Reference, + ident: r.bind_name, + }); + } + } + + Ok(()) + }, +}; + +pub fn resolve_bucket_usage(data: &ResolveUsageData, bucket: Lrc) -> Result> { + Ok(match &data.expr.kind { + UsageExprKind::MethodCall(_) + | UsageExprKind::FieldAccess(_) + | UsageExprKind::CallArg(_) + | UsageExprKind::ConstructorArg(_) => Some(Usage::AccessBucket(AccessBucketUsage { + range: data.expr.range, + bucket, + })), + + _ => { + HANDLER + .with(|h| h.span_err(data.expr.range.to_span(), "invalid use of bucket resource")); + None + } + }) +} + +#[derive(Debug)] +pub struct AccessBucketUsage { + pub range: Range, + pub bucket: Lrc, +} diff --git a/tsparser/src/parser/resources/mod.rs b/tsparser/src/parser/resources/mod.rs index 9493bc3a52..9074ebdea7 100644 --- a/tsparser/src/parser/resources/mod.rs +++ b/tsparser/src/parser/resources/mod.rs @@ -8,6 +8,7 @@ use crate::parser::resources::apis::authhandler::AUTHHANDLER_PARSER; use crate::parser::resources::apis::gateway::GATEWAY_PARSER; use crate::parser::resources::apis::service::SERVICE_PARSER; use crate::parser::resources::infra::cron::CRON_PARSER; +use crate::parser::resources::infra::objects::OBJECTS_PARSER; use crate::parser::resources::infra::pubsub_subscription::SUBSCRIPTION_PARSER; use crate::parser::resources::infra::pubsub_topic::TOPIC_PARSER; use crate::parser::resources::infra::secret::SECRET_PARSER; @@ -25,6 +26,7 @@ pub enum Resource { Gateway(Lrc), Service(Lrc), SQLDatabase(Lrc), + Bucket(Lrc), PubSubTopic(Lrc), PubSubSubscription(Lrc), CronJob(Lrc), @@ -34,6 +36,7 @@ pub enum Resource { #[derive(Debug, Eq, Hash, PartialEq, Clone)] pub enum ResourcePath { SQLDatabase { name: String }, + Bucket { name: String }, } impl Display for Resource { @@ -50,6 +53,7 @@ impl Display for Resource { write!(f, "Gateway({})", gw.name) } Resource::SQLDatabase(db) => write!(f, "SQLDatabase({})", db.name), + Resource::Bucket(db) => write!(f, "Bucket({})", db.name), Resource::PubSubTopic(topic) => write!(f, "PubSubTopic({})", topic.name), Resource::PubSubSubscription(sub) => write!(f, "PubSubSubscription({})", sub.name), Resource::CronJob(cron) => write!(f, "CronJob({})", cron.name), @@ -67,6 +71,7 @@ pub static DEFAULT_RESOURCE_PARSERS: &[&ResourceParser] = &[ &AUTHHANDLER_PARSER, &GATEWAY_PARSER, &SQLDB_PARSER, + &OBJECTS_PARSER, &TOPIC_PARSER, &SUBSCRIPTION_PARSER, &CRON_PARSER, diff --git a/tsparser/src/parser/usageparser/mod.rs b/tsparser/src/parser/usageparser/mod.rs index 48c5b4a8ef..96b5fa30dc 100644 --- a/tsparser/src/parser/usageparser/mod.rs +++ b/tsparser/src/parser/usageparser/mod.rs @@ -214,6 +214,7 @@ pub enum Usage { CallEndpoint(apis::api::CallEndpointUsage), PublishTopic(infra::pubsub_topic::PublishUsage), AccessDatabase(infra::sqldb::AccessDatabaseUsage), + AccessBucket(infra::objects::AccessBucketUsage), } pub struct ResolveUsageData<'a> { From 5f8e5f7aecf788ae9c976912784a783f3ba47c1f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Eriksson?= Date: Fri, 6 Sep 2024 21:24:27 +0200 Subject: [PATCH 02/37] runtimes/js: initial interface sketch --- .../js/encore.dev/storage/objects/bucket.ts | 28 +++++++++++++++++++ runtimes/js/encore.dev/storage/objects/mod.ts | 2 ++ 2 files changed, 30 insertions(+) create mode 100644 runtimes/js/encore.dev/storage/objects/bucket.ts create mode 100644 runtimes/js/encore.dev/storage/objects/mod.ts diff --git a/runtimes/js/encore.dev/storage/objects/bucket.ts b/runtimes/js/encore.dev/storage/objects/bucket.ts new file mode 100644 index 0000000000..f659467291 --- /dev/null +++ b/runtimes/js/encore.dev/storage/objects/bucket.ts @@ -0,0 +1,28 @@ +import { getCurrentRequest } from "../../internal/reqtrack/mod"; +import * as runtime from "../../internal/runtime/mod"; +import { StringLiteral } from "../../internal/utils/constraints"; + +export interface BucketConfig { +} + +/** + * Defines a new Object Storage bucket infrastructure resource. + */ +export class Bucket { + /** + * Creates a new bucket with the given name and configuration + */ + // eslint-disable-next-line @typescript-eslint/no-unused-vars + constructor(name: string, cfg?: BucketConfig) { + // this.impl = runtime.RT.sqlDatabase(name); + } + + /** + * Reference an existing bucket by name. + * To create a new storage bucket, use `new StorageBucket(...)` instead. + */ + static named(name: StringLiteral): Bucket { + return new Bucket(name); + } + +} diff --git a/runtimes/js/encore.dev/storage/objects/mod.ts b/runtimes/js/encore.dev/storage/objects/mod.ts new file mode 100644 index 0000000000..cba84cfc7b --- /dev/null +++ b/runtimes/js/encore.dev/storage/objects/mod.ts @@ -0,0 +1,2 @@ +export { Bucket } from "./bucket"; +export type { BucketConfig } from "./bucket"; From 23c4ce9f58134a05da0bff9ec401ed63b48d55c7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Eriksson?= Date: Fri, 6 Sep 2024 21:27:44 +0200 Subject: [PATCH 03/37] runtimes/core: initial bucket impl --- Cargo.lock | 196 +++++++++++++++++++++++++ runtimes/core/Cargo.toml | 1 + runtimes/core/src/lib.rs | 9 ++ runtimes/core/src/objects/manager.rs | 111 ++++++++++++++ runtimes/core/src/objects/mod.rs | 16 ++ runtimes/core/src/objects/noop/mod.rs | 19 +++ runtimes/core/src/objects/s3/bucket.rs | 19 +++ runtimes/core/src/objects/s3/mod.rs | 33 +++++ 8 files changed, 404 insertions(+) create mode 100644 runtimes/core/src/objects/manager.rs create mode 100644 runtimes/core/src/objects/mod.rs create mode 100644 runtimes/core/src/objects/noop/mod.rs create mode 100644 runtimes/core/src/objects/s3/bucket.rs create mode 100644 runtimes/core/src/objects/s3/mod.rs diff --git a/Cargo.lock b/Cargo.lock index 2c15555331..264fcb18c7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -266,6 +266,20 @@ version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" +[[package]] +name = "attohttpc" +version = "0.28.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a13149d0cf3f7f9b9261fad4ec63b2efbf9a80665f52def86282d26255e6331" +dependencies = [ + "http 1.0.0", + "log", + "native-tls", + "serde", + "serde_json", + "url", +] + [[package]] name = "atty" version = "0.2.14" @@ -326,6 +340,32 @@ dependencies = [ "zeroize", ] +[[package]] +name = "aws-creds" +version = "0.37.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f84143206b9c72b3c5cb65415de60c7539c79cd1559290fddec657939131be0" +dependencies = [ + "attohttpc", + "home", + "log", + "quick-xml", + "rust-ini", + "serde", + "thiserror", + "time", + "url", +] + +[[package]] +name = "aws-region" +version = "0.25.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e9aed3f9c7eac9be28662fdb3b0f4d1951e812f7c64fed4f0327ba702f459b3b" +dependencies = [ + "thiserror", +] + [[package]] name = "aws-runtime" version = "1.1.9" @@ -1077,6 +1117,26 @@ version = "0.9.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8" +[[package]] +name = "const-random" +version = "0.1.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87e00182fe74b066627d63b85fd550ac2998d4b0bd86bfed477a0ae4c7c71359" +dependencies = [ + "const-random-macro", +] + +[[package]] +name = "const-random-macro" +version = "0.1.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f9d839f2a20b0aee515dc581a6172f2321f96cab76c1a38a4c584a194955390e" +dependencies = [ + "getrandom 0.2.11", + "once_cell", + "tiny-keccak", +] + [[package]] name = "constant_time_eq" version = "0.3.0" @@ -1181,6 +1241,12 @@ dependencies = [ "cfg-if", ] +[[package]] +name = "crunchy" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" + [[package]] name = "crypto-common" version = "0.1.6" @@ -1314,6 +1380,15 @@ dependencies = [ "subtle", ] +[[package]] +name = "dlv-list" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "442039f5147480ba31067cb00ada1adae6892028e40e45fc5de7b7df6dcc1b5f" +dependencies = [ + "const-random", +] + [[package]] name = "doc-comment" version = "0.3.3" @@ -1438,6 +1513,7 @@ dependencies = [ "rand 0.8.5", "reqwest 0.12.4", "rsa", + "rust-s3", "serde", "serde_json", "serde_path_to_error", @@ -2686,6 +2762,17 @@ version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0e7465ac9959cc2b1404e8e2367b43684a6d13790fe23056cc8c6c5a6b7bcb94" +[[package]] +name = "maybe-async" +version = "0.2.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5cf92c10c7e361d6b99666ec1c6f9805b0bea2c3bd8c78dc6fe98ac5bd78db11" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.47", +] + [[package]] name = "md-5" version = "0.10.6" @@ -2733,6 +2820,15 @@ dependencies = [ "unicase", ] +[[package]] +name = "minidom" +version = "0.15.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f45614075738ce1b77a1768912a60c0227525971b03e09122a05b8a34a2a6278" +dependencies = [ + "rxml", +] + [[package]] name = "miniz_oxide" version = "0.7.1" @@ -3025,6 +3121,16 @@ dependencies = [ "vcpkg", ] +[[package]] +name = "ordered-multimap" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49203cdcae0030493bad186b28da2fa25645fa276a51b6fec8010d281e02ef79" +dependencies = [ + "dlv-list", + "hashbrown 0.14.3", +] + [[package]] name = "os_pipe" version = "1.1.5" @@ -3805,6 +3911,16 @@ version = "2.28.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "106dd99e98437432fed6519dedecfade6a06a73bb7b2a1e019fdd2bee5778d94" +[[package]] +name = "quick-xml" +version = "0.32.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d3a6e5838b60e0e8fa7a43f22ade549a37d61f8bdbe636d0d7816191de969c2" +dependencies = [ + "memchr", + "serde", +] + [[package]] name = "quickcheck" version = "1.0.3" @@ -4161,6 +4277,54 @@ dependencies = [ "zeroize", ] +[[package]] +name = "rust-ini" +version = "0.21.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e310ef0e1b6eeb79169a1171daf9abcb87a2e17c03bee2c4bb100b55c75409f" +dependencies = [ + "cfg-if", + "ordered-multimap", + "trim-in-place", +] + +[[package]] +name = "rust-s3" +version = "0.35.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3df3f353b1f4209dcf437d777cda90279c397ab15a0cd6fd06bd32c88591533" +dependencies = [ + "async-trait", + "aws-creds", + "aws-region", + "base64 0.22.1", + "bytes", + "cfg-if", + "futures", + "hex", + "hmac", + "http 0.2.11", + "hyper 0.14.28", + "hyper-tls 0.5.0", + "log", + "maybe-async", + "md5", + "minidom", + "native-tls", + "percent-encoding", + "quick-xml", + "serde", + "serde_derive", + "serde_json", + "sha2", + "thiserror", + "time", + "tokio", + "tokio-native-tls", + "tokio-stream", + "url", +] + [[package]] name = "rust_decimal" version = "1.35.0" @@ -4310,6 +4474,23 @@ version = "1.0.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7ffc183a10b4478d04cbbbfc96d0873219d962dd5accaff2ffbd4ceb7df837f4" +[[package]] +name = "rxml" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a98f186c7a2f3abbffb802984b7f1dfd65dac8be1aafdaabbca4137f53f0dff7" +dependencies = [ + "bytes", + "rxml_validation", + "smartstring", +] + +[[package]] +name = "rxml_validation" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22a197350ece202f19a166d1ad6d9d6de145e1d2a8ef47db299abe164dbd7530" + [[package]] name = "ryu" version = "1.0.16" @@ -5251,6 +5432,15 @@ dependencies = [ "time-core", ] +[[package]] +name = "tiny-keccak" +version = "2.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c9d3793400a45f954c52e73d068316d76b6f4e36977e3fcebb13a2721e80237" +dependencies = [ + "crunchy", +] + [[package]] name = "tinyvec" version = "1.6.0" @@ -5667,6 +5857,12 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "trim-in-place" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "343e926fc669bc8cde4fa3129ab681c63671bae288b1f1081ceee6d9d37904fc" + [[package]] name = "try-lock" version = "0.2.5" diff --git a/runtimes/core/Cargo.toml b/runtimes/core/Cargo.toml index 7e345bf262..07721c162f 100644 --- a/runtimes/core/Cargo.toml +++ b/runtimes/core/Cargo.toml @@ -83,6 +83,7 @@ rsa = { version = "0.9.6", features = ["pem"] } flate2 = "1.0.30" urlencoding = "2.1.3" tower-http = { version = "0.5.2", features = ["fs"] } +rust-s3 = "0.35.1" serde_path_to_error = "0.1.16" tracing = "0.1.40" tracing-subscriber = { version = "0.3.18", features = ["alloc", "ansi", "env-filter", "fmt", "matchers", "nu-ansi-term", "once_cell", "regex", "registry", "sharded-slab", "smallvec", "std", "thread_local", "tracing"], default-features = false } diff --git a/runtimes/core/src/lib.rs b/runtimes/core/src/lib.rs index b54e0e2407..c98e794e95 100644 --- a/runtimes/core/src/lib.rs +++ b/runtimes/core/src/lib.rs @@ -28,6 +28,7 @@ pub mod model; mod names; pub mod proccfg; pub mod pubsub; +pub mod objects; pub mod secrets; pub mod sqldb; mod trace; @@ -203,6 +204,7 @@ pub struct Runtime { pubsub: pubsub::Manager, secrets: secrets::Manager, sqldb: sqldb::Manager, + objects: objects::Manager, api: api::Manager, app_meta: meta::AppMeta, runtime: tokio::runtime::Runtime, @@ -329,6 +331,7 @@ impl Runtime { .context("failed to resolve gateway push subscriptions")?; let pubsub = pubsub::Manager::new(tracer.clone(), resources.pubsub_clusters, &md)?; + let objects = objects::Manager::new(tracer.clone(), resources.bucket_clusters, &md); let sqldb = sqldb::ManagerConfig { clusters: resources.sql_clusters, creds: &creds, @@ -376,6 +379,7 @@ impl Runtime { pubsub, secrets, sqldb, + objects, api, app_meta, runtime: tokio_rt, @@ -397,6 +401,11 @@ impl Runtime { &self.sqldb } + #[inline] + pub fn objects(&self) -> &objects::Manager { + &self.objects + } + #[inline] pub fn metadata(&self) -> &metapb::Data { &self.md diff --git a/runtimes/core/src/objects/manager.rs b/runtimes/core/src/objects/manager.rs new file mode 100644 index 0000000000..c3da353aa4 --- /dev/null +++ b/runtimes/core/src/objects/manager.rs @@ -0,0 +1,111 @@ +use std::collections::HashMap; +use std::sync::{Arc, RwLock}; + +use crate::encore::parser::meta::v1 as meta; +use crate::encore::runtime::v1 as pb; +use crate::names::EncoreName; +use crate::objects::noop::NoopCluster; +use crate::objects::{ + noop, Cluster, Bucket, +}; +use crate::trace::{Tracer}; + +pub struct Manager { + tracer: Tracer, + bucket_cfg: HashMap, pb::Bucket)>, + + buckets: Arc>>>, +} + +#[derive(Debug)] +pub struct BucketObj { + name: EncoreName, + tracer: Tracer, + inner: Arc, +} + +impl BucketObj { +} + +impl Manager { + pub fn new(tracer: Tracer, clusters: Vec, md: &meta::Data) -> Self { + let bucket_cfg = make_cfg_maps(clusters, md); + + Self { + tracer, + bucket_cfg, + buckets: Arc::default(), + } + } + + pub fn bucket(&self, name: EncoreName) -> Option { + let inner = self.bucket_impl(name.clone())?; + Some(BucketObj { + name, + inner, + tracer: self.tracer.clone(), + }) + } + + fn bucket_impl(&self, name: EncoreName) -> Option> { + if let Some(bkt) = self.buckets.read().unwrap().get(&name) { + return Some(bkt.clone()); + } + + let bkt = { + if let Some((cluster, bucket_cfg)) = self.bucket_cfg.get(&name) { + cluster.bucket(bucket_cfg) + } else { + Arc::new(noop::NoopBucket) + } + }; + + self.buckets.write().unwrap().insert(name, bkt.clone()); + Some(bkt) + } +} + +fn make_cfg_maps( + clusters: Vec, + md: &meta::Data, +) -> + HashMap, pb::Bucket)> +{ + let mut bucket_map = HashMap::new(); + + for cluster_cfg in clusters { + let cluster = new_cluster(&cluster_cfg); + + for bucket_cfg in cluster_cfg.buckets { + bucket_map.insert( + bucket_cfg.encore_name.clone().into(), + (cluster.clone(), bucket_cfg), + ); + } + } + + bucket_map +} + +fn new_cluster(cluster: &pb::BucketCluster) -> Arc { + // let Some(provider) = &cluster.provider else { + // log::error!("missing PubSub cluster provider: {}", cluster.rid); + // return Arc::new(NoopCluster); + // }; + + // match provider { + // pb::pub_sub_cluster::Provider::Gcp(_) => return Arc::new(gcp::Cluster::new()), + // pb::pub_sub_cluster::Provider::Nsq(cfg) => { + // return Arc::new(nsq::Cluster::new(cfg.hosts[0].clone())); + // } + // pb::pub_sub_cluster::Provider::Aws(_) => return Arc::new(sqs_sns::Cluster::new()), + // pb::pub_sub_cluster::Provider::Encore(_) => { + // log::error!("Encore Cloud Pub/Sub not yet supported: {}", cluster.rid); + // } + // pb::pub_sub_cluster::Provider::Azure(_) => { + // log::error!("Azure Pub/Sub not yet supported: {}", cluster.rid); + // } + // } + + Arc::new(NoopCluster) +} diff --git a/runtimes/core/src/objects/mod.rs b/runtimes/core/src/objects/mod.rs new file mode 100644 index 0000000000..74293af9e3 --- /dev/null +++ b/runtimes/core/src/objects/mod.rs @@ -0,0 +1,16 @@ +use std::fmt::Debug; +use std::sync::Arc; + +pub use manager::{Manager}; + +use crate::encore::runtime::v1 as pb; + +mod s3; +mod noop; +mod manager; + +trait Cluster: Debug + Send + Sync { + fn bucket(&self, cfg: &pb::Bucket) -> Arc; +} + +trait Bucket: Debug + Send + Sync {} diff --git a/runtimes/core/src/objects/noop/mod.rs b/runtimes/core/src/objects/noop/mod.rs new file mode 100644 index 0000000000..675619f460 --- /dev/null +++ b/runtimes/core/src/objects/noop/mod.rs @@ -0,0 +1,19 @@ +use std::sync::Arc; + +use crate::encore::runtime::v1 as pb; +use crate::objects; + +#[derive(Debug)] +pub struct NoopCluster; + +#[derive(Debug)] +pub struct NoopBucket; + +impl objects::Cluster for NoopCluster { + fn bucket(&self, _cfg: &pb::Bucket) -> Arc { + Arc::new(NoopBucket) + } +} + +impl objects::Bucket for NoopBucket { +} diff --git a/runtimes/core/src/objects/s3/bucket.rs b/runtimes/core/src/objects/s3/bucket.rs new file mode 100644 index 0000000000..6bdf5bee9e --- /dev/null +++ b/runtimes/core/src/objects/s3/bucket.rs @@ -0,0 +1,19 @@ +use crate::encore::runtime::v1 as pb; +use crate::objects; + +#[derive(Debug)] +pub struct Bucket { + client: Box, +} + +impl Bucket { + pub(super) fn new(region: s3::Region, creds: s3::creds::Credentials, cfg: &pb::Bucket) -> Self { + let client = s3::Bucket::new(&cfg.cloud_name, region, creds).expect("unable to construct bucket client"); + Self { + client, + } + } +} + +impl objects::Bucket for Bucket { +} diff --git a/runtimes/core/src/objects/s3/mod.rs b/runtimes/core/src/objects/s3/mod.rs new file mode 100644 index 0000000000..1d37e432b8 --- /dev/null +++ b/runtimes/core/src/objects/s3/mod.rs @@ -0,0 +1,33 @@ +use std::sync::Arc; + +use crate::encore::runtime::v1 as pb; +use crate::objects; +use crate::objects::s3::bucket::Bucket; + +mod bucket; + +#[derive(Debug)] +pub struct Cluster { + region: s3::Region, + creds: s3::creds::Credentials, +} + +impl Cluster { + pub fn new(cfg: &pb::BucketCluster) -> Self { + let region = s3::Region::Custom { + region: cfg.region.clone(), + endpoint: cfg.endpoint.clone(), + }; + + // TODO(andre): does this work? + let creds = s3::creds::Credentials::default().unwrap(); + + Self { region, creds } + } +} + +impl objects::Cluster for Cluster { + fn bucket(&self, cfg: &pb::Bucket) -> Arc { + Arc::new(Bucket::new(self.region.clone(), self.creds.clone(), cfg)) + } +} From 36d62c571ac48652d0ecb8f279a314378a6b6159 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Eriksson?= Date: Sat, 19 Oct 2024 00:51:38 +0200 Subject: [PATCH 04/37] WIP on object-storage --- cli/daemon/objects/objects.go | 105 ++ cli/daemon/run/infra/infra.go | 56 +- proto/encore/parser/meta/v1/meta.pb.go | 1135 +++++++++-------- proto/encore/runtime/v1/infra.pb.go | 991 ++++++++------ runtimes/core/src/objects/manager.rs | 52 +- runtimes/core/src/objects/mod.rs | 21 +- runtimes/core/src/objects/noop/mod.rs | 22 +- runtimes/core/src/objects/s3/bucket.rs | 36 +- runtimes/core/src/objects/s3/mod.rs | 4 +- runtimes/js/encore.dev/package.json | 5 + .../js/encore.dev/storage/objects/bucket.ts | 32 +- runtimes/js/src/lib.rs | 1 + runtimes/js/src/objects.rs | 37 + runtimes/js/src/runtime.rs | 12 +- 14 files changed, 1545 insertions(+), 964 deletions(-) create mode 100644 cli/daemon/objects/objects.go create mode 100644 runtimes/js/src/objects.rs diff --git a/cli/daemon/objects/objects.go b/cli/daemon/objects/objects.go new file mode 100644 index 0000000000..94787e3afc --- /dev/null +++ b/cli/daemon/objects/objects.go @@ -0,0 +1,105 @@ +package objects + +import ( + mathrand "math/rand" // nosemgrep + "time" + + "github.com/alicebob/miniredis/v2" + "github.com/cockroachdb/errors" + "go4.org/syncutil" + + meta "encr.dev/proto/encore/parser/meta/v1" +) + +type Server struct { + startOnce syncutil.Once + mini *miniredis.Miniredis + cleanup *time.Ticker + quit chan struct{} + addr string +} + +const tickInterval = 1 * time.Second + +func New() *Server { + return &Server{ + mini: miniredis.NewMiniRedis(), + quit: make(chan struct{}), + } +} + +func (s *Server) Start() error { + return s.startOnce.Do(func() error { + if err := s.mini.Start(); err != nil { + return errors.Wrap(err, "failed to start redis server") + } + s.addr = s.mini.Addr() + s.cleanup = time.NewTicker(tickInterval) + go s.doCleanup() + return nil + }) +} + +func (s *Server) Stop() { + s.mini.Close() + s.cleanup.Stop() + close(s.quit) +} + +func (s *Server) Miniredis() *miniredis.Miniredis { + return s.mini +} + +func (s *Server) Addr() string { + // Ensure the server has been started + if err := s.Start(); err != nil { + panic(err) + } + return s.addr +} + +func (s *Server) doCleanup() { + var acc time.Duration + const cleanupInterval = 15 * time.Second + + for { + select { + case <-s.quit: + return + case <-s.cleanup.C: + } + s.mini.FastForward(tickInterval) + + // Clean up keys every so often + acc += tickInterval + if acc > cleanupInterval { + acc -= cleanupInterval + s.clearKeys() + } + } +} + +// clearKeys clears random keys to get the redis server +// down to 100 persisted keys, as a simple way to bound +// the max memory usage. +func (s *Server) clearKeys() { + const maxKeys = 100 + keys := s.mini.Keys() + if n := len(keys); n > maxKeys { + toDelete := n - maxKeys + deleted := 0 + for deleted < toDelete { + id := mathrand.Intn(len(keys)) + if keys[id] != "" { + s.mini.Del(keys[id]) + keys[id] = "" // mark it as deleted + deleted++ + } + } + } +} + +// IsUsed reports whether the application uses object storage at all. +func IsUsed(md *meta.Data) bool { + return len(md.Buckets) > 0 +} diff --git a/cli/daemon/run/infra/infra.go b/cli/daemon/run/infra/infra.go index 8ca2b22d9e..234e33dfc4 100644 --- a/cli/daemon/run/infra/infra.go +++ b/cli/daemon/run/infra/infra.go @@ -14,6 +14,7 @@ import ( "encore.dev/appruntime/exported/config" "encr.dev/cli/daemon/apps" "encr.dev/cli/daemon/namespace" + "encr.dev/cli/daemon/objects" "encr.dev/cli/daemon/pubsub" "encr.dev/cli/daemon/redis" "encr.dev/cli/daemon/sqldb" @@ -25,9 +26,10 @@ import ( type Type string const ( - PubSub Type = "pubsub" - Cache Type = "cache" - SQLDB Type = "sqldb" + PubSub Type = "pubsub" + Cache Type = "cache" + SQLDB Type = "sqldb" + Objects Type = "objects" ) const ( @@ -99,6 +101,10 @@ func (rm *ResourceManager) StartRequiredServices(a *optracker.AsyncBuildJobs, md if redis.IsUsed(md) && rm.GetRedis() == nil { a.Go("Starting Redis server", true, 250*time.Millisecond, rm.StartRedis) } + + if objects.IsUsed(md) && rm.GetObjects() == nil { + a.Go("Starting Object Storage server", true, 250*time.Millisecond, rm.StartObjects) + } } // StartPubSub starts a PubSub daemon. @@ -151,6 +157,31 @@ func (rm *ResourceManager) GetRedis() *redis.Server { return nil } +// StartObjects starts an Object Storage server. +func (rm *ResourceManager) StartObjects(ctx context.Context) error { + srv := objects.New() + err := srv.Start() + if err != nil { + return err + } + + rm.mutex.Lock() + rm.servers[Objects] = srv + rm.mutex.Unlock() + return nil +} + +// GetObjects returns the Object Storage server if it is running otherwise it returns nil +func (rm *ResourceManager) GetObjects() *objects.Server { + rm.mutex.Lock() + defer rm.mutex.Unlock() + + if srv, found := rm.servers[Objects]; found { + return srv.(*objects.Server) + } + return nil +} + func (rm *ResourceManager) StartSQLCluster(a *optracker.AsyncBuildJobs, md *meta.Data) func(ctx context.Context) error { return func(ctx context.Context) error { // This can be the case in tests. @@ -408,3 +439,22 @@ func (rm *ResourceManager) RedisConfig(redis *meta.CacheCluster) (config.RedisSe return srvCfg, dbCfg, nil } + +// ObjectsConfig returns the Object Storage server configuration. +func (rm *ResourceManager) ObjectsConfig() (config.RedisServer, config.RedisDatabase, error) { + server := rm.GetRedis() + if server == nil { + return config.RedisServer{}, config.RedisDatabase{}, errors.New("no Redis server found") + } + + srvCfg := config.RedisServer{ + Host: server.Addr(), + } + + dbCfg := config.RedisDatabase{ + EncoreName: redis.Name, + KeyPrefix: redis.Name + "/", + } + + return srvCfg, dbCfg, nil +} diff --git a/proto/encore/parser/meta/v1/meta.pb.go b/proto/encore/parser/meta/v1/meta.pb.go index 56db56f9e9..841f94668d 100644 --- a/proto/encore/parser/meta/v1/meta.pb.go +++ b/proto/encore/parser/meta/v1/meta.pb.go @@ -482,7 +482,7 @@ func (x PubSubTopic_DeliveryGuarantee) Number() protoreflect.EnumNumber { // Deprecated: Use PubSubTopic_DeliveryGuarantee.Descriptor instead. func (PubSubTopic_DeliveryGuarantee) EnumDescriptor() ([]byte, []int) { - return file_encore_parser_meta_v1_meta_proto_rawDescGZIP(), []int{25, 0} + return file_encore_parser_meta_v1_meta_proto_rawDescGZIP(), []int{26, 0} } type Metric_MetricKind int32 @@ -531,7 +531,7 @@ func (x Metric_MetricKind) Number() protoreflect.EnumNumber { // Deprecated: Use Metric_MetricKind.Descriptor instead. func (Metric_MetricKind) EnumDescriptor() ([]byte, []int) { - return file_encore_parser_meta_v1_meta_proto_rawDescGZIP(), []int{27, 0} + return file_encore_parser_meta_v1_meta_proto_rawDescGZIP(), []int{28, 0} } // Data is the metadata associated with an app version. @@ -556,6 +556,7 @@ type Data struct { SqlDatabases []*SQLDatabase `protobuf:"bytes,14,rep,name=sql_databases,json=sqlDatabases,proto3" json:"sql_databases,omitempty"` Gateways []*Gateway `protobuf:"bytes,15,rep,name=gateways,proto3" json:"gateways,omitempty"` Language Lang `protobuf:"varint,16,opt,name=language,proto3,enum=encore.parser.meta.v1.Lang" json:"language,omitempty"` + Buckets []*Bucket `protobuf:"bytes,17,rep,name=buckets,proto3" json:"buckets,omitempty"` } func (x *Data) Reset() { @@ -702,6 +703,13 @@ func (x *Data) GetLanguage() Lang { return Lang_GO } +func (x *Data) GetBuckets() []*Bucket { + if x != nil { + return x.Buckets + } + return nil +} + // QualifiedName is a name of an object in a specific package. // It is never an unqualified name, even in circumstances // where a package may refer to its own objects. @@ -866,6 +874,7 @@ type Service struct { Migrations []*DBMigration `protobuf:"bytes,4,rep,name=migrations,proto3" json:"migrations,omitempty"` Databases []string `protobuf:"bytes,5,rep,name=databases,proto3" json:"databases,omitempty"` // databases this service connects to HasConfig bool `protobuf:"varint,6,opt,name=has_config,json=hasConfig,proto3" json:"has_config,omitempty"` // true if the service has uses config + Buckets []string `protobuf:"bytes,7,rep,name=buckets,proto3" json:"buckets,omitempty"` // buckets this service uses } func (x *Service) Reset() { @@ -942,6 +951,13 @@ func (x *Service) GetHasConfig() bool { return false } +func (x *Service) GetBuckets() []string { + if x != nil { + return x.Buckets + } + return nil +} + type Selector struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -2678,6 +2694,61 @@ func (x *DBMigration) GetDescription() string { return "" } +type Bucket struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` + Doc *string `protobuf:"bytes,2,opt,name=doc,proto3,oneof" json:"doc,omitempty"` +} + +func (x *Bucket) Reset() { + *x = Bucket{} + if protoimpl.UnsafeEnabled { + mi := &file_encore_parser_meta_v1_meta_proto_msgTypes[25] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Bucket) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Bucket) ProtoMessage() {} + +func (x *Bucket) ProtoReflect() protoreflect.Message { + mi := &file_encore_parser_meta_v1_meta_proto_msgTypes[25] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Bucket.ProtoReflect.Descriptor instead. +func (*Bucket) Descriptor() ([]byte, []int) { + return file_encore_parser_meta_v1_meta_proto_rawDescGZIP(), []int{25} +} + +func (x *Bucket) GetName() string { + if x != nil { + return x.Name + } + return "" +} + +func (x *Bucket) GetDoc() string { + if x != nil && x.Doc != nil { + return *x.Doc + } + return "" +} + type PubSubTopic struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -2695,7 +2766,7 @@ type PubSubTopic struct { func (x *PubSubTopic) Reset() { *x = PubSubTopic{} if protoimpl.UnsafeEnabled { - mi := &file_encore_parser_meta_v1_meta_proto_msgTypes[25] + mi := &file_encore_parser_meta_v1_meta_proto_msgTypes[26] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2708,7 +2779,7 @@ func (x *PubSubTopic) String() string { func (*PubSubTopic) ProtoMessage() {} func (x *PubSubTopic) ProtoReflect() protoreflect.Message { - mi := &file_encore_parser_meta_v1_meta_proto_msgTypes[25] + mi := &file_encore_parser_meta_v1_meta_proto_msgTypes[26] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2721,7 +2792,7 @@ func (x *PubSubTopic) ProtoReflect() protoreflect.Message { // Deprecated: Use PubSubTopic.ProtoReflect.Descriptor instead. func (*PubSubTopic) Descriptor() ([]byte, []int) { - return file_encore_parser_meta_v1_meta_proto_rawDescGZIP(), []int{25} + return file_encore_parser_meta_v1_meta_proto_rawDescGZIP(), []int{26} } func (x *PubSubTopic) GetName() string { @@ -2787,7 +2858,7 @@ type CacheCluster struct { func (x *CacheCluster) Reset() { *x = CacheCluster{} if protoimpl.UnsafeEnabled { - mi := &file_encore_parser_meta_v1_meta_proto_msgTypes[26] + mi := &file_encore_parser_meta_v1_meta_proto_msgTypes[27] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2800,7 +2871,7 @@ func (x *CacheCluster) String() string { func (*CacheCluster) ProtoMessage() {} func (x *CacheCluster) ProtoReflect() protoreflect.Message { - mi := &file_encore_parser_meta_v1_meta_proto_msgTypes[26] + mi := &file_encore_parser_meta_v1_meta_proto_msgTypes[27] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2813,7 +2884,7 @@ func (x *CacheCluster) ProtoReflect() protoreflect.Message { // Deprecated: Use CacheCluster.ProtoReflect.Descriptor instead. func (*CacheCluster) Descriptor() ([]byte, []int) { - return file_encore_parser_meta_v1_meta_proto_rawDescGZIP(), []int{26} + return file_encore_parser_meta_v1_meta_proto_rawDescGZIP(), []int{27} } func (x *CacheCluster) GetName() string { @@ -2860,7 +2931,7 @@ type Metric struct { func (x *Metric) Reset() { *x = Metric{} if protoimpl.UnsafeEnabled { - mi := &file_encore_parser_meta_v1_meta_proto_msgTypes[27] + mi := &file_encore_parser_meta_v1_meta_proto_msgTypes[28] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2873,7 +2944,7 @@ func (x *Metric) String() string { func (*Metric) ProtoMessage() {} func (x *Metric) ProtoReflect() protoreflect.Message { - mi := &file_encore_parser_meta_v1_meta_proto_msgTypes[27] + mi := &file_encore_parser_meta_v1_meta_proto_msgTypes[28] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2886,7 +2957,7 @@ func (x *Metric) ProtoReflect() protoreflect.Message { // Deprecated: Use Metric.ProtoReflect.Descriptor instead. func (*Metric) Descriptor() ([]byte, []int) { - return file_encore_parser_meta_v1_meta_proto_rawDescGZIP(), []int{27} + return file_encore_parser_meta_v1_meta_proto_rawDescGZIP(), []int{28} } func (x *Metric) GetName() string { @@ -2940,7 +3011,7 @@ type RPC_ExposeOptions struct { func (x *RPC_ExposeOptions) Reset() { *x = RPC_ExposeOptions{} if protoimpl.UnsafeEnabled { - mi := &file_encore_parser_meta_v1_meta_proto_msgTypes[29] + mi := &file_encore_parser_meta_v1_meta_proto_msgTypes[30] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2953,7 +3024,7 @@ func (x *RPC_ExposeOptions) String() string { func (*RPC_ExposeOptions) ProtoMessage() {} func (x *RPC_ExposeOptions) ProtoReflect() protoreflect.Message { - mi := &file_encore_parser_meta_v1_meta_proto_msgTypes[29] + mi := &file_encore_parser_meta_v1_meta_proto_msgTypes[30] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2985,7 +3056,7 @@ type RPC_StaticAssets struct { func (x *RPC_StaticAssets) Reset() { *x = RPC_StaticAssets{} if protoimpl.UnsafeEnabled { - mi := &file_encore_parser_meta_v1_meta_proto_msgTypes[30] + mi := &file_encore_parser_meta_v1_meta_proto_msgTypes[31] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2998,7 +3069,7 @@ func (x *RPC_StaticAssets) String() string { func (*RPC_StaticAssets) ProtoMessage() {} func (x *RPC_StaticAssets) ProtoReflect() protoreflect.Message { - mi := &file_encore_parser_meta_v1_meta_proto_msgTypes[30] + mi := &file_encore_parser_meta_v1_meta_proto_msgTypes[31] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3041,7 +3112,7 @@ type Gateway_Explicit struct { func (x *Gateway_Explicit) Reset() { *x = Gateway_Explicit{} if protoimpl.UnsafeEnabled { - mi := &file_encore_parser_meta_v1_meta_proto_msgTypes[31] + mi := &file_encore_parser_meta_v1_meta_proto_msgTypes[32] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -3054,7 +3125,7 @@ func (x *Gateway_Explicit) String() string { func (*Gateway_Explicit) ProtoMessage() {} func (x *Gateway_Explicit) ProtoReflect() protoreflect.Message { - mi := &file_encore_parser_meta_v1_meta_proto_msgTypes[31] + mi := &file_encore_parser_meta_v1_meta_proto_msgTypes[32] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3095,7 +3166,7 @@ type PubSubTopic_Publisher struct { func (x *PubSubTopic_Publisher) Reset() { *x = PubSubTopic_Publisher{} if protoimpl.UnsafeEnabled { - mi := &file_encore_parser_meta_v1_meta_proto_msgTypes[32] + mi := &file_encore_parser_meta_v1_meta_proto_msgTypes[33] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -3108,7 +3179,7 @@ func (x *PubSubTopic_Publisher) String() string { func (*PubSubTopic_Publisher) ProtoMessage() {} func (x *PubSubTopic_Publisher) ProtoReflect() protoreflect.Message { - mi := &file_encore_parser_meta_v1_meta_proto_msgTypes[32] + mi := &file_encore_parser_meta_v1_meta_proto_msgTypes[33] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3121,7 +3192,7 @@ func (x *PubSubTopic_Publisher) ProtoReflect() protoreflect.Message { // Deprecated: Use PubSubTopic_Publisher.ProtoReflect.Descriptor instead. func (*PubSubTopic_Publisher) Descriptor() ([]byte, []int) { - return file_encore_parser_meta_v1_meta_proto_rawDescGZIP(), []int{25, 0} + return file_encore_parser_meta_v1_meta_proto_rawDescGZIP(), []int{26, 0} } func (x *PubSubTopic_Publisher) GetServiceName() string { @@ -3149,7 +3220,7 @@ type PubSubTopic_Subscription struct { func (x *PubSubTopic_Subscription) Reset() { *x = PubSubTopic_Subscription{} if protoimpl.UnsafeEnabled { - mi := &file_encore_parser_meta_v1_meta_proto_msgTypes[33] + mi := &file_encore_parser_meta_v1_meta_proto_msgTypes[34] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -3162,7 +3233,7 @@ func (x *PubSubTopic_Subscription) String() string { func (*PubSubTopic_Subscription) ProtoMessage() {} func (x *PubSubTopic_Subscription) ProtoReflect() protoreflect.Message { - mi := &file_encore_parser_meta_v1_meta_proto_msgTypes[33] + mi := &file_encore_parser_meta_v1_meta_proto_msgTypes[34] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3175,7 +3246,7 @@ func (x *PubSubTopic_Subscription) ProtoReflect() protoreflect.Message { // Deprecated: Use PubSubTopic_Subscription.ProtoReflect.Descriptor instead. func (*PubSubTopic_Subscription) Descriptor() ([]byte, []int) { - return file_encore_parser_meta_v1_meta_proto_rawDescGZIP(), []int{25, 1} + return file_encore_parser_meta_v1_meta_proto_rawDescGZIP(), []int{26, 1} } func (x *PubSubTopic_Subscription) GetName() string { @@ -3233,7 +3304,7 @@ type PubSubTopic_RetryPolicy struct { func (x *PubSubTopic_RetryPolicy) Reset() { *x = PubSubTopic_RetryPolicy{} if protoimpl.UnsafeEnabled { - mi := &file_encore_parser_meta_v1_meta_proto_msgTypes[34] + mi := &file_encore_parser_meta_v1_meta_proto_msgTypes[35] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -3246,7 +3317,7 @@ func (x *PubSubTopic_RetryPolicy) String() string { func (*PubSubTopic_RetryPolicy) ProtoMessage() {} func (x *PubSubTopic_RetryPolicy) ProtoReflect() protoreflect.Message { - mi := &file_encore_parser_meta_v1_meta_proto_msgTypes[34] + mi := &file_encore_parser_meta_v1_meta_proto_msgTypes[35] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3259,7 +3330,7 @@ func (x *PubSubTopic_RetryPolicy) ProtoReflect() protoreflect.Message { // Deprecated: Use PubSubTopic_RetryPolicy.ProtoReflect.Descriptor instead. func (*PubSubTopic_RetryPolicy) Descriptor() ([]byte, []int) { - return file_encore_parser_meta_v1_meta_proto_rawDescGZIP(), []int{25, 2} + return file_encore_parser_meta_v1_meta_proto_rawDescGZIP(), []int{26, 2} } func (x *PubSubTopic_RetryPolicy) GetMinBackoff() int64 { @@ -3298,7 +3369,7 @@ type CacheCluster_Keyspace struct { func (x *CacheCluster_Keyspace) Reset() { *x = CacheCluster_Keyspace{} if protoimpl.UnsafeEnabled { - mi := &file_encore_parser_meta_v1_meta_proto_msgTypes[35] + mi := &file_encore_parser_meta_v1_meta_proto_msgTypes[36] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -3311,7 +3382,7 @@ func (x *CacheCluster_Keyspace) String() string { func (*CacheCluster_Keyspace) ProtoMessage() {} func (x *CacheCluster_Keyspace) ProtoReflect() protoreflect.Message { - mi := &file_encore_parser_meta_v1_meta_proto_msgTypes[35] + mi := &file_encore_parser_meta_v1_meta_proto_msgTypes[36] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3324,7 +3395,7 @@ func (x *CacheCluster_Keyspace) ProtoReflect() protoreflect.Message { // Deprecated: Use CacheCluster_Keyspace.ProtoReflect.Descriptor instead. func (*CacheCluster_Keyspace) Descriptor() ([]byte, []int) { - return file_encore_parser_meta_v1_meta_proto_rawDescGZIP(), []int{26, 0} + return file_encore_parser_meta_v1_meta_proto_rawDescGZIP(), []int{27, 0} } func (x *CacheCluster_Keyspace) GetKeyType() *v1.Type { @@ -3375,7 +3446,7 @@ type Metric_Label struct { func (x *Metric_Label) Reset() { *x = Metric_Label{} if protoimpl.UnsafeEnabled { - mi := &file_encore_parser_meta_v1_meta_proto_msgTypes[36] + mi := &file_encore_parser_meta_v1_meta_proto_msgTypes[37] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -3388,7 +3459,7 @@ func (x *Metric_Label) String() string { func (*Metric_Label) ProtoMessage() {} func (x *Metric_Label) ProtoReflect() protoreflect.Message { - mi := &file_encore_parser_meta_v1_meta_proto_msgTypes[36] + mi := &file_encore_parser_meta_v1_meta_proto_msgTypes[37] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3401,7 +3472,7 @@ func (x *Metric_Label) ProtoReflect() protoreflect.Message { // Deprecated: Use Metric_Label.ProtoReflect.Descriptor instead. func (*Metric_Label) Descriptor() ([]byte, []int) { - return file_encore_parser_meta_v1_meta_proto_rawDescGZIP(), []int{27, 0} + return file_encore_parser_meta_v1_meta_proto_rawDescGZIP(), []int{28, 0} } func (x *Metric_Label) GetKey() string { @@ -3434,7 +3505,7 @@ var file_encore_parser_meta_v1_meta_proto_rawDesc = []byte{ 0x72, 0x2e, 0x6d, 0x65, 0x74, 0x61, 0x2e, 0x76, 0x31, 0x1a, 0x24, 0x65, 0x6e, 0x63, 0x6f, 0x72, 0x65, 0x2f, 0x70, 0x61, 0x72, 0x73, 0x65, 0x72, 0x2f, 0x73, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x2f, 0x76, 0x31, 0x2f, 0x73, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, - 0xa3, 0x07, 0x0a, 0x04, 0x44, 0x61, 0x74, 0x61, 0x12, 0x1f, 0x0a, 0x0b, 0x6d, 0x6f, 0x64, 0x75, + 0xdc, 0x07, 0x0a, 0x04, 0x44, 0x61, 0x74, 0x61, 0x12, 0x1f, 0x0a, 0x0b, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x5f, 0x70, 0x61, 0x74, 0x68, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x50, 0x61, 0x74, 0x68, 0x12, 0x21, 0x0a, 0x0c, 0x61, 0x70, 0x70, 0x5f, 0x72, 0x65, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, @@ -3491,409 +3562,418 @@ var file_encore_parser_meta_v1_meta_proto_rawDesc = []byte{ 0x61, 0x6e, 0x67, 0x75, 0x61, 0x67, 0x65, 0x18, 0x10, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x1b, 0x2e, 0x65, 0x6e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x70, 0x61, 0x72, 0x73, 0x65, 0x72, 0x2e, 0x6d, 0x65, 0x74, 0x61, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x61, 0x6e, 0x67, 0x52, 0x08, 0x6c, 0x61, 0x6e, 0x67, - 0x75, 0x61, 0x67, 0x65, 0x42, 0x0f, 0x0a, 0x0d, 0x5f, 0x61, 0x75, 0x74, 0x68, 0x5f, 0x68, 0x61, - 0x6e, 0x64, 0x6c, 0x65, 0x72, 0x22, 0x35, 0x0a, 0x0d, 0x51, 0x75, 0x61, 0x6c, 0x69, 0x66, 0x69, - 0x65, 0x64, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x70, 0x6b, 0x67, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x03, 0x70, 0x6b, 0x67, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, - 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x22, 0x8d, 0x02, 0x0a, - 0x07, 0x50, 0x61, 0x63, 0x6b, 0x61, 0x67, 0x65, 0x12, 0x19, 0x0a, 0x08, 0x72, 0x65, 0x6c, 0x5f, - 0x70, 0x61, 0x74, 0x68, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x72, 0x65, 0x6c, 0x50, - 0x61, 0x74, 0x68, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x64, 0x6f, 0x63, 0x18, 0x03, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x64, 0x6f, 0x63, 0x12, 0x21, 0x0a, 0x0c, 0x73, 0x65, 0x72, - 0x76, 0x69, 0x63, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x0b, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x18, 0x0a, 0x07, - 0x73, 0x65, 0x63, 0x72, 0x65, 0x74, 0x73, 0x18, 0x05, 0x20, 0x03, 0x28, 0x09, 0x52, 0x07, 0x73, - 0x65, 0x63, 0x72, 0x65, 0x74, 0x73, 0x12, 0x41, 0x0a, 0x09, 0x72, 0x70, 0x63, 0x5f, 0x63, 0x61, - 0x6c, 0x6c, 0x73, 0x18, 0x06, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x24, 0x2e, 0x65, 0x6e, 0x63, 0x6f, - 0x72, 0x65, 0x2e, 0x70, 0x61, 0x72, 0x73, 0x65, 0x72, 0x2e, 0x6d, 0x65, 0x74, 0x61, 0x2e, 0x76, - 0x31, 0x2e, 0x51, 0x75, 0x61, 0x6c, 0x69, 0x66, 0x69, 0x65, 0x64, 0x4e, 0x61, 0x6d, 0x65, 0x52, - 0x08, 0x72, 0x70, 0x63, 0x43, 0x61, 0x6c, 0x6c, 0x73, 0x12, 0x41, 0x0a, 0x0b, 0x74, 0x72, 0x61, - 0x63, 0x65, 0x5f, 0x6e, 0x6f, 0x64, 0x65, 0x73, 0x18, 0x07, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x20, - 0x2e, 0x65, 0x6e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x70, 0x61, 0x72, 0x73, 0x65, 0x72, 0x2e, 0x6d, - 0x65, 0x74, 0x61, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x72, 0x61, 0x63, 0x65, 0x4e, 0x6f, 0x64, 0x65, - 0x52, 0x0a, 0x74, 0x72, 0x61, 0x63, 0x65, 0x4e, 0x6f, 0x64, 0x65, 0x73, 0x22, 0xe9, 0x01, 0x0a, - 0x07, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x19, 0x0a, 0x08, - 0x72, 0x65, 0x6c, 0x5f, 0x70, 0x61, 0x74, 0x68, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, - 0x72, 0x65, 0x6c, 0x50, 0x61, 0x74, 0x68, 0x12, 0x2e, 0x0a, 0x04, 0x72, 0x70, 0x63, 0x73, 0x18, - 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x65, 0x6e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x70, - 0x61, 0x72, 0x73, 0x65, 0x72, 0x2e, 0x6d, 0x65, 0x74, 0x61, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x50, - 0x43, 0x52, 0x04, 0x72, 0x70, 0x63, 0x73, 0x12, 0x42, 0x0a, 0x0a, 0x6d, 0x69, 0x67, 0x72, 0x61, - 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x22, 0x2e, 0x65, 0x6e, - 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x70, 0x61, 0x72, 0x73, 0x65, 0x72, 0x2e, 0x6d, 0x65, 0x74, 0x61, - 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x42, 0x4d, 0x69, 0x67, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, - 0x0a, 0x6d, 0x69, 0x67, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x1c, 0x0a, 0x09, 0x64, - 0x61, 0x74, 0x61, 0x62, 0x61, 0x73, 0x65, 0x73, 0x18, 0x05, 0x20, 0x03, 0x28, 0x09, 0x52, 0x09, - 0x64, 0x61, 0x74, 0x61, 0x62, 0x61, 0x73, 0x65, 0x73, 0x12, 0x1d, 0x0a, 0x0a, 0x68, 0x61, 0x73, - 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x18, 0x06, 0x20, 0x01, 0x28, 0x08, 0x52, 0x09, 0x68, - 0x61, 0x73, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x22, 0x81, 0x01, 0x0a, 0x08, 0x53, 0x65, 0x6c, - 0x65, 0x63, 0x74, 0x6f, 0x72, 0x12, 0x38, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x0e, 0x32, 0x24, 0x2e, 0x65, 0x6e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x70, 0x61, 0x72, - 0x73, 0x65, 0x72, 0x2e, 0x6d, 0x65, 0x74, 0x61, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x6c, 0x65, - 0x63, 0x74, 0x6f, 0x72, 0x2e, 0x54, 0x79, 0x70, 0x65, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, - 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, - 0x76, 0x61, 0x6c, 0x75, 0x65, 0x22, 0x25, 0x0a, 0x04, 0x54, 0x79, 0x70, 0x65, 0x12, 0x0b, 0x0a, - 0x07, 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x10, 0x00, 0x12, 0x07, 0x0a, 0x03, 0x41, 0x4c, - 0x4c, 0x10, 0x01, 0x12, 0x07, 0x0a, 0x03, 0x54, 0x41, 0x47, 0x10, 0x02, 0x22, 0x85, 0x0b, 0x0a, - 0x03, 0x52, 0x50, 0x43, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x15, 0x0a, 0x03, 0x64, 0x6f, 0x63, 0x18, - 0x02, 0x20, 0x01, 0x28, 0x09, 0x48, 0x00, 0x52, 0x03, 0x64, 0x6f, 0x63, 0x88, 0x01, 0x01, 0x12, - 0x21, 0x0a, 0x0c, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, - 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x4e, 0x61, - 0x6d, 0x65, 0x12, 0x46, 0x0a, 0x0b, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x5f, 0x74, 0x79, 0x70, - 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x25, 0x2e, 0x65, 0x6e, 0x63, 0x6f, 0x72, 0x65, + 0x75, 0x61, 0x67, 0x65, 0x12, 0x37, 0x0a, 0x07, 0x62, 0x75, 0x63, 0x6b, 0x65, 0x74, 0x73, 0x18, + 0x11, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x65, 0x6e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x70, + 0x61, 0x72, 0x73, 0x65, 0x72, 0x2e, 0x6d, 0x65, 0x74, 0x61, 0x2e, 0x76, 0x31, 0x2e, 0x42, 0x75, + 0x63, 0x6b, 0x65, 0x74, 0x52, 0x07, 0x62, 0x75, 0x63, 0x6b, 0x65, 0x74, 0x73, 0x42, 0x0f, 0x0a, + 0x0d, 0x5f, 0x61, 0x75, 0x74, 0x68, 0x5f, 0x68, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x72, 0x22, 0x35, + 0x0a, 0x0d, 0x51, 0x75, 0x61, 0x6c, 0x69, 0x66, 0x69, 0x65, 0x64, 0x4e, 0x61, 0x6d, 0x65, 0x12, + 0x10, 0x0a, 0x03, 0x70, 0x6b, 0x67, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x70, 0x6b, + 0x67, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x22, 0x8d, 0x02, 0x0a, 0x07, 0x50, 0x61, 0x63, 0x6b, 0x61, 0x67, + 0x65, 0x12, 0x19, 0x0a, 0x08, 0x72, 0x65, 0x6c, 0x5f, 0x70, 0x61, 0x74, 0x68, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x07, 0x72, 0x65, 0x6c, 0x50, 0x61, 0x74, 0x68, 0x12, 0x12, 0x0a, 0x04, + 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, + 0x12, 0x10, 0x0a, 0x03, 0x64, 0x6f, 0x63, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x64, + 0x6f, 0x63, 0x12, 0x21, 0x0a, 0x0c, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x5f, 0x6e, 0x61, + 0x6d, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, + 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x73, 0x65, 0x63, 0x72, 0x65, 0x74, 0x73, + 0x18, 0x05, 0x20, 0x03, 0x28, 0x09, 0x52, 0x07, 0x73, 0x65, 0x63, 0x72, 0x65, 0x74, 0x73, 0x12, + 0x41, 0x0a, 0x09, 0x72, 0x70, 0x63, 0x5f, 0x63, 0x61, 0x6c, 0x6c, 0x73, 0x18, 0x06, 0x20, 0x03, + 0x28, 0x0b, 0x32, 0x24, 0x2e, 0x65, 0x6e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x70, 0x61, 0x72, 0x73, + 0x65, 0x72, 0x2e, 0x6d, 0x65, 0x74, 0x61, 0x2e, 0x76, 0x31, 0x2e, 0x51, 0x75, 0x61, 0x6c, 0x69, + 0x66, 0x69, 0x65, 0x64, 0x4e, 0x61, 0x6d, 0x65, 0x52, 0x08, 0x72, 0x70, 0x63, 0x43, 0x61, 0x6c, + 0x6c, 0x73, 0x12, 0x41, 0x0a, 0x0b, 0x74, 0x72, 0x61, 0x63, 0x65, 0x5f, 0x6e, 0x6f, 0x64, 0x65, + 0x73, 0x18, 0x07, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x20, 0x2e, 0x65, 0x6e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x70, 0x61, 0x72, 0x73, 0x65, 0x72, 0x2e, 0x6d, 0x65, 0x74, 0x61, 0x2e, 0x76, 0x31, 0x2e, - 0x52, 0x50, 0x43, 0x2e, 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, 0x54, 0x79, 0x70, 0x65, 0x52, 0x0a, - 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x54, 0x79, 0x70, 0x65, 0x12, 0x49, 0x0a, 0x0e, 0x72, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x5f, 0x73, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x18, 0x05, 0x20, 0x01, - 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x65, 0x6e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x70, 0x61, 0x72, 0x73, - 0x65, 0x72, 0x2e, 0x73, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x79, 0x70, - 0x65, 0x48, 0x01, 0x52, 0x0d, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x53, 0x63, 0x68, 0x65, - 0x6d, 0x61, 0x88, 0x01, 0x01, 0x12, 0x4b, 0x0a, 0x0f, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, - 0x65, 0x5f, 0x73, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1d, - 0x2e, 0x65, 0x6e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x70, 0x61, 0x72, 0x73, 0x65, 0x72, 0x2e, 0x73, - 0x63, 0x68, 0x65, 0x6d, 0x61, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x79, 0x70, 0x65, 0x48, 0x02, 0x52, - 0x0e, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x88, - 0x01, 0x01, 0x12, 0x39, 0x0a, 0x05, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x18, 0x07, 0x20, 0x01, 0x28, - 0x0e, 0x32, 0x23, 0x2e, 0x65, 0x6e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x70, 0x61, 0x72, 0x73, 0x65, - 0x72, 0x2e, 0x6d, 0x65, 0x74, 0x61, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x50, 0x43, 0x2e, 0x50, 0x72, - 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x52, 0x05, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x2e, 0x0a, - 0x03, 0x6c, 0x6f, 0x63, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x65, 0x6e, 0x63, - 0x6f, 0x72, 0x65, 0x2e, 0x70, 0x61, 0x72, 0x73, 0x65, 0x72, 0x2e, 0x73, 0x63, 0x68, 0x65, 0x6d, - 0x61, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x6f, 0x63, 0x52, 0x03, 0x6c, 0x6f, 0x63, 0x12, 0x2f, 0x0a, - 0x04, 0x70, 0x61, 0x74, 0x68, 0x18, 0x09, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x65, 0x6e, - 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x70, 0x61, 0x72, 0x73, 0x65, 0x72, 0x2e, 0x6d, 0x65, 0x74, 0x61, - 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x61, 0x74, 0x68, 0x52, 0x04, 0x70, 0x61, 0x74, 0x68, 0x12, 0x21, - 0x0a, 0x0c, 0x68, 0x74, 0x74, 0x70, 0x5f, 0x6d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x73, 0x18, 0x0a, - 0x20, 0x03, 0x28, 0x09, 0x52, 0x0b, 0x68, 0x74, 0x74, 0x70, 0x4d, 0x65, 0x74, 0x68, 0x6f, 0x64, - 0x73, 0x12, 0x33, 0x0a, 0x04, 0x74, 0x61, 0x67, 0x73, 0x18, 0x0b, 0x20, 0x03, 0x28, 0x0b, 0x32, - 0x1f, 0x2e, 0x65, 0x6e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x70, 0x61, 0x72, 0x73, 0x65, 0x72, 0x2e, - 0x6d, 0x65, 0x74, 0x61, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, - 0x52, 0x04, 0x74, 0x61, 0x67, 0x73, 0x12, 0x1c, 0x0a, 0x09, 0x73, 0x65, 0x6e, 0x73, 0x69, 0x74, - 0x69, 0x76, 0x65, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x08, 0x52, 0x09, 0x73, 0x65, 0x6e, 0x73, 0x69, - 0x74, 0x69, 0x76, 0x65, 0x12, 0x33, 0x0a, 0x15, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x5f, 0x75, 0x6e, - 0x61, 0x75, 0x74, 0x68, 0x65, 0x6e, 0x74, 0x69, 0x63, 0x61, 0x74, 0x65, 0x64, 0x18, 0x0d, 0x20, - 0x01, 0x28, 0x08, 0x52, 0x14, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x55, 0x6e, 0x61, 0x75, 0x74, 0x68, - 0x65, 0x6e, 0x74, 0x69, 0x63, 0x61, 0x74, 0x65, 0x64, 0x12, 0x3e, 0x0a, 0x06, 0x65, 0x78, 0x70, - 0x6f, 0x73, 0x65, 0x18, 0x0e, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x26, 0x2e, 0x65, 0x6e, 0x63, 0x6f, - 0x72, 0x65, 0x2e, 0x70, 0x61, 0x72, 0x73, 0x65, 0x72, 0x2e, 0x6d, 0x65, 0x74, 0x61, 0x2e, 0x76, - 0x31, 0x2e, 0x52, 0x50, 0x43, 0x2e, 0x45, 0x78, 0x70, 0x6f, 0x73, 0x65, 0x45, 0x6e, 0x74, 0x72, - 0x79, 0x52, 0x06, 0x65, 0x78, 0x70, 0x6f, 0x73, 0x65, 0x12, 0x22, 0x0a, 0x0a, 0x62, 0x6f, 0x64, - 0x79, 0x5f, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x18, 0x0f, 0x20, 0x01, 0x28, 0x04, 0x48, 0x03, 0x52, - 0x09, 0x62, 0x6f, 0x64, 0x79, 0x4c, 0x69, 0x6d, 0x69, 0x74, 0x88, 0x01, 0x01, 0x12, 0x2b, 0x0a, - 0x11, 0x73, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x69, 0x6e, 0x67, 0x5f, 0x72, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x18, 0x10, 0x20, 0x01, 0x28, 0x08, 0x52, 0x10, 0x73, 0x74, 0x72, 0x65, 0x61, 0x6d, - 0x69, 0x6e, 0x67, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x2d, 0x0a, 0x12, 0x73, 0x74, - 0x72, 0x65, 0x61, 0x6d, 0x69, 0x6e, 0x67, 0x5f, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, - 0x18, 0x11, 0x20, 0x01, 0x28, 0x08, 0x52, 0x11, 0x73, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x69, 0x6e, - 0x67, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x4d, 0x0a, 0x10, 0x68, 0x61, 0x6e, - 0x64, 0x73, 0x68, 0x61, 0x6b, 0x65, 0x5f, 0x73, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x18, 0x12, 0x20, - 0x01, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x65, 0x6e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x70, 0x61, 0x72, - 0x73, 0x65, 0x72, 0x2e, 0x73, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x79, - 0x70, 0x65, 0x48, 0x04, 0x52, 0x0f, 0x68, 0x61, 0x6e, 0x64, 0x73, 0x68, 0x61, 0x6b, 0x65, 0x53, - 0x63, 0x68, 0x65, 0x6d, 0x61, 0x88, 0x01, 0x01, 0x12, 0x51, 0x0a, 0x0d, 0x73, 0x74, 0x61, 0x74, - 0x69, 0x63, 0x5f, 0x61, 0x73, 0x73, 0x65, 0x74, 0x73, 0x18, 0x13, 0x20, 0x01, 0x28, 0x0b, 0x32, - 0x27, 0x2e, 0x65, 0x6e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x70, 0x61, 0x72, 0x73, 0x65, 0x72, 0x2e, - 0x6d, 0x65, 0x74, 0x61, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x50, 0x43, 0x2e, 0x53, 0x74, 0x61, 0x74, - 0x69, 0x63, 0x41, 0x73, 0x73, 0x65, 0x74, 0x73, 0x48, 0x05, 0x52, 0x0c, 0x73, 0x74, 0x61, 0x74, - 0x69, 0x63, 0x41, 0x73, 0x73, 0x65, 0x74, 0x73, 0x88, 0x01, 0x01, 0x1a, 0x63, 0x0a, 0x0b, 0x45, - 0x78, 0x70, 0x6f, 0x73, 0x65, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, - 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x3e, 0x0a, 0x05, - 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x28, 0x2e, 0x65, 0x6e, - 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x70, 0x61, 0x72, 0x73, 0x65, 0x72, 0x2e, 0x6d, 0x65, 0x74, 0x61, - 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x50, 0x43, 0x2e, 0x45, 0x78, 0x70, 0x6f, 0x73, 0x65, 0x4f, 0x70, - 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, - 0x1a, 0x0f, 0x0a, 0x0d, 0x45, 0x78, 0x70, 0x6f, 0x73, 0x65, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, - 0x73, 0x1a, 0x79, 0x0a, 0x0c, 0x53, 0x74, 0x61, 0x74, 0x69, 0x63, 0x41, 0x73, 0x73, 0x65, 0x74, - 0x73, 0x12, 0x20, 0x0a, 0x0c, 0x64, 0x69, 0x72, 0x5f, 0x72, 0x65, 0x6c, 0x5f, 0x70, 0x61, 0x74, - 0x68, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x64, 0x69, 0x72, 0x52, 0x65, 0x6c, 0x50, - 0x61, 0x74, 0x68, 0x12, 0x30, 0x0a, 0x12, 0x6e, 0x6f, 0x74, 0x5f, 0x66, 0x6f, 0x75, 0x6e, 0x64, - 0x5f, 0x72, 0x65, 0x6c, 0x5f, 0x70, 0x61, 0x74, 0x68, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x48, - 0x00, 0x52, 0x0f, 0x6e, 0x6f, 0x74, 0x46, 0x6f, 0x75, 0x6e, 0x64, 0x52, 0x65, 0x6c, 0x50, 0x61, - 0x74, 0x68, 0x88, 0x01, 0x01, 0x42, 0x15, 0x0a, 0x13, 0x5f, 0x6e, 0x6f, 0x74, 0x5f, 0x66, 0x6f, - 0x75, 0x6e, 0x64, 0x5f, 0x72, 0x65, 0x6c, 0x5f, 0x70, 0x61, 0x74, 0x68, 0x22, 0x2f, 0x0a, 0x0a, - 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, 0x54, 0x79, 0x70, 0x65, 0x12, 0x0b, 0x0a, 0x07, 0x50, 0x52, - 0x49, 0x56, 0x41, 0x54, 0x45, 0x10, 0x00, 0x12, 0x0a, 0x0a, 0x06, 0x50, 0x55, 0x42, 0x4c, 0x49, - 0x43, 0x10, 0x01, 0x12, 0x08, 0x0a, 0x04, 0x41, 0x55, 0x54, 0x48, 0x10, 0x02, 0x22, 0x20, 0x0a, - 0x08, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x12, 0x0b, 0x0a, 0x07, 0x52, 0x45, 0x47, - 0x55, 0x4c, 0x41, 0x52, 0x10, 0x00, 0x12, 0x07, 0x0a, 0x03, 0x52, 0x41, 0x57, 0x10, 0x01, 0x42, - 0x06, 0x0a, 0x04, 0x5f, 0x64, 0x6f, 0x63, 0x42, 0x11, 0x0a, 0x0f, 0x5f, 0x72, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x5f, 0x73, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x42, 0x12, 0x0a, 0x10, 0x5f, 0x72, - 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x5f, 0x73, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x42, 0x0d, - 0x0a, 0x0b, 0x5f, 0x62, 0x6f, 0x64, 0x79, 0x5f, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x42, 0x13, 0x0a, - 0x11, 0x5f, 0x68, 0x61, 0x6e, 0x64, 0x73, 0x68, 0x61, 0x6b, 0x65, 0x5f, 0x73, 0x63, 0x68, 0x65, - 0x6d, 0x61, 0x42, 0x10, 0x0a, 0x0e, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x69, 0x63, 0x5f, 0x61, 0x73, - 0x73, 0x65, 0x74, 0x73, 0x22, 0xd2, 0x02, 0x0a, 0x0b, 0x41, 0x75, 0x74, 0x68, 0x48, 0x61, 0x6e, - 0x64, 0x6c, 0x65, 0x72, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x64, 0x6f, 0x63, 0x18, - 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x64, 0x6f, 0x63, 0x12, 0x19, 0x0a, 0x08, 0x70, 0x6b, - 0x67, 0x5f, 0x70, 0x61, 0x74, 0x68, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x70, 0x6b, - 0x67, 0x50, 0x61, 0x74, 0x68, 0x12, 0x19, 0x0a, 0x08, 0x70, 0x6b, 0x67, 0x5f, 0x6e, 0x61, 0x6d, - 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x70, 0x6b, 0x67, 0x4e, 0x61, 0x6d, 0x65, - 0x12, 0x2e, 0x0a, 0x03, 0x6c, 0x6f, 0x63, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e, - 0x65, 0x6e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x70, 0x61, 0x72, 0x73, 0x65, 0x72, 0x2e, 0x73, 0x63, - 0x68, 0x65, 0x6d, 0x61, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x6f, 0x63, 0x52, 0x03, 0x6c, 0x6f, 0x63, - 0x12, 0x3f, 0x0a, 0x09, 0x61, 0x75, 0x74, 0x68, 0x5f, 0x64, 0x61, 0x74, 0x61, 0x18, 0x06, 0x20, - 0x01, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x65, 0x6e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x70, 0x61, 0x72, - 0x73, 0x65, 0x72, 0x2e, 0x73, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x79, - 0x70, 0x65, 0x48, 0x00, 0x52, 0x08, 0x61, 0x75, 0x74, 0x68, 0x44, 0x61, 0x74, 0x61, 0x88, 0x01, - 0x01, 0x12, 0x3a, 0x0a, 0x06, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x18, 0x07, 0x20, 0x01, 0x28, + 0x54, 0x72, 0x61, 0x63, 0x65, 0x4e, 0x6f, 0x64, 0x65, 0x52, 0x0a, 0x74, 0x72, 0x61, 0x63, 0x65, + 0x4e, 0x6f, 0x64, 0x65, 0x73, 0x22, 0x83, 0x02, 0x0a, 0x07, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, + 0x65, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x19, 0x0a, 0x08, 0x72, 0x65, 0x6c, 0x5f, 0x70, 0x61, 0x74, + 0x68, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x72, 0x65, 0x6c, 0x50, 0x61, 0x74, 0x68, + 0x12, 0x2e, 0x0a, 0x04, 0x72, 0x70, 0x63, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1a, + 0x2e, 0x65, 0x6e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x70, 0x61, 0x72, 0x73, 0x65, 0x72, 0x2e, 0x6d, + 0x65, 0x74, 0x61, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x50, 0x43, 0x52, 0x04, 0x72, 0x70, 0x63, 0x73, + 0x12, 0x42, 0x0a, 0x0a, 0x6d, 0x69, 0x67, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x04, + 0x20, 0x03, 0x28, 0x0b, 0x32, 0x22, 0x2e, 0x65, 0x6e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x70, 0x61, + 0x72, 0x73, 0x65, 0x72, 0x2e, 0x6d, 0x65, 0x74, 0x61, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x42, 0x4d, + 0x69, 0x67, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x0a, 0x6d, 0x69, 0x67, 0x72, 0x61, 0x74, + 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x1c, 0x0a, 0x09, 0x64, 0x61, 0x74, 0x61, 0x62, 0x61, 0x73, 0x65, + 0x73, 0x18, 0x05, 0x20, 0x03, 0x28, 0x09, 0x52, 0x09, 0x64, 0x61, 0x74, 0x61, 0x62, 0x61, 0x73, + 0x65, 0x73, 0x12, 0x1d, 0x0a, 0x0a, 0x68, 0x61, 0x73, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, + 0x18, 0x06, 0x20, 0x01, 0x28, 0x08, 0x52, 0x09, 0x68, 0x61, 0x73, 0x43, 0x6f, 0x6e, 0x66, 0x69, + 0x67, 0x12, 0x18, 0x0a, 0x07, 0x62, 0x75, 0x63, 0x6b, 0x65, 0x74, 0x73, 0x18, 0x07, 0x20, 0x03, + 0x28, 0x09, 0x52, 0x07, 0x62, 0x75, 0x63, 0x6b, 0x65, 0x74, 0x73, 0x22, 0x81, 0x01, 0x0a, 0x08, + 0x53, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x12, 0x38, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x24, 0x2e, 0x65, 0x6e, 0x63, 0x6f, 0x72, 0x65, 0x2e, + 0x70, 0x61, 0x72, 0x73, 0x65, 0x72, 0x2e, 0x6d, 0x65, 0x74, 0x61, 0x2e, 0x76, 0x31, 0x2e, 0x53, + 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x2e, 0x54, 0x79, 0x70, 0x65, 0x52, 0x04, 0x74, 0x79, + 0x70, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x22, 0x25, 0x0a, 0x04, 0x54, 0x79, 0x70, 0x65, + 0x12, 0x0b, 0x0a, 0x07, 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x10, 0x00, 0x12, 0x07, 0x0a, + 0x03, 0x41, 0x4c, 0x4c, 0x10, 0x01, 0x12, 0x07, 0x0a, 0x03, 0x54, 0x41, 0x47, 0x10, 0x02, 0x22, + 0x85, 0x0b, 0x0a, 0x03, 0x52, 0x50, 0x43, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x15, 0x0a, 0x03, 0x64, + 0x6f, 0x63, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x48, 0x00, 0x52, 0x03, 0x64, 0x6f, 0x63, 0x88, + 0x01, 0x01, 0x12, 0x21, 0x0a, 0x0c, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x5f, 0x6e, 0x61, + 0x6d, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, + 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x46, 0x0a, 0x0b, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x5f, + 0x74, 0x79, 0x70, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x25, 0x2e, 0x65, 0x6e, 0x63, + 0x6f, 0x72, 0x65, 0x2e, 0x70, 0x61, 0x72, 0x73, 0x65, 0x72, 0x2e, 0x6d, 0x65, 0x74, 0x61, 0x2e, + 0x76, 0x31, 0x2e, 0x52, 0x50, 0x43, 0x2e, 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, 0x54, 0x79, 0x70, + 0x65, 0x52, 0x0a, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x54, 0x79, 0x70, 0x65, 0x12, 0x49, 0x0a, + 0x0e, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x5f, 0x73, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x18, + 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x65, 0x6e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x70, + 0x61, 0x72, 0x73, 0x65, 0x72, 0x2e, 0x73, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x2e, 0x76, 0x31, 0x2e, + 0x54, 0x79, 0x70, 0x65, 0x48, 0x01, 0x52, 0x0d, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x53, + 0x63, 0x68, 0x65, 0x6d, 0x61, 0x88, 0x01, 0x01, 0x12, 0x4b, 0x0a, 0x0f, 0x72, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x5f, 0x73, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x65, 0x6e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x70, 0x61, 0x72, 0x73, 0x65, 0x72, 0x2e, 0x73, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x79, 0x70, 0x65, - 0x48, 0x01, 0x52, 0x06, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x88, 0x01, 0x01, 0x12, 0x21, 0x0a, - 0x0c, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x08, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x0b, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x4e, 0x61, 0x6d, 0x65, - 0x42, 0x0c, 0x0a, 0x0a, 0x5f, 0x61, 0x75, 0x74, 0x68, 0x5f, 0x64, 0x61, 0x74, 0x61, 0x42, 0x09, - 0x0a, 0x07, 0x5f, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x22, 0x92, 0x02, 0x0a, 0x0a, 0x4d, 0x69, - 0x64, 0x64, 0x6c, 0x65, 0x77, 0x61, 0x72, 0x65, 0x12, 0x38, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x24, 0x2e, 0x65, 0x6e, 0x63, 0x6f, 0x72, 0x65, 0x2e, - 0x70, 0x61, 0x72, 0x73, 0x65, 0x72, 0x2e, 0x6d, 0x65, 0x74, 0x61, 0x2e, 0x76, 0x31, 0x2e, 0x51, - 0x75, 0x61, 0x6c, 0x69, 0x66, 0x69, 0x65, 0x64, 0x4e, 0x61, 0x6d, 0x65, 0x52, 0x04, 0x6e, 0x61, - 0x6d, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x64, 0x6f, 0x63, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x03, 0x64, 0x6f, 0x63, 0x12, 0x2e, 0x0a, 0x03, 0x6c, 0x6f, 0x63, 0x18, 0x03, 0x20, 0x01, 0x28, - 0x0b, 0x32, 0x1c, 0x2e, 0x65, 0x6e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x70, 0x61, 0x72, 0x73, 0x65, - 0x72, 0x2e, 0x73, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x6f, 0x63, 0x52, - 0x03, 0x6c, 0x6f, 0x63, 0x12, 0x16, 0x0a, 0x06, 0x67, 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x18, 0x04, - 0x20, 0x01, 0x28, 0x08, 0x52, 0x06, 0x67, 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x12, 0x26, 0x0a, 0x0c, - 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x05, 0x20, 0x01, - 0x28, 0x09, 0x48, 0x00, 0x52, 0x0b, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x4e, 0x61, 0x6d, - 0x65, 0x88, 0x01, 0x01, 0x12, 0x37, 0x0a, 0x06, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x18, 0x06, - 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x65, 0x6e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x70, 0x61, - 0x72, 0x73, 0x65, 0x72, 0x2e, 0x6d, 0x65, 0x74, 0x61, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x6c, - 0x65, 0x63, 0x74, 0x6f, 0x72, 0x52, 0x06, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x42, 0x0f, 0x0a, - 0x0d, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x22, 0xa0, - 0x08, 0x0a, 0x09, 0x54, 0x72, 0x61, 0x63, 0x65, 0x4e, 0x6f, 0x64, 0x65, 0x12, 0x0e, 0x0a, 0x02, - 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x02, 0x69, 0x64, 0x12, 0x1a, 0x0a, 0x08, - 0x66, 0x69, 0x6c, 0x65, 0x70, 0x61, 0x74, 0x68, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, - 0x66, 0x69, 0x6c, 0x65, 0x70, 0x61, 0x74, 0x68, 0x12, 0x1b, 0x0a, 0x09, 0x73, 0x74, 0x61, 0x72, - 0x74, 0x5f, 0x70, 0x6f, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x05, 0x52, 0x08, 0x73, 0x74, 0x61, - 0x72, 0x74, 0x50, 0x6f, 0x73, 0x12, 0x17, 0x0a, 0x07, 0x65, 0x6e, 0x64, 0x5f, 0x70, 0x6f, 0x73, - 0x18, 0x05, 0x20, 0x01, 0x28, 0x05, 0x52, 0x06, 0x65, 0x6e, 0x64, 0x50, 0x6f, 0x73, 0x12, 0x24, - 0x0a, 0x0e, 0x73, 0x72, 0x63, 0x5f, 0x6c, 0x69, 0x6e, 0x65, 0x5f, 0x73, 0x74, 0x61, 0x72, 0x74, - 0x18, 0x06, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0c, 0x73, 0x72, 0x63, 0x4c, 0x69, 0x6e, 0x65, 0x53, - 0x74, 0x61, 0x72, 0x74, 0x12, 0x20, 0x0a, 0x0c, 0x73, 0x72, 0x63, 0x5f, 0x6c, 0x69, 0x6e, 0x65, - 0x5f, 0x65, 0x6e, 0x64, 0x18, 0x07, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0a, 0x73, 0x72, 0x63, 0x4c, - 0x69, 0x6e, 0x65, 0x45, 0x6e, 0x64, 0x12, 0x22, 0x0a, 0x0d, 0x73, 0x72, 0x63, 0x5f, 0x63, 0x6f, - 0x6c, 0x5f, 0x73, 0x74, 0x61, 0x72, 0x74, 0x18, 0x08, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0b, 0x73, - 0x72, 0x63, 0x43, 0x6f, 0x6c, 0x53, 0x74, 0x61, 0x72, 0x74, 0x12, 0x1e, 0x0a, 0x0b, 0x73, 0x72, - 0x63, 0x5f, 0x63, 0x6f, 0x6c, 0x5f, 0x65, 0x6e, 0x64, 0x18, 0x09, 0x20, 0x01, 0x28, 0x05, 0x52, - 0x09, 0x73, 0x72, 0x63, 0x43, 0x6f, 0x6c, 0x45, 0x6e, 0x64, 0x12, 0x3c, 0x0a, 0x07, 0x72, 0x70, - 0x63, 0x5f, 0x64, 0x65, 0x66, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x21, 0x2e, 0x65, 0x6e, - 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x70, 0x61, 0x72, 0x73, 0x65, 0x72, 0x2e, 0x6d, 0x65, 0x74, 0x61, - 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x50, 0x43, 0x44, 0x65, 0x66, 0x4e, 0x6f, 0x64, 0x65, 0x48, 0x00, - 0x52, 0x06, 0x72, 0x70, 0x63, 0x44, 0x65, 0x66, 0x12, 0x3f, 0x0a, 0x08, 0x72, 0x70, 0x63, 0x5f, - 0x63, 0x61, 0x6c, 0x6c, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x22, 0x2e, 0x65, 0x6e, 0x63, - 0x6f, 0x72, 0x65, 0x2e, 0x70, 0x61, 0x72, 0x73, 0x65, 0x72, 0x2e, 0x6d, 0x65, 0x74, 0x61, 0x2e, - 0x76, 0x31, 0x2e, 0x52, 0x50, 0x43, 0x43, 0x61, 0x6c, 0x6c, 0x4e, 0x6f, 0x64, 0x65, 0x48, 0x00, - 0x52, 0x07, 0x72, 0x70, 0x63, 0x43, 0x61, 0x6c, 0x6c, 0x12, 0x48, 0x0a, 0x0b, 0x73, 0x74, 0x61, - 0x74, 0x69, 0x63, 0x5f, 0x63, 0x61, 0x6c, 0x6c, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x25, + 0x48, 0x02, 0x52, 0x0e, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x53, 0x63, 0x68, 0x65, + 0x6d, 0x61, 0x88, 0x01, 0x01, 0x12, 0x39, 0x0a, 0x05, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x18, 0x07, + 0x20, 0x01, 0x28, 0x0e, 0x32, 0x23, 0x2e, 0x65, 0x6e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x70, 0x61, + 0x72, 0x73, 0x65, 0x72, 0x2e, 0x6d, 0x65, 0x74, 0x61, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x50, 0x43, + 0x2e, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x52, 0x05, 0x70, 0x72, 0x6f, 0x74, 0x6f, + 0x12, 0x2e, 0x0a, 0x03, 0x6c, 0x6f, 0x63, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e, + 0x65, 0x6e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x70, 0x61, 0x72, 0x73, 0x65, 0x72, 0x2e, 0x73, 0x63, + 0x68, 0x65, 0x6d, 0x61, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x6f, 0x63, 0x52, 0x03, 0x6c, 0x6f, 0x63, + 0x12, 0x2f, 0x0a, 0x04, 0x70, 0x61, 0x74, 0x68, 0x18, 0x09, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x65, 0x6e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x70, 0x61, 0x72, 0x73, 0x65, 0x72, 0x2e, 0x6d, - 0x65, 0x74, 0x61, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x69, 0x63, 0x43, 0x61, 0x6c, - 0x6c, 0x4e, 0x6f, 0x64, 0x65, 0x48, 0x00, 0x52, 0x0a, 0x73, 0x74, 0x61, 0x74, 0x69, 0x63, 0x43, - 0x61, 0x6c, 0x6c, 0x12, 0x55, 0x0a, 0x10, 0x61, 0x75, 0x74, 0x68, 0x5f, 0x68, 0x61, 0x6e, 0x64, - 0x6c, 0x65, 0x72, 0x5f, 0x64, 0x65, 0x66, 0x18, 0x0d, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x29, 0x2e, - 0x65, 0x6e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x70, 0x61, 0x72, 0x73, 0x65, 0x72, 0x2e, 0x6d, 0x65, - 0x74, 0x61, 0x2e, 0x76, 0x31, 0x2e, 0x41, 0x75, 0x74, 0x68, 0x48, 0x61, 0x6e, 0x64, 0x6c, 0x65, - 0x72, 0x44, 0x65, 0x66, 0x4e, 0x6f, 0x64, 0x65, 0x48, 0x00, 0x52, 0x0e, 0x61, 0x75, 0x74, 0x68, - 0x48, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x72, 0x44, 0x65, 0x66, 0x12, 0x55, 0x0a, 0x10, 0x70, 0x75, - 0x62, 0x73, 0x75, 0x62, 0x5f, 0x74, 0x6f, 0x70, 0x69, 0x63, 0x5f, 0x64, 0x65, 0x66, 0x18, 0x0e, - 0x20, 0x01, 0x28, 0x0b, 0x32, 0x29, 0x2e, 0x65, 0x6e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x70, 0x61, - 0x72, 0x73, 0x65, 0x72, 0x2e, 0x6d, 0x65, 0x74, 0x61, 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x75, 0x62, - 0x53, 0x75, 0x62, 0x54, 0x6f, 0x70, 0x69, 0x63, 0x44, 0x65, 0x66, 0x4e, 0x6f, 0x64, 0x65, 0x48, - 0x00, 0x52, 0x0e, 0x70, 0x75, 0x62, 0x73, 0x75, 0x62, 0x54, 0x6f, 0x70, 0x69, 0x63, 0x44, 0x65, - 0x66, 0x12, 0x51, 0x0a, 0x0e, 0x70, 0x75, 0x62, 0x73, 0x75, 0x62, 0x5f, 0x70, 0x75, 0x62, 0x6c, - 0x69, 0x73, 0x68, 0x18, 0x0f, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x28, 0x2e, 0x65, 0x6e, 0x63, 0x6f, + 0x65, 0x74, 0x61, 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x61, 0x74, 0x68, 0x52, 0x04, 0x70, 0x61, 0x74, + 0x68, 0x12, 0x21, 0x0a, 0x0c, 0x68, 0x74, 0x74, 0x70, 0x5f, 0x6d, 0x65, 0x74, 0x68, 0x6f, 0x64, + 0x73, 0x18, 0x0a, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0b, 0x68, 0x74, 0x74, 0x70, 0x4d, 0x65, 0x74, + 0x68, 0x6f, 0x64, 0x73, 0x12, 0x33, 0x0a, 0x04, 0x74, 0x61, 0x67, 0x73, 0x18, 0x0b, 0x20, 0x03, + 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x65, 0x6e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x70, 0x61, 0x72, 0x73, + 0x65, 0x72, 0x2e, 0x6d, 0x65, 0x74, 0x61, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x6c, 0x65, 0x63, + 0x74, 0x6f, 0x72, 0x52, 0x04, 0x74, 0x61, 0x67, 0x73, 0x12, 0x1c, 0x0a, 0x09, 0x73, 0x65, 0x6e, + 0x73, 0x69, 0x74, 0x69, 0x76, 0x65, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x08, 0x52, 0x09, 0x73, 0x65, + 0x6e, 0x73, 0x69, 0x74, 0x69, 0x76, 0x65, 0x12, 0x33, 0x0a, 0x15, 0x61, 0x6c, 0x6c, 0x6f, 0x77, + 0x5f, 0x75, 0x6e, 0x61, 0x75, 0x74, 0x68, 0x65, 0x6e, 0x74, 0x69, 0x63, 0x61, 0x74, 0x65, 0x64, + 0x18, 0x0d, 0x20, 0x01, 0x28, 0x08, 0x52, 0x14, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x55, 0x6e, 0x61, + 0x75, 0x74, 0x68, 0x65, 0x6e, 0x74, 0x69, 0x63, 0x61, 0x74, 0x65, 0x64, 0x12, 0x3e, 0x0a, 0x06, + 0x65, 0x78, 0x70, 0x6f, 0x73, 0x65, 0x18, 0x0e, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x26, 0x2e, 0x65, + 0x6e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x70, 0x61, 0x72, 0x73, 0x65, 0x72, 0x2e, 0x6d, 0x65, 0x74, + 0x61, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x50, 0x43, 0x2e, 0x45, 0x78, 0x70, 0x6f, 0x73, 0x65, 0x45, + 0x6e, 0x74, 0x72, 0x79, 0x52, 0x06, 0x65, 0x78, 0x70, 0x6f, 0x73, 0x65, 0x12, 0x22, 0x0a, 0x0a, + 0x62, 0x6f, 0x64, 0x79, 0x5f, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x18, 0x0f, 0x20, 0x01, 0x28, 0x04, + 0x48, 0x03, 0x52, 0x09, 0x62, 0x6f, 0x64, 0x79, 0x4c, 0x69, 0x6d, 0x69, 0x74, 0x88, 0x01, 0x01, + 0x12, 0x2b, 0x0a, 0x11, 0x73, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x69, 0x6e, 0x67, 0x5f, 0x72, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x18, 0x10, 0x20, 0x01, 0x28, 0x08, 0x52, 0x10, 0x73, 0x74, 0x72, + 0x65, 0x61, 0x6d, 0x69, 0x6e, 0x67, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x2d, 0x0a, + 0x12, 0x73, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x69, 0x6e, 0x67, 0x5f, 0x72, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x18, 0x11, 0x20, 0x01, 0x28, 0x08, 0x52, 0x11, 0x73, 0x74, 0x72, 0x65, 0x61, + 0x6d, 0x69, 0x6e, 0x67, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x4d, 0x0a, 0x10, + 0x68, 0x61, 0x6e, 0x64, 0x73, 0x68, 0x61, 0x6b, 0x65, 0x5f, 0x73, 0x63, 0x68, 0x65, 0x6d, 0x61, + 0x18, 0x12, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x65, 0x6e, 0x63, 0x6f, 0x72, 0x65, 0x2e, + 0x70, 0x61, 0x72, 0x73, 0x65, 0x72, 0x2e, 0x73, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x2e, 0x76, 0x31, + 0x2e, 0x54, 0x79, 0x70, 0x65, 0x48, 0x04, 0x52, 0x0f, 0x68, 0x61, 0x6e, 0x64, 0x73, 0x68, 0x61, + 0x6b, 0x65, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x88, 0x01, 0x01, 0x12, 0x51, 0x0a, 0x0d, 0x73, + 0x74, 0x61, 0x74, 0x69, 0x63, 0x5f, 0x61, 0x73, 0x73, 0x65, 0x74, 0x73, 0x18, 0x13, 0x20, 0x01, + 0x28, 0x0b, 0x32, 0x27, 0x2e, 0x65, 0x6e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x70, 0x61, 0x72, 0x73, + 0x65, 0x72, 0x2e, 0x6d, 0x65, 0x74, 0x61, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x50, 0x43, 0x2e, 0x53, + 0x74, 0x61, 0x74, 0x69, 0x63, 0x41, 0x73, 0x73, 0x65, 0x74, 0x73, 0x48, 0x05, 0x52, 0x0c, 0x73, + 0x74, 0x61, 0x74, 0x69, 0x63, 0x41, 0x73, 0x73, 0x65, 0x74, 0x73, 0x88, 0x01, 0x01, 0x1a, 0x63, + 0x0a, 0x0b, 0x45, 0x78, 0x70, 0x6f, 0x73, 0x65, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, + 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, + 0x3e, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x28, + 0x2e, 0x65, 0x6e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x70, 0x61, 0x72, 0x73, 0x65, 0x72, 0x2e, 0x6d, + 0x65, 0x74, 0x61, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x50, 0x43, 0x2e, 0x45, 0x78, 0x70, 0x6f, 0x73, + 0x65, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, + 0x02, 0x38, 0x01, 0x1a, 0x0f, 0x0a, 0x0d, 0x45, 0x78, 0x70, 0x6f, 0x73, 0x65, 0x4f, 0x70, 0x74, + 0x69, 0x6f, 0x6e, 0x73, 0x1a, 0x79, 0x0a, 0x0c, 0x53, 0x74, 0x61, 0x74, 0x69, 0x63, 0x41, 0x73, + 0x73, 0x65, 0x74, 0x73, 0x12, 0x20, 0x0a, 0x0c, 0x64, 0x69, 0x72, 0x5f, 0x72, 0x65, 0x6c, 0x5f, + 0x70, 0x61, 0x74, 0x68, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x64, 0x69, 0x72, 0x52, + 0x65, 0x6c, 0x50, 0x61, 0x74, 0x68, 0x12, 0x30, 0x0a, 0x12, 0x6e, 0x6f, 0x74, 0x5f, 0x66, 0x6f, + 0x75, 0x6e, 0x64, 0x5f, 0x72, 0x65, 0x6c, 0x5f, 0x70, 0x61, 0x74, 0x68, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x09, 0x48, 0x00, 0x52, 0x0f, 0x6e, 0x6f, 0x74, 0x46, 0x6f, 0x75, 0x6e, 0x64, 0x52, 0x65, + 0x6c, 0x50, 0x61, 0x74, 0x68, 0x88, 0x01, 0x01, 0x42, 0x15, 0x0a, 0x13, 0x5f, 0x6e, 0x6f, 0x74, + 0x5f, 0x66, 0x6f, 0x75, 0x6e, 0x64, 0x5f, 0x72, 0x65, 0x6c, 0x5f, 0x70, 0x61, 0x74, 0x68, 0x22, + 0x2f, 0x0a, 0x0a, 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, 0x54, 0x79, 0x70, 0x65, 0x12, 0x0b, 0x0a, + 0x07, 0x50, 0x52, 0x49, 0x56, 0x41, 0x54, 0x45, 0x10, 0x00, 0x12, 0x0a, 0x0a, 0x06, 0x50, 0x55, + 0x42, 0x4c, 0x49, 0x43, 0x10, 0x01, 0x12, 0x08, 0x0a, 0x04, 0x41, 0x55, 0x54, 0x48, 0x10, 0x02, + 0x22, 0x20, 0x0a, 0x08, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x12, 0x0b, 0x0a, 0x07, + 0x52, 0x45, 0x47, 0x55, 0x4c, 0x41, 0x52, 0x10, 0x00, 0x12, 0x07, 0x0a, 0x03, 0x52, 0x41, 0x57, + 0x10, 0x01, 0x42, 0x06, 0x0a, 0x04, 0x5f, 0x64, 0x6f, 0x63, 0x42, 0x11, 0x0a, 0x0f, 0x5f, 0x72, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x5f, 0x73, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x42, 0x12, 0x0a, + 0x10, 0x5f, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x5f, 0x73, 0x63, 0x68, 0x65, 0x6d, + 0x61, 0x42, 0x0d, 0x0a, 0x0b, 0x5f, 0x62, 0x6f, 0x64, 0x79, 0x5f, 0x6c, 0x69, 0x6d, 0x69, 0x74, + 0x42, 0x13, 0x0a, 0x11, 0x5f, 0x68, 0x61, 0x6e, 0x64, 0x73, 0x68, 0x61, 0x6b, 0x65, 0x5f, 0x73, + 0x63, 0x68, 0x65, 0x6d, 0x61, 0x42, 0x10, 0x0a, 0x0e, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x69, 0x63, + 0x5f, 0x61, 0x73, 0x73, 0x65, 0x74, 0x73, 0x22, 0xd2, 0x02, 0x0a, 0x0b, 0x41, 0x75, 0x74, 0x68, + 0x48, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x72, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x64, + 0x6f, 0x63, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x64, 0x6f, 0x63, 0x12, 0x19, 0x0a, + 0x08, 0x70, 0x6b, 0x67, 0x5f, 0x70, 0x61, 0x74, 0x68, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x07, 0x70, 0x6b, 0x67, 0x50, 0x61, 0x74, 0x68, 0x12, 0x19, 0x0a, 0x08, 0x70, 0x6b, 0x67, 0x5f, + 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x70, 0x6b, 0x67, 0x4e, + 0x61, 0x6d, 0x65, 0x12, 0x2e, 0x0a, 0x03, 0x6c, 0x6f, 0x63, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, + 0x32, 0x1c, 0x2e, 0x65, 0x6e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x70, 0x61, 0x72, 0x73, 0x65, 0x72, + 0x2e, 0x73, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x6f, 0x63, 0x52, 0x03, + 0x6c, 0x6f, 0x63, 0x12, 0x3f, 0x0a, 0x09, 0x61, 0x75, 0x74, 0x68, 0x5f, 0x64, 0x61, 0x74, 0x61, + 0x18, 0x06, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x65, 0x6e, 0x63, 0x6f, 0x72, 0x65, 0x2e, + 0x70, 0x61, 0x72, 0x73, 0x65, 0x72, 0x2e, 0x73, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x2e, 0x76, 0x31, + 0x2e, 0x54, 0x79, 0x70, 0x65, 0x48, 0x00, 0x52, 0x08, 0x61, 0x75, 0x74, 0x68, 0x44, 0x61, 0x74, + 0x61, 0x88, 0x01, 0x01, 0x12, 0x3a, 0x0a, 0x06, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x18, 0x07, + 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x65, 0x6e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x70, 0x61, + 0x72, 0x73, 0x65, 0x72, 0x2e, 0x73, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x2e, 0x76, 0x31, 0x2e, 0x54, + 0x79, 0x70, 0x65, 0x48, 0x01, 0x52, 0x06, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x88, 0x01, 0x01, + 0x12, 0x21, 0x0a, 0x0c, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, + 0x18, 0x08, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x4e, + 0x61, 0x6d, 0x65, 0x42, 0x0c, 0x0a, 0x0a, 0x5f, 0x61, 0x75, 0x74, 0x68, 0x5f, 0x64, 0x61, 0x74, + 0x61, 0x42, 0x09, 0x0a, 0x07, 0x5f, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x22, 0x92, 0x02, 0x0a, + 0x0a, 0x4d, 0x69, 0x64, 0x64, 0x6c, 0x65, 0x77, 0x61, 0x72, 0x65, 0x12, 0x38, 0x0a, 0x04, 0x6e, + 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x24, 0x2e, 0x65, 0x6e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x70, 0x61, 0x72, 0x73, 0x65, 0x72, 0x2e, 0x6d, 0x65, 0x74, 0x61, 0x2e, 0x76, - 0x31, 0x2e, 0x50, 0x75, 0x62, 0x53, 0x75, 0x62, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x73, 0x68, 0x4e, - 0x6f, 0x64, 0x65, 0x48, 0x00, 0x52, 0x0d, 0x70, 0x75, 0x62, 0x73, 0x75, 0x62, 0x50, 0x75, 0x62, - 0x6c, 0x69, 0x73, 0x68, 0x12, 0x5a, 0x0a, 0x11, 0x70, 0x75, 0x62, 0x73, 0x75, 0x62, 0x5f, 0x73, - 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x62, 0x65, 0x72, 0x18, 0x10, 0x20, 0x01, 0x28, 0x0b, 0x32, - 0x2b, 0x2e, 0x65, 0x6e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x70, 0x61, 0x72, 0x73, 0x65, 0x72, 0x2e, - 0x6d, 0x65, 0x74, 0x61, 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x75, 0x62, 0x53, 0x75, 0x62, 0x53, 0x75, - 0x62, 0x73, 0x63, 0x72, 0x69, 0x62, 0x65, 0x72, 0x4e, 0x6f, 0x64, 0x65, 0x48, 0x00, 0x52, 0x10, - 0x70, 0x75, 0x62, 0x73, 0x75, 0x62, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x62, 0x65, 0x72, - 0x12, 0x4b, 0x0a, 0x0c, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x5f, 0x69, 0x6e, 0x69, 0x74, - 0x18, 0x11, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x26, 0x2e, 0x65, 0x6e, 0x63, 0x6f, 0x72, 0x65, 0x2e, - 0x70, 0x61, 0x72, 0x73, 0x65, 0x72, 0x2e, 0x6d, 0x65, 0x74, 0x61, 0x2e, 0x76, 0x31, 0x2e, 0x53, - 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x49, 0x6e, 0x69, 0x74, 0x4e, 0x6f, 0x64, 0x65, 0x48, 0x00, - 0x52, 0x0b, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x49, 0x6e, 0x69, 0x74, 0x12, 0x51, 0x0a, - 0x0e, 0x6d, 0x69, 0x64, 0x64, 0x6c, 0x65, 0x77, 0x61, 0x72, 0x65, 0x5f, 0x64, 0x65, 0x66, 0x18, - 0x12, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x28, 0x2e, 0x65, 0x6e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x70, - 0x61, 0x72, 0x73, 0x65, 0x72, 0x2e, 0x6d, 0x65, 0x74, 0x61, 0x2e, 0x76, 0x31, 0x2e, 0x4d, 0x69, - 0x64, 0x64, 0x6c, 0x65, 0x77, 0x61, 0x72, 0x65, 0x44, 0x65, 0x66, 0x4e, 0x6f, 0x64, 0x65, 0x48, - 0x00, 0x52, 0x0d, 0x6d, 0x69, 0x64, 0x64, 0x6c, 0x65, 0x77, 0x61, 0x72, 0x65, 0x44, 0x65, 0x66, - 0x12, 0x54, 0x0a, 0x0e, 0x63, 0x61, 0x63, 0x68, 0x65, 0x5f, 0x6b, 0x65, 0x79, 0x73, 0x70, 0x61, - 0x63, 0x65, 0x18, 0x13, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2b, 0x2e, 0x65, 0x6e, 0x63, 0x6f, 0x72, - 0x65, 0x2e, 0x70, 0x61, 0x72, 0x73, 0x65, 0x72, 0x2e, 0x6d, 0x65, 0x74, 0x61, 0x2e, 0x76, 0x31, - 0x2e, 0x43, 0x61, 0x63, 0x68, 0x65, 0x4b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x44, 0x65, - 0x66, 0x4e, 0x6f, 0x64, 0x65, 0x48, 0x00, 0x52, 0x0d, 0x63, 0x61, 0x63, 0x68, 0x65, 0x4b, 0x65, - 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x42, 0x09, 0x0a, 0x07, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x78, - 0x74, 0x22, 0x64, 0x0a, 0x0a, 0x52, 0x50, 0x43, 0x44, 0x65, 0x66, 0x4e, 0x6f, 0x64, 0x65, 0x12, - 0x21, 0x0a, 0x0c, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x4e, 0x61, - 0x6d, 0x65, 0x12, 0x19, 0x0a, 0x08, 0x72, 0x70, 0x63, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x72, 0x70, 0x63, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x18, 0x0a, - 0x07, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, - 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x22, 0x65, 0x0a, 0x0b, 0x52, 0x50, 0x43, 0x43, 0x61, - 0x6c, 0x6c, 0x4e, 0x6f, 0x64, 0x65, 0x12, 0x21, 0x0a, 0x0c, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, - 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x73, 0x65, - 0x72, 0x76, 0x69, 0x63, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x19, 0x0a, 0x08, 0x72, 0x70, 0x63, - 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x72, 0x70, 0x63, - 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x18, - 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x22, 0xb4, - 0x01, 0x0a, 0x0e, 0x53, 0x74, 0x61, 0x74, 0x69, 0x63, 0x43, 0x61, 0x6c, 0x6c, 0x4e, 0x6f, 0x64, - 0x65, 0x12, 0x47, 0x0a, 0x07, 0x70, 0x61, 0x63, 0x6b, 0x61, 0x67, 0x65, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x0e, 0x32, 0x2d, 0x2e, 0x65, 0x6e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x70, 0x61, 0x72, 0x73, - 0x65, 0x72, 0x2e, 0x6d, 0x65, 0x74, 0x61, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x69, - 0x63, 0x43, 0x61, 0x6c, 0x6c, 0x4e, 0x6f, 0x64, 0x65, 0x2e, 0x50, 0x61, 0x63, 0x6b, 0x61, 0x67, - 0x65, 0x52, 0x07, 0x70, 0x61, 0x63, 0x6b, 0x61, 0x67, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x66, 0x75, - 0x6e, 0x63, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x66, 0x75, 0x6e, 0x63, 0x12, 0x18, - 0x0a, 0x07, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x07, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x22, 0x2b, 0x0a, 0x07, 0x50, 0x61, 0x63, 0x6b, - 0x61, 0x67, 0x65, 0x12, 0x0b, 0x0a, 0x07, 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x10, 0x00, - 0x12, 0x09, 0x0a, 0x05, 0x53, 0x51, 0x4c, 0x44, 0x42, 0x10, 0x01, 0x12, 0x08, 0x0a, 0x04, 0x52, - 0x4c, 0x4f, 0x47, 0x10, 0x02, 0x22, 0x65, 0x0a, 0x12, 0x41, 0x75, 0x74, 0x68, 0x48, 0x61, 0x6e, - 0x64, 0x6c, 0x65, 0x72, 0x44, 0x65, 0x66, 0x4e, 0x6f, 0x64, 0x65, 0x12, 0x21, 0x0a, 0x0c, 0x73, - 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x0b, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x12, - 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, - 0x6d, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x18, 0x03, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x07, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x22, 0x4d, 0x0a, 0x12, + 0x31, 0x2e, 0x51, 0x75, 0x61, 0x6c, 0x69, 0x66, 0x69, 0x65, 0x64, 0x4e, 0x61, 0x6d, 0x65, 0x52, + 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x64, 0x6f, 0x63, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x03, 0x64, 0x6f, 0x63, 0x12, 0x2e, 0x0a, 0x03, 0x6c, 0x6f, 0x63, 0x18, 0x03, + 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x65, 0x6e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x70, 0x61, + 0x72, 0x73, 0x65, 0x72, 0x2e, 0x73, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x2e, 0x76, 0x31, 0x2e, 0x4c, + 0x6f, 0x63, 0x52, 0x03, 0x6c, 0x6f, 0x63, 0x12, 0x16, 0x0a, 0x06, 0x67, 0x6c, 0x6f, 0x62, 0x61, + 0x6c, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x06, 0x67, 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x12, + 0x26, 0x0a, 0x0c, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, + 0x05, 0x20, 0x01, 0x28, 0x09, 0x48, 0x00, 0x52, 0x0b, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, + 0x4e, 0x61, 0x6d, 0x65, 0x88, 0x01, 0x01, 0x12, 0x37, 0x0a, 0x06, 0x74, 0x61, 0x72, 0x67, 0x65, + 0x74, 0x18, 0x06, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x65, 0x6e, 0x63, 0x6f, 0x72, 0x65, + 0x2e, 0x70, 0x61, 0x72, 0x73, 0x65, 0x72, 0x2e, 0x6d, 0x65, 0x74, 0x61, 0x2e, 0x76, 0x31, 0x2e, + 0x53, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x52, 0x06, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, + 0x42, 0x0f, 0x0a, 0x0d, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x5f, 0x6e, 0x61, 0x6d, + 0x65, 0x22, 0xa0, 0x08, 0x0a, 0x09, 0x54, 0x72, 0x61, 0x63, 0x65, 0x4e, 0x6f, 0x64, 0x65, 0x12, + 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x02, 0x69, 0x64, 0x12, + 0x1a, 0x0a, 0x08, 0x66, 0x69, 0x6c, 0x65, 0x70, 0x61, 0x74, 0x68, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x08, 0x66, 0x69, 0x6c, 0x65, 0x70, 0x61, 0x74, 0x68, 0x12, 0x1b, 0x0a, 0x09, 0x73, + 0x74, 0x61, 0x72, 0x74, 0x5f, 0x70, 0x6f, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x05, 0x52, 0x08, + 0x73, 0x74, 0x61, 0x72, 0x74, 0x50, 0x6f, 0x73, 0x12, 0x17, 0x0a, 0x07, 0x65, 0x6e, 0x64, 0x5f, + 0x70, 0x6f, 0x73, 0x18, 0x05, 0x20, 0x01, 0x28, 0x05, 0x52, 0x06, 0x65, 0x6e, 0x64, 0x50, 0x6f, + 0x73, 0x12, 0x24, 0x0a, 0x0e, 0x73, 0x72, 0x63, 0x5f, 0x6c, 0x69, 0x6e, 0x65, 0x5f, 0x73, 0x74, + 0x61, 0x72, 0x74, 0x18, 0x06, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0c, 0x73, 0x72, 0x63, 0x4c, 0x69, + 0x6e, 0x65, 0x53, 0x74, 0x61, 0x72, 0x74, 0x12, 0x20, 0x0a, 0x0c, 0x73, 0x72, 0x63, 0x5f, 0x6c, + 0x69, 0x6e, 0x65, 0x5f, 0x65, 0x6e, 0x64, 0x18, 0x07, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0a, 0x73, + 0x72, 0x63, 0x4c, 0x69, 0x6e, 0x65, 0x45, 0x6e, 0x64, 0x12, 0x22, 0x0a, 0x0d, 0x73, 0x72, 0x63, + 0x5f, 0x63, 0x6f, 0x6c, 0x5f, 0x73, 0x74, 0x61, 0x72, 0x74, 0x18, 0x08, 0x20, 0x01, 0x28, 0x05, + 0x52, 0x0b, 0x73, 0x72, 0x63, 0x43, 0x6f, 0x6c, 0x53, 0x74, 0x61, 0x72, 0x74, 0x12, 0x1e, 0x0a, + 0x0b, 0x73, 0x72, 0x63, 0x5f, 0x63, 0x6f, 0x6c, 0x5f, 0x65, 0x6e, 0x64, 0x18, 0x09, 0x20, 0x01, + 0x28, 0x05, 0x52, 0x09, 0x73, 0x72, 0x63, 0x43, 0x6f, 0x6c, 0x45, 0x6e, 0x64, 0x12, 0x3c, 0x0a, + 0x07, 0x72, 0x70, 0x63, 0x5f, 0x64, 0x65, 0x66, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x21, + 0x2e, 0x65, 0x6e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x70, 0x61, 0x72, 0x73, 0x65, 0x72, 0x2e, 0x6d, + 0x65, 0x74, 0x61, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x50, 0x43, 0x44, 0x65, 0x66, 0x4e, 0x6f, 0x64, + 0x65, 0x48, 0x00, 0x52, 0x06, 0x72, 0x70, 0x63, 0x44, 0x65, 0x66, 0x12, 0x3f, 0x0a, 0x08, 0x72, + 0x70, 0x63, 0x5f, 0x63, 0x61, 0x6c, 0x6c, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x22, 0x2e, + 0x65, 0x6e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x70, 0x61, 0x72, 0x73, 0x65, 0x72, 0x2e, 0x6d, 0x65, + 0x74, 0x61, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x50, 0x43, 0x43, 0x61, 0x6c, 0x6c, 0x4e, 0x6f, 0x64, + 0x65, 0x48, 0x00, 0x52, 0x07, 0x72, 0x70, 0x63, 0x43, 0x61, 0x6c, 0x6c, 0x12, 0x48, 0x0a, 0x0b, + 0x73, 0x74, 0x61, 0x74, 0x69, 0x63, 0x5f, 0x63, 0x61, 0x6c, 0x6c, 0x18, 0x0c, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x25, 0x2e, 0x65, 0x6e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x70, 0x61, 0x72, 0x73, 0x65, + 0x72, 0x2e, 0x6d, 0x65, 0x74, 0x61, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x69, 0x63, + 0x43, 0x61, 0x6c, 0x6c, 0x4e, 0x6f, 0x64, 0x65, 0x48, 0x00, 0x52, 0x0a, 0x73, 0x74, 0x61, 0x74, + 0x69, 0x63, 0x43, 0x61, 0x6c, 0x6c, 0x12, 0x55, 0x0a, 0x10, 0x61, 0x75, 0x74, 0x68, 0x5f, 0x68, + 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x72, 0x5f, 0x64, 0x65, 0x66, 0x18, 0x0d, 0x20, 0x01, 0x28, 0x0b, + 0x32, 0x29, 0x2e, 0x65, 0x6e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x70, 0x61, 0x72, 0x73, 0x65, 0x72, + 0x2e, 0x6d, 0x65, 0x74, 0x61, 0x2e, 0x76, 0x31, 0x2e, 0x41, 0x75, 0x74, 0x68, 0x48, 0x61, 0x6e, + 0x64, 0x6c, 0x65, 0x72, 0x44, 0x65, 0x66, 0x4e, 0x6f, 0x64, 0x65, 0x48, 0x00, 0x52, 0x0e, 0x61, + 0x75, 0x74, 0x68, 0x48, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x72, 0x44, 0x65, 0x66, 0x12, 0x55, 0x0a, + 0x10, 0x70, 0x75, 0x62, 0x73, 0x75, 0x62, 0x5f, 0x74, 0x6f, 0x70, 0x69, 0x63, 0x5f, 0x64, 0x65, + 0x66, 0x18, 0x0e, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x29, 0x2e, 0x65, 0x6e, 0x63, 0x6f, 0x72, 0x65, + 0x2e, 0x70, 0x61, 0x72, 0x73, 0x65, 0x72, 0x2e, 0x6d, 0x65, 0x74, 0x61, 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x75, 0x62, 0x53, 0x75, 0x62, 0x54, 0x6f, 0x70, 0x69, 0x63, 0x44, 0x65, 0x66, 0x4e, 0x6f, - 0x64, 0x65, 0x12, 0x1d, 0x0a, 0x0a, 0x74, 0x6f, 0x70, 0x69, 0x63, 0x5f, 0x6e, 0x61, 0x6d, 0x65, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x74, 0x6f, 0x70, 0x69, 0x63, 0x4e, 0x61, 0x6d, - 0x65, 0x12, 0x18, 0x0a, 0x07, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x18, 0x02, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x07, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x22, 0x4c, 0x0a, 0x11, 0x50, - 0x75, 0x62, 0x53, 0x75, 0x62, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x73, 0x68, 0x4e, 0x6f, 0x64, 0x65, - 0x12, 0x1d, 0x0a, 0x0a, 0x74, 0x6f, 0x70, 0x69, 0x63, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x74, 0x6f, 0x70, 0x69, 0x63, 0x4e, 0x61, 0x6d, 0x65, 0x12, - 0x18, 0x0a, 0x07, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x07, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x22, 0x9b, 0x01, 0x0a, 0x14, 0x50, 0x75, - 0x62, 0x53, 0x75, 0x62, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x62, 0x65, 0x72, 0x4e, 0x6f, - 0x64, 0x65, 0x12, 0x1d, 0x0a, 0x0a, 0x74, 0x6f, 0x70, 0x69, 0x63, 0x5f, 0x6e, 0x61, 0x6d, 0x65, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x74, 0x6f, 0x70, 0x69, 0x63, 0x4e, 0x61, 0x6d, - 0x65, 0x12, 0x27, 0x0a, 0x0f, 0x73, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x62, 0x65, 0x72, 0x5f, - 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x73, 0x75, 0x62, 0x73, - 0x63, 0x72, 0x69, 0x62, 0x65, 0x72, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x21, 0x0a, 0x0c, 0x73, 0x65, - 0x72, 0x76, 0x69, 0x63, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x0b, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x18, 0x0a, - 0x07, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, - 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x22, 0x76, 0x0a, 0x0f, 0x53, 0x65, 0x72, 0x76, 0x69, - 0x63, 0x65, 0x49, 0x6e, 0x69, 0x74, 0x4e, 0x6f, 0x64, 0x65, 0x12, 0x21, 0x0a, 0x0c, 0x73, 0x65, - 0x72, 0x76, 0x69, 0x63, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x0b, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x26, 0x0a, - 0x0f, 0x73, 0x65, 0x74, 0x75, 0x70, 0x5f, 0x66, 0x75, 0x6e, 0x63, 0x5f, 0x6e, 0x61, 0x6d, 0x65, - 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x73, 0x65, 0x74, 0x75, 0x70, 0x46, 0x75, 0x6e, - 0x63, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, - 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x22, - 0x9c, 0x01, 0x0a, 0x11, 0x4d, 0x69, 0x64, 0x64, 0x6c, 0x65, 0x77, 0x61, 0x72, 0x65, 0x44, 0x65, - 0x66, 0x4e, 0x6f, 0x64, 0x65, 0x12, 0x20, 0x0a, 0x0c, 0x70, 0x6b, 0x67, 0x5f, 0x72, 0x65, 0x6c, - 0x5f, 0x70, 0x61, 0x74, 0x68, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x70, 0x6b, 0x67, - 0x52, 0x65, 0x6c, 0x50, 0x61, 0x74, 0x68, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, - 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x63, - 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x63, 0x6f, - 0x6e, 0x74, 0x65, 0x78, 0x74, 0x12, 0x37, 0x0a, 0x06, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x18, - 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x65, 0x6e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x70, - 0x61, 0x72, 0x73, 0x65, 0x72, 0x2e, 0x6d, 0x65, 0x74, 0x61, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, - 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x52, 0x06, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x22, 0x90, - 0x01, 0x0a, 0x14, 0x43, 0x61, 0x63, 0x68, 0x65, 0x4b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, - 0x44, 0x65, 0x66, 0x4e, 0x6f, 0x64, 0x65, 0x12, 0x20, 0x0a, 0x0c, 0x70, 0x6b, 0x67, 0x5f, 0x72, - 0x65, 0x6c, 0x5f, 0x70, 0x61, 0x74, 0x68, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x70, - 0x6b, 0x67, 0x52, 0x65, 0x6c, 0x50, 0x61, 0x74, 0x68, 0x12, 0x19, 0x0a, 0x08, 0x76, 0x61, 0x72, - 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x76, 0x61, 0x72, - 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x21, 0x0a, 0x0c, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x5f, - 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x63, 0x6c, 0x75, 0x73, - 0x74, 0x65, 0x72, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x63, 0x6f, 0x6e, 0x74, 0x65, - 0x78, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x78, - 0x74, 0x22, 0xa1, 0x01, 0x0a, 0x04, 0x50, 0x61, 0x74, 0x68, 0x12, 0x3e, 0x0a, 0x08, 0x73, 0x65, - 0x67, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x22, 0x2e, 0x65, + 0x64, 0x65, 0x48, 0x00, 0x52, 0x0e, 0x70, 0x75, 0x62, 0x73, 0x75, 0x62, 0x54, 0x6f, 0x70, 0x69, + 0x63, 0x44, 0x65, 0x66, 0x12, 0x51, 0x0a, 0x0e, 0x70, 0x75, 0x62, 0x73, 0x75, 0x62, 0x5f, 0x70, + 0x75, 0x62, 0x6c, 0x69, 0x73, 0x68, 0x18, 0x0f, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x28, 0x2e, 0x65, 0x6e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x70, 0x61, 0x72, 0x73, 0x65, 0x72, 0x2e, 0x6d, 0x65, 0x74, - 0x61, 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x61, 0x74, 0x68, 0x53, 0x65, 0x67, 0x6d, 0x65, 0x6e, 0x74, - 0x52, 0x08, 0x73, 0x65, 0x67, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x12, 0x34, 0x0a, 0x04, 0x74, 0x79, - 0x70, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x20, 0x2e, 0x65, 0x6e, 0x63, 0x6f, 0x72, - 0x65, 0x2e, 0x70, 0x61, 0x72, 0x73, 0x65, 0x72, 0x2e, 0x6d, 0x65, 0x74, 0x61, 0x2e, 0x76, 0x31, - 0x2e, 0x50, 0x61, 0x74, 0x68, 0x2e, 0x54, 0x79, 0x70, 0x65, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, - 0x22, 0x23, 0x0a, 0x04, 0x54, 0x79, 0x70, 0x65, 0x12, 0x07, 0x0a, 0x03, 0x55, 0x52, 0x4c, 0x10, - 0x00, 0x12, 0x12, 0x0a, 0x0e, 0x43, 0x41, 0x43, 0x48, 0x45, 0x5f, 0x4b, 0x45, 0x59, 0x53, 0x50, - 0x41, 0x43, 0x45, 0x10, 0x01, 0x22, 0x92, 0x03, 0x0a, 0x0b, 0x50, 0x61, 0x74, 0x68, 0x53, 0x65, - 0x67, 0x6d, 0x65, 0x6e, 0x74, 0x12, 0x42, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x0e, 0x32, 0x2e, 0x2e, 0x65, 0x6e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x70, 0x61, 0x72, - 0x73, 0x65, 0x72, 0x2e, 0x6d, 0x65, 0x74, 0x61, 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x61, 0x74, 0x68, - 0x53, 0x65, 0x67, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x53, 0x65, 0x67, 0x6d, 0x65, 0x6e, 0x74, 0x54, - 0x79, 0x70, 0x65, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, - 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x12, - 0x4b, 0x0a, 0x0a, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x03, 0x20, - 0x01, 0x28, 0x0e, 0x32, 0x2c, 0x2e, 0x65, 0x6e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x70, 0x61, 0x72, - 0x73, 0x65, 0x72, 0x2e, 0x6d, 0x65, 0x74, 0x61, 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x61, 0x74, 0x68, - 0x53, 0x65, 0x67, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x54, 0x79, 0x70, - 0x65, 0x52, 0x09, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x54, 0x79, 0x70, 0x65, 0x22, 0x41, 0x0a, 0x0b, - 0x53, 0x65, 0x67, 0x6d, 0x65, 0x6e, 0x74, 0x54, 0x79, 0x70, 0x65, 0x12, 0x0b, 0x0a, 0x07, 0x4c, - 0x49, 0x54, 0x45, 0x52, 0x41, 0x4c, 0x10, 0x00, 0x12, 0x09, 0x0a, 0x05, 0x50, 0x41, 0x52, 0x41, - 0x4d, 0x10, 0x01, 0x12, 0x0c, 0x0a, 0x08, 0x57, 0x49, 0x4c, 0x44, 0x43, 0x41, 0x52, 0x44, 0x10, - 0x02, 0x12, 0x0c, 0x0a, 0x08, 0x46, 0x41, 0x4c, 0x4c, 0x42, 0x41, 0x43, 0x4b, 0x10, 0x03, 0x22, - 0x98, 0x01, 0x0a, 0x09, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x54, 0x79, 0x70, 0x65, 0x12, 0x0a, 0x0a, - 0x06, 0x53, 0x54, 0x52, 0x49, 0x4e, 0x47, 0x10, 0x00, 0x12, 0x08, 0x0a, 0x04, 0x42, 0x4f, 0x4f, - 0x4c, 0x10, 0x01, 0x12, 0x08, 0x0a, 0x04, 0x49, 0x4e, 0x54, 0x38, 0x10, 0x02, 0x12, 0x09, 0x0a, - 0x05, 0x49, 0x4e, 0x54, 0x31, 0x36, 0x10, 0x03, 0x12, 0x09, 0x0a, 0x05, 0x49, 0x4e, 0x54, 0x33, - 0x32, 0x10, 0x04, 0x12, 0x09, 0x0a, 0x05, 0x49, 0x4e, 0x54, 0x36, 0x34, 0x10, 0x05, 0x12, 0x07, - 0x0a, 0x03, 0x49, 0x4e, 0x54, 0x10, 0x06, 0x12, 0x09, 0x0a, 0x05, 0x55, 0x49, 0x4e, 0x54, 0x38, - 0x10, 0x07, 0x12, 0x0a, 0x0a, 0x06, 0x55, 0x49, 0x4e, 0x54, 0x31, 0x36, 0x10, 0x08, 0x12, 0x0a, - 0x0a, 0x06, 0x55, 0x49, 0x4e, 0x54, 0x33, 0x32, 0x10, 0x09, 0x12, 0x0a, 0x0a, 0x06, 0x55, 0x49, - 0x4e, 0x54, 0x36, 0x34, 0x10, 0x0a, 0x12, 0x08, 0x0a, 0x04, 0x55, 0x49, 0x4e, 0x54, 0x10, 0x0b, - 0x12, 0x08, 0x0a, 0x04, 0x55, 0x55, 0x49, 0x44, 0x10, 0x0c, 0x22, 0x8e, 0x02, 0x0a, 0x07, 0x47, - 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x12, 0x1f, 0x0a, 0x0b, 0x65, 0x6e, 0x63, 0x6f, 0x72, 0x65, - 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x65, 0x6e, 0x63, - 0x6f, 0x72, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x48, 0x0a, 0x08, 0x65, 0x78, 0x70, 0x6c, 0x69, - 0x63, 0x69, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x27, 0x2e, 0x65, 0x6e, 0x63, 0x6f, + 0x61, 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x75, 0x62, 0x53, 0x75, 0x62, 0x50, 0x75, 0x62, 0x6c, 0x69, + 0x73, 0x68, 0x4e, 0x6f, 0x64, 0x65, 0x48, 0x00, 0x52, 0x0d, 0x70, 0x75, 0x62, 0x73, 0x75, 0x62, + 0x50, 0x75, 0x62, 0x6c, 0x69, 0x73, 0x68, 0x12, 0x5a, 0x0a, 0x11, 0x70, 0x75, 0x62, 0x73, 0x75, + 0x62, 0x5f, 0x73, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x62, 0x65, 0x72, 0x18, 0x10, 0x20, 0x01, + 0x28, 0x0b, 0x32, 0x2b, 0x2e, 0x65, 0x6e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x70, 0x61, 0x72, 0x73, + 0x65, 0x72, 0x2e, 0x6d, 0x65, 0x74, 0x61, 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x75, 0x62, 0x53, 0x75, + 0x62, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x62, 0x65, 0x72, 0x4e, 0x6f, 0x64, 0x65, 0x48, + 0x00, 0x52, 0x10, 0x70, 0x75, 0x62, 0x73, 0x75, 0x62, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, + 0x62, 0x65, 0x72, 0x12, 0x4b, 0x0a, 0x0c, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x5f, 0x69, + 0x6e, 0x69, 0x74, 0x18, 0x11, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x26, 0x2e, 0x65, 0x6e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x70, 0x61, 0x72, 0x73, 0x65, 0x72, 0x2e, 0x6d, 0x65, 0x74, 0x61, 0x2e, 0x76, - 0x31, 0x2e, 0x47, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x2e, 0x45, 0x78, 0x70, 0x6c, 0x69, 0x63, - 0x69, 0x74, 0x48, 0x00, 0x52, 0x08, 0x65, 0x78, 0x70, 0x6c, 0x69, 0x63, 0x69, 0x74, 0x88, 0x01, - 0x01, 0x1a, 0x8a, 0x01, 0x0a, 0x08, 0x45, 0x78, 0x70, 0x6c, 0x69, 0x63, 0x69, 0x74, 0x12, 0x21, + 0x31, 0x2e, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x49, 0x6e, 0x69, 0x74, 0x4e, 0x6f, 0x64, + 0x65, 0x48, 0x00, 0x52, 0x0b, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x49, 0x6e, 0x69, 0x74, + 0x12, 0x51, 0x0a, 0x0e, 0x6d, 0x69, 0x64, 0x64, 0x6c, 0x65, 0x77, 0x61, 0x72, 0x65, 0x5f, 0x64, + 0x65, 0x66, 0x18, 0x12, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x28, 0x2e, 0x65, 0x6e, 0x63, 0x6f, 0x72, + 0x65, 0x2e, 0x70, 0x61, 0x72, 0x73, 0x65, 0x72, 0x2e, 0x6d, 0x65, 0x74, 0x61, 0x2e, 0x76, 0x31, + 0x2e, 0x4d, 0x69, 0x64, 0x64, 0x6c, 0x65, 0x77, 0x61, 0x72, 0x65, 0x44, 0x65, 0x66, 0x4e, 0x6f, + 0x64, 0x65, 0x48, 0x00, 0x52, 0x0d, 0x6d, 0x69, 0x64, 0x64, 0x6c, 0x65, 0x77, 0x61, 0x72, 0x65, + 0x44, 0x65, 0x66, 0x12, 0x54, 0x0a, 0x0e, 0x63, 0x61, 0x63, 0x68, 0x65, 0x5f, 0x6b, 0x65, 0x79, + 0x73, 0x70, 0x61, 0x63, 0x65, 0x18, 0x13, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2b, 0x2e, 0x65, 0x6e, + 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x70, 0x61, 0x72, 0x73, 0x65, 0x72, 0x2e, 0x6d, 0x65, 0x74, 0x61, + 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x61, 0x63, 0x68, 0x65, 0x4b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, + 0x65, 0x44, 0x65, 0x66, 0x4e, 0x6f, 0x64, 0x65, 0x48, 0x00, 0x52, 0x0d, 0x63, 0x61, 0x63, 0x68, + 0x65, 0x4b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x42, 0x09, 0x0a, 0x07, 0x63, 0x6f, 0x6e, + 0x74, 0x65, 0x78, 0x74, 0x22, 0x64, 0x0a, 0x0a, 0x52, 0x50, 0x43, 0x44, 0x65, 0x66, 0x4e, 0x6f, + 0x64, 0x65, 0x12, 0x21, 0x0a, 0x0c, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x5f, 0x6e, 0x61, + 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, + 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x19, 0x0a, 0x08, 0x72, 0x70, 0x63, 0x5f, 0x6e, 0x61, 0x6d, + 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x72, 0x70, 0x63, 0x4e, 0x61, 0x6d, 0x65, + 0x12, 0x18, 0x0a, 0x07, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x07, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x22, 0x65, 0x0a, 0x0b, 0x52, 0x50, + 0x43, 0x43, 0x61, 0x6c, 0x6c, 0x4e, 0x6f, 0x64, 0x65, 0x12, 0x21, 0x0a, 0x0c, 0x73, 0x65, 0x72, + 0x76, 0x69, 0x63, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x0b, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x19, 0x0a, 0x08, + 0x72, 0x70, 0x63, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, + 0x72, 0x70, 0x63, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x63, 0x6f, 0x6e, 0x74, 0x65, + 0x78, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x78, + 0x74, 0x22, 0xb4, 0x01, 0x0a, 0x0e, 0x53, 0x74, 0x61, 0x74, 0x69, 0x63, 0x43, 0x61, 0x6c, 0x6c, + 0x4e, 0x6f, 0x64, 0x65, 0x12, 0x47, 0x0a, 0x07, 0x70, 0x61, 0x63, 0x6b, 0x61, 0x67, 0x65, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x2d, 0x2e, 0x65, 0x6e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x70, + 0x61, 0x72, 0x73, 0x65, 0x72, 0x2e, 0x6d, 0x65, 0x74, 0x61, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x74, + 0x61, 0x74, 0x69, 0x63, 0x43, 0x61, 0x6c, 0x6c, 0x4e, 0x6f, 0x64, 0x65, 0x2e, 0x50, 0x61, 0x63, + 0x6b, 0x61, 0x67, 0x65, 0x52, 0x07, 0x70, 0x61, 0x63, 0x6b, 0x61, 0x67, 0x65, 0x12, 0x12, 0x0a, + 0x04, 0x66, 0x75, 0x6e, 0x63, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x66, 0x75, 0x6e, + 0x63, 0x12, 0x18, 0x0a, 0x07, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x18, 0x03, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x07, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x22, 0x2b, 0x0a, 0x07, 0x50, + 0x61, 0x63, 0x6b, 0x61, 0x67, 0x65, 0x12, 0x0b, 0x0a, 0x07, 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, + 0x4e, 0x10, 0x00, 0x12, 0x09, 0x0a, 0x05, 0x53, 0x51, 0x4c, 0x44, 0x42, 0x10, 0x01, 0x12, 0x08, + 0x0a, 0x04, 0x52, 0x4c, 0x4f, 0x47, 0x10, 0x02, 0x22, 0x65, 0x0a, 0x12, 0x41, 0x75, 0x74, 0x68, + 0x48, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x72, 0x44, 0x65, 0x66, 0x4e, 0x6f, 0x64, 0x65, 0x12, 0x21, 0x0a, 0x0c, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x4e, 0x61, 0x6d, - 0x65, 0x12, 0x4a, 0x0a, 0x0c, 0x61, 0x75, 0x74, 0x68, 0x5f, 0x68, 0x61, 0x6e, 0x64, 0x6c, 0x65, - 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x22, 0x2e, 0x65, 0x6e, 0x63, 0x6f, 0x72, 0x65, - 0x2e, 0x70, 0x61, 0x72, 0x73, 0x65, 0x72, 0x2e, 0x6d, 0x65, 0x74, 0x61, 0x2e, 0x76, 0x31, 0x2e, - 0x41, 0x75, 0x74, 0x68, 0x48, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x72, 0x48, 0x00, 0x52, 0x0b, 0x61, - 0x75, 0x74, 0x68, 0x48, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x72, 0x88, 0x01, 0x01, 0x42, 0x0f, 0x0a, - 0x0d, 0x5f, 0x61, 0x75, 0x74, 0x68, 0x5f, 0x68, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x72, 0x42, 0x0b, - 0x0a, 0x09, 0x5f, 0x65, 0x78, 0x70, 0x6c, 0x69, 0x63, 0x69, 0x74, 0x22, 0xac, 0x01, 0x0a, 0x07, - 0x43, 0x72, 0x6f, 0x6e, 0x4a, 0x6f, 0x62, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x14, 0x0a, 0x05, 0x74, 0x69, 0x74, 0x6c, 0x65, - 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x74, 0x69, 0x74, 0x6c, 0x65, 0x12, 0x15, 0x0a, - 0x03, 0x64, 0x6f, 0x63, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x48, 0x00, 0x52, 0x03, 0x64, 0x6f, - 0x63, 0x88, 0x01, 0x01, 0x12, 0x1a, 0x0a, 0x08, 0x73, 0x63, 0x68, 0x65, 0x64, 0x75, 0x6c, 0x65, - 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x73, 0x63, 0x68, 0x65, 0x64, 0x75, 0x6c, 0x65, - 0x12, 0x40, 0x0a, 0x08, 0x65, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x18, 0x05, 0x20, 0x01, - 0x28, 0x0b, 0x32, 0x24, 0x2e, 0x65, 0x6e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x70, 0x61, 0x72, 0x73, - 0x65, 0x72, 0x2e, 0x6d, 0x65, 0x74, 0x61, 0x2e, 0x76, 0x31, 0x2e, 0x51, 0x75, 0x61, 0x6c, 0x69, - 0x66, 0x69, 0x65, 0x64, 0x4e, 0x61, 0x6d, 0x65, 0x52, 0x08, 0x65, 0x6e, 0x64, 0x70, 0x6f, 0x69, - 0x6e, 0x74, 0x42, 0x06, 0x0a, 0x04, 0x5f, 0x64, 0x6f, 0x63, 0x22, 0x95, 0x02, 0x0a, 0x0b, 0x53, - 0x51, 0x4c, 0x44, 0x61, 0x74, 0x61, 0x62, 0x61, 0x73, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, - 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x15, - 0x0a, 0x03, 0x64, 0x6f, 0x63, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x48, 0x00, 0x52, 0x03, 0x64, - 0x6f, 0x63, 0x88, 0x01, 0x01, 0x12, 0x31, 0x0a, 0x12, 0x6d, 0x69, 0x67, 0x72, 0x61, 0x74, 0x69, - 0x6f, 0x6e, 0x5f, 0x72, 0x65, 0x6c, 0x5f, 0x70, 0x61, 0x74, 0x68, 0x18, 0x03, 0x20, 0x01, 0x28, - 0x09, 0x48, 0x01, 0x52, 0x10, 0x6d, 0x69, 0x67, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, - 0x6c, 0x50, 0x61, 0x74, 0x68, 0x88, 0x01, 0x01, 0x12, 0x42, 0x0a, 0x0a, 0x6d, 0x69, 0x67, 0x72, - 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x22, 0x2e, 0x65, + 0x65, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, + 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x22, + 0x4d, 0x0a, 0x12, 0x50, 0x75, 0x62, 0x53, 0x75, 0x62, 0x54, 0x6f, 0x70, 0x69, 0x63, 0x44, 0x65, + 0x66, 0x4e, 0x6f, 0x64, 0x65, 0x12, 0x1d, 0x0a, 0x0a, 0x74, 0x6f, 0x70, 0x69, 0x63, 0x5f, 0x6e, + 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x74, 0x6f, 0x70, 0x69, 0x63, + 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x18, + 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x22, 0x4c, + 0x0a, 0x11, 0x50, 0x75, 0x62, 0x53, 0x75, 0x62, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x73, 0x68, 0x4e, + 0x6f, 0x64, 0x65, 0x12, 0x1d, 0x0a, 0x0a, 0x74, 0x6f, 0x70, 0x69, 0x63, 0x5f, 0x6e, 0x61, 0x6d, + 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x74, 0x6f, 0x70, 0x69, 0x63, 0x4e, 0x61, + 0x6d, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x18, 0x02, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x07, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x22, 0x9b, 0x01, 0x0a, + 0x14, 0x50, 0x75, 0x62, 0x53, 0x75, 0x62, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x62, 0x65, + 0x72, 0x4e, 0x6f, 0x64, 0x65, 0x12, 0x1d, 0x0a, 0x0a, 0x74, 0x6f, 0x70, 0x69, 0x63, 0x5f, 0x6e, + 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x74, 0x6f, 0x70, 0x69, 0x63, + 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x27, 0x0a, 0x0f, 0x73, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x62, + 0x65, 0x72, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x73, + 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x62, 0x65, 0x72, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x21, 0x0a, + 0x0c, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x03, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x0b, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x4e, 0x61, 0x6d, 0x65, + 0x12, 0x18, 0x0a, 0x07, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x07, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x22, 0x76, 0x0a, 0x0f, 0x53, 0x65, + 0x72, 0x76, 0x69, 0x63, 0x65, 0x49, 0x6e, 0x69, 0x74, 0x4e, 0x6f, 0x64, 0x65, 0x12, 0x21, 0x0a, + 0x0c, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x0b, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x4e, 0x61, 0x6d, 0x65, + 0x12, 0x26, 0x0a, 0x0f, 0x73, 0x65, 0x74, 0x75, 0x70, 0x5f, 0x66, 0x75, 0x6e, 0x63, 0x5f, 0x6e, + 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x73, 0x65, 0x74, 0x75, 0x70, + 0x46, 0x75, 0x6e, 0x63, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x63, 0x6f, 0x6e, 0x74, + 0x65, 0x78, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x63, 0x6f, 0x6e, 0x74, 0x65, + 0x78, 0x74, 0x22, 0x9c, 0x01, 0x0a, 0x11, 0x4d, 0x69, 0x64, 0x64, 0x6c, 0x65, 0x77, 0x61, 0x72, + 0x65, 0x44, 0x65, 0x66, 0x4e, 0x6f, 0x64, 0x65, 0x12, 0x20, 0x0a, 0x0c, 0x70, 0x6b, 0x67, 0x5f, + 0x72, 0x65, 0x6c, 0x5f, 0x70, 0x61, 0x74, 0x68, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, + 0x70, 0x6b, 0x67, 0x52, 0x65, 0x6c, 0x50, 0x61, 0x74, 0x68, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, + 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x18, + 0x0a, 0x07, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x07, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x12, 0x37, 0x0a, 0x06, 0x74, 0x61, 0x72, 0x67, + 0x65, 0x74, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x65, 0x6e, 0x63, 0x6f, 0x72, + 0x65, 0x2e, 0x70, 0x61, 0x72, 0x73, 0x65, 0x72, 0x2e, 0x6d, 0x65, 0x74, 0x61, 0x2e, 0x76, 0x31, + 0x2e, 0x53, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x52, 0x06, 0x74, 0x61, 0x72, 0x67, 0x65, + 0x74, 0x22, 0x90, 0x01, 0x0a, 0x14, 0x43, 0x61, 0x63, 0x68, 0x65, 0x4b, 0x65, 0x79, 0x73, 0x70, + 0x61, 0x63, 0x65, 0x44, 0x65, 0x66, 0x4e, 0x6f, 0x64, 0x65, 0x12, 0x20, 0x0a, 0x0c, 0x70, 0x6b, + 0x67, 0x5f, 0x72, 0x65, 0x6c, 0x5f, 0x70, 0x61, 0x74, 0x68, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x0a, 0x70, 0x6b, 0x67, 0x52, 0x65, 0x6c, 0x50, 0x61, 0x74, 0x68, 0x12, 0x19, 0x0a, 0x08, + 0x76, 0x61, 0x72, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, + 0x76, 0x61, 0x72, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x21, 0x0a, 0x0c, 0x63, 0x6c, 0x75, 0x73, 0x74, + 0x65, 0x72, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x63, + 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x63, 0x6f, + 0x6e, 0x74, 0x65, 0x78, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x63, 0x6f, 0x6e, + 0x74, 0x65, 0x78, 0x74, 0x22, 0xa1, 0x01, 0x0a, 0x04, 0x50, 0x61, 0x74, 0x68, 0x12, 0x3e, 0x0a, + 0x08, 0x73, 0x65, 0x67, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, + 0x22, 0x2e, 0x65, 0x6e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x70, 0x61, 0x72, 0x73, 0x65, 0x72, 0x2e, + 0x6d, 0x65, 0x74, 0x61, 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x61, 0x74, 0x68, 0x53, 0x65, 0x67, 0x6d, + 0x65, 0x6e, 0x74, 0x52, 0x08, 0x73, 0x65, 0x67, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x12, 0x34, 0x0a, + 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x20, 0x2e, 0x65, 0x6e, + 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x70, 0x61, 0x72, 0x73, 0x65, 0x72, 0x2e, 0x6d, 0x65, 0x74, 0x61, + 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x61, 0x74, 0x68, 0x2e, 0x54, 0x79, 0x70, 0x65, 0x52, 0x04, 0x74, + 0x79, 0x70, 0x65, 0x22, 0x23, 0x0a, 0x04, 0x54, 0x79, 0x70, 0x65, 0x12, 0x07, 0x0a, 0x03, 0x55, + 0x52, 0x4c, 0x10, 0x00, 0x12, 0x12, 0x0a, 0x0e, 0x43, 0x41, 0x43, 0x48, 0x45, 0x5f, 0x4b, 0x45, + 0x59, 0x53, 0x50, 0x41, 0x43, 0x45, 0x10, 0x01, 0x22, 0x92, 0x03, 0x0a, 0x0b, 0x50, 0x61, 0x74, + 0x68, 0x53, 0x65, 0x67, 0x6d, 0x65, 0x6e, 0x74, 0x12, 0x42, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x2e, 0x2e, 0x65, 0x6e, 0x63, 0x6f, 0x72, 0x65, 0x2e, + 0x70, 0x61, 0x72, 0x73, 0x65, 0x72, 0x2e, 0x6d, 0x65, 0x74, 0x61, 0x2e, 0x76, 0x31, 0x2e, 0x50, + 0x61, 0x74, 0x68, 0x53, 0x65, 0x67, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x53, 0x65, 0x67, 0x6d, 0x65, + 0x6e, 0x74, 0x54, 0x79, 0x70, 0x65, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x14, 0x0a, 0x05, + 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, + 0x75, 0x65, 0x12, 0x4b, 0x0a, 0x0a, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x5f, 0x74, 0x79, 0x70, 0x65, + 0x18, 0x03, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x2c, 0x2e, 0x65, 0x6e, 0x63, 0x6f, 0x72, 0x65, 0x2e, + 0x70, 0x61, 0x72, 0x73, 0x65, 0x72, 0x2e, 0x6d, 0x65, 0x74, 0x61, 0x2e, 0x76, 0x31, 0x2e, 0x50, + 0x61, 0x74, 0x68, 0x53, 0x65, 0x67, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x50, 0x61, 0x72, 0x61, 0x6d, + 0x54, 0x79, 0x70, 0x65, 0x52, 0x09, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x54, 0x79, 0x70, 0x65, 0x22, + 0x41, 0x0a, 0x0b, 0x53, 0x65, 0x67, 0x6d, 0x65, 0x6e, 0x74, 0x54, 0x79, 0x70, 0x65, 0x12, 0x0b, + 0x0a, 0x07, 0x4c, 0x49, 0x54, 0x45, 0x52, 0x41, 0x4c, 0x10, 0x00, 0x12, 0x09, 0x0a, 0x05, 0x50, + 0x41, 0x52, 0x41, 0x4d, 0x10, 0x01, 0x12, 0x0c, 0x0a, 0x08, 0x57, 0x49, 0x4c, 0x44, 0x43, 0x41, + 0x52, 0x44, 0x10, 0x02, 0x12, 0x0c, 0x0a, 0x08, 0x46, 0x41, 0x4c, 0x4c, 0x42, 0x41, 0x43, 0x4b, + 0x10, 0x03, 0x22, 0x98, 0x01, 0x0a, 0x09, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x54, 0x79, 0x70, 0x65, + 0x12, 0x0a, 0x0a, 0x06, 0x53, 0x54, 0x52, 0x49, 0x4e, 0x47, 0x10, 0x00, 0x12, 0x08, 0x0a, 0x04, + 0x42, 0x4f, 0x4f, 0x4c, 0x10, 0x01, 0x12, 0x08, 0x0a, 0x04, 0x49, 0x4e, 0x54, 0x38, 0x10, 0x02, + 0x12, 0x09, 0x0a, 0x05, 0x49, 0x4e, 0x54, 0x31, 0x36, 0x10, 0x03, 0x12, 0x09, 0x0a, 0x05, 0x49, + 0x4e, 0x54, 0x33, 0x32, 0x10, 0x04, 0x12, 0x09, 0x0a, 0x05, 0x49, 0x4e, 0x54, 0x36, 0x34, 0x10, + 0x05, 0x12, 0x07, 0x0a, 0x03, 0x49, 0x4e, 0x54, 0x10, 0x06, 0x12, 0x09, 0x0a, 0x05, 0x55, 0x49, + 0x4e, 0x54, 0x38, 0x10, 0x07, 0x12, 0x0a, 0x0a, 0x06, 0x55, 0x49, 0x4e, 0x54, 0x31, 0x36, 0x10, + 0x08, 0x12, 0x0a, 0x0a, 0x06, 0x55, 0x49, 0x4e, 0x54, 0x33, 0x32, 0x10, 0x09, 0x12, 0x0a, 0x0a, + 0x06, 0x55, 0x49, 0x4e, 0x54, 0x36, 0x34, 0x10, 0x0a, 0x12, 0x08, 0x0a, 0x04, 0x55, 0x49, 0x4e, + 0x54, 0x10, 0x0b, 0x12, 0x08, 0x0a, 0x04, 0x55, 0x55, 0x49, 0x44, 0x10, 0x0c, 0x22, 0x8e, 0x02, + 0x0a, 0x07, 0x47, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x12, 0x1f, 0x0a, 0x0b, 0x65, 0x6e, 0x63, + 0x6f, 0x72, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, + 0x65, 0x6e, 0x63, 0x6f, 0x72, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x48, 0x0a, 0x08, 0x65, 0x78, + 0x70, 0x6c, 0x69, 0x63, 0x69, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x27, 0x2e, 0x65, 0x6e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x70, 0x61, 0x72, 0x73, 0x65, 0x72, 0x2e, 0x6d, 0x65, 0x74, - 0x61, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x42, 0x4d, 0x69, 0x67, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, - 0x52, 0x0a, 0x6d, 0x69, 0x67, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x45, 0x0a, 0x1f, - 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x5f, 0x6e, 0x6f, 0x6e, 0x5f, 0x73, 0x65, 0x71, 0x75, 0x65, 0x6e, - 0x74, 0x69, 0x61, 0x6c, 0x5f, 0x6d, 0x69, 0x67, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, - 0x05, 0x20, 0x01, 0x28, 0x08, 0x52, 0x1c, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x4e, 0x6f, 0x6e, 0x53, - 0x65, 0x71, 0x75, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x4d, 0x69, 0x67, 0x72, 0x61, 0x74, 0x69, - 0x6f, 0x6e, 0x73, 0x42, 0x06, 0x0a, 0x04, 0x5f, 0x64, 0x6f, 0x63, 0x42, 0x15, 0x0a, 0x13, 0x5f, - 0x6d, 0x69, 0x67, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x72, 0x65, 0x6c, 0x5f, 0x70, 0x61, - 0x74, 0x68, 0x22, 0x63, 0x0a, 0x0b, 0x44, 0x42, 0x4d, 0x69, 0x67, 0x72, 0x61, 0x74, 0x69, 0x6f, - 0x6e, 0x12, 0x1a, 0x0a, 0x08, 0x66, 0x69, 0x6c, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x08, 0x66, 0x69, 0x6c, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x16, 0x0a, - 0x06, 0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, 0x06, 0x6e, - 0x75, 0x6d, 0x62, 0x65, 0x72, 0x12, 0x20, 0x0a, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, - 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x64, 0x65, 0x73, 0x63, - 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x22, 0xb8, 0x07, 0x0a, 0x0b, 0x50, 0x75, 0x62, 0x53, + 0x61, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x2e, 0x45, 0x78, 0x70, + 0x6c, 0x69, 0x63, 0x69, 0x74, 0x48, 0x00, 0x52, 0x08, 0x65, 0x78, 0x70, 0x6c, 0x69, 0x63, 0x69, + 0x74, 0x88, 0x01, 0x01, 0x1a, 0x8a, 0x01, 0x0a, 0x08, 0x45, 0x78, 0x70, 0x6c, 0x69, 0x63, 0x69, + 0x74, 0x12, 0x21, 0x0a, 0x0c, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x5f, 0x6e, 0x61, 0x6d, + 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, + 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x4a, 0x0a, 0x0c, 0x61, 0x75, 0x74, 0x68, 0x5f, 0x68, 0x61, 0x6e, + 0x64, 0x6c, 0x65, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x22, 0x2e, 0x65, 0x6e, 0x63, + 0x6f, 0x72, 0x65, 0x2e, 0x70, 0x61, 0x72, 0x73, 0x65, 0x72, 0x2e, 0x6d, 0x65, 0x74, 0x61, 0x2e, + 0x76, 0x31, 0x2e, 0x41, 0x75, 0x74, 0x68, 0x48, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x72, 0x48, 0x00, + 0x52, 0x0b, 0x61, 0x75, 0x74, 0x68, 0x48, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x72, 0x88, 0x01, 0x01, + 0x42, 0x0f, 0x0a, 0x0d, 0x5f, 0x61, 0x75, 0x74, 0x68, 0x5f, 0x68, 0x61, 0x6e, 0x64, 0x6c, 0x65, + 0x72, 0x42, 0x0b, 0x0a, 0x09, 0x5f, 0x65, 0x78, 0x70, 0x6c, 0x69, 0x63, 0x69, 0x74, 0x22, 0xac, + 0x01, 0x0a, 0x07, 0x43, 0x72, 0x6f, 0x6e, 0x4a, 0x6f, 0x62, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x14, 0x0a, 0x05, 0x74, 0x69, + 0x74, 0x6c, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x74, 0x69, 0x74, 0x6c, 0x65, + 0x12, 0x15, 0x0a, 0x03, 0x64, 0x6f, 0x63, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x48, 0x00, 0x52, + 0x03, 0x64, 0x6f, 0x63, 0x88, 0x01, 0x01, 0x12, 0x1a, 0x0a, 0x08, 0x73, 0x63, 0x68, 0x65, 0x64, + 0x75, 0x6c, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x73, 0x63, 0x68, 0x65, 0x64, + 0x75, 0x6c, 0x65, 0x12, 0x40, 0x0a, 0x08, 0x65, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x18, + 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x24, 0x2e, 0x65, 0x6e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x70, + 0x61, 0x72, 0x73, 0x65, 0x72, 0x2e, 0x6d, 0x65, 0x74, 0x61, 0x2e, 0x76, 0x31, 0x2e, 0x51, 0x75, + 0x61, 0x6c, 0x69, 0x66, 0x69, 0x65, 0x64, 0x4e, 0x61, 0x6d, 0x65, 0x52, 0x08, 0x65, 0x6e, 0x64, + 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x42, 0x06, 0x0a, 0x04, 0x5f, 0x64, 0x6f, 0x63, 0x22, 0x95, 0x02, + 0x0a, 0x0b, 0x53, 0x51, 0x4c, 0x44, 0x61, 0x74, 0x61, 0x62, 0x61, 0x73, 0x65, 0x12, 0x12, 0x0a, + 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, + 0x65, 0x12, 0x15, 0x0a, 0x03, 0x64, 0x6f, 0x63, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x48, 0x00, + 0x52, 0x03, 0x64, 0x6f, 0x63, 0x88, 0x01, 0x01, 0x12, 0x31, 0x0a, 0x12, 0x6d, 0x69, 0x67, 0x72, + 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x72, 0x65, 0x6c, 0x5f, 0x70, 0x61, 0x74, 0x68, 0x18, 0x03, + 0x20, 0x01, 0x28, 0x09, 0x48, 0x01, 0x52, 0x10, 0x6d, 0x69, 0x67, 0x72, 0x61, 0x74, 0x69, 0x6f, + 0x6e, 0x52, 0x65, 0x6c, 0x50, 0x61, 0x74, 0x68, 0x88, 0x01, 0x01, 0x12, 0x42, 0x0a, 0x0a, 0x6d, + 0x69, 0x67, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, + 0x22, 0x2e, 0x65, 0x6e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x70, 0x61, 0x72, 0x73, 0x65, 0x72, 0x2e, + 0x6d, 0x65, 0x74, 0x61, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x42, 0x4d, 0x69, 0x67, 0x72, 0x61, 0x74, + 0x69, 0x6f, 0x6e, 0x52, 0x0a, 0x6d, 0x69, 0x67, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, + 0x45, 0x0a, 0x1f, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x5f, 0x6e, 0x6f, 0x6e, 0x5f, 0x73, 0x65, 0x71, + 0x75, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x5f, 0x6d, 0x69, 0x67, 0x72, 0x61, 0x74, 0x69, 0x6f, + 0x6e, 0x73, 0x18, 0x05, 0x20, 0x01, 0x28, 0x08, 0x52, 0x1c, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x4e, + 0x6f, 0x6e, 0x53, 0x65, 0x71, 0x75, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x4d, 0x69, 0x67, 0x72, + 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x42, 0x06, 0x0a, 0x04, 0x5f, 0x64, 0x6f, 0x63, 0x42, 0x15, + 0x0a, 0x13, 0x5f, 0x6d, 0x69, 0x67, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x72, 0x65, 0x6c, + 0x5f, 0x70, 0x61, 0x74, 0x68, 0x22, 0x63, 0x0a, 0x0b, 0x44, 0x42, 0x4d, 0x69, 0x67, 0x72, 0x61, + 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x1a, 0x0a, 0x08, 0x66, 0x69, 0x6c, 0x65, 0x6e, 0x61, 0x6d, 0x65, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x66, 0x69, 0x6c, 0x65, 0x6e, 0x61, 0x6d, 0x65, + 0x12, 0x16, 0x0a, 0x06, 0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, + 0x52, 0x06, 0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x12, 0x20, 0x0a, 0x0b, 0x64, 0x65, 0x73, 0x63, + 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x64, + 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x22, 0x3b, 0x0a, 0x06, 0x42, 0x75, + 0x63, 0x6b, 0x65, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x15, 0x0a, 0x03, 0x64, 0x6f, 0x63, 0x18, + 0x02, 0x20, 0x01, 0x28, 0x09, 0x48, 0x00, 0x52, 0x03, 0x64, 0x6f, 0x63, 0x88, 0x01, 0x01, 0x42, + 0x06, 0x0a, 0x04, 0x5f, 0x64, 0x6f, 0x63, 0x22, 0xb8, 0x07, 0x0a, 0x0b, 0x50, 0x75, 0x62, 0x53, 0x75, 0x62, 0x54, 0x6f, 0x70, 0x69, 0x63, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x15, 0x0a, 0x03, 0x64, 0x6f, 0x63, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x48, 0x00, 0x52, 0x03, 0x64, 0x6f, 0x63, 0x88, @@ -4027,7 +4107,7 @@ func file_encore_parser_meta_v1_meta_proto_rawDescGZIP() []byte { } var file_encore_parser_meta_v1_meta_proto_enumTypes = make([]protoimpl.EnumInfo, 10) -var file_encore_parser_meta_v1_meta_proto_msgTypes = make([]protoimpl.MessageInfo, 37) +var file_encore_parser_meta_v1_meta_proto_msgTypes = make([]protoimpl.MessageInfo, 38) var file_encore_parser_meta_v1_meta_proto_goTypes = []interface{}{ (Lang)(0), // 0: encore.parser.meta.v1.Lang (Selector_Type)(0), // 1: encore.parser.meta.v1.Selector.Type @@ -4064,96 +4144,98 @@ var file_encore_parser_meta_v1_meta_proto_goTypes = []interface{}{ (*CronJob)(nil), // 32: encore.parser.meta.v1.CronJob (*SQLDatabase)(nil), // 33: encore.parser.meta.v1.SQLDatabase (*DBMigration)(nil), // 34: encore.parser.meta.v1.DBMigration - (*PubSubTopic)(nil), // 35: encore.parser.meta.v1.PubSubTopic - (*CacheCluster)(nil), // 36: encore.parser.meta.v1.CacheCluster - (*Metric)(nil), // 37: encore.parser.meta.v1.Metric - nil, // 38: encore.parser.meta.v1.RPC.ExposeEntry - (*RPC_ExposeOptions)(nil), // 39: encore.parser.meta.v1.RPC.ExposeOptions - (*RPC_StaticAssets)(nil), // 40: encore.parser.meta.v1.RPC.StaticAssets - (*Gateway_Explicit)(nil), // 41: encore.parser.meta.v1.Gateway.Explicit - (*PubSubTopic_Publisher)(nil), // 42: encore.parser.meta.v1.PubSubTopic.Publisher - (*PubSubTopic_Subscription)(nil), // 43: encore.parser.meta.v1.PubSubTopic.Subscription - (*PubSubTopic_RetryPolicy)(nil), // 44: encore.parser.meta.v1.PubSubTopic.RetryPolicy - (*CacheCluster_Keyspace)(nil), // 45: encore.parser.meta.v1.CacheCluster.Keyspace - (*Metric_Label)(nil), // 46: encore.parser.meta.v1.Metric.Label - (*v1.Decl)(nil), // 47: encore.parser.schema.v1.Decl - (*v1.Type)(nil), // 48: encore.parser.schema.v1.Type - (*v1.Loc)(nil), // 49: encore.parser.schema.v1.Loc - (v1.Builtin)(0), // 50: encore.parser.schema.v1.Builtin + (*Bucket)(nil), // 35: encore.parser.meta.v1.Bucket + (*PubSubTopic)(nil), // 36: encore.parser.meta.v1.PubSubTopic + (*CacheCluster)(nil), // 37: encore.parser.meta.v1.CacheCluster + (*Metric)(nil), // 38: encore.parser.meta.v1.Metric + nil, // 39: encore.parser.meta.v1.RPC.ExposeEntry + (*RPC_ExposeOptions)(nil), // 40: encore.parser.meta.v1.RPC.ExposeOptions + (*RPC_StaticAssets)(nil), // 41: encore.parser.meta.v1.RPC.StaticAssets + (*Gateway_Explicit)(nil), // 42: encore.parser.meta.v1.Gateway.Explicit + (*PubSubTopic_Publisher)(nil), // 43: encore.parser.meta.v1.PubSubTopic.Publisher + (*PubSubTopic_Subscription)(nil), // 44: encore.parser.meta.v1.PubSubTopic.Subscription + (*PubSubTopic_RetryPolicy)(nil), // 45: encore.parser.meta.v1.PubSubTopic.RetryPolicy + (*CacheCluster_Keyspace)(nil), // 46: encore.parser.meta.v1.CacheCluster.Keyspace + (*Metric_Label)(nil), // 47: encore.parser.meta.v1.Metric.Label + (*v1.Decl)(nil), // 48: encore.parser.schema.v1.Decl + (*v1.Type)(nil), // 49: encore.parser.schema.v1.Type + (*v1.Loc)(nil), // 50: encore.parser.schema.v1.Loc + (v1.Builtin)(0), // 51: encore.parser.schema.v1.Builtin } var file_encore_parser_meta_v1_meta_proto_depIdxs = []int32{ - 47, // 0: encore.parser.meta.v1.Data.decls:type_name -> encore.parser.schema.v1.Decl + 48, // 0: encore.parser.meta.v1.Data.decls:type_name -> encore.parser.schema.v1.Decl 12, // 1: encore.parser.meta.v1.Data.pkgs:type_name -> encore.parser.meta.v1.Package 13, // 2: encore.parser.meta.v1.Data.svcs:type_name -> encore.parser.meta.v1.Service 16, // 3: encore.parser.meta.v1.Data.auth_handler:type_name -> encore.parser.meta.v1.AuthHandler 32, // 4: encore.parser.meta.v1.Data.cron_jobs:type_name -> encore.parser.meta.v1.CronJob - 35, // 5: encore.parser.meta.v1.Data.pubsub_topics:type_name -> encore.parser.meta.v1.PubSubTopic + 36, // 5: encore.parser.meta.v1.Data.pubsub_topics:type_name -> encore.parser.meta.v1.PubSubTopic 17, // 6: encore.parser.meta.v1.Data.middleware:type_name -> encore.parser.meta.v1.Middleware - 36, // 7: encore.parser.meta.v1.Data.cache_clusters:type_name -> encore.parser.meta.v1.CacheCluster - 37, // 8: encore.parser.meta.v1.Data.metrics:type_name -> encore.parser.meta.v1.Metric + 37, // 7: encore.parser.meta.v1.Data.cache_clusters:type_name -> encore.parser.meta.v1.CacheCluster + 38, // 8: encore.parser.meta.v1.Data.metrics:type_name -> encore.parser.meta.v1.Metric 33, // 9: encore.parser.meta.v1.Data.sql_databases:type_name -> encore.parser.meta.v1.SQLDatabase 31, // 10: encore.parser.meta.v1.Data.gateways:type_name -> encore.parser.meta.v1.Gateway 0, // 11: encore.parser.meta.v1.Data.language:type_name -> encore.parser.meta.v1.Lang - 11, // 12: encore.parser.meta.v1.Package.rpc_calls:type_name -> encore.parser.meta.v1.QualifiedName - 18, // 13: encore.parser.meta.v1.Package.trace_nodes:type_name -> encore.parser.meta.v1.TraceNode - 15, // 14: encore.parser.meta.v1.Service.rpcs:type_name -> encore.parser.meta.v1.RPC - 34, // 15: encore.parser.meta.v1.Service.migrations:type_name -> encore.parser.meta.v1.DBMigration - 1, // 16: encore.parser.meta.v1.Selector.type:type_name -> encore.parser.meta.v1.Selector.Type - 2, // 17: encore.parser.meta.v1.RPC.access_type:type_name -> encore.parser.meta.v1.RPC.AccessType - 48, // 18: encore.parser.meta.v1.RPC.request_schema:type_name -> encore.parser.schema.v1.Type - 48, // 19: encore.parser.meta.v1.RPC.response_schema:type_name -> encore.parser.schema.v1.Type - 3, // 20: encore.parser.meta.v1.RPC.proto:type_name -> encore.parser.meta.v1.RPC.Protocol - 49, // 21: encore.parser.meta.v1.RPC.loc:type_name -> encore.parser.schema.v1.Loc - 29, // 22: encore.parser.meta.v1.RPC.path:type_name -> encore.parser.meta.v1.Path - 14, // 23: encore.parser.meta.v1.RPC.tags:type_name -> encore.parser.meta.v1.Selector - 38, // 24: encore.parser.meta.v1.RPC.expose:type_name -> encore.parser.meta.v1.RPC.ExposeEntry - 48, // 25: encore.parser.meta.v1.RPC.handshake_schema:type_name -> encore.parser.schema.v1.Type - 40, // 26: encore.parser.meta.v1.RPC.static_assets:type_name -> encore.parser.meta.v1.RPC.StaticAssets - 49, // 27: encore.parser.meta.v1.AuthHandler.loc:type_name -> encore.parser.schema.v1.Loc - 48, // 28: encore.parser.meta.v1.AuthHandler.auth_data:type_name -> encore.parser.schema.v1.Type - 48, // 29: encore.parser.meta.v1.AuthHandler.params:type_name -> encore.parser.schema.v1.Type - 11, // 30: encore.parser.meta.v1.Middleware.name:type_name -> encore.parser.meta.v1.QualifiedName - 49, // 31: encore.parser.meta.v1.Middleware.loc:type_name -> encore.parser.schema.v1.Loc - 14, // 32: encore.parser.meta.v1.Middleware.target:type_name -> encore.parser.meta.v1.Selector - 19, // 33: encore.parser.meta.v1.TraceNode.rpc_def:type_name -> encore.parser.meta.v1.RPCDefNode - 20, // 34: encore.parser.meta.v1.TraceNode.rpc_call:type_name -> encore.parser.meta.v1.RPCCallNode - 21, // 35: encore.parser.meta.v1.TraceNode.static_call:type_name -> encore.parser.meta.v1.StaticCallNode - 22, // 36: encore.parser.meta.v1.TraceNode.auth_handler_def:type_name -> encore.parser.meta.v1.AuthHandlerDefNode - 23, // 37: encore.parser.meta.v1.TraceNode.pubsub_topic_def:type_name -> encore.parser.meta.v1.PubSubTopicDefNode - 24, // 38: encore.parser.meta.v1.TraceNode.pubsub_publish:type_name -> encore.parser.meta.v1.PubSubPublishNode - 25, // 39: encore.parser.meta.v1.TraceNode.pubsub_subscriber:type_name -> encore.parser.meta.v1.PubSubSubscriberNode - 26, // 40: encore.parser.meta.v1.TraceNode.service_init:type_name -> encore.parser.meta.v1.ServiceInitNode - 27, // 41: encore.parser.meta.v1.TraceNode.middleware_def:type_name -> encore.parser.meta.v1.MiddlewareDefNode - 28, // 42: encore.parser.meta.v1.TraceNode.cache_keyspace:type_name -> encore.parser.meta.v1.CacheKeyspaceDefNode - 4, // 43: encore.parser.meta.v1.StaticCallNode.package:type_name -> encore.parser.meta.v1.StaticCallNode.Package - 14, // 44: encore.parser.meta.v1.MiddlewareDefNode.target:type_name -> encore.parser.meta.v1.Selector - 30, // 45: encore.parser.meta.v1.Path.segments:type_name -> encore.parser.meta.v1.PathSegment - 5, // 46: encore.parser.meta.v1.Path.type:type_name -> encore.parser.meta.v1.Path.Type - 6, // 47: encore.parser.meta.v1.PathSegment.type:type_name -> encore.parser.meta.v1.PathSegment.SegmentType - 7, // 48: encore.parser.meta.v1.PathSegment.value_type:type_name -> encore.parser.meta.v1.PathSegment.ParamType - 41, // 49: encore.parser.meta.v1.Gateway.explicit:type_name -> encore.parser.meta.v1.Gateway.Explicit - 11, // 50: encore.parser.meta.v1.CronJob.endpoint:type_name -> encore.parser.meta.v1.QualifiedName - 34, // 51: encore.parser.meta.v1.SQLDatabase.migrations:type_name -> encore.parser.meta.v1.DBMigration - 48, // 52: encore.parser.meta.v1.PubSubTopic.message_type:type_name -> encore.parser.schema.v1.Type - 8, // 53: encore.parser.meta.v1.PubSubTopic.delivery_guarantee:type_name -> encore.parser.meta.v1.PubSubTopic.DeliveryGuarantee - 42, // 54: encore.parser.meta.v1.PubSubTopic.publishers:type_name -> encore.parser.meta.v1.PubSubTopic.Publisher - 43, // 55: encore.parser.meta.v1.PubSubTopic.subscriptions:type_name -> encore.parser.meta.v1.PubSubTopic.Subscription - 45, // 56: encore.parser.meta.v1.CacheCluster.keyspaces:type_name -> encore.parser.meta.v1.CacheCluster.Keyspace - 50, // 57: encore.parser.meta.v1.Metric.value_type:type_name -> encore.parser.schema.v1.Builtin - 9, // 58: encore.parser.meta.v1.Metric.kind:type_name -> encore.parser.meta.v1.Metric.MetricKind - 46, // 59: encore.parser.meta.v1.Metric.labels:type_name -> encore.parser.meta.v1.Metric.Label - 39, // 60: encore.parser.meta.v1.RPC.ExposeEntry.value:type_name -> encore.parser.meta.v1.RPC.ExposeOptions - 16, // 61: encore.parser.meta.v1.Gateway.Explicit.auth_handler:type_name -> encore.parser.meta.v1.AuthHandler - 44, // 62: encore.parser.meta.v1.PubSubTopic.Subscription.retry_policy:type_name -> encore.parser.meta.v1.PubSubTopic.RetryPolicy - 48, // 63: encore.parser.meta.v1.CacheCluster.Keyspace.key_type:type_name -> encore.parser.schema.v1.Type - 48, // 64: encore.parser.meta.v1.CacheCluster.Keyspace.value_type:type_name -> encore.parser.schema.v1.Type - 29, // 65: encore.parser.meta.v1.CacheCluster.Keyspace.path_pattern:type_name -> encore.parser.meta.v1.Path - 50, // 66: encore.parser.meta.v1.Metric.Label.type:type_name -> encore.parser.schema.v1.Builtin - 67, // [67:67] is the sub-list for method output_type - 67, // [67:67] is the sub-list for method input_type - 67, // [67:67] is the sub-list for extension type_name - 67, // [67:67] is the sub-list for extension extendee - 0, // [0:67] is the sub-list for field type_name + 35, // 12: encore.parser.meta.v1.Data.buckets:type_name -> encore.parser.meta.v1.Bucket + 11, // 13: encore.parser.meta.v1.Package.rpc_calls:type_name -> encore.parser.meta.v1.QualifiedName + 18, // 14: encore.parser.meta.v1.Package.trace_nodes:type_name -> encore.parser.meta.v1.TraceNode + 15, // 15: encore.parser.meta.v1.Service.rpcs:type_name -> encore.parser.meta.v1.RPC + 34, // 16: encore.parser.meta.v1.Service.migrations:type_name -> encore.parser.meta.v1.DBMigration + 1, // 17: encore.parser.meta.v1.Selector.type:type_name -> encore.parser.meta.v1.Selector.Type + 2, // 18: encore.parser.meta.v1.RPC.access_type:type_name -> encore.parser.meta.v1.RPC.AccessType + 49, // 19: encore.parser.meta.v1.RPC.request_schema:type_name -> encore.parser.schema.v1.Type + 49, // 20: encore.parser.meta.v1.RPC.response_schema:type_name -> encore.parser.schema.v1.Type + 3, // 21: encore.parser.meta.v1.RPC.proto:type_name -> encore.parser.meta.v1.RPC.Protocol + 50, // 22: encore.parser.meta.v1.RPC.loc:type_name -> encore.parser.schema.v1.Loc + 29, // 23: encore.parser.meta.v1.RPC.path:type_name -> encore.parser.meta.v1.Path + 14, // 24: encore.parser.meta.v1.RPC.tags:type_name -> encore.parser.meta.v1.Selector + 39, // 25: encore.parser.meta.v1.RPC.expose:type_name -> encore.parser.meta.v1.RPC.ExposeEntry + 49, // 26: encore.parser.meta.v1.RPC.handshake_schema:type_name -> encore.parser.schema.v1.Type + 41, // 27: encore.parser.meta.v1.RPC.static_assets:type_name -> encore.parser.meta.v1.RPC.StaticAssets + 50, // 28: encore.parser.meta.v1.AuthHandler.loc:type_name -> encore.parser.schema.v1.Loc + 49, // 29: encore.parser.meta.v1.AuthHandler.auth_data:type_name -> encore.parser.schema.v1.Type + 49, // 30: encore.parser.meta.v1.AuthHandler.params:type_name -> encore.parser.schema.v1.Type + 11, // 31: encore.parser.meta.v1.Middleware.name:type_name -> encore.parser.meta.v1.QualifiedName + 50, // 32: encore.parser.meta.v1.Middleware.loc:type_name -> encore.parser.schema.v1.Loc + 14, // 33: encore.parser.meta.v1.Middleware.target:type_name -> encore.parser.meta.v1.Selector + 19, // 34: encore.parser.meta.v1.TraceNode.rpc_def:type_name -> encore.parser.meta.v1.RPCDefNode + 20, // 35: encore.parser.meta.v1.TraceNode.rpc_call:type_name -> encore.parser.meta.v1.RPCCallNode + 21, // 36: encore.parser.meta.v1.TraceNode.static_call:type_name -> encore.parser.meta.v1.StaticCallNode + 22, // 37: encore.parser.meta.v1.TraceNode.auth_handler_def:type_name -> encore.parser.meta.v1.AuthHandlerDefNode + 23, // 38: encore.parser.meta.v1.TraceNode.pubsub_topic_def:type_name -> encore.parser.meta.v1.PubSubTopicDefNode + 24, // 39: encore.parser.meta.v1.TraceNode.pubsub_publish:type_name -> encore.parser.meta.v1.PubSubPublishNode + 25, // 40: encore.parser.meta.v1.TraceNode.pubsub_subscriber:type_name -> encore.parser.meta.v1.PubSubSubscriberNode + 26, // 41: encore.parser.meta.v1.TraceNode.service_init:type_name -> encore.parser.meta.v1.ServiceInitNode + 27, // 42: encore.parser.meta.v1.TraceNode.middleware_def:type_name -> encore.parser.meta.v1.MiddlewareDefNode + 28, // 43: encore.parser.meta.v1.TraceNode.cache_keyspace:type_name -> encore.parser.meta.v1.CacheKeyspaceDefNode + 4, // 44: encore.parser.meta.v1.StaticCallNode.package:type_name -> encore.parser.meta.v1.StaticCallNode.Package + 14, // 45: encore.parser.meta.v1.MiddlewareDefNode.target:type_name -> encore.parser.meta.v1.Selector + 30, // 46: encore.parser.meta.v1.Path.segments:type_name -> encore.parser.meta.v1.PathSegment + 5, // 47: encore.parser.meta.v1.Path.type:type_name -> encore.parser.meta.v1.Path.Type + 6, // 48: encore.parser.meta.v1.PathSegment.type:type_name -> encore.parser.meta.v1.PathSegment.SegmentType + 7, // 49: encore.parser.meta.v1.PathSegment.value_type:type_name -> encore.parser.meta.v1.PathSegment.ParamType + 42, // 50: encore.parser.meta.v1.Gateway.explicit:type_name -> encore.parser.meta.v1.Gateway.Explicit + 11, // 51: encore.parser.meta.v1.CronJob.endpoint:type_name -> encore.parser.meta.v1.QualifiedName + 34, // 52: encore.parser.meta.v1.SQLDatabase.migrations:type_name -> encore.parser.meta.v1.DBMigration + 49, // 53: encore.parser.meta.v1.PubSubTopic.message_type:type_name -> encore.parser.schema.v1.Type + 8, // 54: encore.parser.meta.v1.PubSubTopic.delivery_guarantee:type_name -> encore.parser.meta.v1.PubSubTopic.DeliveryGuarantee + 43, // 55: encore.parser.meta.v1.PubSubTopic.publishers:type_name -> encore.parser.meta.v1.PubSubTopic.Publisher + 44, // 56: encore.parser.meta.v1.PubSubTopic.subscriptions:type_name -> encore.parser.meta.v1.PubSubTopic.Subscription + 46, // 57: encore.parser.meta.v1.CacheCluster.keyspaces:type_name -> encore.parser.meta.v1.CacheCluster.Keyspace + 51, // 58: encore.parser.meta.v1.Metric.value_type:type_name -> encore.parser.schema.v1.Builtin + 9, // 59: encore.parser.meta.v1.Metric.kind:type_name -> encore.parser.meta.v1.Metric.MetricKind + 47, // 60: encore.parser.meta.v1.Metric.labels:type_name -> encore.parser.meta.v1.Metric.Label + 40, // 61: encore.parser.meta.v1.RPC.ExposeEntry.value:type_name -> encore.parser.meta.v1.RPC.ExposeOptions + 16, // 62: encore.parser.meta.v1.Gateway.Explicit.auth_handler:type_name -> encore.parser.meta.v1.AuthHandler + 45, // 63: encore.parser.meta.v1.PubSubTopic.Subscription.retry_policy:type_name -> encore.parser.meta.v1.PubSubTopic.RetryPolicy + 49, // 64: encore.parser.meta.v1.CacheCluster.Keyspace.key_type:type_name -> encore.parser.schema.v1.Type + 49, // 65: encore.parser.meta.v1.CacheCluster.Keyspace.value_type:type_name -> encore.parser.schema.v1.Type + 29, // 66: encore.parser.meta.v1.CacheCluster.Keyspace.path_pattern:type_name -> encore.parser.meta.v1.Path + 51, // 67: encore.parser.meta.v1.Metric.Label.type:type_name -> encore.parser.schema.v1.Builtin + 68, // [68:68] is the sub-list for method output_type + 68, // [68:68] is the sub-list for method input_type + 68, // [68:68] is the sub-list for extension type_name + 68, // [68:68] is the sub-list for extension extendee + 0, // [0:68] is the sub-list for field type_name } func init() { file_encore_parser_meta_v1_meta_proto_init() } @@ -4463,7 +4545,7 @@ func file_encore_parser_meta_v1_meta_proto_init() { } } file_encore_parser_meta_v1_meta_proto_msgTypes[25].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*PubSubTopic); i { + switch v := v.(*Bucket); i { case 0: return &v.state case 1: @@ -4475,7 +4557,7 @@ func file_encore_parser_meta_v1_meta_proto_init() { } } file_encore_parser_meta_v1_meta_proto_msgTypes[26].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*CacheCluster); i { + switch v := v.(*PubSubTopic); i { case 0: return &v.state case 1: @@ -4487,6 +4569,18 @@ func file_encore_parser_meta_v1_meta_proto_init() { } } file_encore_parser_meta_v1_meta_proto_msgTypes[27].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*CacheCluster); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_encore_parser_meta_v1_meta_proto_msgTypes[28].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*Metric); i { case 0: return &v.state @@ -4498,7 +4592,7 @@ func file_encore_parser_meta_v1_meta_proto_init() { return nil } } - file_encore_parser_meta_v1_meta_proto_msgTypes[29].Exporter = func(v interface{}, i int) interface{} { + file_encore_parser_meta_v1_meta_proto_msgTypes[30].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*RPC_ExposeOptions); i { case 0: return &v.state @@ -4510,7 +4604,7 @@ func file_encore_parser_meta_v1_meta_proto_init() { return nil } } - file_encore_parser_meta_v1_meta_proto_msgTypes[30].Exporter = func(v interface{}, i int) interface{} { + file_encore_parser_meta_v1_meta_proto_msgTypes[31].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*RPC_StaticAssets); i { case 0: return &v.state @@ -4522,7 +4616,7 @@ func file_encore_parser_meta_v1_meta_proto_init() { return nil } } - file_encore_parser_meta_v1_meta_proto_msgTypes[31].Exporter = func(v interface{}, i int) interface{} { + file_encore_parser_meta_v1_meta_proto_msgTypes[32].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*Gateway_Explicit); i { case 0: return &v.state @@ -4534,7 +4628,7 @@ func file_encore_parser_meta_v1_meta_proto_init() { return nil } } - file_encore_parser_meta_v1_meta_proto_msgTypes[32].Exporter = func(v interface{}, i int) interface{} { + file_encore_parser_meta_v1_meta_proto_msgTypes[33].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*PubSubTopic_Publisher); i { case 0: return &v.state @@ -4546,7 +4640,7 @@ func file_encore_parser_meta_v1_meta_proto_init() { return nil } } - file_encore_parser_meta_v1_meta_proto_msgTypes[33].Exporter = func(v interface{}, i int) interface{} { + file_encore_parser_meta_v1_meta_proto_msgTypes[34].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*PubSubTopic_Subscription); i { case 0: return &v.state @@ -4558,7 +4652,7 @@ func file_encore_parser_meta_v1_meta_proto_init() { return nil } } - file_encore_parser_meta_v1_meta_proto_msgTypes[34].Exporter = func(v interface{}, i int) interface{} { + file_encore_parser_meta_v1_meta_proto_msgTypes[35].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*PubSubTopic_RetryPolicy); i { case 0: return &v.state @@ -4570,7 +4664,7 @@ func file_encore_parser_meta_v1_meta_proto_init() { return nil } } - file_encore_parser_meta_v1_meta_proto_msgTypes[35].Exporter = func(v interface{}, i int) interface{} { + file_encore_parser_meta_v1_meta_proto_msgTypes[36].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*CacheCluster_Keyspace); i { case 0: return &v.state @@ -4582,7 +4676,7 @@ func file_encore_parser_meta_v1_meta_proto_init() { return nil } } - file_encore_parser_meta_v1_meta_proto_msgTypes[36].Exporter = func(v interface{}, i int) interface{} { + file_encore_parser_meta_v1_meta_proto_msgTypes[37].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*Metric_Label); i { case 0: return &v.state @@ -4615,17 +4709,18 @@ func file_encore_parser_meta_v1_meta_proto_init() { file_encore_parser_meta_v1_meta_proto_msgTypes[22].OneofWrappers = []interface{}{} file_encore_parser_meta_v1_meta_proto_msgTypes[23].OneofWrappers = []interface{}{} file_encore_parser_meta_v1_meta_proto_msgTypes[25].OneofWrappers = []interface{}{} - file_encore_parser_meta_v1_meta_proto_msgTypes[27].OneofWrappers = []interface{}{} - file_encore_parser_meta_v1_meta_proto_msgTypes[30].OneofWrappers = []interface{}{} + file_encore_parser_meta_v1_meta_proto_msgTypes[26].OneofWrappers = []interface{}{} + file_encore_parser_meta_v1_meta_proto_msgTypes[28].OneofWrappers = []interface{}{} file_encore_parser_meta_v1_meta_proto_msgTypes[31].OneofWrappers = []interface{}{} - file_encore_parser_meta_v1_meta_proto_msgTypes[33].OneofWrappers = []interface{}{} + file_encore_parser_meta_v1_meta_proto_msgTypes[32].OneofWrappers = []interface{}{} + file_encore_parser_meta_v1_meta_proto_msgTypes[34].OneofWrappers = []interface{}{} type x struct{} out := protoimpl.TypeBuilder{ File: protoimpl.DescBuilder{ GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_encore_parser_meta_v1_meta_proto_rawDesc, NumEnums: 10, - NumMessages: 37, + NumMessages: 38, NumExtensions: 0, NumServices: 0, }, diff --git a/proto/encore/runtime/v1/infra.pb.go b/proto/encore/runtime/v1/infra.pb.go index 3f42fe86ae..fda85c8515 100644 --- a/proto/encore/runtime/v1/infra.pb.go +++ b/proto/encore/runtime/v1/infra.pb.go @@ -1522,6 +1522,144 @@ type PubSubSubscription_GcpConfig struct { func (*PubSubSubscription_GcpConfig) isPubSubSubscription_ProviderConfig() {} +type BucketCluster struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // The unique resource id for this cluster. + Rid string `protobuf:"bytes,1,opt,name=rid,proto3" json:"rid,omitempty"` + Buckets []*Bucket `protobuf:"bytes,2,rep,name=buckets,proto3" json:"buckets,omitempty"` + Region string `protobuf:"bytes,3,opt,name=region,proto3" json:"region,omitempty"` + Endpoint string `protobuf:"bytes,4,opt,name=endpoint,proto3" json:"endpoint,omitempty"` +} + +func (x *BucketCluster) Reset() { + *x = BucketCluster{} + if protoimpl.UnsafeEnabled { + mi := &file_encore_runtime_v1_infra_proto_msgTypes[17] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *BucketCluster) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*BucketCluster) ProtoMessage() {} + +func (x *BucketCluster) ProtoReflect() protoreflect.Message { + mi := &file_encore_runtime_v1_infra_proto_msgTypes[17] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use BucketCluster.ProtoReflect.Descriptor instead. +func (*BucketCluster) Descriptor() ([]byte, []int) { + return file_encore_runtime_v1_infra_proto_rawDescGZIP(), []int{17} +} + +func (x *BucketCluster) GetRid() string { + if x != nil { + return x.Rid + } + return "" +} + +func (x *BucketCluster) GetBuckets() []*Bucket { + if x != nil { + return x.Buckets + } + return nil +} + +func (x *BucketCluster) GetRegion() string { + if x != nil { + return x.Region + } + return "" +} + +func (x *BucketCluster) GetEndpoint() string { + if x != nil { + return x.Endpoint + } + return "" +} + +type Bucket struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // The unique resource id for this bucket. + Rid string `protobuf:"bytes,1,opt,name=rid,proto3" json:"rid,omitempty"` + // The encore name of the bucket. + EncoreName string `protobuf:"bytes,2,opt,name=encore_name,json=encoreName,proto3" json:"encore_name,omitempty"` + // The cloud name of the bucket. + CloudName string `protobuf:"bytes,3,opt,name=cloud_name,json=cloudName,proto3" json:"cloud_name,omitempty"` +} + +func (x *Bucket) Reset() { + *x = Bucket{} + if protoimpl.UnsafeEnabled { + mi := &file_encore_runtime_v1_infra_proto_msgTypes[18] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Bucket) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Bucket) ProtoMessage() {} + +func (x *Bucket) ProtoReflect() protoreflect.Message { + mi := &file_encore_runtime_v1_infra_proto_msgTypes[18] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Bucket.ProtoReflect.Descriptor instead. +func (*Bucket) Descriptor() ([]byte, []int) { + return file_encore_runtime_v1_infra_proto_rawDescGZIP(), []int{18} +} + +func (x *Bucket) GetRid() string { + if x != nil { + return x.Rid + } + return "" +} + +func (x *Bucket) GetEncoreName() string { + if x != nil { + return x.EncoreName + } + return "" +} + +func (x *Bucket) GetCloudName() string { + if x != nil { + return x.CloudName + } + return "" +} + type Gateway struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -1543,7 +1681,7 @@ type Gateway struct { func (x *Gateway) Reset() { *x = Gateway{} if protoimpl.UnsafeEnabled { - mi := &file_encore_runtime_v1_infra_proto_msgTypes[17] + mi := &file_encore_runtime_v1_infra_proto_msgTypes[19] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1556,7 +1694,7 @@ func (x *Gateway) String() string { func (*Gateway) ProtoMessage() {} func (x *Gateway) ProtoReflect() protoreflect.Message { - mi := &file_encore_runtime_v1_infra_proto_msgTypes[17] + mi := &file_encore_runtime_v1_infra_proto_msgTypes[19] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1569,7 +1707,7 @@ func (x *Gateway) ProtoReflect() protoreflect.Message { // Deprecated: Use Gateway.ProtoReflect.Descriptor instead. func (*Gateway) Descriptor() ([]byte, []int) { - return file_encore_runtime_v1_infra_proto_rawDescGZIP(), []int{17} + return file_encore_runtime_v1_infra_proto_rawDescGZIP(), []int{19} } func (x *Gateway) GetRid() string { @@ -1620,7 +1758,7 @@ type Infrastructure_Credentials struct { func (x *Infrastructure_Credentials) Reset() { *x = Infrastructure_Credentials{} if protoimpl.UnsafeEnabled { - mi := &file_encore_runtime_v1_infra_proto_msgTypes[18] + mi := &file_encore_runtime_v1_infra_proto_msgTypes[20] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1633,7 +1771,7 @@ func (x *Infrastructure_Credentials) String() string { func (*Infrastructure_Credentials) ProtoMessage() {} func (x *Infrastructure_Credentials) ProtoReflect() protoreflect.Message { - mi := &file_encore_runtime_v1_infra_proto_msgTypes[18] + mi := &file_encore_runtime_v1_infra_proto_msgTypes[20] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1680,12 +1818,13 @@ type Infrastructure_Resources struct { PubsubClusters []*PubSubCluster `protobuf:"bytes,3,rep,name=pubsub_clusters,json=pubsubClusters,proto3" json:"pubsub_clusters,omitempty"` RedisClusters []*RedisCluster `protobuf:"bytes,4,rep,name=redis_clusters,json=redisClusters,proto3" json:"redis_clusters,omitempty"` AppSecrets []*AppSecret `protobuf:"bytes,5,rep,name=app_secrets,json=appSecrets,proto3" json:"app_secrets,omitempty"` + BucketClusters []*BucketCluster `protobuf:"bytes,6,rep,name=bucket_clusters,json=bucketClusters,proto3" json:"bucket_clusters,omitempty"` } func (x *Infrastructure_Resources) Reset() { *x = Infrastructure_Resources{} if protoimpl.UnsafeEnabled { - mi := &file_encore_runtime_v1_infra_proto_msgTypes[19] + mi := &file_encore_runtime_v1_infra_proto_msgTypes[21] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1698,7 +1837,7 @@ func (x *Infrastructure_Resources) String() string { func (*Infrastructure_Resources) ProtoMessage() {} func (x *Infrastructure_Resources) ProtoReflect() protoreflect.Message { - mi := &file_encore_runtime_v1_infra_proto_msgTypes[19] + mi := &file_encore_runtime_v1_infra_proto_msgTypes[21] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1749,6 +1888,13 @@ func (x *Infrastructure_Resources) GetAppSecrets() []*AppSecret { return nil } +func (x *Infrastructure_Resources) GetBucketClusters() []*BucketCluster { + if x != nil { + return x.BucketClusters + } + return nil +} + type RedisRole_AuthACL struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -1761,7 +1907,7 @@ type RedisRole_AuthACL struct { func (x *RedisRole_AuthACL) Reset() { *x = RedisRole_AuthACL{} if protoimpl.UnsafeEnabled { - mi := &file_encore_runtime_v1_infra_proto_msgTypes[20] + mi := &file_encore_runtime_v1_infra_proto_msgTypes[22] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1774,7 +1920,7 @@ func (x *RedisRole_AuthACL) String() string { func (*RedisRole_AuthACL) ProtoMessage() {} func (x *RedisRole_AuthACL) ProtoReflect() protoreflect.Message { - mi := &file_encore_runtime_v1_infra_proto_msgTypes[20] + mi := &file_encore_runtime_v1_infra_proto_msgTypes[22] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1813,7 +1959,7 @@ type PubSubCluster_EncoreCloud struct { func (x *PubSubCluster_EncoreCloud) Reset() { *x = PubSubCluster_EncoreCloud{} if protoimpl.UnsafeEnabled { - mi := &file_encore_runtime_v1_infra_proto_msgTypes[21] + mi := &file_encore_runtime_v1_infra_proto_msgTypes[23] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1826,7 +1972,7 @@ func (x *PubSubCluster_EncoreCloud) String() string { func (*PubSubCluster_EncoreCloud) ProtoMessage() {} func (x *PubSubCluster_EncoreCloud) ProtoReflect() protoreflect.Message { - mi := &file_encore_runtime_v1_infra_proto_msgTypes[21] + mi := &file_encore_runtime_v1_infra_proto_msgTypes[23] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1851,7 +1997,7 @@ type PubSubCluster_AWSSqsSns struct { func (x *PubSubCluster_AWSSqsSns) Reset() { *x = PubSubCluster_AWSSqsSns{} if protoimpl.UnsafeEnabled { - mi := &file_encore_runtime_v1_infra_proto_msgTypes[22] + mi := &file_encore_runtime_v1_infra_proto_msgTypes[24] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1864,7 +2010,7 @@ func (x *PubSubCluster_AWSSqsSns) String() string { func (*PubSubCluster_AWSSqsSns) ProtoMessage() {} func (x *PubSubCluster_AWSSqsSns) ProtoReflect() protoreflect.Message { - mi := &file_encore_runtime_v1_infra_proto_msgTypes[22] + mi := &file_encore_runtime_v1_infra_proto_msgTypes[24] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1889,7 +2035,7 @@ type PubSubCluster_GCPPubSub struct { func (x *PubSubCluster_GCPPubSub) Reset() { *x = PubSubCluster_GCPPubSub{} if protoimpl.UnsafeEnabled { - mi := &file_encore_runtime_v1_infra_proto_msgTypes[23] + mi := &file_encore_runtime_v1_infra_proto_msgTypes[25] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1902,7 +2048,7 @@ func (x *PubSubCluster_GCPPubSub) String() string { func (*PubSubCluster_GCPPubSub) ProtoMessage() {} func (x *PubSubCluster_GCPPubSub) ProtoReflect() protoreflect.Message { - mi := &file_encore_runtime_v1_infra_proto_msgTypes[23] + mi := &file_encore_runtime_v1_infra_proto_msgTypes[25] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1930,7 +2076,7 @@ type PubSubCluster_NSQ struct { func (x *PubSubCluster_NSQ) Reset() { *x = PubSubCluster_NSQ{} if protoimpl.UnsafeEnabled { - mi := &file_encore_runtime_v1_infra_proto_msgTypes[24] + mi := &file_encore_runtime_v1_infra_proto_msgTypes[26] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1943,7 +2089,7 @@ func (x *PubSubCluster_NSQ) String() string { func (*PubSubCluster_NSQ) ProtoMessage() {} func (x *PubSubCluster_NSQ) ProtoReflect() protoreflect.Message { - mi := &file_encore_runtime_v1_infra_proto_msgTypes[24] + mi := &file_encore_runtime_v1_infra_proto_msgTypes[26] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1977,7 +2123,7 @@ type PubSubCluster_AzureServiceBus struct { func (x *PubSubCluster_AzureServiceBus) Reset() { *x = PubSubCluster_AzureServiceBus{} if protoimpl.UnsafeEnabled { - mi := &file_encore_runtime_v1_infra_proto_msgTypes[25] + mi := &file_encore_runtime_v1_infra_proto_msgTypes[27] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1990,7 +2136,7 @@ func (x *PubSubCluster_AzureServiceBus) String() string { func (*PubSubCluster_AzureServiceBus) ProtoMessage() {} func (x *PubSubCluster_AzureServiceBus) ProtoReflect() protoreflect.Message { - mi := &file_encore_runtime_v1_infra_proto_msgTypes[25] + mi := &file_encore_runtime_v1_infra_proto_msgTypes[27] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2025,7 +2171,7 @@ type PubSubTopic_GCPConfig struct { func (x *PubSubTopic_GCPConfig) Reset() { *x = PubSubTopic_GCPConfig{} if protoimpl.UnsafeEnabled { - mi := &file_encore_runtime_v1_infra_proto_msgTypes[26] + mi := &file_encore_runtime_v1_infra_proto_msgTypes[28] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2038,7 +2184,7 @@ func (x *PubSubTopic_GCPConfig) String() string { func (*PubSubTopic_GCPConfig) ProtoMessage() {} func (x *PubSubTopic_GCPConfig) ProtoReflect() protoreflect.Message { - mi := &file_encore_runtime_v1_infra_proto_msgTypes[26] + mi := &file_encore_runtime_v1_infra_proto_msgTypes[28] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2079,7 +2225,7 @@ type PubSubSubscription_GCPConfig struct { func (x *PubSubSubscription_GCPConfig) Reset() { *x = PubSubSubscription_GCPConfig{} if protoimpl.UnsafeEnabled { - mi := &file_encore_runtime_v1_infra_proto_msgTypes[27] + mi := &file_encore_runtime_v1_infra_proto_msgTypes[29] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2092,7 +2238,7 @@ func (x *PubSubSubscription_GCPConfig) String() string { func (*PubSubSubscription_GCPConfig) ProtoMessage() {} func (x *PubSubSubscription_GCPConfig) ProtoReflect() protoreflect.Message { - mi := &file_encore_runtime_v1_infra_proto_msgTypes[27] + mi := &file_encore_runtime_v1_infra_proto_msgTypes[29] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2173,7 +2319,7 @@ type Gateway_CORS struct { func (x *Gateway_CORS) Reset() { *x = Gateway_CORS{} if protoimpl.UnsafeEnabled { - mi := &file_encore_runtime_v1_infra_proto_msgTypes[28] + mi := &file_encore_runtime_v1_infra_proto_msgTypes[30] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2186,7 +2332,7 @@ func (x *Gateway_CORS) String() string { func (*Gateway_CORS) ProtoMessage() {} func (x *Gateway_CORS) ProtoReflect() protoreflect.Message { - mi := &file_encore_runtime_v1_infra_proto_msgTypes[28] + mi := &file_encore_runtime_v1_infra_proto_msgTypes[30] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2199,7 +2345,7 @@ func (x *Gateway_CORS) ProtoReflect() protoreflect.Message { // Deprecated: Use Gateway_CORS.ProtoReflect.Descriptor instead. func (*Gateway_CORS) Descriptor() ([]byte, []int) { - return file_encore_runtime_v1_infra_proto_rawDescGZIP(), []int{17, 0} + return file_encore_runtime_v1_infra_proto_rawDescGZIP(), []int{19, 0} } func (x *Gateway_CORS) GetDebug() bool { @@ -2299,7 +2445,7 @@ type Gateway_CORSAllowedOrigins struct { func (x *Gateway_CORSAllowedOrigins) Reset() { *x = Gateway_CORSAllowedOrigins{} if protoimpl.UnsafeEnabled { - mi := &file_encore_runtime_v1_infra_proto_msgTypes[29] + mi := &file_encore_runtime_v1_infra_proto_msgTypes[31] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2312,7 +2458,7 @@ func (x *Gateway_CORSAllowedOrigins) String() string { func (*Gateway_CORSAllowedOrigins) ProtoMessage() {} func (x *Gateway_CORSAllowedOrigins) ProtoReflect() protoreflect.Message { - mi := &file_encore_runtime_v1_infra_proto_msgTypes[29] + mi := &file_encore_runtime_v1_infra_proto_msgTypes[31] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2325,7 +2471,7 @@ func (x *Gateway_CORSAllowedOrigins) ProtoReflect() protoreflect.Message { // Deprecated: Use Gateway_CORSAllowedOrigins.ProtoReflect.Descriptor instead. func (*Gateway_CORSAllowedOrigins) Descriptor() ([]byte, []int) { - return file_encore_runtime_v1_infra_proto_rawDescGZIP(), []int{17, 1} + return file_encore_runtime_v1_infra_proto_rawDescGZIP(), []int{19, 1} } func (x *Gateway_CORSAllowedOrigins) GetAllowedOrigins() []string { @@ -2343,7 +2489,7 @@ var file_encore_runtime_v1_infra_proto_rawDesc = []byte{ 0x11, 0x65, 0x6e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x1a, 0x22, 0x65, 0x6e, 0x63, 0x6f, 0x72, 0x65, 0x2f, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2f, 0x76, 0x31, 0x2f, 0x73, 0x65, 0x63, 0x72, 0x65, 0x74, 0x64, 0x61, 0x74, 0x61, - 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xd0, 0x05, 0x0a, 0x0e, 0x49, 0x6e, 0x66, 0x72, 0x61, + 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x9b, 0x06, 0x0a, 0x0e, 0x49, 0x6e, 0x66, 0x72, 0x61, 0x73, 0x74, 0x72, 0x75, 0x63, 0x74, 0x75, 0x72, 0x65, 0x12, 0x49, 0x0a, 0x09, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2b, 0x2e, 0x65, 0x6e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, @@ -2367,7 +2513,7 @@ var file_encore_runtime_v1_infra_proto_rawDesc = []byte{ 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x65, 0x6e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x64, 0x69, 0x73, 0x52, 0x6f, 0x6c, 0x65, 0x52, 0x0a, 0x72, 0x65, 0x64, 0x69, 0x73, 0x52, 0x6f, 0x6c, 0x65, 0x73, 0x1a, - 0xd7, 0x02, 0x0a, 0x09, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x12, 0x36, 0x0a, + 0xa2, 0x03, 0x0a, 0x09, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x12, 0x36, 0x0a, 0x08, 0x67, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x65, 0x6e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x52, 0x08, 0x67, 0x61, 0x74, @@ -2388,320 +2534,339 @@ var file_encore_runtime_v1_infra_proto_rawDesc = []byte{ 0x70, 0x5f, 0x73, 0x65, 0x63, 0x72, 0x65, 0x74, 0x73, 0x18, 0x05, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x65, 0x6e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x41, 0x70, 0x70, 0x53, 0x65, 0x63, 0x72, 0x65, 0x74, 0x52, 0x0a, 0x61, - 0x70, 0x70, 0x53, 0x65, 0x63, 0x72, 0x65, 0x74, 0x73, 0x22, 0x94, 0x01, 0x0a, 0x0a, 0x53, 0x51, - 0x4c, 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x12, 0x10, 0x0a, 0x03, 0x72, 0x69, 0x64, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x72, 0x69, 0x64, 0x12, 0x36, 0x0a, 0x07, 0x73, 0x65, - 0x72, 0x76, 0x65, 0x72, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x65, 0x6e, + 0x70, 0x70, 0x53, 0x65, 0x63, 0x72, 0x65, 0x74, 0x73, 0x12, 0x49, 0x0a, 0x0f, 0x62, 0x75, 0x63, + 0x6b, 0x65, 0x74, 0x5f, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x73, 0x18, 0x06, 0x20, 0x03, + 0x28, 0x0b, 0x32, 0x20, 0x2e, 0x65, 0x6e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x72, 0x75, 0x6e, 0x74, + 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x42, 0x75, 0x63, 0x6b, 0x65, 0x74, 0x43, 0x6c, 0x75, + 0x73, 0x74, 0x65, 0x72, 0x52, 0x0e, 0x62, 0x75, 0x63, 0x6b, 0x65, 0x74, 0x43, 0x6c, 0x75, 0x73, + 0x74, 0x65, 0x72, 0x73, 0x22, 0x94, 0x01, 0x0a, 0x0a, 0x53, 0x51, 0x4c, 0x43, 0x6c, 0x75, 0x73, + 0x74, 0x65, 0x72, 0x12, 0x10, 0x0a, 0x03, 0x72, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x03, 0x72, 0x69, 0x64, 0x12, 0x36, 0x0a, 0x07, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x73, + 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x65, 0x6e, 0x63, 0x6f, 0x72, 0x65, 0x2e, + 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x51, 0x4c, 0x53, 0x65, + 0x72, 0x76, 0x65, 0x72, 0x52, 0x07, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x73, 0x12, 0x3c, 0x0a, + 0x09, 0x64, 0x61, 0x74, 0x61, 0x62, 0x61, 0x73, 0x65, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, + 0x32, 0x1e, 0x2e, 0x65, 0x6e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, + 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x51, 0x4c, 0x44, 0x61, 0x74, 0x61, 0x62, 0x61, 0x73, 0x65, + 0x52, 0x09, 0x64, 0x61, 0x74, 0x61, 0x62, 0x61, 0x73, 0x65, 0x73, 0x22, 0x94, 0x01, 0x0a, 0x09, + 0x54, 0x4c, 0x53, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x29, 0x0a, 0x0e, 0x73, 0x65, 0x72, + 0x76, 0x65, 0x72, 0x5f, 0x63, 0x61, 0x5f, 0x63, 0x65, 0x72, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x09, 0x48, 0x00, 0x52, 0x0c, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x43, 0x61, 0x43, 0x65, 0x72, + 0x74, 0x88, 0x01, 0x01, 0x12, 0x49, 0x0a, 0x21, 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x5f, + 0x74, 0x6c, 0x73, 0x5f, 0x68, 0x6f, 0x73, 0x74, 0x6e, 0x61, 0x6d, 0x65, 0x5f, 0x76, 0x65, 0x72, + 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, + 0x1e, 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x54, 0x6c, 0x73, 0x48, 0x6f, 0x73, 0x74, 0x6e, + 0x61, 0x6d, 0x65, 0x56, 0x65, 0x72, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x42, + 0x11, 0x0a, 0x0f, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x63, 0x61, 0x5f, 0x63, 0x65, + 0x72, 0x74, 0x22, 0xb5, 0x01, 0x0a, 0x09, 0x53, 0x51, 0x4c, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, + 0x12, 0x10, 0x0a, 0x03, 0x72, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x72, + 0x69, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x68, 0x6f, 0x73, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x04, 0x68, 0x6f, 0x73, 0x74, 0x12, 0x31, 0x0a, 0x04, 0x6b, 0x69, 0x6e, 0x64, 0x18, 0x03, + 0x20, 0x01, 0x28, 0x0e, 0x32, 0x1d, 0x2e, 0x65, 0x6e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x72, 0x75, + 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x4b, + 0x69, 0x6e, 0x64, 0x52, 0x04, 0x6b, 0x69, 0x6e, 0x64, 0x12, 0x40, 0x0a, 0x0a, 0x74, 0x6c, 0x73, + 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e, + 0x65, 0x6e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, + 0x31, 0x2e, 0x54, 0x4c, 0x53, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x48, 0x00, 0x52, 0x09, 0x74, + 0x6c, 0x73, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x88, 0x01, 0x01, 0x42, 0x0d, 0x0a, 0x0b, 0x5f, + 0x74, 0x6c, 0x73, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x22, 0x63, 0x0a, 0x0a, 0x43, 0x6c, + 0x69, 0x65, 0x6e, 0x74, 0x43, 0x65, 0x72, 0x74, 0x12, 0x10, 0x0a, 0x03, 0x72, 0x69, 0x64, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x72, 0x69, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x63, 0x65, + 0x72, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x63, 0x65, 0x72, 0x74, 0x12, 0x2f, + 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x65, 0x6e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, - 0x53, 0x51, 0x4c, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x52, 0x07, 0x73, 0x65, 0x72, 0x76, 0x65, - 0x72, 0x73, 0x12, 0x3c, 0x0a, 0x09, 0x64, 0x61, 0x74, 0x61, 0x62, 0x61, 0x73, 0x65, 0x73, 0x18, - 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1e, 0x2e, 0x65, 0x6e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x72, - 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x51, 0x4c, 0x44, 0x61, 0x74, - 0x61, 0x62, 0x61, 0x73, 0x65, 0x52, 0x09, 0x64, 0x61, 0x74, 0x61, 0x62, 0x61, 0x73, 0x65, 0x73, - 0x22, 0x94, 0x01, 0x0a, 0x09, 0x54, 0x4c, 0x53, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x29, - 0x0a, 0x0e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x63, 0x61, 0x5f, 0x63, 0x65, 0x72, 0x74, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x48, 0x00, 0x52, 0x0c, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, - 0x43, 0x61, 0x43, 0x65, 0x72, 0x74, 0x88, 0x01, 0x01, 0x12, 0x49, 0x0a, 0x21, 0x64, 0x69, 0x73, - 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x74, 0x6c, 0x73, 0x5f, 0x68, 0x6f, 0x73, 0x74, 0x6e, 0x61, 0x6d, - 0x65, 0x5f, 0x76, 0x65, 0x72, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x02, - 0x20, 0x01, 0x28, 0x08, 0x52, 0x1e, 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x54, 0x6c, 0x73, - 0x48, 0x6f, 0x73, 0x74, 0x6e, 0x61, 0x6d, 0x65, 0x56, 0x65, 0x72, 0x69, 0x66, 0x69, 0x63, 0x61, - 0x74, 0x69, 0x6f, 0x6e, 0x42, 0x11, 0x0a, 0x0f, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, - 0x63, 0x61, 0x5f, 0x63, 0x65, 0x72, 0x74, 0x22, 0xb5, 0x01, 0x0a, 0x09, 0x53, 0x51, 0x4c, 0x53, - 0x65, 0x72, 0x76, 0x65, 0x72, 0x12, 0x10, 0x0a, 0x03, 0x72, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x03, 0x72, 0x69, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x68, 0x6f, 0x73, 0x74, 0x18, - 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x68, 0x6f, 0x73, 0x74, 0x12, 0x31, 0x0a, 0x04, 0x6b, - 0x69, 0x6e, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x1d, 0x2e, 0x65, 0x6e, 0x63, 0x6f, - 0x72, 0x65, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, - 0x72, 0x76, 0x65, 0x72, 0x4b, 0x69, 0x6e, 0x64, 0x52, 0x04, 0x6b, 0x69, 0x6e, 0x64, 0x12, 0x40, - 0x0a, 0x0a, 0x74, 0x6c, 0x73, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x18, 0x04, 0x20, 0x01, - 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x65, 0x6e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x72, 0x75, 0x6e, 0x74, - 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x4c, 0x53, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, - 0x48, 0x00, 0x52, 0x09, 0x74, 0x6c, 0x73, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x88, 0x01, 0x01, - 0x42, 0x0d, 0x0a, 0x0b, 0x5f, 0x74, 0x6c, 0x73, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x22, - 0x63, 0x0a, 0x0a, 0x43, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x43, 0x65, 0x72, 0x74, 0x12, 0x10, 0x0a, + 0x53, 0x65, 0x63, 0x72, 0x65, 0x74, 0x44, 0x61, 0x74, 0x61, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x22, + 0xb3, 0x01, 0x0a, 0x07, 0x53, 0x51, 0x4c, 0x52, 0x6f, 0x6c, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x72, + 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x72, 0x69, 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, 0x12, 0x39, 0x0a, 0x08, 0x70, 0x61, 0x73, + 0x73, 0x77, 0x6f, 0x72, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x65, 0x6e, + 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, + 0x53, 0x65, 0x63, 0x72, 0x65, 0x74, 0x44, 0x61, 0x74, 0x61, 0x52, 0x08, 0x70, 0x61, 0x73, 0x73, + 0x77, 0x6f, 0x72, 0x64, 0x12, 0x2b, 0x0a, 0x0f, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x5f, 0x63, + 0x65, 0x72, 0x74, 0x5f, 0x72, 0x69, 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x48, 0x00, 0x52, + 0x0d, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x43, 0x65, 0x72, 0x74, 0x52, 0x69, 0x64, 0x88, 0x01, + 0x01, 0x42, 0x12, 0x0a, 0x10, 0x5f, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x5f, 0x63, 0x65, 0x72, + 0x74, 0x5f, 0x72, 0x69, 0x64, 0x22, 0xa4, 0x01, 0x0a, 0x0b, 0x53, 0x51, 0x4c, 0x44, 0x61, 0x74, + 0x61, 0x62, 0x61, 0x73, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x72, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x03, 0x72, 0x69, 0x64, 0x12, 0x1f, 0x0a, 0x0b, 0x65, 0x6e, 0x63, 0x6f, 0x72, + 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x65, 0x6e, + 0x63, 0x6f, 0x72, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x1d, 0x0a, 0x0a, 0x63, 0x6c, 0x6f, 0x75, + 0x64, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x63, 0x6c, + 0x6f, 0x75, 0x64, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x43, 0x0a, 0x0a, 0x63, 0x6f, 0x6e, 0x6e, 0x5f, + 0x70, 0x6f, 0x6f, 0x6c, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x24, 0x2e, 0x65, 0x6e, + 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, + 0x53, 0x51, 0x4c, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x50, 0x6f, 0x6f, + 0x6c, 0x52, 0x09, 0x63, 0x6f, 0x6e, 0x6e, 0x50, 0x6f, 0x6f, 0x6c, 0x73, 0x22, 0xa1, 0x01, 0x0a, + 0x11, 0x53, 0x51, 0x4c, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x50, 0x6f, + 0x6f, 0x6c, 0x12, 0x1f, 0x0a, 0x0b, 0x69, 0x73, 0x5f, 0x72, 0x65, 0x61, 0x64, 0x6f, 0x6e, 0x6c, + 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0a, 0x69, 0x73, 0x52, 0x65, 0x61, 0x64, 0x6f, + 0x6e, 0x6c, 0x79, 0x12, 0x19, 0x0a, 0x08, 0x72, 0x6f, 0x6c, 0x65, 0x5f, 0x72, 0x69, 0x64, 0x18, + 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x72, 0x6f, 0x6c, 0x65, 0x52, 0x69, 0x64, 0x12, 0x27, + 0x0a, 0x0f, 0x6d, 0x69, 0x6e, 0x5f, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, + 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0e, 0x6d, 0x69, 0x6e, 0x43, 0x6f, 0x6e, 0x6e, + 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x27, 0x0a, 0x0f, 0x6d, 0x61, 0x78, 0x5f, 0x63, + 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x05, + 0x52, 0x0e, 0x6d, 0x61, 0x78, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, + 0x22, 0x9a, 0x01, 0x0a, 0x0c, 0x52, 0x65, 0x64, 0x69, 0x73, 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, + 0x72, 0x12, 0x10, 0x0a, 0x03, 0x72, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, + 0x72, 0x69, 0x64, 0x12, 0x38, 0x0a, 0x07, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x73, 0x18, 0x02, + 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1e, 0x2e, 0x65, 0x6e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x72, 0x75, + 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x64, 0x69, 0x73, 0x53, 0x65, + 0x72, 0x76, 0x65, 0x72, 0x52, 0x07, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x73, 0x12, 0x3e, 0x0a, + 0x09, 0x64, 0x61, 0x74, 0x61, 0x62, 0x61, 0x73, 0x65, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, + 0x32, 0x20, 0x2e, 0x65, 0x6e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, + 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x64, 0x69, 0x73, 0x44, 0x61, 0x74, 0x61, 0x62, 0x61, + 0x73, 0x65, 0x52, 0x09, 0x64, 0x61, 0x74, 0x61, 0x62, 0x61, 0x73, 0x65, 0x73, 0x22, 0xb7, 0x01, + 0x0a, 0x0b, 0x52, 0x65, 0x64, 0x69, 0x73, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x12, 0x10, 0x0a, 0x03, 0x72, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x72, 0x69, 0x64, 0x12, - 0x12, 0x0a, 0x04, 0x63, 0x65, 0x72, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x63, - 0x65, 0x72, 0x74, 0x12, 0x2f, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, - 0x32, 0x1d, 0x2e, 0x65, 0x6e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, - 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x63, 0x72, 0x65, 0x74, 0x44, 0x61, 0x74, 0x61, 0x52, - 0x03, 0x6b, 0x65, 0x79, 0x22, 0xb3, 0x01, 0x0a, 0x07, 0x53, 0x51, 0x4c, 0x52, 0x6f, 0x6c, 0x65, - 0x12, 0x10, 0x0a, 0x03, 0x72, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x72, - 0x69, 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, 0x12, 0x39, - 0x0a, 0x08, 0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, - 0x32, 0x1d, 0x2e, 0x65, 0x6e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, - 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x63, 0x72, 0x65, 0x74, 0x44, 0x61, 0x74, 0x61, 0x52, - 0x08, 0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x12, 0x2b, 0x0a, 0x0f, 0x63, 0x6c, 0x69, - 0x65, 0x6e, 0x74, 0x5f, 0x63, 0x65, 0x72, 0x74, 0x5f, 0x72, 0x69, 0x64, 0x18, 0x04, 0x20, 0x01, - 0x28, 0x09, 0x48, 0x00, 0x52, 0x0d, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x43, 0x65, 0x72, 0x74, - 0x52, 0x69, 0x64, 0x88, 0x01, 0x01, 0x42, 0x12, 0x0a, 0x10, 0x5f, 0x63, 0x6c, 0x69, 0x65, 0x6e, - 0x74, 0x5f, 0x63, 0x65, 0x72, 0x74, 0x5f, 0x72, 0x69, 0x64, 0x22, 0xa4, 0x01, 0x0a, 0x0b, 0x53, - 0x51, 0x4c, 0x44, 0x61, 0x74, 0x61, 0x62, 0x61, 0x73, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x72, 0x69, - 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x72, 0x69, 0x64, 0x12, 0x1f, 0x0a, 0x0b, - 0x65, 0x6e, 0x63, 0x6f, 0x72, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x0a, 0x65, 0x6e, 0x63, 0x6f, 0x72, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x1d, 0x0a, - 0x0a, 0x63, 0x6c, 0x6f, 0x75, 0x64, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x09, 0x63, 0x6c, 0x6f, 0x75, 0x64, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x43, 0x0a, 0x0a, - 0x63, 0x6f, 0x6e, 0x6e, 0x5f, 0x70, 0x6f, 0x6f, 0x6c, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, - 0x32, 0x24, 0x2e, 0x65, 0x6e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, - 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x51, 0x4c, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, - 0x6f, 0x6e, 0x50, 0x6f, 0x6f, 0x6c, 0x52, 0x09, 0x63, 0x6f, 0x6e, 0x6e, 0x50, 0x6f, 0x6f, 0x6c, - 0x73, 0x22, 0xa1, 0x01, 0x0a, 0x11, 0x53, 0x51, 0x4c, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, - 0x69, 0x6f, 0x6e, 0x50, 0x6f, 0x6f, 0x6c, 0x12, 0x1f, 0x0a, 0x0b, 0x69, 0x73, 0x5f, 0x72, 0x65, - 0x61, 0x64, 0x6f, 0x6e, 0x6c, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0a, 0x69, 0x73, - 0x52, 0x65, 0x61, 0x64, 0x6f, 0x6e, 0x6c, 0x79, 0x12, 0x19, 0x0a, 0x08, 0x72, 0x6f, 0x6c, 0x65, - 0x5f, 0x72, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x72, 0x6f, 0x6c, 0x65, - 0x52, 0x69, 0x64, 0x12, 0x27, 0x0a, 0x0f, 0x6d, 0x69, 0x6e, 0x5f, 0x63, 0x6f, 0x6e, 0x6e, 0x65, - 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0e, 0x6d, 0x69, - 0x6e, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x27, 0x0a, 0x0f, - 0x6d, 0x61, 0x78, 0x5f, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, - 0x04, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0e, 0x6d, 0x61, 0x78, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, - 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x22, 0x9a, 0x01, 0x0a, 0x0c, 0x52, 0x65, 0x64, 0x69, 0x73, 0x43, - 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x12, 0x10, 0x0a, 0x03, 0x72, 0x69, 0x64, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x03, 0x72, 0x69, 0x64, 0x12, 0x38, 0x0a, 0x07, 0x73, 0x65, 0x72, 0x76, - 0x65, 0x72, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1e, 0x2e, 0x65, 0x6e, 0x63, 0x6f, - 0x72, 0x65, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, - 0x64, 0x69, 0x73, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x52, 0x07, 0x73, 0x65, 0x72, 0x76, 0x65, - 0x72, 0x73, 0x12, 0x3e, 0x0a, 0x09, 0x64, 0x61, 0x74, 0x61, 0x62, 0x61, 0x73, 0x65, 0x73, 0x18, - 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x20, 0x2e, 0x65, 0x6e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x72, - 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x64, 0x69, 0x73, 0x44, - 0x61, 0x74, 0x61, 0x62, 0x61, 0x73, 0x65, 0x52, 0x09, 0x64, 0x61, 0x74, 0x61, 0x62, 0x61, 0x73, - 0x65, 0x73, 0x22, 0xb7, 0x01, 0x0a, 0x0b, 0x52, 0x65, 0x64, 0x69, 0x73, 0x53, 0x65, 0x72, 0x76, - 0x65, 0x72, 0x12, 0x10, 0x0a, 0x03, 0x72, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x03, 0x72, 0x69, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x68, 0x6f, 0x73, 0x74, 0x18, 0x02, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x04, 0x68, 0x6f, 0x73, 0x74, 0x12, 0x31, 0x0a, 0x04, 0x6b, 0x69, 0x6e, 0x64, - 0x18, 0x03, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x1d, 0x2e, 0x65, 0x6e, 0x63, 0x6f, 0x72, 0x65, 0x2e, - 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x72, 0x76, 0x65, - 0x72, 0x4b, 0x69, 0x6e, 0x64, 0x52, 0x04, 0x6b, 0x69, 0x6e, 0x64, 0x12, 0x40, 0x0a, 0x0a, 0x74, - 0x6c, 0x73, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, - 0x1c, 0x2e, 0x65, 0x6e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, - 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x4c, 0x53, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x48, 0x00, 0x52, - 0x09, 0x74, 0x6c, 0x73, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x88, 0x01, 0x01, 0x42, 0x0d, 0x0a, - 0x0b, 0x5f, 0x74, 0x6c, 0x73, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x22, 0xa3, 0x01, 0x0a, - 0x13, 0x52, 0x65, 0x64, 0x69, 0x73, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, - 0x50, 0x6f, 0x6f, 0x6c, 0x12, 0x1f, 0x0a, 0x0b, 0x69, 0x73, 0x5f, 0x72, 0x65, 0x61, 0x64, 0x6f, - 0x6e, 0x6c, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0a, 0x69, 0x73, 0x52, 0x65, 0x61, - 0x64, 0x6f, 0x6e, 0x6c, 0x79, 0x12, 0x19, 0x0a, 0x08, 0x72, 0x6f, 0x6c, 0x65, 0x5f, 0x72, 0x69, - 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x72, 0x6f, 0x6c, 0x65, 0x52, 0x69, 0x64, - 0x12, 0x27, 0x0a, 0x0f, 0x6d, 0x69, 0x6e, 0x5f, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, - 0x6f, 0x6e, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0e, 0x6d, 0x69, 0x6e, 0x43, 0x6f, - 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x27, 0x0a, 0x0f, 0x6d, 0x61, 0x78, - 0x5f, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x04, 0x20, 0x01, - 0x28, 0x05, 0x52, 0x0e, 0x6d, 0x61, 0x78, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f, - 0x6e, 0x73, 0x22, 0xc4, 0x02, 0x0a, 0x09, 0x52, 0x65, 0x64, 0x69, 0x73, 0x52, 0x6f, 0x6c, 0x65, - 0x12, 0x10, 0x0a, 0x03, 0x72, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x72, - 0x69, 0x64, 0x12, 0x2b, 0x0a, 0x0f, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x5f, 0x63, 0x65, 0x72, - 0x74, 0x5f, 0x72, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x48, 0x01, 0x52, 0x0d, 0x63, - 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x43, 0x65, 0x72, 0x74, 0x52, 0x69, 0x64, 0x88, 0x01, 0x01, 0x12, - 0x38, 0x0a, 0x03, 0x61, 0x63, 0x6c, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x24, 0x2e, 0x65, - 0x6e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, - 0x2e, 0x52, 0x65, 0x64, 0x69, 0x73, 0x52, 0x6f, 0x6c, 0x65, 0x2e, 0x41, 0x75, 0x74, 0x68, 0x41, - 0x43, 0x4c, 0x48, 0x00, 0x52, 0x03, 0x61, 0x63, 0x6c, 0x12, 0x40, 0x0a, 0x0b, 0x61, 0x75, 0x74, - 0x68, 0x5f, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1d, - 0x2e, 0x65, 0x6e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, - 0x76, 0x31, 0x2e, 0x53, 0x65, 0x63, 0x72, 0x65, 0x74, 0x44, 0x61, 0x74, 0x61, 0x48, 0x00, 0x52, - 0x0a, 0x61, 0x75, 0x74, 0x68, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x1a, 0x60, 0x0a, 0x07, 0x41, - 0x75, 0x74, 0x68, 0x41, 0x43, 0x4c, 0x12, 0x1a, 0x0a, 0x08, 0x75, 0x73, 0x65, 0x72, 0x6e, 0x61, - 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x75, 0x73, 0x65, 0x72, 0x6e, 0x61, - 0x6d, 0x65, 0x12, 0x39, 0x0a, 0x08, 0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x18, 0x02, + 0x12, 0x0a, 0x04, 0x68, 0x6f, 0x73, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x68, + 0x6f, 0x73, 0x74, 0x12, 0x31, 0x0a, 0x04, 0x6b, 0x69, 0x6e, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, + 0x0e, 0x32, 0x1d, 0x2e, 0x65, 0x6e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, + 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x4b, 0x69, 0x6e, 0x64, + 0x52, 0x04, 0x6b, 0x69, 0x6e, 0x64, 0x12, 0x40, 0x0a, 0x0a, 0x74, 0x6c, 0x73, 0x5f, 0x63, 0x6f, + 0x6e, 0x66, 0x69, 0x67, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x65, 0x6e, 0x63, + 0x6f, 0x72, 0x65, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x54, + 0x4c, 0x53, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x48, 0x00, 0x52, 0x09, 0x74, 0x6c, 0x73, 0x43, + 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x88, 0x01, 0x01, 0x42, 0x0d, 0x0a, 0x0b, 0x5f, 0x74, 0x6c, 0x73, + 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x22, 0xa3, 0x01, 0x0a, 0x13, 0x52, 0x65, 0x64, 0x69, + 0x73, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x50, 0x6f, 0x6f, 0x6c, 0x12, + 0x1f, 0x0a, 0x0b, 0x69, 0x73, 0x5f, 0x72, 0x65, 0x61, 0x64, 0x6f, 0x6e, 0x6c, 0x79, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x08, 0x52, 0x0a, 0x69, 0x73, 0x52, 0x65, 0x61, 0x64, 0x6f, 0x6e, 0x6c, 0x79, + 0x12, 0x19, 0x0a, 0x08, 0x72, 0x6f, 0x6c, 0x65, 0x5f, 0x72, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x07, 0x72, 0x6f, 0x6c, 0x65, 0x52, 0x69, 0x64, 0x12, 0x27, 0x0a, 0x0f, 0x6d, + 0x69, 0x6e, 0x5f, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x03, + 0x20, 0x01, 0x28, 0x05, 0x52, 0x0e, 0x6d, 0x69, 0x6e, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, + 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x27, 0x0a, 0x0f, 0x6d, 0x61, 0x78, 0x5f, 0x63, 0x6f, 0x6e, 0x6e, + 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0e, 0x6d, + 0x61, 0x78, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x22, 0xc4, 0x02, + 0x0a, 0x09, 0x52, 0x65, 0x64, 0x69, 0x73, 0x52, 0x6f, 0x6c, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x72, + 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x72, 0x69, 0x64, 0x12, 0x2b, 0x0a, + 0x0f, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x5f, 0x63, 0x65, 0x72, 0x74, 0x5f, 0x72, 0x69, 0x64, + 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x48, 0x01, 0x52, 0x0d, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, + 0x43, 0x65, 0x72, 0x74, 0x52, 0x69, 0x64, 0x88, 0x01, 0x01, 0x12, 0x38, 0x0a, 0x03, 0x61, 0x63, + 0x6c, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x24, 0x2e, 0x65, 0x6e, 0x63, 0x6f, 0x72, 0x65, + 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x64, 0x69, + 0x73, 0x52, 0x6f, 0x6c, 0x65, 0x2e, 0x41, 0x75, 0x74, 0x68, 0x41, 0x43, 0x4c, 0x48, 0x00, 0x52, + 0x03, 0x61, 0x63, 0x6c, 0x12, 0x40, 0x0a, 0x0b, 0x61, 0x75, 0x74, 0x68, 0x5f, 0x73, 0x74, 0x72, + 0x69, 0x6e, 0x67, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x65, 0x6e, 0x63, 0x6f, + 0x72, 0x65, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, + 0x63, 0x72, 0x65, 0x74, 0x44, 0x61, 0x74, 0x61, 0x48, 0x00, 0x52, 0x0a, 0x61, 0x75, 0x74, 0x68, + 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x1a, 0x60, 0x0a, 0x07, 0x41, 0x75, 0x74, 0x68, 0x41, 0x43, + 0x4c, 0x12, 0x1a, 0x0a, 0x08, 0x75, 0x73, 0x65, 0x72, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x08, 0x75, 0x73, 0x65, 0x72, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x39, 0x0a, + 0x08, 0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x1d, 0x2e, 0x65, 0x6e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, + 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x63, 0x72, 0x65, 0x74, 0x44, 0x61, 0x74, 0x61, 0x52, 0x08, + 0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x42, 0x06, 0x0a, 0x04, 0x61, 0x75, 0x74, 0x68, + 0x42, 0x12, 0x0a, 0x10, 0x5f, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x5f, 0x63, 0x65, 0x72, 0x74, + 0x5f, 0x72, 0x69, 0x64, 0x22, 0xdf, 0x01, 0x0a, 0x0d, 0x52, 0x65, 0x64, 0x69, 0x73, 0x44, 0x61, + 0x74, 0x61, 0x62, 0x61, 0x73, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x72, 0x69, 0x64, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x03, 0x72, 0x69, 0x64, 0x12, 0x1f, 0x0a, 0x0b, 0x65, 0x6e, 0x63, 0x6f, + 0x72, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x65, + 0x6e, 0x63, 0x6f, 0x72, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x21, 0x0a, 0x0c, 0x64, 0x61, 0x74, + 0x61, 0x62, 0x61, 0x73, 0x65, 0x5f, 0x69, 0x64, 0x78, 0x18, 0x03, 0x20, 0x01, 0x28, 0x05, 0x52, + 0x0b, 0x64, 0x61, 0x74, 0x61, 0x62, 0x61, 0x73, 0x65, 0x49, 0x64, 0x78, 0x12, 0x22, 0x0a, 0x0a, + 0x6b, 0x65, 0x79, 0x5f, 0x70, 0x72, 0x65, 0x66, 0x69, 0x78, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, + 0x48, 0x00, 0x52, 0x09, 0x6b, 0x65, 0x79, 0x50, 0x72, 0x65, 0x66, 0x69, 0x78, 0x88, 0x01, 0x01, + 0x12, 0x45, 0x0a, 0x0a, 0x63, 0x6f, 0x6e, 0x6e, 0x5f, 0x70, 0x6f, 0x6f, 0x6c, 0x73, 0x18, 0x05, + 0x20, 0x03, 0x28, 0x0b, 0x32, 0x26, 0x2e, 0x65, 0x6e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x72, 0x75, + 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x64, 0x69, 0x73, 0x43, 0x6f, + 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x50, 0x6f, 0x6f, 0x6c, 0x52, 0x09, 0x63, 0x6f, + 0x6e, 0x6e, 0x50, 0x6f, 0x6f, 0x6c, 0x73, 0x42, 0x0d, 0x0a, 0x0b, 0x5f, 0x6b, 0x65, 0x79, 0x5f, + 0x70, 0x72, 0x65, 0x66, 0x69, 0x78, 0x22, 0x71, 0x0a, 0x09, 0x41, 0x70, 0x70, 0x53, 0x65, 0x63, + 0x72, 0x65, 0x74, 0x12, 0x10, 0x0a, 0x03, 0x72, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x03, 0x72, 0x69, 0x64, 0x12, 0x1f, 0x0a, 0x0b, 0x65, 0x6e, 0x63, 0x6f, 0x72, 0x65, 0x5f, + 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x65, 0x6e, 0x63, 0x6f, + 0x72, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x31, 0x0a, 0x04, 0x64, 0x61, 0x74, 0x61, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x65, 0x6e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x63, 0x72, 0x65, 0x74, 0x44, - 0x61, 0x74, 0x61, 0x52, 0x08, 0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x42, 0x06, 0x0a, - 0x04, 0x61, 0x75, 0x74, 0x68, 0x42, 0x12, 0x0a, 0x10, 0x5f, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, - 0x5f, 0x63, 0x65, 0x72, 0x74, 0x5f, 0x72, 0x69, 0x64, 0x22, 0xdf, 0x01, 0x0a, 0x0d, 0x52, 0x65, - 0x64, 0x69, 0x73, 0x44, 0x61, 0x74, 0x61, 0x62, 0x61, 0x73, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x72, - 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x72, 0x69, 0x64, 0x12, 0x1f, 0x0a, - 0x0b, 0x65, 0x6e, 0x63, 0x6f, 0x72, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x0a, 0x65, 0x6e, 0x63, 0x6f, 0x72, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x21, - 0x0a, 0x0c, 0x64, 0x61, 0x74, 0x61, 0x62, 0x61, 0x73, 0x65, 0x5f, 0x69, 0x64, 0x78, 0x18, 0x03, - 0x20, 0x01, 0x28, 0x05, 0x52, 0x0b, 0x64, 0x61, 0x74, 0x61, 0x62, 0x61, 0x73, 0x65, 0x49, 0x64, - 0x78, 0x12, 0x22, 0x0a, 0x0a, 0x6b, 0x65, 0x79, 0x5f, 0x70, 0x72, 0x65, 0x66, 0x69, 0x78, 0x18, - 0x04, 0x20, 0x01, 0x28, 0x09, 0x48, 0x00, 0x52, 0x09, 0x6b, 0x65, 0x79, 0x50, 0x72, 0x65, 0x66, - 0x69, 0x78, 0x88, 0x01, 0x01, 0x12, 0x45, 0x0a, 0x0a, 0x63, 0x6f, 0x6e, 0x6e, 0x5f, 0x70, 0x6f, - 0x6f, 0x6c, 0x73, 0x18, 0x05, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x26, 0x2e, 0x65, 0x6e, 0x63, 0x6f, - 0x72, 0x65, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, - 0x64, 0x69, 0x73, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x50, 0x6f, 0x6f, - 0x6c, 0x52, 0x09, 0x63, 0x6f, 0x6e, 0x6e, 0x50, 0x6f, 0x6f, 0x6c, 0x73, 0x42, 0x0d, 0x0a, 0x0b, - 0x5f, 0x6b, 0x65, 0x79, 0x5f, 0x70, 0x72, 0x65, 0x66, 0x69, 0x78, 0x22, 0x71, 0x0a, 0x09, 0x41, - 0x70, 0x70, 0x53, 0x65, 0x63, 0x72, 0x65, 0x74, 0x12, 0x10, 0x0a, 0x03, 0x72, 0x69, 0x64, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x72, 0x69, 0x64, 0x12, 0x1f, 0x0a, 0x0b, 0x65, 0x6e, - 0x63, 0x6f, 0x72, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x0a, 0x65, 0x6e, 0x63, 0x6f, 0x72, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x31, 0x0a, 0x04, 0x64, - 0x61, 0x74, 0x61, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x65, 0x6e, 0x63, 0x6f, - 0x72, 0x65, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, - 0x63, 0x72, 0x65, 0x74, 0x44, 0x61, 0x74, 0x61, 0x52, 0x04, 0x64, 0x61, 0x74, 0x61, 0x22, 0xf5, - 0x04, 0x0a, 0x0d, 0x50, 0x75, 0x62, 0x53, 0x75, 0x62, 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, - 0x12, 0x10, 0x0a, 0x03, 0x72, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x72, - 0x69, 0x64, 0x12, 0x36, 0x0a, 0x06, 0x74, 0x6f, 0x70, 0x69, 0x63, 0x73, 0x18, 0x02, 0x20, 0x03, - 0x28, 0x0b, 0x32, 0x1e, 0x2e, 0x65, 0x6e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x72, 0x75, 0x6e, 0x74, + 0x61, 0x74, 0x61, 0x52, 0x04, 0x64, 0x61, 0x74, 0x61, 0x22, 0xf5, 0x04, 0x0a, 0x0d, 0x50, 0x75, + 0x62, 0x53, 0x75, 0x62, 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x12, 0x10, 0x0a, 0x03, 0x72, + 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x72, 0x69, 0x64, 0x12, 0x36, 0x0a, + 0x06, 0x74, 0x6f, 0x70, 0x69, 0x63, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1e, 0x2e, + 0x65, 0x6e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, + 0x31, 0x2e, 0x50, 0x75, 0x62, 0x53, 0x75, 0x62, 0x54, 0x6f, 0x70, 0x69, 0x63, 0x52, 0x06, 0x74, + 0x6f, 0x70, 0x69, 0x63, 0x73, 0x12, 0x4b, 0x0a, 0x0d, 0x73, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, + 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x25, 0x2e, 0x65, + 0x6e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, + 0x2e, 0x50, 0x75, 0x62, 0x53, 0x75, 0x62, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, + 0x69, 0x6f, 0x6e, 0x52, 0x0d, 0x73, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, + 0x6e, 0x73, 0x12, 0x46, 0x0a, 0x06, 0x65, 0x6e, 0x63, 0x6f, 0x72, 0x65, 0x18, 0x05, 0x20, 0x01, + 0x28, 0x0b, 0x32, 0x2c, 0x2e, 0x65, 0x6e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x72, 0x75, 0x6e, 0x74, + 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x75, 0x62, 0x53, 0x75, 0x62, 0x43, 0x6c, 0x75, + 0x73, 0x74, 0x65, 0x72, 0x2e, 0x45, 0x6e, 0x63, 0x6f, 0x72, 0x65, 0x43, 0x6c, 0x6f, 0x75, 0x64, + 0x48, 0x00, 0x52, 0x06, 0x65, 0x6e, 0x63, 0x6f, 0x72, 0x65, 0x12, 0x3e, 0x0a, 0x03, 0x61, 0x77, + 0x73, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2a, 0x2e, 0x65, 0x6e, 0x63, 0x6f, 0x72, 0x65, + 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x75, 0x62, 0x53, + 0x75, 0x62, 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x2e, 0x41, 0x57, 0x53, 0x53, 0x71, 0x73, + 0x53, 0x6e, 0x73, 0x48, 0x00, 0x52, 0x03, 0x61, 0x77, 0x73, 0x12, 0x3e, 0x0a, 0x03, 0x67, 0x63, + 0x70, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2a, 0x2e, 0x65, 0x6e, 0x63, 0x6f, 0x72, 0x65, + 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x75, 0x62, 0x53, + 0x75, 0x62, 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x2e, 0x47, 0x43, 0x50, 0x50, 0x75, 0x62, + 0x53, 0x75, 0x62, 0x48, 0x00, 0x52, 0x03, 0x67, 0x63, 0x70, 0x12, 0x48, 0x0a, 0x05, 0x61, 0x7a, + 0x75, 0x72, 0x65, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x30, 0x2e, 0x65, 0x6e, 0x63, 0x6f, + 0x72, 0x65, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x75, + 0x62, 0x53, 0x75, 0x62, 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x2e, 0x41, 0x7a, 0x75, 0x72, + 0x65, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x42, 0x75, 0x73, 0x48, 0x00, 0x52, 0x05, 0x61, + 0x7a, 0x75, 0x72, 0x65, 0x12, 0x38, 0x0a, 0x03, 0x6e, 0x73, 0x71, 0x18, 0x09, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x24, 0x2e, 0x65, 0x6e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, + 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x75, 0x62, 0x53, 0x75, 0x62, 0x43, 0x6c, 0x75, 0x73, + 0x74, 0x65, 0x72, 0x2e, 0x4e, 0x53, 0x51, 0x48, 0x00, 0x52, 0x03, 0x6e, 0x73, 0x71, 0x1a, 0x0d, + 0x0a, 0x0b, 0x45, 0x6e, 0x63, 0x6f, 0x72, 0x65, 0x43, 0x6c, 0x6f, 0x75, 0x64, 0x1a, 0x0b, 0x0a, + 0x09, 0x41, 0x57, 0x53, 0x53, 0x71, 0x73, 0x53, 0x6e, 0x73, 0x1a, 0x0b, 0x0a, 0x09, 0x47, 0x43, + 0x50, 0x50, 0x75, 0x62, 0x53, 0x75, 0x62, 0x1a, 0x1b, 0x0a, 0x03, 0x4e, 0x53, 0x51, 0x12, 0x14, + 0x0a, 0x05, 0x68, 0x6f, 0x73, 0x74, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x05, 0x68, + 0x6f, 0x73, 0x74, 0x73, 0x1a, 0x2f, 0x0a, 0x0f, 0x41, 0x7a, 0x75, 0x72, 0x65, 0x53, 0x65, 0x72, + 0x76, 0x69, 0x63, 0x65, 0x42, 0x75, 0x73, 0x12, 0x1c, 0x0a, 0x09, 0x6e, 0x61, 0x6d, 0x65, 0x73, + 0x70, 0x61, 0x63, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x6e, 0x61, 0x6d, 0x65, + 0x73, 0x70, 0x61, 0x63, 0x65, 0x42, 0x0a, 0x0a, 0x08, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, + 0x72, 0x22, 0x8b, 0x04, 0x0a, 0x0b, 0x50, 0x75, 0x62, 0x53, 0x75, 0x62, 0x54, 0x6f, 0x70, 0x69, + 0x63, 0x12, 0x10, 0x0a, 0x03, 0x72, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, + 0x72, 0x69, 0x64, 0x12, 0x1f, 0x0a, 0x0b, 0x65, 0x6e, 0x63, 0x6f, 0x72, 0x65, 0x5f, 0x6e, 0x61, + 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x65, 0x6e, 0x63, 0x6f, 0x72, 0x65, + 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x1d, 0x0a, 0x0a, 0x63, 0x6c, 0x6f, 0x75, 0x64, 0x5f, 0x6e, 0x61, + 0x6d, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x63, 0x6c, 0x6f, 0x75, 0x64, 0x4e, + 0x61, 0x6d, 0x65, 0x12, 0x5f, 0x0a, 0x12, 0x64, 0x65, 0x6c, 0x69, 0x76, 0x65, 0x72, 0x79, 0x5f, + 0x67, 0x75, 0x61, 0x72, 0x61, 0x6e, 0x74, 0x65, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0e, 0x32, + 0x30, 0x2e, 0x65, 0x6e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, + 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x75, 0x62, 0x53, 0x75, 0x62, 0x54, 0x6f, 0x70, 0x69, 0x63, 0x2e, + 0x44, 0x65, 0x6c, 0x69, 0x76, 0x65, 0x72, 0x79, 0x47, 0x75, 0x61, 0x72, 0x61, 0x6e, 0x74, 0x65, + 0x65, 0x52, 0x11, 0x64, 0x65, 0x6c, 0x69, 0x76, 0x65, 0x72, 0x79, 0x47, 0x75, 0x61, 0x72, 0x61, + 0x6e, 0x74, 0x65, 0x65, 0x12, 0x28, 0x0a, 0x0d, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x69, 0x6e, 0x67, + 0x5f, 0x61, 0x74, 0x74, 0x72, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x48, 0x01, 0x52, 0x0c, 0x6f, + 0x72, 0x64, 0x65, 0x72, 0x69, 0x6e, 0x67, 0x41, 0x74, 0x74, 0x72, 0x88, 0x01, 0x01, 0x12, 0x49, + 0x0a, 0x0a, 0x67, 0x63, 0x70, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x18, 0x0a, 0x20, 0x01, + 0x28, 0x0b, 0x32, 0x28, 0x2e, 0x65, 0x6e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x75, 0x62, 0x53, 0x75, 0x62, 0x54, 0x6f, 0x70, - 0x69, 0x63, 0x52, 0x06, 0x74, 0x6f, 0x70, 0x69, 0x63, 0x73, 0x12, 0x4b, 0x0a, 0x0d, 0x73, 0x75, - 0x62, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, - 0x0b, 0x32, 0x25, 0x2e, 0x65, 0x6e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, + 0x69, 0x63, 0x2e, 0x47, 0x43, 0x50, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x48, 0x00, 0x52, 0x09, + 0x67, 0x63, 0x70, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x1a, 0x2a, 0x0a, 0x09, 0x47, 0x43, 0x50, + 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x1d, 0x0a, 0x0a, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, + 0x74, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x70, 0x72, 0x6f, 0x6a, + 0x65, 0x63, 0x74, 0x49, 0x64, 0x22, 0x82, 0x01, 0x0a, 0x11, 0x44, 0x65, 0x6c, 0x69, 0x76, 0x65, + 0x72, 0x79, 0x47, 0x75, 0x61, 0x72, 0x61, 0x6e, 0x74, 0x65, 0x65, 0x12, 0x22, 0x0a, 0x1e, 0x44, + 0x45, 0x4c, 0x49, 0x56, 0x45, 0x52, 0x59, 0x5f, 0x47, 0x55, 0x41, 0x52, 0x41, 0x4e, 0x54, 0x45, + 0x45, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, + 0x24, 0x0a, 0x20, 0x44, 0x45, 0x4c, 0x49, 0x56, 0x45, 0x52, 0x59, 0x5f, 0x47, 0x55, 0x41, 0x52, + 0x41, 0x4e, 0x54, 0x45, 0x45, 0x5f, 0x41, 0x54, 0x5f, 0x4c, 0x45, 0x41, 0x53, 0x54, 0x5f, 0x4f, + 0x4e, 0x43, 0x45, 0x10, 0x01, 0x12, 0x23, 0x0a, 0x1f, 0x44, 0x45, 0x4c, 0x49, 0x56, 0x45, 0x52, + 0x59, 0x5f, 0x47, 0x55, 0x41, 0x52, 0x41, 0x4e, 0x54, 0x45, 0x45, 0x5f, 0x45, 0x58, 0x41, 0x43, + 0x54, 0x4c, 0x59, 0x5f, 0x4f, 0x4e, 0x43, 0x45, 0x10, 0x02, 0x42, 0x11, 0x0a, 0x0f, 0x70, 0x72, + 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x42, 0x10, 0x0a, + 0x0e, 0x5f, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x69, 0x6e, 0x67, 0x5f, 0x61, 0x74, 0x74, 0x72, 0x22, + 0xb4, 0x04, 0x0a, 0x12, 0x50, 0x75, 0x62, 0x53, 0x75, 0x62, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, + 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x10, 0x0a, 0x03, 0x72, 0x69, 0x64, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x03, 0x72, 0x69, 0x64, 0x12, 0x2a, 0x0a, 0x11, 0x74, 0x6f, 0x70, 0x69, + 0x63, 0x5f, 0x65, 0x6e, 0x63, 0x6f, 0x72, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x0f, 0x74, 0x6f, 0x70, 0x69, 0x63, 0x45, 0x6e, 0x63, 0x6f, 0x72, 0x65, + 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x38, 0x0a, 0x18, 0x73, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x70, + 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x65, 0x6e, 0x63, 0x6f, 0x72, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, + 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x16, 0x73, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x70, + 0x74, 0x69, 0x6f, 0x6e, 0x45, 0x6e, 0x63, 0x6f, 0x72, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x28, + 0x0a, 0x10, 0x74, 0x6f, 0x70, 0x69, 0x63, 0x5f, 0x63, 0x6c, 0x6f, 0x75, 0x64, 0x5f, 0x6e, 0x61, + 0x6d, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x74, 0x6f, 0x70, 0x69, 0x63, 0x43, + 0x6c, 0x6f, 0x75, 0x64, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x36, 0x0a, 0x17, 0x73, 0x75, 0x62, 0x73, + 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x63, 0x6c, 0x6f, 0x75, 0x64, 0x5f, 0x6e, + 0x61, 0x6d, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x15, 0x73, 0x75, 0x62, 0x73, 0x63, + 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x43, 0x6c, 0x6f, 0x75, 0x64, 0x4e, 0x61, 0x6d, 0x65, + 0x12, 0x1b, 0x0a, 0x09, 0x70, 0x75, 0x73, 0x68, 0x5f, 0x6f, 0x6e, 0x6c, 0x79, 0x18, 0x06, 0x20, + 0x01, 0x28, 0x08, 0x52, 0x08, 0x70, 0x75, 0x73, 0x68, 0x4f, 0x6e, 0x6c, 0x79, 0x12, 0x50, 0x0a, + 0x0a, 0x67, 0x63, 0x70, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x18, 0x0a, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x2f, 0x2e, 0x65, 0x6e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x75, 0x62, 0x53, 0x75, 0x62, 0x53, 0x75, 0x62, 0x73, - 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x0d, 0x73, 0x75, 0x62, 0x73, 0x63, 0x72, - 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x46, 0x0a, 0x06, 0x65, 0x6e, 0x63, 0x6f, 0x72, - 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2c, 0x2e, 0x65, 0x6e, 0x63, 0x6f, 0x72, 0x65, - 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x75, 0x62, 0x53, - 0x75, 0x62, 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x2e, 0x45, 0x6e, 0x63, 0x6f, 0x72, 0x65, - 0x43, 0x6c, 0x6f, 0x75, 0x64, 0x48, 0x00, 0x52, 0x06, 0x65, 0x6e, 0x63, 0x6f, 0x72, 0x65, 0x12, - 0x3e, 0x0a, 0x03, 0x61, 0x77, 0x73, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2a, 0x2e, 0x65, - 0x6e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, - 0x2e, 0x50, 0x75, 0x62, 0x53, 0x75, 0x62, 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x2e, 0x41, - 0x57, 0x53, 0x53, 0x71, 0x73, 0x53, 0x6e, 0x73, 0x48, 0x00, 0x52, 0x03, 0x61, 0x77, 0x73, 0x12, - 0x3e, 0x0a, 0x03, 0x67, 0x63, 0x70, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2a, 0x2e, 0x65, - 0x6e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, - 0x2e, 0x50, 0x75, 0x62, 0x53, 0x75, 0x62, 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x2e, 0x47, - 0x43, 0x50, 0x50, 0x75, 0x62, 0x53, 0x75, 0x62, 0x48, 0x00, 0x52, 0x03, 0x67, 0x63, 0x70, 0x12, - 0x48, 0x0a, 0x05, 0x61, 0x7a, 0x75, 0x72, 0x65, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x30, + 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x47, 0x43, 0x50, 0x43, 0x6f, 0x6e, 0x66, + 0x69, 0x67, 0x48, 0x00, 0x52, 0x09, 0x67, 0x63, 0x70, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x1a, + 0xc1, 0x01, 0x0a, 0x09, 0x47, 0x43, 0x50, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x1d, 0x0a, + 0x0a, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x09, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x49, 0x64, 0x12, 0x35, 0x0a, 0x14, + 0x70, 0x75, 0x73, 0x68, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x5f, 0x61, 0x63, 0x63, + 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x48, 0x00, 0x52, 0x12, 0x70, 0x75, + 0x73, 0x68, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, + 0x88, 0x01, 0x01, 0x12, 0x2f, 0x0a, 0x11, 0x70, 0x75, 0x73, 0x68, 0x5f, 0x6a, 0x77, 0x74, 0x5f, + 0x61, 0x75, 0x64, 0x69, 0x65, 0x6e, 0x63, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x48, 0x01, + 0x52, 0x0f, 0x70, 0x75, 0x73, 0x68, 0x4a, 0x77, 0x74, 0x41, 0x75, 0x64, 0x69, 0x65, 0x6e, 0x63, + 0x65, 0x88, 0x01, 0x01, 0x42, 0x17, 0x0a, 0x15, 0x5f, 0x70, 0x75, 0x73, 0x68, 0x5f, 0x73, 0x65, + 0x72, 0x76, 0x69, 0x63, 0x65, 0x5f, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x42, 0x14, 0x0a, + 0x12, 0x5f, 0x70, 0x75, 0x73, 0x68, 0x5f, 0x6a, 0x77, 0x74, 0x5f, 0x61, 0x75, 0x64, 0x69, 0x65, + 0x6e, 0x63, 0x65, 0x42, 0x11, 0x0a, 0x0f, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x5f, + 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x22, 0x8a, 0x01, 0x0a, 0x0d, 0x42, 0x75, 0x63, 0x6b, 0x65, + 0x74, 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x12, 0x10, 0x0a, 0x03, 0x72, 0x69, 0x64, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x72, 0x69, 0x64, 0x12, 0x33, 0x0a, 0x07, 0x62, 0x75, + 0x63, 0x6b, 0x65, 0x74, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x65, 0x6e, + 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, + 0x42, 0x75, 0x63, 0x6b, 0x65, 0x74, 0x52, 0x07, 0x62, 0x75, 0x63, 0x6b, 0x65, 0x74, 0x73, 0x12, + 0x16, 0x0a, 0x06, 0x72, 0x65, 0x67, 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x06, 0x72, 0x65, 0x67, 0x69, 0x6f, 0x6e, 0x12, 0x1a, 0x0a, 0x08, 0x65, 0x6e, 0x64, 0x70, 0x6f, + 0x69, 0x6e, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x65, 0x6e, 0x64, 0x70, 0x6f, + 0x69, 0x6e, 0x74, 0x22, 0x5a, 0x0a, 0x06, 0x42, 0x75, 0x63, 0x6b, 0x65, 0x74, 0x12, 0x10, 0x0a, + 0x03, 0x72, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x72, 0x69, 0x64, 0x12, + 0x1f, 0x0a, 0x0b, 0x65, 0x6e, 0x63, 0x6f, 0x72, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x65, 0x6e, 0x63, 0x6f, 0x72, 0x65, 0x4e, 0x61, 0x6d, 0x65, + 0x12, 0x1d, 0x0a, 0x0a, 0x63, 0x6c, 0x6f, 0x75, 0x64, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x03, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x63, 0x6c, 0x6f, 0x75, 0x64, 0x4e, 0x61, 0x6d, 0x65, 0x22, + 0xb9, 0x06, 0x0a, 0x07, 0x47, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x72, + 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x72, 0x69, 0x64, 0x12, 0x1f, 0x0a, + 0x0b, 0x65, 0x6e, 0x63, 0x6f, 0x72, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x0a, 0x65, 0x6e, 0x63, 0x6f, 0x72, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x19, + 0x0a, 0x08, 0x62, 0x61, 0x73, 0x65, 0x5f, 0x75, 0x72, 0x6c, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x07, 0x62, 0x61, 0x73, 0x65, 0x55, 0x72, 0x6c, 0x12, 0x1c, 0x0a, 0x09, 0x68, 0x6f, 0x73, + 0x74, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x09, 0x52, 0x09, 0x68, 0x6f, + 0x73, 0x74, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x12, 0x33, 0x0a, 0x04, 0x63, 0x6f, 0x72, 0x73, 0x18, + 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x65, 0x6e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x72, + 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x61, 0x74, 0x65, 0x77, 0x61, + 0x79, 0x2e, 0x43, 0x4f, 0x52, 0x53, 0x52, 0x04, 0x63, 0x6f, 0x72, 0x73, 0x1a, 0xcd, 0x04, 0x0a, + 0x04, 0x43, 0x4f, 0x52, 0x53, 0x12, 0x14, 0x0a, 0x05, 0x64, 0x65, 0x62, 0x75, 0x67, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x08, 0x52, 0x05, 0x64, 0x65, 0x62, 0x75, 0x67, 0x12, 0x2f, 0x0a, 0x13, 0x64, + 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x63, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, + 0x6c, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x12, 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, + 0x65, 0x43, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x73, 0x12, 0x58, 0x0a, 0x0f, + 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x65, 0x64, 0x5f, 0x6f, 0x72, 0x69, 0x67, 0x69, 0x6e, 0x73, 0x18, + 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2d, 0x2e, 0x65, 0x6e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x72, + 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x61, 0x74, 0x65, 0x77, 0x61, + 0x79, 0x2e, 0x43, 0x4f, 0x52, 0x53, 0x41, 0x6c, 0x6c, 0x6f, 0x77, 0x65, 0x64, 0x4f, 0x72, 0x69, + 0x67, 0x69, 0x6e, 0x73, 0x48, 0x00, 0x52, 0x0e, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x65, 0x64, 0x4f, + 0x72, 0x69, 0x67, 0x69, 0x6e, 0x73, 0x12, 0x59, 0x0a, 0x29, 0x75, 0x6e, 0x73, 0x61, 0x66, 0x65, + 0x5f, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x5f, 0x61, 0x6c, 0x6c, 0x5f, 0x6f, 0x72, 0x69, 0x67, 0x69, + 0x6e, 0x73, 0x5f, 0x77, 0x69, 0x74, 0x68, 0x5f, 0x63, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, + 0x61, 0x6c, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x48, 0x00, 0x52, 0x24, 0x75, 0x6e, 0x73, + 0x61, 0x66, 0x65, 0x41, 0x6c, 0x6c, 0x6f, 0x77, 0x41, 0x6c, 0x6c, 0x4f, 0x72, 0x69, 0x67, 0x69, + 0x6e, 0x73, 0x57, 0x69, 0x74, 0x68, 0x43, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, + 0x73, 0x12, 0x7c, 0x0a, 0x23, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x65, 0x64, 0x5f, 0x6f, 0x72, 0x69, + 0x67, 0x69, 0x6e, 0x73, 0x5f, 0x77, 0x69, 0x74, 0x68, 0x6f, 0x75, 0x74, 0x5f, 0x63, 0x72, 0x65, + 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x73, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2d, 0x2e, 0x65, 0x6e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, - 0x76, 0x31, 0x2e, 0x50, 0x75, 0x62, 0x53, 0x75, 0x62, 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, - 0x2e, 0x41, 0x7a, 0x75, 0x72, 0x65, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x42, 0x75, 0x73, - 0x48, 0x00, 0x52, 0x05, 0x61, 0x7a, 0x75, 0x72, 0x65, 0x12, 0x38, 0x0a, 0x03, 0x6e, 0x73, 0x71, - 0x18, 0x09, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x24, 0x2e, 0x65, 0x6e, 0x63, 0x6f, 0x72, 0x65, 0x2e, - 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x75, 0x62, 0x53, 0x75, - 0x62, 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x2e, 0x4e, 0x53, 0x51, 0x48, 0x00, 0x52, 0x03, - 0x6e, 0x73, 0x71, 0x1a, 0x0d, 0x0a, 0x0b, 0x45, 0x6e, 0x63, 0x6f, 0x72, 0x65, 0x43, 0x6c, 0x6f, - 0x75, 0x64, 0x1a, 0x0b, 0x0a, 0x09, 0x41, 0x57, 0x53, 0x53, 0x71, 0x73, 0x53, 0x6e, 0x73, 0x1a, - 0x0b, 0x0a, 0x09, 0x47, 0x43, 0x50, 0x50, 0x75, 0x62, 0x53, 0x75, 0x62, 0x1a, 0x1b, 0x0a, 0x03, - 0x4e, 0x53, 0x51, 0x12, 0x14, 0x0a, 0x05, 0x68, 0x6f, 0x73, 0x74, 0x73, 0x18, 0x01, 0x20, 0x03, - 0x28, 0x09, 0x52, 0x05, 0x68, 0x6f, 0x73, 0x74, 0x73, 0x1a, 0x2f, 0x0a, 0x0f, 0x41, 0x7a, 0x75, - 0x72, 0x65, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x42, 0x75, 0x73, 0x12, 0x1c, 0x0a, 0x09, - 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x09, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x42, 0x0a, 0x0a, 0x08, 0x70, 0x72, - 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x22, 0x8b, 0x04, 0x0a, 0x0b, 0x50, 0x75, 0x62, 0x53, 0x75, - 0x62, 0x54, 0x6f, 0x70, 0x69, 0x63, 0x12, 0x10, 0x0a, 0x03, 0x72, 0x69, 0x64, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x03, 0x72, 0x69, 0x64, 0x12, 0x1f, 0x0a, 0x0b, 0x65, 0x6e, 0x63, 0x6f, - 0x72, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x65, - 0x6e, 0x63, 0x6f, 0x72, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x1d, 0x0a, 0x0a, 0x63, 0x6c, 0x6f, - 0x75, 0x64, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x63, - 0x6c, 0x6f, 0x75, 0x64, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x5f, 0x0a, 0x12, 0x64, 0x65, 0x6c, 0x69, - 0x76, 0x65, 0x72, 0x79, 0x5f, 0x67, 0x75, 0x61, 0x72, 0x61, 0x6e, 0x74, 0x65, 0x65, 0x18, 0x04, - 0x20, 0x01, 0x28, 0x0e, 0x32, 0x30, 0x2e, 0x65, 0x6e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x72, 0x75, - 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x75, 0x62, 0x53, 0x75, 0x62, 0x54, - 0x6f, 0x70, 0x69, 0x63, 0x2e, 0x44, 0x65, 0x6c, 0x69, 0x76, 0x65, 0x72, 0x79, 0x47, 0x75, 0x61, - 0x72, 0x61, 0x6e, 0x74, 0x65, 0x65, 0x52, 0x11, 0x64, 0x65, 0x6c, 0x69, 0x76, 0x65, 0x72, 0x79, - 0x47, 0x75, 0x61, 0x72, 0x61, 0x6e, 0x74, 0x65, 0x65, 0x12, 0x28, 0x0a, 0x0d, 0x6f, 0x72, 0x64, - 0x65, 0x72, 0x69, 0x6e, 0x67, 0x5f, 0x61, 0x74, 0x74, 0x72, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, - 0x48, 0x01, 0x52, 0x0c, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x69, 0x6e, 0x67, 0x41, 0x74, 0x74, 0x72, - 0x88, 0x01, 0x01, 0x12, 0x49, 0x0a, 0x0a, 0x67, 0x63, 0x70, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69, - 0x67, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x28, 0x2e, 0x65, 0x6e, 0x63, 0x6f, 0x72, 0x65, - 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x75, 0x62, 0x53, - 0x75, 0x62, 0x54, 0x6f, 0x70, 0x69, 0x63, 0x2e, 0x47, 0x43, 0x50, 0x43, 0x6f, 0x6e, 0x66, 0x69, - 0x67, 0x48, 0x00, 0x52, 0x09, 0x67, 0x63, 0x70, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x1a, 0x2a, - 0x0a, 0x09, 0x47, 0x43, 0x50, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x1d, 0x0a, 0x0a, 0x70, - 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x09, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x49, 0x64, 0x22, 0x82, 0x01, 0x0a, 0x11, 0x44, - 0x65, 0x6c, 0x69, 0x76, 0x65, 0x72, 0x79, 0x47, 0x75, 0x61, 0x72, 0x61, 0x6e, 0x74, 0x65, 0x65, - 0x12, 0x22, 0x0a, 0x1e, 0x44, 0x45, 0x4c, 0x49, 0x56, 0x45, 0x52, 0x59, 0x5f, 0x47, 0x55, 0x41, - 0x52, 0x41, 0x4e, 0x54, 0x45, 0x45, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, - 0x45, 0x44, 0x10, 0x00, 0x12, 0x24, 0x0a, 0x20, 0x44, 0x45, 0x4c, 0x49, 0x56, 0x45, 0x52, 0x59, - 0x5f, 0x47, 0x55, 0x41, 0x52, 0x41, 0x4e, 0x54, 0x45, 0x45, 0x5f, 0x41, 0x54, 0x5f, 0x4c, 0x45, - 0x41, 0x53, 0x54, 0x5f, 0x4f, 0x4e, 0x43, 0x45, 0x10, 0x01, 0x12, 0x23, 0x0a, 0x1f, 0x44, 0x45, - 0x4c, 0x49, 0x56, 0x45, 0x52, 0x59, 0x5f, 0x47, 0x55, 0x41, 0x52, 0x41, 0x4e, 0x54, 0x45, 0x45, - 0x5f, 0x45, 0x58, 0x41, 0x43, 0x54, 0x4c, 0x59, 0x5f, 0x4f, 0x4e, 0x43, 0x45, 0x10, 0x02, 0x42, - 0x11, 0x0a, 0x0f, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x5f, 0x63, 0x6f, 0x6e, 0x66, - 0x69, 0x67, 0x42, 0x10, 0x0a, 0x0e, 0x5f, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x69, 0x6e, 0x67, 0x5f, - 0x61, 0x74, 0x74, 0x72, 0x22, 0xb4, 0x04, 0x0a, 0x12, 0x50, 0x75, 0x62, 0x53, 0x75, 0x62, 0x53, - 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x10, 0x0a, 0x03, 0x72, - 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x72, 0x69, 0x64, 0x12, 0x2a, 0x0a, - 0x11, 0x74, 0x6f, 0x70, 0x69, 0x63, 0x5f, 0x65, 0x6e, 0x63, 0x6f, 0x72, 0x65, 0x5f, 0x6e, 0x61, - 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, 0x74, 0x6f, 0x70, 0x69, 0x63, 0x45, - 0x6e, 0x63, 0x6f, 0x72, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x38, 0x0a, 0x18, 0x73, 0x75, 0x62, - 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x65, 0x6e, 0x63, 0x6f, 0x72, 0x65, - 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x16, 0x73, 0x75, 0x62, - 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x45, 0x6e, 0x63, 0x6f, 0x72, 0x65, 0x4e, - 0x61, 0x6d, 0x65, 0x12, 0x28, 0x0a, 0x10, 0x74, 0x6f, 0x70, 0x69, 0x63, 0x5f, 0x63, 0x6c, 0x6f, - 0x75, 0x64, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x74, - 0x6f, 0x70, 0x69, 0x63, 0x43, 0x6c, 0x6f, 0x75, 0x64, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x36, 0x0a, - 0x17, 0x73, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x63, 0x6c, - 0x6f, 0x75, 0x64, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x15, - 0x73, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x43, 0x6c, 0x6f, 0x75, - 0x64, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x1b, 0x0a, 0x09, 0x70, 0x75, 0x73, 0x68, 0x5f, 0x6f, 0x6e, - 0x6c, 0x79, 0x18, 0x06, 0x20, 0x01, 0x28, 0x08, 0x52, 0x08, 0x70, 0x75, 0x73, 0x68, 0x4f, 0x6e, - 0x6c, 0x79, 0x12, 0x50, 0x0a, 0x0a, 0x67, 0x63, 0x70, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, - 0x18, 0x0a, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2f, 0x2e, 0x65, 0x6e, 0x63, 0x6f, 0x72, 0x65, 0x2e, - 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x75, 0x62, 0x53, 0x75, - 0x62, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x47, 0x43, - 0x50, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x48, 0x00, 0x52, 0x09, 0x67, 0x63, 0x70, 0x43, 0x6f, - 0x6e, 0x66, 0x69, 0x67, 0x1a, 0xc1, 0x01, 0x0a, 0x09, 0x47, 0x43, 0x50, 0x43, 0x6f, 0x6e, 0x66, - 0x69, 0x67, 0x12, 0x1d, 0x0a, 0x0a, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x5f, 0x69, 0x64, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x49, - 0x64, 0x12, 0x35, 0x0a, 0x14, 0x70, 0x75, 0x73, 0x68, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, - 0x65, 0x5f, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x48, - 0x00, 0x52, 0x12, 0x70, 0x75, 0x73, 0x68, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x41, 0x63, - 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x88, 0x01, 0x01, 0x12, 0x2f, 0x0a, 0x11, 0x70, 0x75, 0x73, 0x68, - 0x5f, 0x6a, 0x77, 0x74, 0x5f, 0x61, 0x75, 0x64, 0x69, 0x65, 0x6e, 0x63, 0x65, 0x18, 0x03, 0x20, - 0x01, 0x28, 0x09, 0x48, 0x01, 0x52, 0x0f, 0x70, 0x75, 0x73, 0x68, 0x4a, 0x77, 0x74, 0x41, 0x75, - 0x64, 0x69, 0x65, 0x6e, 0x63, 0x65, 0x88, 0x01, 0x01, 0x42, 0x17, 0x0a, 0x15, 0x5f, 0x70, 0x75, - 0x73, 0x68, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x5f, 0x61, 0x63, 0x63, 0x6f, 0x75, - 0x6e, 0x74, 0x42, 0x14, 0x0a, 0x12, 0x5f, 0x70, 0x75, 0x73, 0x68, 0x5f, 0x6a, 0x77, 0x74, 0x5f, - 0x61, 0x75, 0x64, 0x69, 0x65, 0x6e, 0x63, 0x65, 0x42, 0x11, 0x0a, 0x0f, 0x70, 0x72, 0x6f, 0x76, - 0x69, 0x64, 0x65, 0x72, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x22, 0xb9, 0x06, 0x0a, 0x07, - 0x47, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x72, 0x69, 0x64, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x72, 0x69, 0x64, 0x12, 0x1f, 0x0a, 0x0b, 0x65, 0x6e, 0x63, - 0x6f, 0x72, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, - 0x65, 0x6e, 0x63, 0x6f, 0x72, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x19, 0x0a, 0x08, 0x62, 0x61, - 0x73, 0x65, 0x5f, 0x75, 0x72, 0x6c, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x62, 0x61, - 0x73, 0x65, 0x55, 0x72, 0x6c, 0x12, 0x1c, 0x0a, 0x09, 0x68, 0x6f, 0x73, 0x74, 0x6e, 0x61, 0x6d, - 0x65, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x09, 0x52, 0x09, 0x68, 0x6f, 0x73, 0x74, 0x6e, 0x61, - 0x6d, 0x65, 0x73, 0x12, 0x33, 0x0a, 0x04, 0x63, 0x6f, 0x72, 0x73, 0x18, 0x05, 0x20, 0x01, 0x28, - 0x0b, 0x32, 0x1f, 0x2e, 0x65, 0x6e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, - 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x2e, 0x43, 0x4f, - 0x52, 0x53, 0x52, 0x04, 0x63, 0x6f, 0x72, 0x73, 0x1a, 0xcd, 0x04, 0x0a, 0x04, 0x43, 0x4f, 0x52, - 0x53, 0x12, 0x14, 0x0a, 0x05, 0x64, 0x65, 0x62, 0x75, 0x67, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, - 0x52, 0x05, 0x64, 0x65, 0x62, 0x75, 0x67, 0x12, 0x2f, 0x0a, 0x13, 0x64, 0x69, 0x73, 0x61, 0x62, - 0x6c, 0x65, 0x5f, 0x63, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x73, 0x18, 0x02, - 0x20, 0x01, 0x28, 0x08, 0x52, 0x12, 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x43, 0x72, 0x65, - 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x73, 0x12, 0x58, 0x0a, 0x0f, 0x61, 0x6c, 0x6c, 0x6f, - 0x77, 0x65, 0x64, 0x5f, 0x6f, 0x72, 0x69, 0x67, 0x69, 0x6e, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, - 0x0b, 0x32, 0x2d, 0x2e, 0x65, 0x6e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, - 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x2e, 0x43, 0x4f, - 0x52, 0x53, 0x41, 0x6c, 0x6c, 0x6f, 0x77, 0x65, 0x64, 0x4f, 0x72, 0x69, 0x67, 0x69, 0x6e, 0x73, - 0x48, 0x00, 0x52, 0x0e, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x65, 0x64, 0x4f, 0x72, 0x69, 0x67, 0x69, - 0x6e, 0x73, 0x12, 0x59, 0x0a, 0x29, 0x75, 0x6e, 0x73, 0x61, 0x66, 0x65, 0x5f, 0x61, 0x6c, 0x6c, - 0x6f, 0x77, 0x5f, 0x61, 0x6c, 0x6c, 0x5f, 0x6f, 0x72, 0x69, 0x67, 0x69, 0x6e, 0x73, 0x5f, 0x77, - 0x69, 0x74, 0x68, 0x5f, 0x63, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x73, 0x18, - 0x04, 0x20, 0x01, 0x28, 0x08, 0x48, 0x00, 0x52, 0x24, 0x75, 0x6e, 0x73, 0x61, 0x66, 0x65, 0x41, - 0x6c, 0x6c, 0x6f, 0x77, 0x41, 0x6c, 0x6c, 0x4f, 0x72, 0x69, 0x67, 0x69, 0x6e, 0x73, 0x57, 0x69, - 0x74, 0x68, 0x43, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x73, 0x12, 0x7c, 0x0a, - 0x23, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x65, 0x64, 0x5f, 0x6f, 0x72, 0x69, 0x67, 0x69, 0x6e, 0x73, - 0x5f, 0x77, 0x69, 0x74, 0x68, 0x6f, 0x75, 0x74, 0x5f, 0x63, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, - 0x69, 0x61, 0x6c, 0x73, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2d, 0x2e, 0x65, 0x6e, 0x63, - 0x6f, 0x72, 0x65, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x47, - 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x2e, 0x43, 0x4f, 0x52, 0x53, 0x41, 0x6c, 0x6c, 0x6f, 0x77, - 0x65, 0x64, 0x4f, 0x72, 0x69, 0x67, 0x69, 0x6e, 0x73, 0x52, 0x20, 0x61, 0x6c, 0x6c, 0x6f, 0x77, - 0x65, 0x64, 0x4f, 0x72, 0x69, 0x67, 0x69, 0x6e, 0x73, 0x57, 0x69, 0x74, 0x68, 0x6f, 0x75, 0x74, - 0x43, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x73, 0x12, 0x32, 0x0a, 0x15, 0x65, - 0x78, 0x74, 0x72, 0x61, 0x5f, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x65, 0x64, 0x5f, 0x68, 0x65, 0x61, - 0x64, 0x65, 0x72, 0x73, 0x18, 0x06, 0x20, 0x03, 0x28, 0x09, 0x52, 0x13, 0x65, 0x78, 0x74, 0x72, - 0x61, 0x41, 0x6c, 0x6c, 0x6f, 0x77, 0x65, 0x64, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x73, 0x12, - 0x32, 0x0a, 0x15, 0x65, 0x78, 0x74, 0x72, 0x61, 0x5f, 0x65, 0x78, 0x70, 0x6f, 0x73, 0x65, 0x64, - 0x5f, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x73, 0x18, 0x07, 0x20, 0x03, 0x28, 0x09, 0x52, 0x13, - 0x65, 0x78, 0x74, 0x72, 0x61, 0x45, 0x78, 0x70, 0x6f, 0x73, 0x65, 0x64, 0x48, 0x65, 0x61, 0x64, - 0x65, 0x72, 0x73, 0x12, 0x3f, 0x0a, 0x1c, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x5f, 0x70, 0x72, 0x69, - 0x76, 0x61, 0x74, 0x65, 0x5f, 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x5f, 0x61, 0x63, 0x63, - 0x65, 0x73, 0x73, 0x18, 0x08, 0x20, 0x01, 0x28, 0x08, 0x52, 0x19, 0x61, 0x6c, 0x6c, 0x6f, 0x77, - 0x50, 0x72, 0x69, 0x76, 0x61, 0x74, 0x65, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x41, 0x63, - 0x63, 0x65, 0x73, 0x73, 0x42, 0x22, 0x0a, 0x20, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x65, 0x64, 0x5f, - 0x6f, 0x72, 0x69, 0x67, 0x69, 0x6e, 0x73, 0x5f, 0x77, 0x69, 0x74, 0x68, 0x5f, 0x63, 0x72, 0x65, - 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x73, 0x1a, 0x3d, 0x0a, 0x12, 0x43, 0x4f, 0x52, 0x53, - 0x41, 0x6c, 0x6c, 0x6f, 0x77, 0x65, 0x64, 0x4f, 0x72, 0x69, 0x67, 0x69, 0x6e, 0x73, 0x12, 0x27, - 0x0a, 0x0f, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x65, 0x64, 0x5f, 0x6f, 0x72, 0x69, 0x67, 0x69, 0x6e, - 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0e, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x65, 0x64, - 0x4f, 0x72, 0x69, 0x67, 0x69, 0x6e, 0x73, 0x2a, 0x7d, 0x0a, 0x0a, 0x53, 0x65, 0x72, 0x76, 0x65, - 0x72, 0x4b, 0x69, 0x6e, 0x64, 0x12, 0x1b, 0x0a, 0x17, 0x53, 0x45, 0x52, 0x56, 0x45, 0x52, 0x5f, - 0x4b, 0x49, 0x4e, 0x44, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, - 0x10, 0x00, 0x12, 0x17, 0x0a, 0x13, 0x53, 0x45, 0x52, 0x56, 0x45, 0x52, 0x5f, 0x4b, 0x49, 0x4e, - 0x44, 0x5f, 0x50, 0x52, 0x49, 0x4d, 0x41, 0x52, 0x59, 0x10, 0x01, 0x12, 0x1b, 0x0a, 0x17, 0x53, - 0x45, 0x52, 0x56, 0x45, 0x52, 0x5f, 0x4b, 0x49, 0x4e, 0x44, 0x5f, 0x48, 0x4f, 0x54, 0x5f, 0x53, - 0x54, 0x41, 0x4e, 0x44, 0x42, 0x59, 0x10, 0x02, 0x12, 0x1c, 0x0a, 0x18, 0x53, 0x45, 0x52, 0x56, - 0x45, 0x52, 0x5f, 0x4b, 0x49, 0x4e, 0x44, 0x5f, 0x52, 0x45, 0x41, 0x44, 0x5f, 0x52, 0x45, 0x50, - 0x4c, 0x49, 0x43, 0x41, 0x10, 0x03, 0x42, 0x2c, 0x5a, 0x2a, 0x65, 0x6e, 0x63, 0x72, 0x2e, 0x64, - 0x65, 0x76, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x65, 0x6e, 0x63, 0x6f, 0x72, 0x65, 0x2f, - 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2f, 0x76, 0x31, 0x3b, 0x72, 0x75, 0x6e, 0x74, 0x69, - 0x6d, 0x65, 0x76, 0x31, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x76, 0x31, 0x2e, 0x47, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x2e, 0x43, 0x4f, 0x52, 0x53, 0x41, + 0x6c, 0x6c, 0x6f, 0x77, 0x65, 0x64, 0x4f, 0x72, 0x69, 0x67, 0x69, 0x6e, 0x73, 0x52, 0x20, 0x61, + 0x6c, 0x6c, 0x6f, 0x77, 0x65, 0x64, 0x4f, 0x72, 0x69, 0x67, 0x69, 0x6e, 0x73, 0x57, 0x69, 0x74, + 0x68, 0x6f, 0x75, 0x74, 0x43, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x73, 0x12, + 0x32, 0x0a, 0x15, 0x65, 0x78, 0x74, 0x72, 0x61, 0x5f, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x65, 0x64, + 0x5f, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x73, 0x18, 0x06, 0x20, 0x03, 0x28, 0x09, 0x52, 0x13, + 0x65, 0x78, 0x74, 0x72, 0x61, 0x41, 0x6c, 0x6c, 0x6f, 0x77, 0x65, 0x64, 0x48, 0x65, 0x61, 0x64, + 0x65, 0x72, 0x73, 0x12, 0x32, 0x0a, 0x15, 0x65, 0x78, 0x74, 0x72, 0x61, 0x5f, 0x65, 0x78, 0x70, + 0x6f, 0x73, 0x65, 0x64, 0x5f, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x73, 0x18, 0x07, 0x20, 0x03, + 0x28, 0x09, 0x52, 0x13, 0x65, 0x78, 0x74, 0x72, 0x61, 0x45, 0x78, 0x70, 0x6f, 0x73, 0x65, 0x64, + 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x73, 0x12, 0x3f, 0x0a, 0x1c, 0x61, 0x6c, 0x6c, 0x6f, 0x77, + 0x5f, 0x70, 0x72, 0x69, 0x76, 0x61, 0x74, 0x65, 0x5f, 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, + 0x5f, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x18, 0x08, 0x20, 0x01, 0x28, 0x08, 0x52, 0x19, 0x61, + 0x6c, 0x6c, 0x6f, 0x77, 0x50, 0x72, 0x69, 0x76, 0x61, 0x74, 0x65, 0x4e, 0x65, 0x74, 0x77, 0x6f, + 0x72, 0x6b, 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, 0x42, 0x22, 0x0a, 0x20, 0x61, 0x6c, 0x6c, 0x6f, + 0x77, 0x65, 0x64, 0x5f, 0x6f, 0x72, 0x69, 0x67, 0x69, 0x6e, 0x73, 0x5f, 0x77, 0x69, 0x74, 0x68, + 0x5f, 0x63, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x73, 0x1a, 0x3d, 0x0a, 0x12, + 0x43, 0x4f, 0x52, 0x53, 0x41, 0x6c, 0x6c, 0x6f, 0x77, 0x65, 0x64, 0x4f, 0x72, 0x69, 0x67, 0x69, + 0x6e, 0x73, 0x12, 0x27, 0x0a, 0x0f, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x65, 0x64, 0x5f, 0x6f, 0x72, + 0x69, 0x67, 0x69, 0x6e, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0e, 0x61, 0x6c, 0x6c, + 0x6f, 0x77, 0x65, 0x64, 0x4f, 0x72, 0x69, 0x67, 0x69, 0x6e, 0x73, 0x2a, 0x7d, 0x0a, 0x0a, 0x53, + 0x65, 0x72, 0x76, 0x65, 0x72, 0x4b, 0x69, 0x6e, 0x64, 0x12, 0x1b, 0x0a, 0x17, 0x53, 0x45, 0x52, + 0x56, 0x45, 0x52, 0x5f, 0x4b, 0x49, 0x4e, 0x44, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, + 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x17, 0x0a, 0x13, 0x53, 0x45, 0x52, 0x56, 0x45, 0x52, + 0x5f, 0x4b, 0x49, 0x4e, 0x44, 0x5f, 0x50, 0x52, 0x49, 0x4d, 0x41, 0x52, 0x59, 0x10, 0x01, 0x12, + 0x1b, 0x0a, 0x17, 0x53, 0x45, 0x52, 0x56, 0x45, 0x52, 0x5f, 0x4b, 0x49, 0x4e, 0x44, 0x5f, 0x48, + 0x4f, 0x54, 0x5f, 0x53, 0x54, 0x41, 0x4e, 0x44, 0x42, 0x59, 0x10, 0x02, 0x12, 0x1c, 0x0a, 0x18, + 0x53, 0x45, 0x52, 0x56, 0x45, 0x52, 0x5f, 0x4b, 0x49, 0x4e, 0x44, 0x5f, 0x52, 0x45, 0x41, 0x44, + 0x5f, 0x52, 0x45, 0x50, 0x4c, 0x49, 0x43, 0x41, 0x10, 0x03, 0x42, 0x2c, 0x5a, 0x2a, 0x65, 0x6e, + 0x63, 0x72, 0x2e, 0x64, 0x65, 0x76, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x65, 0x6e, 0x63, + 0x6f, 0x72, 0x65, 0x2f, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2f, 0x76, 0x31, 0x3b, 0x72, + 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x76, 0x31, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( @@ -2717,7 +2882,7 @@ func file_encore_runtime_v1_infra_proto_rawDescGZIP() []byte { } var file_encore_runtime_v1_infra_proto_enumTypes = make([]protoimpl.EnumInfo, 2) -var file_encore_runtime_v1_infra_proto_msgTypes = make([]protoimpl.MessageInfo, 30) +var file_encore_runtime_v1_infra_proto_msgTypes = make([]protoimpl.MessageInfo, 32) var file_encore_runtime_v1_infra_proto_goTypes = []interface{}{ (ServerKind)(0), // 0: encore.runtime.v1.ServerKind (PubSubTopic_DeliveryGuarantee)(0), // 1: encore.runtime.v1.PubSubTopic.DeliveryGuarantee @@ -2738,66 +2903,70 @@ var file_encore_runtime_v1_infra_proto_goTypes = []interface{}{ (*PubSubCluster)(nil), // 16: encore.runtime.v1.PubSubCluster (*PubSubTopic)(nil), // 17: encore.runtime.v1.PubSubTopic (*PubSubSubscription)(nil), // 18: encore.runtime.v1.PubSubSubscription - (*Gateway)(nil), // 19: encore.runtime.v1.Gateway - (*Infrastructure_Credentials)(nil), // 20: encore.runtime.v1.Infrastructure.Credentials - (*Infrastructure_Resources)(nil), // 21: encore.runtime.v1.Infrastructure.Resources - (*RedisRole_AuthACL)(nil), // 22: encore.runtime.v1.RedisRole.AuthACL - (*PubSubCluster_EncoreCloud)(nil), // 23: encore.runtime.v1.PubSubCluster.EncoreCloud - (*PubSubCluster_AWSSqsSns)(nil), // 24: encore.runtime.v1.PubSubCluster.AWSSqsSns - (*PubSubCluster_GCPPubSub)(nil), // 25: encore.runtime.v1.PubSubCluster.GCPPubSub - (*PubSubCluster_NSQ)(nil), // 26: encore.runtime.v1.PubSubCluster.NSQ - (*PubSubCluster_AzureServiceBus)(nil), // 27: encore.runtime.v1.PubSubCluster.AzureServiceBus - (*PubSubTopic_GCPConfig)(nil), // 28: encore.runtime.v1.PubSubTopic.GCPConfig - (*PubSubSubscription_GCPConfig)(nil), // 29: encore.runtime.v1.PubSubSubscription.GCPConfig - (*Gateway_CORS)(nil), // 30: encore.runtime.v1.Gateway.CORS - (*Gateway_CORSAllowedOrigins)(nil), // 31: encore.runtime.v1.Gateway.CORSAllowedOrigins - (*SecretData)(nil), // 32: encore.runtime.v1.SecretData + (*BucketCluster)(nil), // 19: encore.runtime.v1.BucketCluster + (*Bucket)(nil), // 20: encore.runtime.v1.Bucket + (*Gateway)(nil), // 21: encore.runtime.v1.Gateway + (*Infrastructure_Credentials)(nil), // 22: encore.runtime.v1.Infrastructure.Credentials + (*Infrastructure_Resources)(nil), // 23: encore.runtime.v1.Infrastructure.Resources + (*RedisRole_AuthACL)(nil), // 24: encore.runtime.v1.RedisRole.AuthACL + (*PubSubCluster_EncoreCloud)(nil), // 25: encore.runtime.v1.PubSubCluster.EncoreCloud + (*PubSubCluster_AWSSqsSns)(nil), // 26: encore.runtime.v1.PubSubCluster.AWSSqsSns + (*PubSubCluster_GCPPubSub)(nil), // 27: encore.runtime.v1.PubSubCluster.GCPPubSub + (*PubSubCluster_NSQ)(nil), // 28: encore.runtime.v1.PubSubCluster.NSQ + (*PubSubCluster_AzureServiceBus)(nil), // 29: encore.runtime.v1.PubSubCluster.AzureServiceBus + (*PubSubTopic_GCPConfig)(nil), // 30: encore.runtime.v1.PubSubTopic.GCPConfig + (*PubSubSubscription_GCPConfig)(nil), // 31: encore.runtime.v1.PubSubSubscription.GCPConfig + (*Gateway_CORS)(nil), // 32: encore.runtime.v1.Gateway.CORS + (*Gateway_CORSAllowedOrigins)(nil), // 33: encore.runtime.v1.Gateway.CORSAllowedOrigins + (*SecretData)(nil), // 34: encore.runtime.v1.SecretData } var file_encore_runtime_v1_infra_proto_depIdxs = []int32{ - 21, // 0: encore.runtime.v1.Infrastructure.resources:type_name -> encore.runtime.v1.Infrastructure.Resources - 20, // 1: encore.runtime.v1.Infrastructure.credentials:type_name -> encore.runtime.v1.Infrastructure.Credentials + 23, // 0: encore.runtime.v1.Infrastructure.resources:type_name -> encore.runtime.v1.Infrastructure.Resources + 22, // 1: encore.runtime.v1.Infrastructure.credentials:type_name -> encore.runtime.v1.Infrastructure.Credentials 5, // 2: encore.runtime.v1.SQLCluster.servers:type_name -> encore.runtime.v1.SQLServer 8, // 3: encore.runtime.v1.SQLCluster.databases:type_name -> encore.runtime.v1.SQLDatabase 0, // 4: encore.runtime.v1.SQLServer.kind:type_name -> encore.runtime.v1.ServerKind 4, // 5: encore.runtime.v1.SQLServer.tls_config:type_name -> encore.runtime.v1.TLSConfig - 32, // 6: encore.runtime.v1.ClientCert.key:type_name -> encore.runtime.v1.SecretData - 32, // 7: encore.runtime.v1.SQLRole.password:type_name -> encore.runtime.v1.SecretData + 34, // 6: encore.runtime.v1.ClientCert.key:type_name -> encore.runtime.v1.SecretData + 34, // 7: encore.runtime.v1.SQLRole.password:type_name -> encore.runtime.v1.SecretData 9, // 8: encore.runtime.v1.SQLDatabase.conn_pools:type_name -> encore.runtime.v1.SQLConnectionPool 11, // 9: encore.runtime.v1.RedisCluster.servers:type_name -> encore.runtime.v1.RedisServer 14, // 10: encore.runtime.v1.RedisCluster.databases:type_name -> encore.runtime.v1.RedisDatabase 0, // 11: encore.runtime.v1.RedisServer.kind:type_name -> encore.runtime.v1.ServerKind 4, // 12: encore.runtime.v1.RedisServer.tls_config:type_name -> encore.runtime.v1.TLSConfig - 22, // 13: encore.runtime.v1.RedisRole.acl:type_name -> encore.runtime.v1.RedisRole.AuthACL - 32, // 14: encore.runtime.v1.RedisRole.auth_string:type_name -> encore.runtime.v1.SecretData + 24, // 13: encore.runtime.v1.RedisRole.acl:type_name -> encore.runtime.v1.RedisRole.AuthACL + 34, // 14: encore.runtime.v1.RedisRole.auth_string:type_name -> encore.runtime.v1.SecretData 12, // 15: encore.runtime.v1.RedisDatabase.conn_pools:type_name -> encore.runtime.v1.RedisConnectionPool - 32, // 16: encore.runtime.v1.AppSecret.data:type_name -> encore.runtime.v1.SecretData + 34, // 16: encore.runtime.v1.AppSecret.data:type_name -> encore.runtime.v1.SecretData 17, // 17: encore.runtime.v1.PubSubCluster.topics:type_name -> encore.runtime.v1.PubSubTopic 18, // 18: encore.runtime.v1.PubSubCluster.subscriptions:type_name -> encore.runtime.v1.PubSubSubscription - 23, // 19: encore.runtime.v1.PubSubCluster.encore:type_name -> encore.runtime.v1.PubSubCluster.EncoreCloud - 24, // 20: encore.runtime.v1.PubSubCluster.aws:type_name -> encore.runtime.v1.PubSubCluster.AWSSqsSns - 25, // 21: encore.runtime.v1.PubSubCluster.gcp:type_name -> encore.runtime.v1.PubSubCluster.GCPPubSub - 27, // 22: encore.runtime.v1.PubSubCluster.azure:type_name -> encore.runtime.v1.PubSubCluster.AzureServiceBus - 26, // 23: encore.runtime.v1.PubSubCluster.nsq:type_name -> encore.runtime.v1.PubSubCluster.NSQ + 25, // 19: encore.runtime.v1.PubSubCluster.encore:type_name -> encore.runtime.v1.PubSubCluster.EncoreCloud + 26, // 20: encore.runtime.v1.PubSubCluster.aws:type_name -> encore.runtime.v1.PubSubCluster.AWSSqsSns + 27, // 21: encore.runtime.v1.PubSubCluster.gcp:type_name -> encore.runtime.v1.PubSubCluster.GCPPubSub + 29, // 22: encore.runtime.v1.PubSubCluster.azure:type_name -> encore.runtime.v1.PubSubCluster.AzureServiceBus + 28, // 23: encore.runtime.v1.PubSubCluster.nsq:type_name -> encore.runtime.v1.PubSubCluster.NSQ 1, // 24: encore.runtime.v1.PubSubTopic.delivery_guarantee:type_name -> encore.runtime.v1.PubSubTopic.DeliveryGuarantee - 28, // 25: encore.runtime.v1.PubSubTopic.gcp_config:type_name -> encore.runtime.v1.PubSubTopic.GCPConfig - 29, // 26: encore.runtime.v1.PubSubSubscription.gcp_config:type_name -> encore.runtime.v1.PubSubSubscription.GCPConfig - 30, // 27: encore.runtime.v1.Gateway.cors:type_name -> encore.runtime.v1.Gateway.CORS - 6, // 28: encore.runtime.v1.Infrastructure.Credentials.client_certs:type_name -> encore.runtime.v1.ClientCert - 7, // 29: encore.runtime.v1.Infrastructure.Credentials.sql_roles:type_name -> encore.runtime.v1.SQLRole - 13, // 30: encore.runtime.v1.Infrastructure.Credentials.redis_roles:type_name -> encore.runtime.v1.RedisRole - 19, // 31: encore.runtime.v1.Infrastructure.Resources.gateways:type_name -> encore.runtime.v1.Gateway - 3, // 32: encore.runtime.v1.Infrastructure.Resources.sql_clusters:type_name -> encore.runtime.v1.SQLCluster - 16, // 33: encore.runtime.v1.Infrastructure.Resources.pubsub_clusters:type_name -> encore.runtime.v1.PubSubCluster - 10, // 34: encore.runtime.v1.Infrastructure.Resources.redis_clusters:type_name -> encore.runtime.v1.RedisCluster - 15, // 35: encore.runtime.v1.Infrastructure.Resources.app_secrets:type_name -> encore.runtime.v1.AppSecret - 32, // 36: encore.runtime.v1.RedisRole.AuthACL.password:type_name -> encore.runtime.v1.SecretData - 31, // 37: encore.runtime.v1.Gateway.CORS.allowed_origins:type_name -> encore.runtime.v1.Gateway.CORSAllowedOrigins - 31, // 38: encore.runtime.v1.Gateway.CORS.allowed_origins_without_credentials:type_name -> encore.runtime.v1.Gateway.CORSAllowedOrigins - 39, // [39:39] is the sub-list for method output_type - 39, // [39:39] is the sub-list for method input_type - 39, // [39:39] is the sub-list for extension type_name - 39, // [39:39] is the sub-list for extension extendee - 0, // [0:39] is the sub-list for field type_name + 30, // 25: encore.runtime.v1.PubSubTopic.gcp_config:type_name -> encore.runtime.v1.PubSubTopic.GCPConfig + 31, // 26: encore.runtime.v1.PubSubSubscription.gcp_config:type_name -> encore.runtime.v1.PubSubSubscription.GCPConfig + 20, // 27: encore.runtime.v1.BucketCluster.buckets:type_name -> encore.runtime.v1.Bucket + 32, // 28: encore.runtime.v1.Gateway.cors:type_name -> encore.runtime.v1.Gateway.CORS + 6, // 29: encore.runtime.v1.Infrastructure.Credentials.client_certs:type_name -> encore.runtime.v1.ClientCert + 7, // 30: encore.runtime.v1.Infrastructure.Credentials.sql_roles:type_name -> encore.runtime.v1.SQLRole + 13, // 31: encore.runtime.v1.Infrastructure.Credentials.redis_roles:type_name -> encore.runtime.v1.RedisRole + 21, // 32: encore.runtime.v1.Infrastructure.Resources.gateways:type_name -> encore.runtime.v1.Gateway + 3, // 33: encore.runtime.v1.Infrastructure.Resources.sql_clusters:type_name -> encore.runtime.v1.SQLCluster + 16, // 34: encore.runtime.v1.Infrastructure.Resources.pubsub_clusters:type_name -> encore.runtime.v1.PubSubCluster + 10, // 35: encore.runtime.v1.Infrastructure.Resources.redis_clusters:type_name -> encore.runtime.v1.RedisCluster + 15, // 36: encore.runtime.v1.Infrastructure.Resources.app_secrets:type_name -> encore.runtime.v1.AppSecret + 19, // 37: encore.runtime.v1.Infrastructure.Resources.bucket_clusters:type_name -> encore.runtime.v1.BucketCluster + 34, // 38: encore.runtime.v1.RedisRole.AuthACL.password:type_name -> encore.runtime.v1.SecretData + 33, // 39: encore.runtime.v1.Gateway.CORS.allowed_origins:type_name -> encore.runtime.v1.Gateway.CORSAllowedOrigins + 33, // 40: encore.runtime.v1.Gateway.CORS.allowed_origins_without_credentials:type_name -> encore.runtime.v1.Gateway.CORSAllowedOrigins + 41, // [41:41] is the sub-list for method output_type + 41, // [41:41] is the sub-list for method input_type + 41, // [41:41] is the sub-list for extension type_name + 41, // [41:41] is the sub-list for extension extendee + 0, // [0:41] is the sub-list for field type_name } func init() { file_encore_runtime_v1_infra_proto_init() } @@ -3012,7 +3181,7 @@ func file_encore_runtime_v1_infra_proto_init() { } } file_encore_runtime_v1_infra_proto_msgTypes[17].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*Gateway); i { + switch v := v.(*BucketCluster); i { case 0: return &v.state case 1: @@ -3024,7 +3193,7 @@ func file_encore_runtime_v1_infra_proto_init() { } } file_encore_runtime_v1_infra_proto_msgTypes[18].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*Infrastructure_Credentials); i { + switch v := v.(*Bucket); i { case 0: return &v.state case 1: @@ -3036,7 +3205,7 @@ func file_encore_runtime_v1_infra_proto_init() { } } file_encore_runtime_v1_infra_proto_msgTypes[19].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*Infrastructure_Resources); i { + switch v := v.(*Gateway); i { case 0: return &v.state case 1: @@ -3048,7 +3217,7 @@ func file_encore_runtime_v1_infra_proto_init() { } } file_encore_runtime_v1_infra_proto_msgTypes[20].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*RedisRole_AuthACL); i { + switch v := v.(*Infrastructure_Credentials); i { case 0: return &v.state case 1: @@ -3060,7 +3229,7 @@ func file_encore_runtime_v1_infra_proto_init() { } } file_encore_runtime_v1_infra_proto_msgTypes[21].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*PubSubCluster_EncoreCloud); i { + switch v := v.(*Infrastructure_Resources); i { case 0: return &v.state case 1: @@ -3072,7 +3241,7 @@ func file_encore_runtime_v1_infra_proto_init() { } } file_encore_runtime_v1_infra_proto_msgTypes[22].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*PubSubCluster_AWSSqsSns); i { + switch v := v.(*RedisRole_AuthACL); i { case 0: return &v.state case 1: @@ -3084,7 +3253,7 @@ func file_encore_runtime_v1_infra_proto_init() { } } file_encore_runtime_v1_infra_proto_msgTypes[23].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*PubSubCluster_GCPPubSub); i { + switch v := v.(*PubSubCluster_EncoreCloud); i { case 0: return &v.state case 1: @@ -3096,7 +3265,7 @@ func file_encore_runtime_v1_infra_proto_init() { } } file_encore_runtime_v1_infra_proto_msgTypes[24].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*PubSubCluster_NSQ); i { + switch v := v.(*PubSubCluster_AWSSqsSns); i { case 0: return &v.state case 1: @@ -3108,7 +3277,7 @@ func file_encore_runtime_v1_infra_proto_init() { } } file_encore_runtime_v1_infra_proto_msgTypes[25].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*PubSubCluster_AzureServiceBus); i { + switch v := v.(*PubSubCluster_GCPPubSub); i { case 0: return &v.state case 1: @@ -3120,7 +3289,7 @@ func file_encore_runtime_v1_infra_proto_init() { } } file_encore_runtime_v1_infra_proto_msgTypes[26].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*PubSubTopic_GCPConfig); i { + switch v := v.(*PubSubCluster_NSQ); i { case 0: return &v.state case 1: @@ -3132,7 +3301,7 @@ func file_encore_runtime_v1_infra_proto_init() { } } file_encore_runtime_v1_infra_proto_msgTypes[27].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*PubSubSubscription_GCPConfig); i { + switch v := v.(*PubSubCluster_AzureServiceBus); i { case 0: return &v.state case 1: @@ -3144,7 +3313,7 @@ func file_encore_runtime_v1_infra_proto_init() { } } file_encore_runtime_v1_infra_proto_msgTypes[28].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*Gateway_CORS); i { + switch v := v.(*PubSubTopic_GCPConfig); i { case 0: return &v.state case 1: @@ -3156,6 +3325,30 @@ func file_encore_runtime_v1_infra_proto_init() { } } file_encore_runtime_v1_infra_proto_msgTypes[29].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*PubSubSubscription_GCPConfig); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_encore_runtime_v1_infra_proto_msgTypes[30].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Gateway_CORS); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_encore_runtime_v1_infra_proto_msgTypes[31].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*Gateway_CORSAllowedOrigins); i { case 0: return &v.state @@ -3190,8 +3383,8 @@ func file_encore_runtime_v1_infra_proto_init() { file_encore_runtime_v1_infra_proto_msgTypes[16].OneofWrappers = []interface{}{ (*PubSubSubscription_GcpConfig)(nil), } - file_encore_runtime_v1_infra_proto_msgTypes[27].OneofWrappers = []interface{}{} - file_encore_runtime_v1_infra_proto_msgTypes[28].OneofWrappers = []interface{}{ + file_encore_runtime_v1_infra_proto_msgTypes[29].OneofWrappers = []interface{}{} + file_encore_runtime_v1_infra_proto_msgTypes[30].OneofWrappers = []interface{}{ (*Gateway_CORS_AllowedOrigins)(nil), (*Gateway_CORS_UnsafeAllowAllOriginsWithCredentials)(nil), } @@ -3201,7 +3394,7 @@ func file_encore_runtime_v1_infra_proto_init() { GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_encore_runtime_v1_infra_proto_rawDesc, NumEnums: 2, - NumMessages: 30, + NumMessages: 32, NumExtensions: 0, NumServices: 0, }, diff --git a/runtimes/core/src/objects/manager.rs b/runtimes/core/src/objects/manager.rs index c3da353aa4..97dca39168 100644 --- a/runtimes/core/src/objects/manager.rs +++ b/runtimes/core/src/objects/manager.rs @@ -5,26 +5,41 @@ use crate::encore::parser::meta::v1 as meta; use crate::encore::runtime::v1 as pb; use crate::names::EncoreName; use crate::objects::noop::NoopCluster; -use crate::objects::{ - noop, Cluster, Bucket, -}; -use crate::trace::{Tracer}; +use crate::objects::{noop, BucketImpl, ClusterImpl, ObjectImpl}; +use crate::trace::Tracer; pub struct Manager { tracer: Tracer, - bucket_cfg: HashMap, pb::Bucket)>, + bucket_cfg: HashMap, pb::Bucket)>, - buckets: Arc>>>, + buckets: Arc>>>, } #[derive(Debug)] -pub struct BucketObj { - name: EncoreName, +pub struct Bucket { tracer: Tracer, - inner: Arc, + imp: Arc, } -impl BucketObj { +impl Bucket { + pub fn object(&self, name: String) -> Object { + Object { + imp: self.imp.object(name), + tracer: self.tracer.clone(), + } + } +} + +#[derive(Debug)] +pub struct Object { + tracer: Tracer, + imp: Arc, +} + +impl Object { + pub async fn exists(&self) -> bool { + self.imp.exists().await + } } impl Manager { @@ -38,16 +53,15 @@ impl Manager { } } - pub fn bucket(&self, name: EncoreName) -> Option { - let inner = self.bucket_impl(name.clone())?; - Some(BucketObj { - name, - inner, + pub fn bucket(&self, name: EncoreName) -> Option { + let imp = self.bucket_impl(name)?; + Some(Bucket { + imp, tracer: self.tracer.clone(), }) } - fn bucket_impl(&self, name: EncoreName) -> Option> { + fn bucket_impl(&self, name: EncoreName) -> Option> { if let Some(bkt) = self.buckets.read().unwrap().get(&name) { return Some(bkt.clone()); } @@ -68,9 +82,7 @@ impl Manager { fn make_cfg_maps( clusters: Vec, md: &meta::Data, -) -> - HashMap, pb::Bucket)> -{ +) -> HashMap, pb::Bucket)> { let mut bucket_map = HashMap::new(); for cluster_cfg in clusters { @@ -87,7 +99,7 @@ fn make_cfg_maps( bucket_map } -fn new_cluster(cluster: &pb::BucketCluster) -> Arc { +fn new_cluster(cluster: &pb::BucketCluster) -> Arc { // let Some(provider) = &cluster.provider else { // log::error!("missing PubSub cluster provider: {}", cluster.rid); // return Arc::new(NoopCluster); diff --git a/runtimes/core/src/objects/mod.rs b/runtimes/core/src/objects/mod.rs index 74293af9e3..9bb3f370e4 100644 --- a/runtimes/core/src/objects/mod.rs +++ b/runtimes/core/src/objects/mod.rs @@ -1,16 +1,23 @@ -use std::fmt::Debug; +use std::future::Future; use std::sync::Arc; +use std::{fmt::Debug, pin::Pin}; -pub use manager::{Manager}; +pub use manager::{Bucket, Manager, Object}; use crate::encore::runtime::v1 as pb; -mod s3; -mod noop; mod manager; +mod noop; +mod s3; -trait Cluster: Debug + Send + Sync { - fn bucket(&self, cfg: &pb::Bucket) -> Arc; +trait ClusterImpl: Debug + Send + Sync { + fn bucket(&self, cfg: &pb::Bucket) -> Arc; } -trait Bucket: Debug + Send + Sync {} +trait BucketImpl: Debug + Send + Sync { + fn object(&self, name: String) -> Arc; +} + +trait ObjectImpl: Debug + Send + Sync { + fn exists(&self) -> Pin + Send + 'static>>; +} diff --git a/runtimes/core/src/objects/noop/mod.rs b/runtimes/core/src/objects/noop/mod.rs index 675619f460..e5ad8cbb5d 100644 --- a/runtimes/core/src/objects/noop/mod.rs +++ b/runtimes/core/src/objects/noop/mod.rs @@ -1,5 +1,9 @@ +use std::future::Future; +use std::pin::Pin; use std::sync::Arc; +use futures::future; + use crate::encore::runtime::v1 as pb; use crate::objects; @@ -9,11 +13,23 @@ pub struct NoopCluster; #[derive(Debug)] pub struct NoopBucket; -impl objects::Cluster for NoopCluster { - fn bucket(&self, _cfg: &pb::Bucket) -> Arc { +#[derive(Debug)] +pub struct NoopObject; + +impl objects::ClusterImpl for NoopCluster { + fn bucket(&self, _cfg: &pb::Bucket) -> Arc { Arc::new(NoopBucket) } } -impl objects::Bucket for NoopBucket { +impl objects::BucketImpl for NoopBucket { + fn object(&self, _name: String) -> Arc { + Arc::new(NoopObject) + } +} + +impl objects::ObjectImpl for NoopObject { + fn exists(&self) -> Pin + Send + 'static>> { + Box::pin(future::ready(false)) + } } diff --git a/runtimes/core/src/objects/s3/bucket.rs b/runtimes/core/src/objects/s3/bucket.rs index 6bdf5bee9e..672c990ab4 100644 --- a/runtimes/core/src/objects/s3/bucket.rs +++ b/runtimes/core/src/objects/s3/bucket.rs @@ -1,19 +1,43 @@ +use std::future::Future; +use std::pin::Pin; +use std::sync::Arc; + +use futures::future; + use crate::encore::runtime::v1 as pb; use crate::objects; #[derive(Debug)] pub struct Bucket { - client: Box, + client: Arc, } impl Bucket { pub(super) fn new(region: s3::Region, creds: s3::creds::Credentials, cfg: &pb::Bucket) -> Self { - let client = s3::Bucket::new(&cfg.cloud_name, region, creds).expect("unable to construct bucket client"); - Self { - client, - } + let client = s3::Bucket::new(&cfg.cloud_name, region, creds) + .expect("unable to construct bucket client"); + let client = Arc::from(client); + Self { client } + } +} + +impl objects::BucketImpl for Bucket { + fn object(&self, name: String) -> Arc { + Arc::new(Object { + client: self.client.clone(), + name, + }) } } -impl objects::Bucket for Bucket { +#[derive(Debug)] +struct Object { + client: Arc, + name: String, +} + +impl objects::ObjectImpl for Object { + fn exists(&self) -> Pin + Send + 'static>> { + Box::pin(future::ready(true)) + } } diff --git a/runtimes/core/src/objects/s3/mod.rs b/runtimes/core/src/objects/s3/mod.rs index 1d37e432b8..53be5f7bb2 100644 --- a/runtimes/core/src/objects/s3/mod.rs +++ b/runtimes/core/src/objects/s3/mod.rs @@ -26,8 +26,8 @@ impl Cluster { } } -impl objects::Cluster for Cluster { - fn bucket(&self, cfg: &pb::Bucket) -> Arc { +impl objects::ClusterImpl for Cluster { + fn bucket(&self, cfg: &pb::Bucket) -> Arc { Arc::new(Bucket::new(self.region.clone(), self.creds.clone(), cfg)) } } diff --git a/runtimes/js/encore.dev/package.json b/runtimes/js/encore.dev/package.json index 72048793d2..7da031e527 100644 --- a/runtimes/js/encore.dev/package.json +++ b/runtimes/js/encore.dev/package.json @@ -64,6 +64,11 @@ "bun": "./storage/sqldb/mod.ts", "default": "./dist/storage/sqldb/mod.js" }, + "./storage/objects": { + "types": "./storage/objects/mod.ts", + "bun": "./storage/objects/mod.ts", + "default": "./dist/storage/objects/mod.js" + }, "./internal/codegen/*": { "types": "./internal/codegen/*.ts", "bun": "./internal/codegen/*.ts", diff --git a/runtimes/js/encore.dev/storage/objects/bucket.ts b/runtimes/js/encore.dev/storage/objects/bucket.ts index f659467291..beea518c9c 100644 --- a/runtimes/js/encore.dev/storage/objects/bucket.ts +++ b/runtimes/js/encore.dev/storage/objects/bucket.ts @@ -9,12 +9,14 @@ export interface BucketConfig { * Defines a new Object Storage bucket infrastructure resource. */ export class Bucket { + impl: runtime.Bucket; + /** * Creates a new bucket with the given name and configuration */ // eslint-disable-next-line @typescript-eslint/no-unused-vars - constructor(name: string, cfg?: BucketConfig) { - // this.impl = runtime.RT.sqlDatabase(name); + constructor(name: string, cfg: BucketConfig) { + this.impl = runtime.RT.bucket(name); } /** @@ -22,7 +24,31 @@ export class Bucket { * To create a new storage bucket, use `new StorageBucket(...)` instead. */ static named(name: StringLiteral): Bucket { - return new Bucket(name); + return new Bucket(name, {}); + } + + object(name: string): BucketObject { + const impl = this.impl.object(name); + return new BucketObject(impl, this, name); + } +} + +export class BucketObject { + private impl: runtime.BucketObject; + public readonly bucket: Bucket; + public readonly name: string; + + constructor(impl: runtime.BucketObject, bucket: Bucket, name: string) { + this.impl = impl; + this.bucket = bucket; + this.name = name; } + /** + * Returns whether the object exists in the bucket. + * Throws an error on network failure. + */ + async exists(): Promise { + return this.impl.exists(); + } } diff --git a/runtimes/js/src/lib.rs b/runtimes/js/src/lib.rs index 79f7099d3c..7f7e4ccc76 100644 --- a/runtimes/js/src/lib.rs +++ b/runtimes/js/src/lib.rs @@ -8,6 +8,7 @@ mod meta; mod napi_util; pub mod pubsub; mod pvalue; +pub mod objects; mod raw_api; mod request_meta; pub mod runtime; diff --git a/runtimes/js/src/objects.rs b/runtimes/js/src/objects.rs new file mode 100644 index 0000000000..7cf8567a6b --- /dev/null +++ b/runtimes/js/src/objects.rs @@ -0,0 +1,37 @@ +use napi_derive::napi; + +use encore_runtime_core::objects::{Bucket as CoreBucket, Object as CoreObject}; + +#[napi] +pub struct Bucket { + bkt: CoreBucket, +} + +#[napi] +impl Bucket { + pub(crate) fn new(bkt: CoreBucket) -> Self { + Self { bkt } + } + + #[napi] + pub fn object(&self, name: String) -> BucketObject { + BucketObject::new(self.bkt.object(name)) + } +} + +#[napi] +pub struct BucketObject { + obj: CoreObject, +} + +#[napi] +impl BucketObject { + pub(crate) fn new(obj: CoreObject) -> Self { + Self { obj } + } + + #[napi] + pub async fn exists(&self) -> bool { + self.obj.exists().await + } +} diff --git a/runtimes/js/src/runtime.rs b/runtimes/js/src/runtime.rs index 67a32e22f4..5173e76182 100644 --- a/runtimes/js/src/runtime.rs +++ b/runtimes/js/src/runtime.rs @@ -5,7 +5,7 @@ use crate::pubsub::{PubSubSubscription, PubSubSubscriptionConfig, PubSubTopic}; use crate::pvalue::{parse_pvalues, PVals}; use crate::secret::Secret; use crate::sqldb::SQLDatabase; -use crate::{meta, websocket_api}; +use crate::{meta, objects, websocket_api}; use encore_runtime_core::api::PValues; use encore_runtime_core::pubsub::SubName; use encore_runtime_core::{api, EncoreName, EndpointName}; @@ -94,6 +94,16 @@ impl Runtime { Ok(PubSubTopic::new(topic)) } + #[napi] + pub fn bucket(&self, encore_name: String) -> napi::Result { + let bkt = self + .runtime + .objects() + .bucket(encore_name.into()) + .ok_or_else(|| Error::new(Status::GenericFailure, "bucket not found"))?; + Ok(objects::Bucket::new(bkt)) + } + #[napi] pub fn gateway( &self, From 65501624257cc2db0f5006c23a7be2a53aeccc3d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Eriksson?= Date: Sun, 20 Oct 2024 21:30:08 +0200 Subject: [PATCH 05/37] Add basic demo impl with `exists` --- Cargo.lock | 87 +++- cli/daemon/objects/objects.go | 99 ++--- cli/daemon/run/infra/infra.go | 25 +- cli/daemon/run/runtime_config2.go | 27 ++ go.mod | 51 ++- go.sum | 112 +++-- pkg/rtconfgen/infra_builder.go | 45 ++ proto/encore/runtime/v1/infra.pb.go | 412 +++++++++++++----- proto/encore/runtime/v1/infra.proto | 19 +- runtimes/core/Cargo.toml | 1 + runtimes/core/src/infracfg.rs | 1 + runtimes/core/src/objects/gcs/bucket.rs | 67 +++ runtimes/core/src/objects/gcs/mod.rs | 65 +++ runtimes/core/src/objects/manager.rs | 50 +-- runtimes/core/src/objects/mod.rs | 8 +- runtimes/core/src/objects/noop/mod.rs | 26 +- runtimes/core/src/objects/s3/bucket.rs | 18 +- runtimes/core/src/objects/s3/mod.rs | 21 +- .../go/appruntime/exported/config/config.go | 22 + runtimes/js/src/objects.rs | 4 +- tsparser/src/parser/usageparser/mod.rs | 5 + 21 files changed, 859 insertions(+), 306 deletions(-) create mode 100644 runtimes/core/src/objects/gcs/bucket.rs create mode 100644 runtimes/core/src/objects/gcs/mod.rs diff --git a/Cargo.lock b/Cargo.lock index 264fcb18c7..57c4d80259 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1486,6 +1486,7 @@ dependencies = [ "google-cloud-gax", "google-cloud-googleapis", "google-cloud-pubsub", + "google-cloud-storage", "hex", "hmac", "http 1.0.0", @@ -1968,7 +1969,7 @@ checksum = "af1087f1fbd2dd3f58c17c7574ddd99cd61cbbbc2c4dc81114b8687209b196cb" dependencies = [ "async-trait", "base64 0.21.5", - "google-cloud-metadata", + "google-cloud-metadata 0.4.0", "google-cloud-token", "home", "jsonwebtoken 8.3.0", @@ -1982,6 +1983,28 @@ dependencies = [ "urlencoding", ] +[[package]] +name = "google-cloud-auth" +version = "0.17.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "357160f51a60ec3e32169ad687f4abe0ee1e90c73b449aa5d11256c4f1cf2ff6" +dependencies = [ + "async-trait", + "base64 0.21.5", + "google-cloud-metadata 0.5.0", + "google-cloud-token", + "home", + "jsonwebtoken 9.2.0", + "reqwest 0.12.4", + "serde", + "serde_json", + "thiserror", + "time", + "tokio", + "tracing", + "urlencoding", +] + [[package]] name = "google-cloud-gax" version = "0.17.0" @@ -2020,6 +2043,17 @@ dependencies = [ "tokio", ] +[[package]] +name = "google-cloud-metadata" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "04f945a208886a13d07636f38fb978da371d0abc3e34bad338124b9f8c135a8f" +dependencies = [ + "reqwest 0.12.4", + "thiserror", + "tokio", +] + [[package]] name = "google-cloud-pubsub" version = "0.22.1" @@ -2028,7 +2062,7 @@ checksum = "0f6e4fdcd8303ad0d0cdb8b5722aa3a57de9534af27d4da71fc4d3179174a896" dependencies = [ "async-channel", "async-stream", - "google-cloud-auth", + "google-cloud-auth 0.13.0", "google-cloud-gax", "google-cloud-googleapis", "google-cloud-token", @@ -2039,6 +2073,39 @@ dependencies = [ "tracing", ] +[[package]] +name = "google-cloud-storage" +version = "0.22.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7347a3d65cd64db51e5b4aebf0c68c484042948c6d53f856f58269bc9816360" +dependencies = [ + "anyhow", + "async-stream", + "async-trait", + "base64 0.21.5", + "bytes", + "futures-util", + "google-cloud-auth 0.17.1", + "google-cloud-metadata 0.5.0", + "google-cloud-token", + "hex", + "once_cell", + "percent-encoding", + "pkcs8", + "regex", + "reqwest 0.12.4", + "reqwest-middleware", + "ring 0.17.7", + "serde", + "serde_json", + "sha2", + "thiserror", + "time", + "tokio", + "tracing", + "url", +] + [[package]] name = "google-cloud-token" version = "0.1.2" @@ -4184,6 +4251,7 @@ dependencies = [ "js-sys", "log", "mime", + "mime_guess", "native-tls", "once_cell", "percent-encoding", @@ -4206,6 +4274,21 @@ dependencies = [ "winreg 0.52.0", ] +[[package]] +name = "reqwest-middleware" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "562ceb5a604d3f7c885a792d42c199fd8af239d0a51b2fa6a78aafa092452b04" +dependencies = [ + "anyhow", + "async-trait", + "http 1.0.0", + "reqwest 0.12.4", + "serde", + "thiserror", + "tower-service", +] + [[package]] name = "ring" version = "0.16.20" diff --git a/cli/daemon/objects/objects.go b/cli/daemon/objects/objects.go index 94787e3afc..70cb565217 100644 --- a/cli/daemon/objects/objects.go +++ b/cli/daemon/objects/objects.go @@ -1,11 +1,15 @@ package objects import ( - mathrand "math/rand" // nosemgrep - "time" + // nosemgrep + + "fmt" + "net" + "net/http" - "github.com/alicebob/miniredis/v2" "github.com/cockroachdb/errors" + "github.com/fullstorydev/emulators/storage/gcsemu" + "github.com/rs/zerolog/log" "go4.org/syncutil" meta "encr.dev/proto/encore/parser/meta/v1" @@ -13,90 +17,53 @@ import ( type Server struct { startOnce syncutil.Once - mini *miniredis.Miniredis - cleanup *time.Ticker - quit chan struct{} - addr string + cancel func() // set by Start + emu *gcsemu.GcsEmu + ln net.Listener + srv *http.Server } -const tickInterval = 1 * time.Second - func New() *Server { return &Server{ - mini: miniredis.NewMiniRedis(), - quit: make(chan struct{}), + // TODO set up dir storage + emu: gcsemu.NewGcsEmu(gcsemu.Options{}), } } func (s *Server) Start() error { return s.startOnce.Do(func() error { - if err := s.mini.Start(); err != nil { - return errors.Wrap(err, "failed to start redis server") + mux := http.NewServeMux() + ln, err := net.Listen("tcp", "127.0.0.1:0") + if err != nil { + return errors.Wrap(err, "listen tcp") } - s.addr = s.mini.Addr() - s.cleanup = time.NewTicker(tickInterval) - go s.doCleanup() + s.emu.Register(mux) + s.ln = ln + s.srv = &http.Server{Handler: mux} + + go func() { + if err := s.srv.Serve(ln); !errors.Is(err, http.ErrServerClosed) { + log.Error().Err(err).Msg("unable to listen to gcs server") + } + }() + + s.emu.InitBucket("test") + return nil }) } func (s *Server) Stop() { - s.mini.Close() - s.cleanup.Stop() - close(s.quit) -} - -func (s *Server) Miniredis() *miniredis.Miniredis { - return s.mini + _ = s.srv.Close() } -func (s *Server) Addr() string { +func (s *Server) Endpoint() string { // Ensure the server has been started if err := s.Start(); err != nil { panic(err) } - return s.addr -} - -func (s *Server) doCleanup() { - var acc time.Duration - const cleanupInterval = 15 * time.Second - - for { - select { - case <-s.quit: - return - case <-s.cleanup.C: - } - s.mini.FastForward(tickInterval) - - // Clean up keys every so often - acc += tickInterval - if acc > cleanupInterval { - acc -= cleanupInterval - s.clearKeys() - } - } -} - -// clearKeys clears random keys to get the redis server -// down to 100 persisted keys, as a simple way to bound -// the max memory usage. -func (s *Server) clearKeys() { - const maxKeys = 100 - keys := s.mini.Keys() - if n := len(keys); n > maxKeys { - toDelete := n - maxKeys - deleted := 0 - for deleted < toDelete { - id := mathrand.Intn(len(keys)) - if keys[id] != "" { - s.mini.Del(keys[id]) - keys[id] = "" // mark it as deleted - deleted++ - } - } - } + port := s.ln.Addr().(*net.TCPAddr).Port + return fmt.Sprintf("http://localhost:%d", port) } // IsUsed reports whether the application uses object storage at all. diff --git a/cli/daemon/run/infra/infra.go b/cli/daemon/run/infra/infra.go index 234e33dfc4..408ae46c7a 100644 --- a/cli/daemon/run/infra/infra.go +++ b/cli/daemon/run/infra/infra.go @@ -440,21 +440,16 @@ func (rm *ResourceManager) RedisConfig(redis *meta.CacheCluster) (config.RedisSe return srvCfg, dbCfg, nil } -// ObjectsConfig returns the Object Storage server configuration. -func (rm *ResourceManager) ObjectsConfig() (config.RedisServer, config.RedisDatabase, error) { - server := rm.GetRedis() - if server == nil { - return config.RedisServer{}, config.RedisDatabase{}, errors.New("no Redis server found") - } - - srvCfg := config.RedisServer{ - Host: server.Addr(), +// BucketProviderConfig returns the bucket provider configuration. +func (rm *ResourceManager) BucketProviderConfig() (config.BucketProvider, error) { + obj := rm.GetObjects() + if obj == nil { + return config.BucketProvider{}, errors.New("no object storage found") } - dbCfg := config.RedisDatabase{ - EncoreName: redis.Name, - KeyPrefix: redis.Name + "/", - } - - return srvCfg, dbCfg, nil + return config.BucketProvider{ + GCS: &config.GCSBucketProvider{ + Endpoint: obj.Endpoint(), + }, + }, nil } diff --git a/cli/daemon/run/runtime_config2.go b/cli/daemon/run/runtime_config2.go index ecf921cbf4..c10c2c88ff 100644 --- a/cli/daemon/run/runtime_config2.go +++ b/cli/daemon/run/runtime_config2.go @@ -58,6 +58,7 @@ type RuntimeConfigGenerator struct { PubSubTopicConfig(topic *meta.PubSubTopic) (config.PubsubProvider, config.PubsubTopic, error) PubSubSubscriptionConfig(topic *meta.PubSubTopic, sub *meta.PubSubTopic_Subscription) (config.PubsubSubscription, error) RedisConfig(redis *meta.CacheCluster) (config.RedisServer, config.RedisDatabase, error) + BucketProviderConfig() (config.BucketProvider, error) } AppID option.Option[string] @@ -337,6 +338,32 @@ func (g *RuntimeConfigGenerator) initialize() error { } } + if len(g.md.Buckets) > 0 { + bktProviderConfig, err := g.infraManager.BucketProviderConfig() + if err != nil { + return errors.Wrap(err, "failed to generate bucket provider config") + } + + cluster := g.conf.Infra.BucketCluster(&runtimev1.BucketCluster{ + Rid: newRid(), + Provider: &runtimev1.BucketCluster_Gcs{ + Gcs: &runtimev1.BucketCluster_GCS{ + Endpoint: &bktProviderConfig.GCS.Endpoint, + }, + }, + }) + + for _, bkt := range g.md.Buckets { + bktRid := newRid() + + cluster.Bucket(&runtimev1.Bucket{ + Rid: bktRid, + EncoreName: bkt.Name, + CloudName: bkt.Name, + }) + } + } + for secretName, secretVal := range g.DefinedSecrets { g.conf.Infra.AppSecret(&runtimev1.AppSecret{ Rid: newRid(), diff --git a/go.mod b/go.mod index 2363d98341..facc77e876 100644 --- a/go.mod +++ b/go.mod @@ -23,6 +23,7 @@ require ( github.com/fmstephe/unsafeutil v1.0.0 github.com/frankban/quicktest v1.14.5 github.com/fsnotify/fsnotify v1.6.0 + github.com/fullstorydev/emulators/storage v0.0.0-20241015152032-cb8c4efd6784 github.com/getkin/kin-openapi v0.115.0 github.com/gofrs/uuid v4.4.0+incompatible github.com/golang-migrate/migrate/v4 v4.15.2 @@ -63,23 +64,29 @@ require ( go.encore.dev/platform-sdk v1.1.0 go.uber.org/goleak v1.2.1 go4.org v0.0.0-20230225012048-214862532bf5 - golang.org/x/crypto v0.25.0 + golang.org/x/crypto v0.28.0 golang.org/x/exp v0.0.0-20240222234643-814bf88cf225 golang.org/x/mod v0.17.0 - golang.org/x/net v0.27.0 - golang.org/x/oauth2 v0.22.0 + golang.org/x/net v0.30.0 + golang.org/x/oauth2 v0.23.0 golang.org/x/sync v0.8.0 - golang.org/x/sys v0.22.0 - golang.org/x/term v0.22.0 - golang.org/x/text v0.16.0 + golang.org/x/sys v0.26.0 + golang.org/x/term v0.25.0 + golang.org/x/text v0.19.0 golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d - google.golang.org/genproto/googleapis/rpc v0.0.0-20240730163845-b1a4ccb954bf - google.golang.org/grpc v1.64.1 - google.golang.org/protobuf v1.34.2 + google.golang.org/genproto/googleapis/rpc v0.0.0-20241007155032-5fefd90f89a9 + google.golang.org/grpc v1.67.1 + google.golang.org/protobuf v1.35.1 sigs.k8s.io/yaml v1.3.0 ) require ( + cloud.google.com/go v0.115.1 // indirect + cloud.google.com/go/auth v0.9.8 // indirect + cloud.google.com/go/auth/oauth2adapt v0.2.4 // indirect + cloud.google.com/go/compute/metadata v0.5.2 // indirect + cloud.google.com/go/iam v1.2.1 // indirect + cloud.google.com/go/storage v1.43.0 // indirect github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161 // indirect github.com/Microsoft/go-winio v0.6.1 // indirect github.com/alicebob/gopher-json v0.0.0-20200520072559-a9ecdc9d1d3a // indirect @@ -88,8 +95,9 @@ require ( github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect github.com/benbjohnson/clock v1.3.5 // indirect github.com/blang/semver v3.5.1+incompatible // indirect + github.com/bluele/gcache v0.0.2 // indirect github.com/bmizerany/perks v0.0.0-20141205001514-d9a9656a3a4b // indirect - github.com/cespare/xxhash/v2 v2.2.0 // indirect + github.com/cespare/xxhash/v2 v2.3.0 // indirect github.com/cockroachdb/apd/v2 v2.0.2 // indirect github.com/cockroachdb/logtags v0.0.0-20230118201751-21c54148d20b // indirect github.com/cockroachdb/redact v1.1.5 // indirect @@ -107,17 +115,24 @@ require ( github.com/docker/go-units v0.5.0 // indirect github.com/dustin/go-humanize v1.0.1 // indirect github.com/emicklei/proto v1.9.2 // indirect + github.com/felixge/httpsnoop v1.0.4 // indirect github.com/getsentry/sentry-go v0.25.0 // indirect + github.com/go-logr/logr v1.4.2 // indirect + github.com/go-logr/stdr v1.2.2 // indirect github.com/go-openapi/jsonpointer v0.20.0 // indirect github.com/go-openapi/swag v0.22.4 // indirect github.com/go-redis/redis/v8 v8.11.5 // indirect github.com/go-sql-driver/mysql v1.7.1 // indirect github.com/gogo/protobuf v1.3.2 // indirect - github.com/golang/glog v1.2.0 // indirect + github.com/golang/glog v1.2.2 // indirect + github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect github.com/golang/snappy v0.0.4 // indirect - github.com/google/btree v1.1.2 // indirect + github.com/google/btree v1.1.3 // indirect github.com/google/cel-go v0.18.2 // indirect + github.com/google/s2a-go v0.1.8 // indirect github.com/google/uuid v1.6.0 // indirect + github.com/googleapis/enterprise-certificate-proxy v0.3.4 // indirect + github.com/googleapis/gax-go/v2 v2.13.0 // indirect github.com/hashicorp/errwrap v1.1.0 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect github.com/invopop/yaml v0.1.0 // indirect @@ -168,14 +183,22 @@ require ( github.com/wasilibs/go-pgquery v0.0.0-20231208014744-de63626a1e99 // indirect github.com/wasilibs/wazerox v0.0.0-20231208014050-e6b725634531 // indirect github.com/yuin/gopher-lua v0.0.0-20220504180219-658193537a64 // indirect + go.opencensus.io v0.24.0 // indirect + go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.54.0 // indirect + go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.54.0 // indirect + go.opentelemetry.io/otel v1.29.0 // indirect + go.opentelemetry.io/otel/metric v1.29.0 // indirect + go.opentelemetry.io/otel/trace v1.29.0 // indirect go.uber.org/atomic v1.11.0 // indirect go.uber.org/multierr v1.11.0 // indirect go.uber.org/zap v1.26.0 // indirect - google.golang.org/genproto/googleapis/api v0.0.0-20240725223205-93522f1f2a9f // indirect + golang.org/x/time v0.7.0 // indirect + google.golang.org/api v0.200.0 // indirect + google.golang.org/genproto v0.0.0-20241007155032-5fefd90f89a9 // indirect + google.golang.org/genproto/googleapis/api v0.0.0-20240930140551-af27646dc61f // indirect gopkg.in/natefinch/lumberjack.v2 v2.2.1 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect - gotest.tools/v3 v3.4.0 // indirect lukechampine.com/uint128 v1.2.0 // indirect modernc.org/cc/v3 v3.40.0 // indirect modernc.org/ccgo/v3 v3.16.13 // indirect diff --git a/go.sum b/go.sum index a944905d8e..9f61607cf6 100644 --- a/go.sum +++ b/go.sum @@ -29,15 +29,27 @@ cloud.google.com/go v0.94.1/go.mod h1:qAlAugsXlC+JWO+Bke5vCtc9ONxjQT3drlTTnAplMW cloud.google.com/go v0.97.0/go.mod h1:GF7l59pYBVlXQIBLx3a761cZ41F9bBH3JUlihCt2Udc= cloud.google.com/go v0.98.0/go.mod h1:ua6Ush4NALrHk5QXDWnjvZHN93OuF0HfuEPq9I1X0cM= cloud.google.com/go v0.99.0/go.mod h1:w0Xx2nLzqWJPuozYQX+hFfCSI8WioryfRDzkoI/Y2ZA= +cloud.google.com/go v0.115.1 h1:Jo0SM9cQnSkYfp44+v+NQXHpcHqlnRJk2qxh6yvxxxQ= +cloud.google.com/go v0.115.1/go.mod h1:DuujITeaufu3gL68/lOFIirVNJwQeyf5UXyi+Wbgknc= +cloud.google.com/go/auth v0.9.8 h1:+CSJ0Gw9iVeSENVCKJoLHhdUykDgXSc4Qn+gu2BRtR8= +cloud.google.com/go/auth v0.9.8/go.mod h1:xxA5AqpDrvS+Gkmo9RqrGGRh6WSNKKOXhY3zNOr38tI= +cloud.google.com/go/auth/oauth2adapt v0.2.4 h1:0GWE/FUsXhf6C+jAkWgYm7X9tK8cuEIfy19DBn6B6bY= +cloud.google.com/go/auth/oauth2adapt v0.2.4/go.mod h1:jC/jOpwFP6JBxhB3P5Rr0a9HLMC/Pe3eaL4NmdvqPtc= cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE= cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc= cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg= cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc= cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ= +cloud.google.com/go/compute/metadata v0.5.2 h1:UxK4uu/Tn+I3p2dYWTfiX4wva7aYlKixAHn3fyqngqo= +cloud.google.com/go/compute/metadata v0.5.2/go.mod h1:C66sj2AluDcIqakBq/M8lw8/ybHgOZqin2obFxa/E5k= cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk= cloud.google.com/go/firestore v1.1.0/go.mod h1:ulACoGHTpvq5r8rxGJ4ddJZBZqakUQqClKRT5SZwBmk= +cloud.google.com/go/iam v1.2.1 h1:QFct02HRb7H12J/3utj0qf5tobFh9V4vR6h9eX5EBRU= +cloud.google.com/go/iam v1.2.1/go.mod h1:3VUIJDPpwT6p/amXRC5GY8fCCh70lxPygguVtI0Z4/g= +cloud.google.com/go/longrunning v0.6.1 h1:lOLTFxYpr8hcRtcwWir5ITh1PAKUD/sG2lKrTSYjyMc= +cloud.google.com/go/longrunning v0.6.1/go.mod h1:nHISoOZpBcmlwbJmiVk5oDRz0qG/ZxPynEGs1iZ79s0= cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I= cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw= cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA= @@ -48,6 +60,8 @@ cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0Zeo cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk= cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs= cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0= +cloud.google.com/go/storage v1.43.0 h1:CcxnSohZwizt4LCzQHWvBf1/kvtHUn7gk9QERXPyXFs= +cloud.google.com/go/storage v1.43.0/go.mod h1:ajvxEa7WmZS1PxvKRq4bq0tFT3vMd502JwstCcYv0Q0= cuelang.org/go v0.4.3 h1:W3oBBjDTm7+IZfCKZAmC8uDG0eYfJL4Pp/xbbCMKaVo= cuelang.org/go v0.4.3/go.mod h1:7805vR9H+VoBNdWFdI7jyDR3QLUPp4+naHfbcgp55HI= dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= @@ -192,6 +206,8 @@ github.com/bketelsen/crypt v0.0.3-0.20200106085610-5cbc8cc4026c/go.mod h1:MKsuJm github.com/blang/semver v3.1.0+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk= github.com/blang/semver v3.5.1+incompatible h1:cQNTCjp13qL8KC3Nbxr/y2Bqb63oX6wdnnjpJbkM4JQ= github.com/blang/semver v3.5.1+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk= +github.com/bluele/gcache v0.0.2 h1:WcbfdXICg7G/DGBh1PFfcirkWOQV+v077yF1pSy3DGw= +github.com/bluele/gcache v0.0.2/go.mod h1:m15KV+ECjptwSPxKhOhQoAFQVtUFjTVkc3H8o0t/fp0= github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869/go.mod h1:Ekp36dRnpXw/yCqJaO+ZrUyxD+3VXMFFr56k5XYrpB4= github.com/bmizerany/perks v0.0.0-20141205001514-d9a9656a3a4b h1:AP/Y7sqYicnjGDfD5VcY4CIfh1hRXBUavxrvELjTiOE= github.com/bmizerany/perks v0.0.0-20141205001514-d9a9656a3a4b/go.mod h1:ac9efd0D1fsDb3EJvhqgXRbFx7bs2wqZ10HQPeU8U/Q= @@ -215,8 +231,8 @@ github.com/certifi/gocertifi v0.0.0-20200922220541-2c3bb06c6054/go.mod h1:sGbDF6 github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= -github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44= -github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= +github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/charmbracelet/bubbles v0.16.1 h1:6uzpAAaT9ZqKssntbvZMlksWHruQLNxg49H5WdeuYSY= github.com/charmbracelet/bubbles v0.16.1/go.mod h1:2QCp9LFlEsBQMvIYERr7Ww2H2bA7xen1idUDIzm/+Xc= github.com/charmbracelet/bubbletea v0.24.2 h1:uaQIKx9Ai6Gdh5zpTbGiWpytMU+CfsPp06RaW2cx/SY= @@ -476,6 +492,8 @@ github.com/fatih/color v1.15.0/go.mod h1:0h5ZqXfHYED7Bhv2ZJamyIOUej9KtShiJESRwBD github.com/fatih/structtag v1.2.0 h1:/OdNE99OxoI/PqaW/SuSK9uxxT3f/tcSZgon/ssNSx4= github.com/fatih/structtag v1.2.0/go.mod h1:mBJUNpUnHmRKrKlQQlmCrh5PuhftFbNv8Ys4/aAZl94= github.com/felixge/httpsnoop v1.0.1/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= +github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg= +github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= github.com/fmstephe/unsafeutil v1.0.0 h1:hWKjyW7jOL7rfCiBgX61tGy742pZ3C3VpHcGwTAgB2w= github.com/fmstephe/unsafeutil v1.0.0/go.mod h1:00y9QPGpX2A5iB0UmPDtnSpO4c2XsRQu3dQYuGL8+RA= github.com/fogleman/gg v1.2.1-0.20190220221249-0403632d5b90/go.mod h1:R/bRT+9gY/C5z7JzPU0zXsXHKM4/ayA+zqcVNZzPa1k= @@ -492,6 +510,8 @@ github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4 github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw= github.com/fsouza/fake-gcs-server v1.17.0/go.mod h1:D1rTE4YCyHFNa99oyJJ5HyclvN/0uQR+pM/VdlL83bw= github.com/fullsailor/pkcs7 v0.0.0-20190404230743-d7302db945fa/go.mod h1:KnogPXtdwXqoenmZCw6S+25EAm2MkxbG0deNDu4cbSA= +github.com/fullstorydev/emulators/storage v0.0.0-20241015152032-cb8c4efd6784 h1:4dnqzguS8chqXw3ZqcPAD/4KdPx5RbohzGsKOYOFw1w= +github.com/fullstorydev/emulators/storage v0.0.0-20241015152032-cb8c4efd6784/go.mod h1:HNCVyL+WaX0AiVFKXzRAT3uIHTFo/Tr0K1lJvNV+qU0= github.com/gabriel-vasile/mimetype v1.3.1/go.mod h1:fA8fi6KUiG7MgQQ+mEWotXoEOvmxRtOJlERCzSmRvr8= github.com/gabriel-vasile/mimetype v1.4.0/go.mod h1:fA8fi6KUiG7MgQQ+mEWotXoEOvmxRtOJlERCzSmRvr8= github.com/garyburd/redigo v0.0.0-20150301180006-535138d7bcd7/go.mod h1:NR3MbYisc3/PwhQ00EMzDiPmrwpPxAn5GI05/YaO1SY= @@ -525,7 +545,10 @@ github.com/go-logr/logr v0.4.0/go.mod h1:z6/tIYblkpsD+a4lm/fGIIU9mZ+XfAiaFtq7xTg github.com/go-logr/logr v1.2.0/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= github.com/go-logr/logr v1.2.1/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY= +github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= github.com/go-logr/stdr v1.2.0/go.mod h1:YkVgnZu1ZjjL7xTxrfm/LLZBfkhTqSR1ydtm6jTKKwI= +github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= github.com/go-openapi/jsonpointer v0.0.0-20160704185906-46af16f9f7b1/go.mod h1:+35s3my2LFTysnkMfxsJBAMHj/DoqoB9knIWoYG/Vk0= github.com/go-openapi/jsonpointer v0.19.2/go.mod h1:3akKfEdA7DF1sugOqz1dVQHBcuDBPKZGEoHC/NkiQRg= @@ -607,13 +630,14 @@ github.com/golang-migrate/migrate/v4 v4.15.2/go.mod h1:f2toGLkYqD3JH+Todi4aZ2Zdb github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe/go.mod h1:8vg3r2VgvsThLBIFL93Qb5yWzgyZWhEmBwUJWevAkK0= github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0/go.mod h1:E/TSTwGwJL78qG/PmXZO1EjYhfJinVAhrmmHX6Z8B9k= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= -github.com/golang/glog v1.2.0 h1:uCdmnmatrKCgMBlM4rMuJZWOkPDqdbZPnrMXDY4gI68= -github.com/golang/glog v1.2.0/go.mod h1:6AhwSGph0fcJtXVM/PEHPqZlFeoLxhs7/t5UDAwmO+w= +github.com/golang/glog v1.2.2 h1:1+mZ9upx1Dh6FmUTFR1naJ77miKiXgALjWOZ3NVFPmY= +github.com/golang/glog v1.2.2/go.mod h1:6AhwSGph0fcJtXVM/PEHPqZlFeoLxhs7/t5UDAwmO+w= github.com/golang/groupcache v0.0.0-20160516000752-02826c3e7903/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE= github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= @@ -652,8 +676,8 @@ github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEW github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/btree v1.0.1/go.mod h1:xXMiIv4Fb/0kKde4SpL7qlzvu5cMJDRkFDxJfI9uaxA= -github.com/google/btree v1.1.2 h1:xf4v41cLI2Z6FxbKm+8Bu+m8ifhj15JuZ9sa0jZCMUU= -github.com/google/btree v1.1.2/go.mod h1:qOPhT0dTNdNzV6Z/lhRX0YXUafgPLFUh+gZMl761Gm4= +github.com/google/btree v1.1.3 h1:CVpQJjYgC4VbzxeGVHfvZrv1ctoYCAI8vbl07Fcxlyg= +github.com/google/btree v1.1.3/go.mod h1:qOPhT0dTNdNzV6Z/lhRX0YXUafgPLFUh+gZMl761Gm4= github.com/google/cel-go v0.18.2 h1:L0B6sNBSVmt0OyECi8v6VOS74KOc9W/tLiWKfZABvf4= github.com/google/cel-go v0.18.2/go.mod h1:kWcIzTsPX0zmQ+H3TirHstLLf9ep5QTsZBN9u4dOYLg= github.com/google/flatbuffers v2.0.0+incompatible/go.mod h1:1AeVuKshWv4vARoZatz6mlQ0JxURH0Kv5+zNeJKJCa8= @@ -681,10 +705,13 @@ github.com/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17 github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/gofuzz v1.1.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/google/martian v2.1.0+incompatible h1:/CP5g8u/VJHijgedC/Legn3BAbAaWPgecwXBIDzw5no= github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= github.com/google/martian/v3 v3.1.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= github.com/google/martian/v3 v3.2.1/go.mod h1:oBOf6HBosgwRXnUGWUB05QECsc6uvmMiJ3+6W4l/CUk= +github.com/google/martian/v3 v3.3.3 h1:DIhPTQrbPkgs2yJYdXU/eNACCG5DVQjySNRNlflZ9Fc= +github.com/google/martian/v3 v3.3.3/go.mod h1:iEPrYcgCF7jA9OtScMFQyAlZZ4YXTKEtJ1E6RWzmBA0= github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= @@ -704,6 +731,8 @@ github.com/google/pprof v0.0.0-20221118152302-e6195bd50e26/go.mod h1:dDKJzRmX4S3 github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= github.com/google/renameio/v2 v2.0.0 h1:UifI23ZTGY8Tt29JbYFiuyIU3eX+RNFtUwefq9qAhxg= github.com/google/renameio/v2 v2.0.0/go.mod h1:BtmJXm5YlszgC+TD4HOEEUFgkJP3nLxehU6hfe7jRt4= +github.com/google/s2a-go v0.1.8 h1:zZDs9gcbt9ZPLV0ndSyQk6Kacx2g/X+SKYovpnz3SMM= +github.com/google/s2a-go v0.1.8/go.mod h1:6iNWHTpQ+nfNRN5E00MSdfDwVesa8hhS32PhPO8deJA= github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= @@ -711,10 +740,14 @@ github.com/google/uuid v1.2.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+ github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/googleapis/enterprise-certificate-proxy v0.3.4 h1:XYIDZApgAnrN1c855gTgghdIA6Stxb52D5RnLI1SLyw= +github.com/googleapis/enterprise-certificate-proxy v0.3.4/go.mod h1:YKe7cfqYXjKGpGvmSg28/fFvhNzinZQm8DGnaburhGA= github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= github.com/googleapis/gax-go/v2 v2.1.0/go.mod h1:Q3nei7sK6ybPYH7twZdmQpAd1MKb7pfu6SK+H1/DsU0= github.com/googleapis/gax-go/v2 v2.1.1/go.mod h1:hddJymUZASv3XPyGkUpKj8pPO47Rmb0eJc8R6ouapiM= +github.com/googleapis/gax-go/v2 v2.13.0 h1:yitjD5f7jQHhyDsnhKEBU52NdvvdSeGzlAnDPT0hH1s= +github.com/googleapis/gax-go/v2 v2.13.0/go.mod h1:Z/fvTZXF8/uw7Xu5GuslPw+bplx6SS338j1Is2S+B7A= github.com/googleapis/gnostic v0.4.1/go.mod h1:LRhVm6pbyptWbWbuZ38d1eyptfvIytN3ir6b65WBswg= github.com/googleapis/gnostic v0.5.1/go.mod h1:6U4PtQXGIEt/Z3h5MAT7FNofLnw9vXk2cUuW7uA/OeU= github.com/googleapis/gnostic v0.5.5/go.mod h1:7+EbHbldMins07ALC74bsA81Ovc97DwqyJO1AENw9kA= @@ -1383,25 +1416,39 @@ go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk= go.opencensus.io v0.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E= +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 v0.20.0/go.mod h1:G/EtFaa6qaN7+LxqfIAT3GiZa7Wv5DTBUzl5H4LY0Kc= go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.20.0/go.mod h1:oVGt1LRbBOBq1A5BQLlUg9UaU/54aiHw8cgjV3aWZ/E= go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.28.0/go.mod h1:vEhqr0m4eTc+DWxfsXoXue2GBgV2uUwVznkGIHW/e5w= +go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.54.0 h1:r6I7RJCN86bpD/FQwedZ0vSixDpwuWREjW9oRMsmqDc= +go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.54.0/go.mod h1:B9yO6b04uB80CzjedvewuqDhxJxi11s7/GtiGa8bAjI= go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.20.0/go.mod h1:2AboqHi0CiIZU0qwhtUfCYD1GeUzvvIXWNkhDt7ZMG4= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.54.0 h1:TT4fX+nBOA/+LUkobKGW1ydGcn+G3vRw9+g5HwCphpk= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.54.0/go.mod h1:L7UH0GbB0p47T4Rri3uHjbpCFYrVrwc1I25QhNPiGK8= go.opentelemetry.io/otel v0.20.0/go.mod h1:Y3ugLH2oa81t5QO+Lty+zXf8zC9L26ax4Nzoxm/dooo= go.opentelemetry.io/otel v1.3.0/go.mod h1:PWIKzi6JCp7sM0k9yZ43VX+T345uNbAkDKwHVjb2PTs= +go.opentelemetry.io/otel v1.29.0 h1:PdomN/Al4q/lN6iBJEN3AwPvUiHPMlt93c8bqTG5Llw= +go.opentelemetry.io/otel v1.29.0/go.mod h1:N/WtXPs1CNCUEx+Agz5uouwCba+i+bJGFicT8SR4NP8= go.opentelemetry.io/otel/exporters/otlp v0.20.0/go.mod h1:YIieizyaN77rtLJra0buKiNBOm9XQfkPEKBeuhoMwAM= go.opentelemetry.io/otel/exporters/otlp/internal/retry v1.3.0/go.mod h1:VpP4/RMn8bv8gNo9uK7/IMY4mtWLELsS+JIP0inH0h4= go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.3.0/go.mod h1:hO1KLR7jcKaDDKDkvI9dP/FIhpmna5lkqPUQdEjFAM8= go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.3.0/go.mod h1:keUU7UfnwWTWpJ+FWnyqmogPa82nuU5VUANFq49hlMY= go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.3.0/go.mod h1:QNX1aly8ehqqX1LEa6YniTU7VY9I6R3X/oPxhGdTceE= go.opentelemetry.io/otel/metric v0.20.0/go.mod h1:598I5tYlH1vzBjn+BTuhzTCSb/9debfNp6R3s7Pr1eU= +go.opentelemetry.io/otel/metric v1.29.0 h1:vPf/HFWTNkPu1aYeIsc98l4ktOQaL6LeSoeV2g+8YLc= +go.opentelemetry.io/otel/metric v1.29.0/go.mod h1:auu/QWieFVWx+DmQOUMgj0F8LHWdgalxXqvp7BII/W8= go.opentelemetry.io/otel/oteltest v0.20.0/go.mod h1:L7bgKf9ZB7qCwT9Up7i9/pn0PWIa9FqQ2IQ8LoxiGnw= go.opentelemetry.io/otel/sdk v0.20.0/go.mod h1:g/IcepuwNsoiX5Byy2nNV0ySUF1em498m7hBWC279Yc= go.opentelemetry.io/otel/sdk v1.3.0/go.mod h1:rIo4suHNhQwBIPg9axF8V9CA72Wz2mKF1teNrup8yzs= +go.opentelemetry.io/otel/sdk v1.29.0 h1:vkqKjk7gwhS8VaWb0POZKmIEDimRCMsopNYnriHyryo= +go.opentelemetry.io/otel/sdk v1.29.0/go.mod h1:pM8Dx5WKnvxLCb+8lG1PRNIDxu9g9b9g59Qr7hfAAok= go.opentelemetry.io/otel/sdk/export/metric v0.20.0/go.mod h1:h7RBNMsDJ5pmI1zExLi+bJK+Dr8NQCh0qGhm1KDnNlE= go.opentelemetry.io/otel/sdk/metric v0.20.0/go.mod h1:knxiS8Xd4E/N+ZqKmUPf3gTTZ4/0TjTXukfxjzSTpHE= go.opentelemetry.io/otel/trace v0.20.0/go.mod h1:6GjCW8zgDjwGHGa6GkyeB8+/5vjT16gUEi0Nf1iBdgw= go.opentelemetry.io/otel/trace v1.3.0/go.mod h1:c/VDhno8888bvQYmbYLqe41/Ldmr/KKunbvWM4/fEjk= +go.opentelemetry.io/otel/trace v1.29.0 h1:J/8ZNK4XgR7a21DZUAsbF8pZ5Jcw1VhACmnYt39JTi4= +go.opentelemetry.io/otel/trace v1.29.0/go.mod h1:eHl3w0sp3paPkYstJOmAimxhiFXPg+MMTlEh3nsQgWQ= go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI= go.opentelemetry.io/proto/otlp v0.11.0/go.mod h1:QpEjXPrNQzrFDZgoTo49dgHR9RYRSrg3NAKnUGl9YpQ= go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= @@ -1458,8 +1505,8 @@ golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5y golang.org/x/crypto v0.0.0-20210817164053-32db794688a5/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.6.0/go.mod h1:OFC/31mSvZgRz0V1QTNCzfAI1aIRzbiufJtkMIlEp58= -golang.org/x/crypto v0.25.0 h1:ypSNr+bnYL2YhwoMt2zPxHFmbAN1KZs/njMG3hxUp30= -golang.org/x/crypto v0.25.0/go.mod h1:T+wALwcMOSE0kXgUAnPAHqTLW+XHgcELELW8VaDgm/M= +golang.org/x/crypto v0.28.0 h1:GBDwsMXVQi34v5CCYUm2jkJvu4cbtru2U4TN2PSyQnw= +golang.org/x/crypto v0.28.0/go.mod h1:rmgy+3RHxRZMyY0jjAJShp2zgEdOqj2AO7U0pYmeQ7U= golang.org/x/exp v0.0.0-20180321215751-8460e604b9de/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20180807140117-3d87b88a115f/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= @@ -1581,8 +1628,8 @@ golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= -golang.org/x/net v0.27.0 h1:5K3Njcw06/l2y9vpGCSdcxWOYHOUk3dVNGDXN+FvAys= -golang.org/x/net v0.27.0/go.mod h1:dDi0PyhWNoiUOrAS8uXv/vnScO4wnHQO4mj9fn/RytE= +golang.org/x/net v0.30.0 h1:AcW1SDZMkb8IpzCdQUaIq2sP4sZ4zw+55h6ynffypl4= +golang.org/x/net v0.30.0/go.mod h1:2wGyMJ5iFasEhkwi13ChkO/t1ECNC4X4eBKkVFyYFlU= golang.org/x/oauth2 v0.0.0-20180227000427-d7d64896b5ff/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20181106182150-f42d05182288/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= @@ -1601,8 +1648,8 @@ golang.org/x/oauth2 v0.0.0-20210628180205-a41e5a781914/go.mod h1:KelEdhl1UZF7XfJ golang.org/x/oauth2 v0.0.0-20210805134026-6f1e6394065a/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20210819190943-2bc19b11175f/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.22.0 h1:BzDx2FehcG7jJwgWLELCdmLuxk2i+x9UDpSiss2u0ZA= -golang.org/x/oauth2 v0.22.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI= +golang.org/x/oauth2 v0.23.0 h1:PbgcYx2W7i4LvjJWEbf0ngHV6qJYr86PkAV3bXdLEbs= +golang.org/x/oauth2 v0.23.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -1748,16 +1795,16 @@ golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.22.0 h1:RI27ohtqKCnwULzJLqkv897zojh5/DwS/ENaMzUOaWI= -golang.org/x/sys v0.22.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.26.0 h1:KHjCJyddX0LoSTb3J+vWpupP9p0oznkqVk/IfjymZbo= +golang.org/x/sys v0.26.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210220032956-6a3ed077a48d/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210615171337-6886f2dfbf5b/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= -golang.org/x/term v0.22.0 h1:BbsgPEJULsl2fV/AT3v15Mjva5yXKQDyKf+TbDz7QJk= -golang.org/x/term v0.22.0/go.mod h1:F3qCibpT5AMpCRfhfT53vVJwhLtIVHhB9XDjfFvnMI4= +golang.org/x/term v0.25.0 h1:WtHI/ltw4NvSUig5KARz9h521QvRC8RmF/cuYqifU24= +golang.org/x/term v0.25.0/go.mod h1:RPyXicDX+6vLxogjjRxjgD2TKtmAO6NZBsBRfrOLu7M= 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= @@ -1768,8 +1815,8 @@ golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= -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/text v0.19.0 h1:kTxAhCbGbxhK0IwgSKiMO5awPoDQ0RpfiVYBfK860YM= +golang.org/x/text v0.19.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= @@ -1779,8 +1826,8 @@ golang.org/x/time v0.0.0-20200630173020-3af7569d3a1e/go.mod h1:tRJNPiyCQ0inRvYxb golang.org/x/time v0.0.0-20210220033141-f8bda1e9f3ba/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20210723032227-1f47c861a9ac/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20220224211638-0e9765cccd65/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.6.0 h1:eTDhh4ZXt5Qf0augr54TN6suAUudPcawVZeIAPU7D4U= -golang.org/x/time v0.6.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= +golang.org/x/time v0.7.0 h1:ntUhktv3OPE6TgYxXWv9vKvUSJyIFJlyohwbkEwPrKQ= +golang.org/x/time v0.7.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180525024113-a5b4c53f6e8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= @@ -1906,6 +1953,8 @@ google.golang.org/api v0.56.0/go.mod h1:38yMfeP1kfjsl8isn0tliTjIb1rJXcQi4UXlbqiv google.golang.org/api v0.57.0/go.mod h1:dVPlbZyBo2/OjBpmvNdpn2GRm6rPy75jyU7bmhdrMgI= google.golang.org/api v0.61.0/go.mod h1:xQRti5UdCmoCEqFxcz93fTl338AVqDgyaDRuOZ3hg9I= google.golang.org/api v0.62.0/go.mod h1:dKmwPCydfsad4qCH08MSdgWjfHOyfpd4VtDGgRFdavw= +google.golang.org/api v0.200.0 h1:0ytfNWn101is6e9VBoct2wrGDjOi5vn7jw5KtaQgDrU= +google.golang.org/api v0.200.0/go.mod h1:Tc5u9kcbjO7A8SwGlYj4IiVifJU01UqXtEgDMYmBmV8= google.golang.org/appengine v1.0.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= google.golang.org/appengine v1.3.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= @@ -1986,10 +2035,12 @@ google.golang.org/genproto v0.0.0-20211206160659-862468c7d6e0/go.mod h1:5CzLGKJ6 google.golang.org/genproto v0.0.0-20211208223120-3a66f561d7aa/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= google.golang.org/genproto v0.0.0-20220111164026-67b88f271998/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= google.golang.org/genproto v0.0.0-20220314164441-57ef72a4c106/go.mod h1:hAL49I2IFola2sVEjAn7MEwsja0xp51I0tlGAf9hz4E= -google.golang.org/genproto/googleapis/api v0.0.0-20240725223205-93522f1f2a9f h1:b1Ln/PG8orm0SsBbHZWke8dDp2lrCD4jSmfglFpTZbk= -google.golang.org/genproto/googleapis/api v0.0.0-20240725223205-93522f1f2a9f/go.mod h1:AHT0dDg3SoMOgZGnZk29b5xTbPHMoEC8qthmBLJCpys= -google.golang.org/genproto/googleapis/rpc v0.0.0-20240730163845-b1a4ccb954bf h1:liao9UHurZLtiEwBgT9LMOnKYsHze6eA6w1KQCMVN2Q= -google.golang.org/genproto/googleapis/rpc v0.0.0-20240730163845-b1a4ccb954bf/go.mod h1:Ue6ibwXGpU+dqIcODieyLOcgj7z8+IcskoNIgZxtrFY= +google.golang.org/genproto v0.0.0-20241007155032-5fefd90f89a9 h1:nFS3IivktIU5Mk6KQa+v6RKkHUpdQpphqGNLxqNnbEk= +google.golang.org/genproto v0.0.0-20241007155032-5fefd90f89a9/go.mod h1:tEzYTYZxbmVNOu0OAFH9HzdJtLn6h4Aj89zzlBCdHms= +google.golang.org/genproto/googleapis/api v0.0.0-20240930140551-af27646dc61f h1:jTm13A2itBi3La6yTGqn8bVSrc3ZZ1r8ENHlIXBfnRA= +google.golang.org/genproto/googleapis/api v0.0.0-20240930140551-af27646dc61f/go.mod h1:CLGoBuH1VHxAUXVPP8FfPwPEVJB6lz3URE5mY2SuayE= +google.golang.org/genproto/googleapis/rpc v0.0.0-20241007155032-5fefd90f89a9 h1:QCqS/PdaHTSWGvupk2F/ehwHtGc0/GYkT+3GAcR1CCc= +google.golang.org/genproto/googleapis/rpc v0.0.0-20241007155032-5fefd90f89a9/go.mod h1:GX3210XPVPUjJbTUbvwI8f2IpZDMZuPJWDzDuebbviI= google.golang.org/grpc v0.0.0-20160317175043-d3ddb4469d5a/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= @@ -2023,8 +2074,8 @@ google.golang.org/grpc v1.40.1/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9K google.golang.org/grpc v1.42.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU= google.golang.org/grpc v1.43.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU= google.golang.org/grpc v1.45.0/go.mod h1:lN7owxKUQEqMfSyQikvvk5tf/6zMPsrK+ONuO11+0rQ= -google.golang.org/grpc v1.64.1 h1:LKtvyfbX3UGVPFcGqJ9ItpVWW6oN/2XqTxfAnwRRXiA= -google.golang.org/grpc v1.64.1/go.mod h1:hiQF4LFZelK2WKaP6W0L92zGHtiQdZxk8CrSdvyjeP0= +google.golang.org/grpc v1.67.1 h1:zWnc1Vrcno+lHZCOofnIMvycFcc0QRGIzm9dhnDX68E= +google.golang.org/grpc v1.67.1/go.mod h1:1gLDyUQU7CTLJI90u3nXZ9ekeghjeM7pTDZlqFNg2AA= google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.1.0/go.mod h1:6Kw0yEErY5E/yWrBtf03jp27GLLJujG4z/JK95pnjjw= 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= @@ -2040,8 +2091,8 @@ google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp0 google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= -google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg= -google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw= +google.golang.org/protobuf v1.35.1 h1:m3LfL6/Ca+fqnjnlqQXNpFPABW1UD7mjh8KO2mKFytA= +google.golang.org/protobuf v1.35.1/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE= gopkg.in/airbrake/gobrake.v2 v2.0.9/go.mod h1:/h5ZAUhDkGaJfjzjKLSjv6zCL6O0LLBxU4K+aSYdM/U= gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= @@ -2087,12 +2138,13 @@ gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gorm.io/driver/postgres v1.0.8/go.mod h1:4eOzrI1MUfm6ObJU/UcmbXyiHSs8jSwH95G5P5dxcAg= gorm.io/gorm v1.20.12/go.mod h1:0HFTzE/SqkGTzK6TlDPPQbAYCluiVvhzoA1+aVyzenw= gorm.io/gorm v1.21.4/go.mod h1:0HFTzE/SqkGTzK6TlDPPQbAYCluiVvhzoA1+aVyzenw= +gotest.tools v2.2.0+incompatible h1:VsBPFP1AI068pPrMxtb/S8Zkgf9xEmTLJjfM+P5UIEo= gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw= gotest.tools/v3 v3.0.2/go.mod h1:3SzNCllyD9/Y+b5r9JIKQ474KzkZyqLqEfYqMsX94Bk= gotest.tools/v3 v3.0.3/go.mod h1:Z7Lb0S5l+klDB31fvDQX8ss/FlKDxtlFlw3Oa8Ymbl8= gotest.tools/v3 v3.1.0/go.mod h1:fHy7eyTmJFO5bQbUsEGQ1v4m2J3Jz9eWL54TP2/ZuYQ= -gotest.tools/v3 v3.4.0 h1:ZazjZUfuVeZGLAmlKKuyv3IKP5orXcwtOwDQH6YVr6o= -gotest.tools/v3 v3.4.0/go.mod h1:CtbdzLSsqVhDgMtKsx03ird5YTGB3ar27v0u/yKBW5g= +gotest.tools/v3 v3.5.1 h1:EENdUnS3pdur5nybKYIh2Vfgc8IUNBjxDPSjtiJcOzU= +gotest.tools/v3 v3.5.1/go.mod h1:isy3WKz7GK6uNw/sbHzfKBLvlvXwUyV06n6brMxxopU= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= diff --git a/pkg/rtconfgen/infra_builder.go b/pkg/rtconfgen/infra_builder.go index dab5aa7842..6cd4aa4a0c 100644 --- a/pkg/rtconfgen/infra_builder.go +++ b/pkg/rtconfgen/infra_builder.go @@ -195,6 +195,34 @@ type RedisServer struct { b *InfraBuilder } +func (b *InfraBuilder) BucketCluster(p *runtimev1.BucketCluster) *BucketCluster { + return b.BucketClusterFn(p.Rid, tofn(p)) +} + +func (b *InfraBuilder) BucketClusterFn(rid string, fn func() *runtimev1.BucketCluster) *BucketCluster { + val := addResFunc(&b.infra.Resources.BucketClusters, b.rs, rid, fn) + return &BucketCluster{Val: val, b: b} +} + +type BucketCluster struct { + Val *runtimev1.BucketCluster + b *InfraBuilder +} + +func (c *BucketCluster) Bucket(p *runtimev1.Bucket) *Bucket { + return c.BucketFn(p.Rid, tofn(p)) +} + +func (c *BucketCluster) BucketFn(rid string, fn func() *runtimev1.Bucket) *Bucket { + val := addResFunc(&c.Val.Buckets, c.b.rs, rid, fn) + return &Bucket{Val: val, b: c.b} +} + +type Bucket struct { + Val *runtimev1.Bucket + b *InfraBuilder +} + func (b *InfraBuilder) Gateway(gw *runtimev1.Gateway) *Gateway { return b.GatewayFn(gw.Rid, tofn(gw)) } @@ -252,6 +280,16 @@ func reduceForServices(infra *runtimev1.Infrastructure, md *meta.Data, svcs []st } } + bucketsToKeep := make(map[string]bool) + for _, svc := range md.Svcs { + if !svcNames[svc.Name] { + continue + } + for _, bktName := range svc.Buckets { + bucketsToKeep[bktName] = true + } + } + type subKey struct { topicName string subName string @@ -299,6 +337,13 @@ func reduceForServices(infra *runtimev1.Infrastructure, md *meta.Data, svcs []st }) } + for _, cluster := range infra.Resources.BucketClusters { + cluster.Buckets = slices.DeleteFunc(cluster.Buckets, func(t *runtimev1.Bucket) bool { + _, found := bucketsToKeep[t.EncoreName] + return !found + }) + } + secretsToKeep := secretsUsedByServices(md, svcNames) infra.Resources.AppSecrets = slices.DeleteFunc(infra.Resources.AppSecrets, func(t *runtimev1.AppSecret) bool { _, found := secretsToKeep[t.EncoreName] diff --git a/proto/encore/runtime/v1/infra.pb.go b/proto/encore/runtime/v1/infra.pb.go index fda85c8515..3ed6fdfab3 100644 --- a/proto/encore/runtime/v1/infra.pb.go +++ b/proto/encore/runtime/v1/infra.pb.go @@ -1528,10 +1528,13 @@ type BucketCluster struct { unknownFields protoimpl.UnknownFields // The unique resource id for this cluster. - Rid string `protobuf:"bytes,1,opt,name=rid,proto3" json:"rid,omitempty"` - Buckets []*Bucket `protobuf:"bytes,2,rep,name=buckets,proto3" json:"buckets,omitempty"` - Region string `protobuf:"bytes,3,opt,name=region,proto3" json:"region,omitempty"` - Endpoint string `protobuf:"bytes,4,opt,name=endpoint,proto3" json:"endpoint,omitempty"` + Rid string `protobuf:"bytes,1,opt,name=rid,proto3" json:"rid,omitempty"` + Buckets []*Bucket `protobuf:"bytes,2,rep,name=buckets,proto3" json:"buckets,omitempty"` + // Types that are assignable to Provider: + // + // *BucketCluster_S3_ + // *BucketCluster_Gcs + Provider isBucketCluster_Provider `protobuf_oneof:"provider"` } func (x *BucketCluster) Reset() { @@ -1580,20 +1583,43 @@ func (x *BucketCluster) GetBuckets() []*Bucket { return nil } -func (x *BucketCluster) GetRegion() string { - if x != nil { - return x.Region +func (m *BucketCluster) GetProvider() isBucketCluster_Provider { + if m != nil { + return m.Provider } - return "" + return nil } -func (x *BucketCluster) GetEndpoint() string { - if x != nil { - return x.Endpoint +func (x *BucketCluster) GetS3() *BucketCluster_S3 { + if x, ok := x.GetProvider().(*BucketCluster_S3_); ok { + return x.S3 } - return "" + return nil +} + +func (x *BucketCluster) GetGcs() *BucketCluster_GCS { + if x, ok := x.GetProvider().(*BucketCluster_Gcs); ok { + return x.Gcs + } + return nil +} + +type isBucketCluster_Provider interface { + isBucketCluster_Provider() +} + +type BucketCluster_S3_ struct { + S3 *BucketCluster_S3 `protobuf:"bytes,10,opt,name=s3,proto3,oneof"` +} + +type BucketCluster_Gcs struct { + Gcs *BucketCluster_GCS `protobuf:"bytes,11,opt,name=gcs,proto3,oneof"` } +func (*BucketCluster_S3_) isBucketCluster_Provider() {} + +func (*BucketCluster_Gcs) isBucketCluster_Provider() {} + type Bucket struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -2275,6 +2301,111 @@ func (x *PubSubSubscription_GCPConfig) GetPushJwtAudience() string { return "" } +type BucketCluster_S3 struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // Region to connect to. + Region string `protobuf:"bytes,1,opt,name=region,proto3" json:"region,omitempty"` + // Endpoint override, if any. Must be specified if using a non-standard AWS region. + Endpoint *string `protobuf:"bytes,2,opt,name=endpoint,proto3,oneof" json:"endpoint,omitempty"` +} + +func (x *BucketCluster_S3) Reset() { + *x = BucketCluster_S3{} + if protoimpl.UnsafeEnabled { + mi := &file_encore_runtime_v1_infra_proto_msgTypes[30] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *BucketCluster_S3) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*BucketCluster_S3) ProtoMessage() {} + +func (x *BucketCluster_S3) ProtoReflect() protoreflect.Message { + mi := &file_encore_runtime_v1_infra_proto_msgTypes[30] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use BucketCluster_S3.ProtoReflect.Descriptor instead. +func (*BucketCluster_S3) Descriptor() ([]byte, []int) { + return file_encore_runtime_v1_infra_proto_rawDescGZIP(), []int{17, 0} +} + +func (x *BucketCluster_S3) GetRegion() string { + if x != nil { + return x.Region + } + return "" +} + +func (x *BucketCluster_S3) GetEndpoint() string { + if x != nil && x.Endpoint != nil { + return *x.Endpoint + } + return "" +} + +type BucketCluster_GCS struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // Endpoint override, if any. Defaults to https://storage.googleapis.com if unset. + Endpoint *string `protobuf:"bytes,1,opt,name=endpoint,proto3,oneof" json:"endpoint,omitempty"` +} + +func (x *BucketCluster_GCS) Reset() { + *x = BucketCluster_GCS{} + if protoimpl.UnsafeEnabled { + mi := &file_encore_runtime_v1_infra_proto_msgTypes[31] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *BucketCluster_GCS) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*BucketCluster_GCS) ProtoMessage() {} + +func (x *BucketCluster_GCS) ProtoReflect() protoreflect.Message { + mi := &file_encore_runtime_v1_infra_proto_msgTypes[31] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use BucketCluster_GCS.ProtoReflect.Descriptor instead. +func (*BucketCluster_GCS) Descriptor() ([]byte, []int) { + return file_encore_runtime_v1_infra_proto_rawDescGZIP(), []int{17, 1} +} + +func (x *BucketCluster_GCS) GetEndpoint() string { + if x != nil && x.Endpoint != nil { + return *x.Endpoint + } + return "" +} + // CORS describes the CORS configuration for a gateway. type Gateway_CORS struct { state protoimpl.MessageState @@ -2319,7 +2450,7 @@ type Gateway_CORS struct { func (x *Gateway_CORS) Reset() { *x = Gateway_CORS{} if protoimpl.UnsafeEnabled { - mi := &file_encore_runtime_v1_infra_proto_msgTypes[30] + mi := &file_encore_runtime_v1_infra_proto_msgTypes[32] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2332,7 +2463,7 @@ func (x *Gateway_CORS) String() string { func (*Gateway_CORS) ProtoMessage() {} func (x *Gateway_CORS) ProtoReflect() protoreflect.Message { - mi := &file_encore_runtime_v1_infra_proto_msgTypes[30] + mi := &file_encore_runtime_v1_infra_proto_msgTypes[32] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2445,7 +2576,7 @@ type Gateway_CORSAllowedOrigins struct { func (x *Gateway_CORSAllowedOrigins) Reset() { *x = Gateway_CORSAllowedOrigins{} if protoimpl.UnsafeEnabled { - mi := &file_encore_runtime_v1_infra_proto_msgTypes[31] + mi := &file_encore_runtime_v1_infra_proto_msgTypes[33] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2458,7 +2589,7 @@ func (x *Gateway_CORSAllowedOrigins) String() string { func (*Gateway_CORSAllowedOrigins) ProtoMessage() {} func (x *Gateway_CORSAllowedOrigins) ProtoReflect() protoreflect.Message { - mi := &file_encore_runtime_v1_infra_proto_msgTypes[31] + mi := &file_encore_runtime_v1_infra_proto_msgTypes[33] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2789,84 +2920,97 @@ var file_encore_runtime_v1_infra_proto_rawDesc = []byte{ 0x72, 0x76, 0x69, 0x63, 0x65, 0x5f, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x42, 0x14, 0x0a, 0x12, 0x5f, 0x70, 0x75, 0x73, 0x68, 0x5f, 0x6a, 0x77, 0x74, 0x5f, 0x61, 0x75, 0x64, 0x69, 0x65, 0x6e, 0x63, 0x65, 0x42, 0x11, 0x0a, 0x0f, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x5f, - 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x22, 0x8a, 0x01, 0x0a, 0x0d, 0x42, 0x75, 0x63, 0x6b, 0x65, + 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x22, 0xd4, 0x02, 0x0a, 0x0d, 0x42, 0x75, 0x63, 0x6b, 0x65, 0x74, 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x12, 0x10, 0x0a, 0x03, 0x72, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x72, 0x69, 0x64, 0x12, 0x33, 0x0a, 0x07, 0x62, 0x75, 0x63, 0x6b, 0x65, 0x74, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x65, 0x6e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x42, 0x75, 0x63, 0x6b, 0x65, 0x74, 0x52, 0x07, 0x62, 0x75, 0x63, 0x6b, 0x65, 0x74, 0x73, 0x12, - 0x16, 0x0a, 0x06, 0x72, 0x65, 0x67, 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x06, 0x72, 0x65, 0x67, 0x69, 0x6f, 0x6e, 0x12, 0x1a, 0x0a, 0x08, 0x65, 0x6e, 0x64, 0x70, 0x6f, - 0x69, 0x6e, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x65, 0x6e, 0x64, 0x70, 0x6f, - 0x69, 0x6e, 0x74, 0x22, 0x5a, 0x0a, 0x06, 0x42, 0x75, 0x63, 0x6b, 0x65, 0x74, 0x12, 0x10, 0x0a, - 0x03, 0x72, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x72, 0x69, 0x64, 0x12, - 0x1f, 0x0a, 0x0b, 0x65, 0x6e, 0x63, 0x6f, 0x72, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x65, 0x6e, 0x63, 0x6f, 0x72, 0x65, 0x4e, 0x61, 0x6d, 0x65, - 0x12, 0x1d, 0x0a, 0x0a, 0x63, 0x6c, 0x6f, 0x75, 0x64, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x03, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x63, 0x6c, 0x6f, 0x75, 0x64, 0x4e, 0x61, 0x6d, 0x65, 0x22, - 0xb9, 0x06, 0x0a, 0x07, 0x47, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x72, - 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x72, 0x69, 0x64, 0x12, 0x1f, 0x0a, - 0x0b, 0x65, 0x6e, 0x63, 0x6f, 0x72, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x0a, 0x65, 0x6e, 0x63, 0x6f, 0x72, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x19, - 0x0a, 0x08, 0x62, 0x61, 0x73, 0x65, 0x5f, 0x75, 0x72, 0x6c, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x07, 0x62, 0x61, 0x73, 0x65, 0x55, 0x72, 0x6c, 0x12, 0x1c, 0x0a, 0x09, 0x68, 0x6f, 0x73, - 0x74, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x09, 0x52, 0x09, 0x68, 0x6f, - 0x73, 0x74, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x12, 0x33, 0x0a, 0x04, 0x63, 0x6f, 0x72, 0x73, 0x18, - 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x65, 0x6e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x72, - 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x61, 0x74, 0x65, 0x77, 0x61, - 0x79, 0x2e, 0x43, 0x4f, 0x52, 0x53, 0x52, 0x04, 0x63, 0x6f, 0x72, 0x73, 0x1a, 0xcd, 0x04, 0x0a, - 0x04, 0x43, 0x4f, 0x52, 0x53, 0x12, 0x14, 0x0a, 0x05, 0x64, 0x65, 0x62, 0x75, 0x67, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x08, 0x52, 0x05, 0x64, 0x65, 0x62, 0x75, 0x67, 0x12, 0x2f, 0x0a, 0x13, 0x64, - 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x63, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, - 0x6c, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x12, 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, - 0x65, 0x43, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x73, 0x12, 0x58, 0x0a, 0x0f, + 0x35, 0x0a, 0x02, 0x73, 0x33, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x23, 0x2e, 0x65, 0x6e, + 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, + 0x42, 0x75, 0x63, 0x6b, 0x65, 0x74, 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x2e, 0x53, 0x33, + 0x48, 0x00, 0x52, 0x02, 0x73, 0x33, 0x12, 0x38, 0x0a, 0x03, 0x67, 0x63, 0x73, 0x18, 0x0b, 0x20, + 0x01, 0x28, 0x0b, 0x32, 0x24, 0x2e, 0x65, 0x6e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x72, 0x75, 0x6e, + 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x42, 0x75, 0x63, 0x6b, 0x65, 0x74, 0x43, 0x6c, + 0x75, 0x73, 0x74, 0x65, 0x72, 0x2e, 0x47, 0x43, 0x53, 0x48, 0x00, 0x52, 0x03, 0x67, 0x63, 0x73, + 0x1a, 0x4a, 0x0a, 0x02, 0x53, 0x33, 0x12, 0x16, 0x0a, 0x06, 0x72, 0x65, 0x67, 0x69, 0x6f, 0x6e, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x72, 0x65, 0x67, 0x69, 0x6f, 0x6e, 0x12, 0x1f, + 0x0a, 0x08, 0x65, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, + 0x48, 0x00, 0x52, 0x08, 0x65, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x88, 0x01, 0x01, 0x42, + 0x0b, 0x0a, 0x09, 0x5f, 0x65, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x1a, 0x33, 0x0a, 0x03, + 0x47, 0x43, 0x53, 0x12, 0x1f, 0x0a, 0x08, 0x65, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x09, 0x48, 0x00, 0x52, 0x08, 0x65, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, + 0x74, 0x88, 0x01, 0x01, 0x42, 0x0b, 0x0a, 0x09, 0x5f, 0x65, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, + 0x74, 0x42, 0x0a, 0x0a, 0x08, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x22, 0x5a, 0x0a, + 0x06, 0x42, 0x75, 0x63, 0x6b, 0x65, 0x74, 0x12, 0x10, 0x0a, 0x03, 0x72, 0x69, 0x64, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x72, 0x69, 0x64, 0x12, 0x1f, 0x0a, 0x0b, 0x65, 0x6e, 0x63, + 0x6f, 0x72, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, + 0x65, 0x6e, 0x63, 0x6f, 0x72, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x1d, 0x0a, 0x0a, 0x63, 0x6c, + 0x6f, 0x75, 0x64, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, + 0x63, 0x6c, 0x6f, 0x75, 0x64, 0x4e, 0x61, 0x6d, 0x65, 0x22, 0xb9, 0x06, 0x0a, 0x07, 0x47, 0x61, + 0x74, 0x65, 0x77, 0x61, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x72, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x03, 0x72, 0x69, 0x64, 0x12, 0x1f, 0x0a, 0x0b, 0x65, 0x6e, 0x63, 0x6f, 0x72, + 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x65, 0x6e, + 0x63, 0x6f, 0x72, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x19, 0x0a, 0x08, 0x62, 0x61, 0x73, 0x65, + 0x5f, 0x75, 0x72, 0x6c, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x62, 0x61, 0x73, 0x65, + 0x55, 0x72, 0x6c, 0x12, 0x1c, 0x0a, 0x09, 0x68, 0x6f, 0x73, 0x74, 0x6e, 0x61, 0x6d, 0x65, 0x73, + 0x18, 0x04, 0x20, 0x03, 0x28, 0x09, 0x52, 0x09, 0x68, 0x6f, 0x73, 0x74, 0x6e, 0x61, 0x6d, 0x65, + 0x73, 0x12, 0x33, 0x0a, 0x04, 0x63, 0x6f, 0x72, 0x73, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x1f, 0x2e, 0x65, 0x6e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, + 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x2e, 0x43, 0x4f, 0x52, 0x53, + 0x52, 0x04, 0x63, 0x6f, 0x72, 0x73, 0x1a, 0xcd, 0x04, 0x0a, 0x04, 0x43, 0x4f, 0x52, 0x53, 0x12, + 0x14, 0x0a, 0x05, 0x64, 0x65, 0x62, 0x75, 0x67, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x05, + 0x64, 0x65, 0x62, 0x75, 0x67, 0x12, 0x2f, 0x0a, 0x13, 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, + 0x5f, 0x63, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x73, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x08, 0x52, 0x12, 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x43, 0x72, 0x65, 0x64, 0x65, + 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x73, 0x12, 0x58, 0x0a, 0x0f, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x65, + 0x64, 0x5f, 0x6f, 0x72, 0x69, 0x67, 0x69, 0x6e, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x2d, 0x2e, 0x65, 0x6e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, + 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x2e, 0x43, 0x4f, 0x52, 0x53, + 0x41, 0x6c, 0x6c, 0x6f, 0x77, 0x65, 0x64, 0x4f, 0x72, 0x69, 0x67, 0x69, 0x6e, 0x73, 0x48, 0x00, + 0x52, 0x0e, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x65, 0x64, 0x4f, 0x72, 0x69, 0x67, 0x69, 0x6e, 0x73, + 0x12, 0x59, 0x0a, 0x29, 0x75, 0x6e, 0x73, 0x61, 0x66, 0x65, 0x5f, 0x61, 0x6c, 0x6c, 0x6f, 0x77, + 0x5f, 0x61, 0x6c, 0x6c, 0x5f, 0x6f, 0x72, 0x69, 0x67, 0x69, 0x6e, 0x73, 0x5f, 0x77, 0x69, 0x74, + 0x68, 0x5f, 0x63, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x73, 0x18, 0x04, 0x20, + 0x01, 0x28, 0x08, 0x48, 0x00, 0x52, 0x24, 0x75, 0x6e, 0x73, 0x61, 0x66, 0x65, 0x41, 0x6c, 0x6c, + 0x6f, 0x77, 0x41, 0x6c, 0x6c, 0x4f, 0x72, 0x69, 0x67, 0x69, 0x6e, 0x73, 0x57, 0x69, 0x74, 0x68, + 0x43, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x73, 0x12, 0x7c, 0x0a, 0x23, 0x61, + 0x6c, 0x6c, 0x6f, 0x77, 0x65, 0x64, 0x5f, 0x6f, 0x72, 0x69, 0x67, 0x69, 0x6e, 0x73, 0x5f, 0x77, + 0x69, 0x74, 0x68, 0x6f, 0x75, 0x74, 0x5f, 0x63, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, + 0x6c, 0x73, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2d, 0x2e, 0x65, 0x6e, 0x63, 0x6f, 0x72, + 0x65, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x61, 0x74, + 0x65, 0x77, 0x61, 0x79, 0x2e, 0x43, 0x4f, 0x52, 0x53, 0x41, 0x6c, 0x6c, 0x6f, 0x77, 0x65, 0x64, + 0x4f, 0x72, 0x69, 0x67, 0x69, 0x6e, 0x73, 0x52, 0x20, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x65, 0x64, + 0x4f, 0x72, 0x69, 0x67, 0x69, 0x6e, 0x73, 0x57, 0x69, 0x74, 0x68, 0x6f, 0x75, 0x74, 0x43, 0x72, + 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x73, 0x12, 0x32, 0x0a, 0x15, 0x65, 0x78, 0x74, + 0x72, 0x61, 0x5f, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x65, 0x64, 0x5f, 0x68, 0x65, 0x61, 0x64, 0x65, + 0x72, 0x73, 0x18, 0x06, 0x20, 0x03, 0x28, 0x09, 0x52, 0x13, 0x65, 0x78, 0x74, 0x72, 0x61, 0x41, + 0x6c, 0x6c, 0x6f, 0x77, 0x65, 0x64, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x73, 0x12, 0x32, 0x0a, + 0x15, 0x65, 0x78, 0x74, 0x72, 0x61, 0x5f, 0x65, 0x78, 0x70, 0x6f, 0x73, 0x65, 0x64, 0x5f, 0x68, + 0x65, 0x61, 0x64, 0x65, 0x72, 0x73, 0x18, 0x07, 0x20, 0x03, 0x28, 0x09, 0x52, 0x13, 0x65, 0x78, + 0x74, 0x72, 0x61, 0x45, 0x78, 0x70, 0x6f, 0x73, 0x65, 0x64, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, + 0x73, 0x12, 0x3f, 0x0a, 0x1c, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x5f, 0x70, 0x72, 0x69, 0x76, 0x61, + 0x74, 0x65, 0x5f, 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x5f, 0x61, 0x63, 0x63, 0x65, 0x73, + 0x73, 0x18, 0x08, 0x20, 0x01, 0x28, 0x08, 0x52, 0x19, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x50, 0x72, + 0x69, 0x76, 0x61, 0x74, 0x65, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x41, 0x63, 0x63, 0x65, + 0x73, 0x73, 0x42, 0x22, 0x0a, 0x20, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x65, 0x64, 0x5f, 0x6f, 0x72, + 0x69, 0x67, 0x69, 0x6e, 0x73, 0x5f, 0x77, 0x69, 0x74, 0x68, 0x5f, 0x63, 0x72, 0x65, 0x64, 0x65, + 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x73, 0x1a, 0x3d, 0x0a, 0x12, 0x43, 0x4f, 0x52, 0x53, 0x41, 0x6c, + 0x6c, 0x6f, 0x77, 0x65, 0x64, 0x4f, 0x72, 0x69, 0x67, 0x69, 0x6e, 0x73, 0x12, 0x27, 0x0a, 0x0f, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x65, 0x64, 0x5f, 0x6f, 0x72, 0x69, 0x67, 0x69, 0x6e, 0x73, 0x18, - 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2d, 0x2e, 0x65, 0x6e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x72, - 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x61, 0x74, 0x65, 0x77, 0x61, - 0x79, 0x2e, 0x43, 0x4f, 0x52, 0x53, 0x41, 0x6c, 0x6c, 0x6f, 0x77, 0x65, 0x64, 0x4f, 0x72, 0x69, - 0x67, 0x69, 0x6e, 0x73, 0x48, 0x00, 0x52, 0x0e, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x65, 0x64, 0x4f, - 0x72, 0x69, 0x67, 0x69, 0x6e, 0x73, 0x12, 0x59, 0x0a, 0x29, 0x75, 0x6e, 0x73, 0x61, 0x66, 0x65, - 0x5f, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x5f, 0x61, 0x6c, 0x6c, 0x5f, 0x6f, 0x72, 0x69, 0x67, 0x69, - 0x6e, 0x73, 0x5f, 0x77, 0x69, 0x74, 0x68, 0x5f, 0x63, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, - 0x61, 0x6c, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x48, 0x00, 0x52, 0x24, 0x75, 0x6e, 0x73, - 0x61, 0x66, 0x65, 0x41, 0x6c, 0x6c, 0x6f, 0x77, 0x41, 0x6c, 0x6c, 0x4f, 0x72, 0x69, 0x67, 0x69, - 0x6e, 0x73, 0x57, 0x69, 0x74, 0x68, 0x43, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, - 0x73, 0x12, 0x7c, 0x0a, 0x23, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x65, 0x64, 0x5f, 0x6f, 0x72, 0x69, - 0x67, 0x69, 0x6e, 0x73, 0x5f, 0x77, 0x69, 0x74, 0x68, 0x6f, 0x75, 0x74, 0x5f, 0x63, 0x72, 0x65, - 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x73, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2d, - 0x2e, 0x65, 0x6e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, - 0x76, 0x31, 0x2e, 0x47, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x2e, 0x43, 0x4f, 0x52, 0x53, 0x41, - 0x6c, 0x6c, 0x6f, 0x77, 0x65, 0x64, 0x4f, 0x72, 0x69, 0x67, 0x69, 0x6e, 0x73, 0x52, 0x20, 0x61, - 0x6c, 0x6c, 0x6f, 0x77, 0x65, 0x64, 0x4f, 0x72, 0x69, 0x67, 0x69, 0x6e, 0x73, 0x57, 0x69, 0x74, - 0x68, 0x6f, 0x75, 0x74, 0x43, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x73, 0x12, - 0x32, 0x0a, 0x15, 0x65, 0x78, 0x74, 0x72, 0x61, 0x5f, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x65, 0x64, - 0x5f, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x73, 0x18, 0x06, 0x20, 0x03, 0x28, 0x09, 0x52, 0x13, - 0x65, 0x78, 0x74, 0x72, 0x61, 0x41, 0x6c, 0x6c, 0x6f, 0x77, 0x65, 0x64, 0x48, 0x65, 0x61, 0x64, - 0x65, 0x72, 0x73, 0x12, 0x32, 0x0a, 0x15, 0x65, 0x78, 0x74, 0x72, 0x61, 0x5f, 0x65, 0x78, 0x70, - 0x6f, 0x73, 0x65, 0x64, 0x5f, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x73, 0x18, 0x07, 0x20, 0x03, - 0x28, 0x09, 0x52, 0x13, 0x65, 0x78, 0x74, 0x72, 0x61, 0x45, 0x78, 0x70, 0x6f, 0x73, 0x65, 0x64, - 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x73, 0x12, 0x3f, 0x0a, 0x1c, 0x61, 0x6c, 0x6c, 0x6f, 0x77, - 0x5f, 0x70, 0x72, 0x69, 0x76, 0x61, 0x74, 0x65, 0x5f, 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, - 0x5f, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x18, 0x08, 0x20, 0x01, 0x28, 0x08, 0x52, 0x19, 0x61, - 0x6c, 0x6c, 0x6f, 0x77, 0x50, 0x72, 0x69, 0x76, 0x61, 0x74, 0x65, 0x4e, 0x65, 0x74, 0x77, 0x6f, - 0x72, 0x6b, 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, 0x42, 0x22, 0x0a, 0x20, 0x61, 0x6c, 0x6c, 0x6f, - 0x77, 0x65, 0x64, 0x5f, 0x6f, 0x72, 0x69, 0x67, 0x69, 0x6e, 0x73, 0x5f, 0x77, 0x69, 0x74, 0x68, - 0x5f, 0x63, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x73, 0x1a, 0x3d, 0x0a, 0x12, - 0x43, 0x4f, 0x52, 0x53, 0x41, 0x6c, 0x6c, 0x6f, 0x77, 0x65, 0x64, 0x4f, 0x72, 0x69, 0x67, 0x69, - 0x6e, 0x73, 0x12, 0x27, 0x0a, 0x0f, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x65, 0x64, 0x5f, 0x6f, 0x72, - 0x69, 0x67, 0x69, 0x6e, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0e, 0x61, 0x6c, 0x6c, - 0x6f, 0x77, 0x65, 0x64, 0x4f, 0x72, 0x69, 0x67, 0x69, 0x6e, 0x73, 0x2a, 0x7d, 0x0a, 0x0a, 0x53, - 0x65, 0x72, 0x76, 0x65, 0x72, 0x4b, 0x69, 0x6e, 0x64, 0x12, 0x1b, 0x0a, 0x17, 0x53, 0x45, 0x52, - 0x56, 0x45, 0x52, 0x5f, 0x4b, 0x49, 0x4e, 0x44, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, - 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x17, 0x0a, 0x13, 0x53, 0x45, 0x52, 0x56, 0x45, 0x52, - 0x5f, 0x4b, 0x49, 0x4e, 0x44, 0x5f, 0x50, 0x52, 0x49, 0x4d, 0x41, 0x52, 0x59, 0x10, 0x01, 0x12, - 0x1b, 0x0a, 0x17, 0x53, 0x45, 0x52, 0x56, 0x45, 0x52, 0x5f, 0x4b, 0x49, 0x4e, 0x44, 0x5f, 0x48, - 0x4f, 0x54, 0x5f, 0x53, 0x54, 0x41, 0x4e, 0x44, 0x42, 0x59, 0x10, 0x02, 0x12, 0x1c, 0x0a, 0x18, - 0x53, 0x45, 0x52, 0x56, 0x45, 0x52, 0x5f, 0x4b, 0x49, 0x4e, 0x44, 0x5f, 0x52, 0x45, 0x41, 0x44, - 0x5f, 0x52, 0x45, 0x50, 0x4c, 0x49, 0x43, 0x41, 0x10, 0x03, 0x42, 0x2c, 0x5a, 0x2a, 0x65, 0x6e, - 0x63, 0x72, 0x2e, 0x64, 0x65, 0x76, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x65, 0x6e, 0x63, - 0x6f, 0x72, 0x65, 0x2f, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2f, 0x76, 0x31, 0x3b, 0x72, - 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x76, 0x31, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0e, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x65, 0x64, 0x4f, 0x72, + 0x69, 0x67, 0x69, 0x6e, 0x73, 0x2a, 0x7d, 0x0a, 0x0a, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x4b, + 0x69, 0x6e, 0x64, 0x12, 0x1b, 0x0a, 0x17, 0x53, 0x45, 0x52, 0x56, 0x45, 0x52, 0x5f, 0x4b, 0x49, + 0x4e, 0x44, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, + 0x12, 0x17, 0x0a, 0x13, 0x53, 0x45, 0x52, 0x56, 0x45, 0x52, 0x5f, 0x4b, 0x49, 0x4e, 0x44, 0x5f, + 0x50, 0x52, 0x49, 0x4d, 0x41, 0x52, 0x59, 0x10, 0x01, 0x12, 0x1b, 0x0a, 0x17, 0x53, 0x45, 0x52, + 0x56, 0x45, 0x52, 0x5f, 0x4b, 0x49, 0x4e, 0x44, 0x5f, 0x48, 0x4f, 0x54, 0x5f, 0x53, 0x54, 0x41, + 0x4e, 0x44, 0x42, 0x59, 0x10, 0x02, 0x12, 0x1c, 0x0a, 0x18, 0x53, 0x45, 0x52, 0x56, 0x45, 0x52, + 0x5f, 0x4b, 0x49, 0x4e, 0x44, 0x5f, 0x52, 0x45, 0x41, 0x44, 0x5f, 0x52, 0x45, 0x50, 0x4c, 0x49, + 0x43, 0x41, 0x10, 0x03, 0x42, 0x2c, 0x5a, 0x2a, 0x65, 0x6e, 0x63, 0x72, 0x2e, 0x64, 0x65, 0x76, + 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x65, 0x6e, 0x63, 0x6f, 0x72, 0x65, 0x2f, 0x72, 0x75, + 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2f, 0x76, 0x31, 0x3b, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, + 0x76, 0x31, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( @@ -2882,7 +3026,7 @@ func file_encore_runtime_v1_infra_proto_rawDescGZIP() []byte { } var file_encore_runtime_v1_infra_proto_enumTypes = make([]protoimpl.EnumInfo, 2) -var file_encore_runtime_v1_infra_proto_msgTypes = make([]protoimpl.MessageInfo, 32) +var file_encore_runtime_v1_infra_proto_msgTypes = make([]protoimpl.MessageInfo, 34) var file_encore_runtime_v1_infra_proto_goTypes = []interface{}{ (ServerKind)(0), // 0: encore.runtime.v1.ServerKind (PubSubTopic_DeliveryGuarantee)(0), // 1: encore.runtime.v1.PubSubTopic.DeliveryGuarantee @@ -2916,9 +3060,11 @@ var file_encore_runtime_v1_infra_proto_goTypes = []interface{}{ (*PubSubCluster_AzureServiceBus)(nil), // 29: encore.runtime.v1.PubSubCluster.AzureServiceBus (*PubSubTopic_GCPConfig)(nil), // 30: encore.runtime.v1.PubSubTopic.GCPConfig (*PubSubSubscription_GCPConfig)(nil), // 31: encore.runtime.v1.PubSubSubscription.GCPConfig - (*Gateway_CORS)(nil), // 32: encore.runtime.v1.Gateway.CORS - (*Gateway_CORSAllowedOrigins)(nil), // 33: encore.runtime.v1.Gateway.CORSAllowedOrigins - (*SecretData)(nil), // 34: encore.runtime.v1.SecretData + (*BucketCluster_S3)(nil), // 32: encore.runtime.v1.BucketCluster.S3 + (*BucketCluster_GCS)(nil), // 33: encore.runtime.v1.BucketCluster.GCS + (*Gateway_CORS)(nil), // 34: encore.runtime.v1.Gateway.CORS + (*Gateway_CORSAllowedOrigins)(nil), // 35: encore.runtime.v1.Gateway.CORSAllowedOrigins + (*SecretData)(nil), // 36: encore.runtime.v1.SecretData } var file_encore_runtime_v1_infra_proto_depIdxs = []int32{ 23, // 0: encore.runtime.v1.Infrastructure.resources:type_name -> encore.runtime.v1.Infrastructure.Resources @@ -2927,17 +3073,17 @@ var file_encore_runtime_v1_infra_proto_depIdxs = []int32{ 8, // 3: encore.runtime.v1.SQLCluster.databases:type_name -> encore.runtime.v1.SQLDatabase 0, // 4: encore.runtime.v1.SQLServer.kind:type_name -> encore.runtime.v1.ServerKind 4, // 5: encore.runtime.v1.SQLServer.tls_config:type_name -> encore.runtime.v1.TLSConfig - 34, // 6: encore.runtime.v1.ClientCert.key:type_name -> encore.runtime.v1.SecretData - 34, // 7: encore.runtime.v1.SQLRole.password:type_name -> encore.runtime.v1.SecretData + 36, // 6: encore.runtime.v1.ClientCert.key:type_name -> encore.runtime.v1.SecretData + 36, // 7: encore.runtime.v1.SQLRole.password:type_name -> encore.runtime.v1.SecretData 9, // 8: encore.runtime.v1.SQLDatabase.conn_pools:type_name -> encore.runtime.v1.SQLConnectionPool 11, // 9: encore.runtime.v1.RedisCluster.servers:type_name -> encore.runtime.v1.RedisServer 14, // 10: encore.runtime.v1.RedisCluster.databases:type_name -> encore.runtime.v1.RedisDatabase 0, // 11: encore.runtime.v1.RedisServer.kind:type_name -> encore.runtime.v1.ServerKind 4, // 12: encore.runtime.v1.RedisServer.tls_config:type_name -> encore.runtime.v1.TLSConfig 24, // 13: encore.runtime.v1.RedisRole.acl:type_name -> encore.runtime.v1.RedisRole.AuthACL - 34, // 14: encore.runtime.v1.RedisRole.auth_string:type_name -> encore.runtime.v1.SecretData + 36, // 14: encore.runtime.v1.RedisRole.auth_string:type_name -> encore.runtime.v1.SecretData 12, // 15: encore.runtime.v1.RedisDatabase.conn_pools:type_name -> encore.runtime.v1.RedisConnectionPool - 34, // 16: encore.runtime.v1.AppSecret.data:type_name -> encore.runtime.v1.SecretData + 36, // 16: encore.runtime.v1.AppSecret.data:type_name -> encore.runtime.v1.SecretData 17, // 17: encore.runtime.v1.PubSubCluster.topics:type_name -> encore.runtime.v1.PubSubTopic 18, // 18: encore.runtime.v1.PubSubCluster.subscriptions:type_name -> encore.runtime.v1.PubSubSubscription 25, // 19: encore.runtime.v1.PubSubCluster.encore:type_name -> encore.runtime.v1.PubSubCluster.EncoreCloud @@ -2949,24 +3095,26 @@ var file_encore_runtime_v1_infra_proto_depIdxs = []int32{ 30, // 25: encore.runtime.v1.PubSubTopic.gcp_config:type_name -> encore.runtime.v1.PubSubTopic.GCPConfig 31, // 26: encore.runtime.v1.PubSubSubscription.gcp_config:type_name -> encore.runtime.v1.PubSubSubscription.GCPConfig 20, // 27: encore.runtime.v1.BucketCluster.buckets:type_name -> encore.runtime.v1.Bucket - 32, // 28: encore.runtime.v1.Gateway.cors:type_name -> encore.runtime.v1.Gateway.CORS - 6, // 29: encore.runtime.v1.Infrastructure.Credentials.client_certs:type_name -> encore.runtime.v1.ClientCert - 7, // 30: encore.runtime.v1.Infrastructure.Credentials.sql_roles:type_name -> encore.runtime.v1.SQLRole - 13, // 31: encore.runtime.v1.Infrastructure.Credentials.redis_roles:type_name -> encore.runtime.v1.RedisRole - 21, // 32: encore.runtime.v1.Infrastructure.Resources.gateways:type_name -> encore.runtime.v1.Gateway - 3, // 33: encore.runtime.v1.Infrastructure.Resources.sql_clusters:type_name -> encore.runtime.v1.SQLCluster - 16, // 34: encore.runtime.v1.Infrastructure.Resources.pubsub_clusters:type_name -> encore.runtime.v1.PubSubCluster - 10, // 35: encore.runtime.v1.Infrastructure.Resources.redis_clusters:type_name -> encore.runtime.v1.RedisCluster - 15, // 36: encore.runtime.v1.Infrastructure.Resources.app_secrets:type_name -> encore.runtime.v1.AppSecret - 19, // 37: encore.runtime.v1.Infrastructure.Resources.bucket_clusters:type_name -> encore.runtime.v1.BucketCluster - 34, // 38: encore.runtime.v1.RedisRole.AuthACL.password:type_name -> encore.runtime.v1.SecretData - 33, // 39: encore.runtime.v1.Gateway.CORS.allowed_origins:type_name -> encore.runtime.v1.Gateway.CORSAllowedOrigins - 33, // 40: encore.runtime.v1.Gateway.CORS.allowed_origins_without_credentials:type_name -> encore.runtime.v1.Gateway.CORSAllowedOrigins - 41, // [41:41] is the sub-list for method output_type - 41, // [41:41] is the sub-list for method input_type - 41, // [41:41] is the sub-list for extension type_name - 41, // [41:41] is the sub-list for extension extendee - 0, // [0:41] is the sub-list for field type_name + 32, // 28: encore.runtime.v1.BucketCluster.s3:type_name -> encore.runtime.v1.BucketCluster.S3 + 33, // 29: encore.runtime.v1.BucketCluster.gcs:type_name -> encore.runtime.v1.BucketCluster.GCS + 34, // 30: encore.runtime.v1.Gateway.cors:type_name -> encore.runtime.v1.Gateway.CORS + 6, // 31: encore.runtime.v1.Infrastructure.Credentials.client_certs:type_name -> encore.runtime.v1.ClientCert + 7, // 32: encore.runtime.v1.Infrastructure.Credentials.sql_roles:type_name -> encore.runtime.v1.SQLRole + 13, // 33: encore.runtime.v1.Infrastructure.Credentials.redis_roles:type_name -> encore.runtime.v1.RedisRole + 21, // 34: encore.runtime.v1.Infrastructure.Resources.gateways:type_name -> encore.runtime.v1.Gateway + 3, // 35: encore.runtime.v1.Infrastructure.Resources.sql_clusters:type_name -> encore.runtime.v1.SQLCluster + 16, // 36: encore.runtime.v1.Infrastructure.Resources.pubsub_clusters:type_name -> encore.runtime.v1.PubSubCluster + 10, // 37: encore.runtime.v1.Infrastructure.Resources.redis_clusters:type_name -> encore.runtime.v1.RedisCluster + 15, // 38: encore.runtime.v1.Infrastructure.Resources.app_secrets:type_name -> encore.runtime.v1.AppSecret + 19, // 39: encore.runtime.v1.Infrastructure.Resources.bucket_clusters:type_name -> encore.runtime.v1.BucketCluster + 36, // 40: encore.runtime.v1.RedisRole.AuthACL.password:type_name -> encore.runtime.v1.SecretData + 35, // 41: encore.runtime.v1.Gateway.CORS.allowed_origins:type_name -> encore.runtime.v1.Gateway.CORSAllowedOrigins + 35, // 42: encore.runtime.v1.Gateway.CORS.allowed_origins_without_credentials:type_name -> encore.runtime.v1.Gateway.CORSAllowedOrigins + 43, // [43:43] is the sub-list for method output_type + 43, // [43:43] is the sub-list for method input_type + 43, // [43:43] is the sub-list for extension type_name + 43, // [43:43] is the sub-list for extension extendee + 0, // [0:43] is the sub-list for field type_name } func init() { file_encore_runtime_v1_infra_proto_init() } @@ -3337,7 +3485,7 @@ func file_encore_runtime_v1_infra_proto_init() { } } file_encore_runtime_v1_infra_proto_msgTypes[30].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*Gateway_CORS); i { + switch v := v.(*BucketCluster_S3); i { case 0: return &v.state case 1: @@ -3349,6 +3497,30 @@ func file_encore_runtime_v1_infra_proto_init() { } } file_encore_runtime_v1_infra_proto_msgTypes[31].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*BucketCluster_GCS); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_encore_runtime_v1_infra_proto_msgTypes[32].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Gateway_CORS); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_encore_runtime_v1_infra_proto_msgTypes[33].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*Gateway_CORSAllowedOrigins); i { case 0: return &v.state @@ -3383,8 +3555,14 @@ func file_encore_runtime_v1_infra_proto_init() { file_encore_runtime_v1_infra_proto_msgTypes[16].OneofWrappers = []interface{}{ (*PubSubSubscription_GcpConfig)(nil), } + file_encore_runtime_v1_infra_proto_msgTypes[17].OneofWrappers = []interface{}{ + (*BucketCluster_S3_)(nil), + (*BucketCluster_Gcs)(nil), + } file_encore_runtime_v1_infra_proto_msgTypes[29].OneofWrappers = []interface{}{} - file_encore_runtime_v1_infra_proto_msgTypes[30].OneofWrappers = []interface{}{ + file_encore_runtime_v1_infra_proto_msgTypes[30].OneofWrappers = []interface{}{} + file_encore_runtime_v1_infra_proto_msgTypes[31].OneofWrappers = []interface{}{} + file_encore_runtime_v1_infra_proto_msgTypes[32].OneofWrappers = []interface{}{ (*Gateway_CORS_AllowedOrigins)(nil), (*Gateway_CORS_UnsafeAllowAllOriginsWithCredentials)(nil), } @@ -3394,7 +3572,7 @@ func file_encore_runtime_v1_infra_proto_init() { GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_encore_runtime_v1_infra_proto_rawDesc, NumEnums: 2, - NumMessages: 32, + NumMessages: 34, NumExtensions: 0, NumServices: 0, }, diff --git a/proto/encore/runtime/v1/infra.proto b/proto/encore/runtime/v1/infra.proto index 9c0691f5bf..fabaa7a106 100644 --- a/proto/encore/runtime/v1/infra.proto +++ b/proto/encore/runtime/v1/infra.proto @@ -312,8 +312,23 @@ message BucketCluster { repeated Bucket buckets = 2; - string region = 3; - string endpoint = 4; + oneof provider { + S3 s3 = 10; + GCS gcs = 11; + } + + message S3 { + // Region to connect to. + string region = 1; + + // Endpoint override, if any. Must be specified if using a non-standard AWS region. + optional string endpoint = 2; + } + + message GCS { + // Endpoint override, if any. Defaults to https://storage.googleapis.com if unset. + optional string endpoint = 1; + } } message Bucket { diff --git a/runtimes/core/Cargo.toml b/runtimes/core/Cargo.toml index 07721c162f..11ca146731 100644 --- a/runtimes/core/Cargo.toml +++ b/runtimes/core/Cargo.toml @@ -84,6 +84,7 @@ flate2 = "1.0.30" urlencoding = "2.1.3" tower-http = { version = "0.5.2", features = ["fs"] } rust-s3 = "0.35.1" +google-cloud-storage = "0.22.1" serde_path_to_error = "0.1.16" tracing = "0.1.40" tracing-subscriber = { version = "0.3.18", features = ["alloc", "ansi", "env-filter", "fmt", "matchers", "nu-ansi-term", "once_cell", "regex", "registry", "sharded-slab", "smallvec", "std", "thread_local", "tracing"], default-features = false } diff --git a/runtimes/core/src/infracfg.rs b/runtimes/core/src/infracfg.rs index 5f7d4373ce..1212fdbf7b 100644 --- a/runtimes/core/src/infracfg.rs +++ b/runtimes/core/src/infracfg.rs @@ -892,6 +892,7 @@ pub fn map_infra_to_runtime(infra: InfraConfig) -> RuntimeConfig { sql_clusters: sql_clusters.unwrap_or_default(), pubsub_clusters: pubsub_clusters.unwrap_or_default(), redis_clusters: redis_clusters.unwrap_or_default(), + bucket_clusters: vec![], // TODO app_secrets, }); diff --git a/runtimes/core/src/objects/gcs/bucket.rs b/runtimes/core/src/objects/gcs/bucket.rs new file mode 100644 index 0000000000..50b0267721 --- /dev/null +++ b/runtimes/core/src/objects/gcs/bucket.rs @@ -0,0 +1,67 @@ +use anyhow::Result; +use std::future::Future; +use std::pin::Pin; +use std::sync::Arc; + +use crate::encore::runtime::v1 as pb; +use crate::{objects, CloudName}; +use google_cloud_storage as gcs; + +use super::LazyGCSClient; + +#[derive(Debug)] +pub struct Bucket { + client: Arc, + name: CloudName, +} + +impl Bucket { + pub(super) fn new(client: Arc, cfg: &pb::Bucket) -> Self { + Self { + client, + name: cfg.cloud_name.clone().into(), + } + } +} + +impl objects::BucketImpl for Bucket { + fn object(self: Arc, name: String) -> Arc { + Arc::new(Object { bkt: self, name }) + } +} + +#[derive(Debug)] +struct Object { + bkt: Arc, + name: String, +} + +impl objects::ObjectImpl for Object { + fn exists(self: Arc) -> Pin> + Send>> { + Box::pin(async move { + match self.bkt.client.get().await { + Ok(client) => { + use gcs::http::{error::ErrorResponse, Error}; + let req = &gcs::http::objects::get::GetObjectRequest { + bucket: self.bkt.name.to_string(), + object: self.name.clone(), + ..Default::default() + }; + + match client.get_object(req).await { + Ok(_obj) => Ok(true), + Err(Error::Response(ErrorResponse { code: 404, .. })) => Ok(false), + Err(Error::HttpClient(err)) + if err.status().is_some_and(|v| v.as_u16() == 404) => + { + Ok(false) + } + + Err(err) => Err(err.into()), + } + } + Err(err) => Err(anyhow::anyhow!("unable to resolve client: {}", err)), + } + }) + } +} diff --git a/runtimes/core/src/objects/gcs/mod.rs b/runtimes/core/src/objects/gcs/mod.rs new file mode 100644 index 0000000000..f1845a8f3f --- /dev/null +++ b/runtimes/core/src/objects/gcs/mod.rs @@ -0,0 +1,65 @@ +use std::fmt::Debug; +use std::sync::Arc; + +use crate::encore::runtime::v1 as pb; +use crate::objects; +use crate::objects::gcs::bucket::Bucket; +use anyhow::Context; +use google_cloud_storage as gcs; + +mod bucket; + +#[derive(Debug)] +pub struct Cluster { + client: Arc, +} + +impl Cluster { + pub fn new(cfg: pb::bucket_cluster::Gcs) -> Self { + let client = Arc::new(LazyGCSClient::new(cfg)); + Self { client } + } +} + +impl objects::ClusterImpl for Cluster { + fn bucket(self: Arc, cfg: &pb::Bucket) -> Arc { + Arc::new(Bucket::new(self.client.clone(), cfg)) + } +} + +struct LazyGCSClient { + cfg: pb::bucket_cluster::Gcs, + cell: tokio::sync::OnceCell>, +} + +impl Debug for LazyGCSClient { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("LazyGCPClient").finish() + } +} + +impl LazyGCSClient { + fn new(cfg: pb::bucket_cluster::Gcs) -> Self { + Self { + cfg, + cell: tokio::sync::OnceCell::new(), + } + } + + async fn get(&self) -> &anyhow::Result { + self.cell + .get_or_init(|| async { + let mut config = gcs::client::ClientConfig::default() + .with_auth() + .await + .context("get client config")?; + + if let Some(endpoint) = &self.cfg.endpoint { + config.storage_endpoint.clone_from(endpoint); + } + + Ok(gcs::client::Client::new(config)) + }) + .await + } +} diff --git a/runtimes/core/src/objects/manager.rs b/runtimes/core/src/objects/manager.rs index 97dca39168..bddb2a1b2e 100644 --- a/runtimes/core/src/objects/manager.rs +++ b/runtimes/core/src/objects/manager.rs @@ -4,8 +4,7 @@ use std::sync::{Arc, RwLock}; use crate::encore::parser::meta::v1 as meta; use crate::encore::runtime::v1 as pb; use crate::names::EncoreName; -use crate::objects::noop::NoopCluster; -use crate::objects::{noop, BucketImpl, ClusterImpl, ObjectImpl}; +use crate::objects::{gcs, noop, s3, BucketImpl, ClusterImpl, ObjectImpl}; use crate::trace::Tracer; pub struct Manager { @@ -24,21 +23,21 @@ pub struct Bucket { impl Bucket { pub fn object(&self, name: String) -> Object { Object { - imp: self.imp.object(name), - tracer: self.tracer.clone(), + imp: self.imp.clone().object(name), + _tracer: self.tracer.clone(), } } } #[derive(Debug)] pub struct Object { - tracer: Tracer, + _tracer: Tracer, imp: Arc, } impl Object { - pub async fn exists(&self) -> bool { - self.imp.exists().await + pub async fn exists(&self) -> anyhow::Result { + self.imp.clone().exists().await } } @@ -68,9 +67,9 @@ impl Manager { let bkt = { if let Some((cluster, bucket_cfg)) = self.bucket_cfg.get(&name) { - cluster.bucket(bucket_cfg) + cluster.clone().bucket(bucket_cfg) } else { - Arc::new(noop::NoopBucket) + Arc::new(noop::Bucket) } }; @@ -81,7 +80,7 @@ impl Manager { fn make_cfg_maps( clusters: Vec, - md: &meta::Data, + _md: &meta::Data, ) -> HashMap, pb::Bucket)> { let mut bucket_map = HashMap::new(); @@ -100,24 +99,15 @@ fn make_cfg_maps( } fn new_cluster(cluster: &pb::BucketCluster) -> Arc { - // let Some(provider) = &cluster.provider else { - // log::error!("missing PubSub cluster provider: {}", cluster.rid); - // return Arc::new(NoopCluster); - // }; - - // match provider { - // pb::pub_sub_cluster::Provider::Gcp(_) => return Arc::new(gcp::Cluster::new()), - // pb::pub_sub_cluster::Provider::Nsq(cfg) => { - // return Arc::new(nsq::Cluster::new(cfg.hosts[0].clone())); - // } - // pb::pub_sub_cluster::Provider::Aws(_) => return Arc::new(sqs_sns::Cluster::new()), - // pb::pub_sub_cluster::Provider::Encore(_) => { - // log::error!("Encore Cloud Pub/Sub not yet supported: {}", cluster.rid); - // } - // pb::pub_sub_cluster::Provider::Azure(_) => { - // log::error!("Azure Pub/Sub not yet supported: {}", cluster.rid); - // } - // } - - Arc::new(NoopCluster) + let Some(provider) = &cluster.provider else { + log::error!("missing bucket cluster provider: {}", cluster.rid); + return Arc::new(noop::Cluster); + }; + + match provider { + pb::bucket_cluster::Provider::S3(s3cfg) => return Arc::new(s3::Cluster::new(s3cfg)), + pb::bucket_cluster::Provider::Gcs(gcscfg) => { + return Arc::new(gcs::Cluster::new(gcscfg.clone())) + } + } } diff --git a/runtimes/core/src/objects/mod.rs b/runtimes/core/src/objects/mod.rs index 9bb3f370e4..bae16dd5ab 100644 --- a/runtimes/core/src/objects/mod.rs +++ b/runtimes/core/src/objects/mod.rs @@ -1,3 +1,4 @@ +use anyhow::Result; use std::future::Future; use std::sync::Arc; use std::{fmt::Debug, pin::Pin}; @@ -6,18 +7,19 @@ pub use manager::{Bucket, Manager, Object}; use crate::encore::runtime::v1 as pb; +mod gcs; mod manager; mod noop; mod s3; trait ClusterImpl: Debug + Send + Sync { - fn bucket(&self, cfg: &pb::Bucket) -> Arc; + fn bucket(self: Arc, cfg: &pb::Bucket) -> Arc; } trait BucketImpl: Debug + Send + Sync { - fn object(&self, name: String) -> Arc; + fn object(self: Arc, name: String) -> Arc; } trait ObjectImpl: Debug + Send + Sync { - fn exists(&self) -> Pin + Send + 'static>>; + fn exists(self: Arc) -> Pin> + Send>>; } diff --git a/runtimes/core/src/objects/noop/mod.rs b/runtimes/core/src/objects/noop/mod.rs index e5ad8cbb5d..e354ff37ac 100644 --- a/runtimes/core/src/objects/noop/mod.rs +++ b/runtimes/core/src/objects/noop/mod.rs @@ -8,28 +8,30 @@ use crate::encore::runtime::v1 as pb; use crate::objects; #[derive(Debug)] -pub struct NoopCluster; +pub struct Cluster; #[derive(Debug)] -pub struct NoopBucket; +pub struct Bucket; #[derive(Debug)] -pub struct NoopObject; +pub struct Object; -impl objects::ClusterImpl for NoopCluster { - fn bucket(&self, _cfg: &pb::Bucket) -> Arc { - Arc::new(NoopBucket) +impl objects::ClusterImpl for Cluster { + fn bucket(self: Arc, _cfg: &pb::Bucket) -> Arc { + Arc::new(Bucket) } } -impl objects::BucketImpl for NoopBucket { - fn object(&self, _name: String) -> Arc { - Arc::new(NoopObject) +impl objects::BucketImpl for Bucket { + fn object(self: Arc, _name: String) -> Arc { + Arc::new(Object) } } -impl objects::ObjectImpl for NoopObject { - fn exists(&self) -> Pin + Send + 'static>> { - Box::pin(future::ready(false)) +impl objects::ObjectImpl for Object { + fn exists( + self: Arc, + ) -> Pin> + Send + 'static>> { + Box::pin(future::ready(Ok(false))) } } diff --git a/runtimes/core/src/objects/s3/bucket.rs b/runtimes/core/src/objects/s3/bucket.rs index 672c990ab4..c6dc7b13aa 100644 --- a/runtimes/core/src/objects/s3/bucket.rs +++ b/runtimes/core/src/objects/s3/bucket.rs @@ -2,8 +2,6 @@ use std::future::Future; use std::pin::Pin; use std::sync::Arc; -use futures::future; - use crate::encore::runtime::v1 as pb; use crate::objects; @@ -15,14 +13,15 @@ pub struct Bucket { impl Bucket { pub(super) fn new(region: s3::Region, creds: s3::creds::Credentials, cfg: &pb::Bucket) -> Self { let client = s3::Bucket::new(&cfg.cloud_name, region, creds) - .expect("unable to construct bucket client"); + .expect("unable to construct bucket client") + .with_path_style(); let client = Arc::from(client); Self { client } } } impl objects::BucketImpl for Bucket { - fn object(&self, name: String) -> Arc { + fn object(self: Arc, name: String) -> Arc { Arc::new(Object { client: self.client.clone(), name, @@ -37,7 +36,14 @@ struct Object { } impl objects::ObjectImpl for Object { - fn exists(&self) -> Pin + Send + 'static>> { - Box::pin(future::ready(true)) + fn exists(self: Arc) -> Pin> + Send>> { + Box::pin(async move { + let res = self.client.head_object(&self.name).await; + match res { + Ok(_) => Ok(true), + Err(s3::error::S3Error::HttpFailWithBody(404, _)) => Ok(false), + Err(err) => Err(err.into()), + } + }) } } diff --git a/runtimes/core/src/objects/s3/mod.rs b/runtimes/core/src/objects/s3/mod.rs index 53be5f7bb2..3e8a07ee06 100644 --- a/runtimes/core/src/objects/s3/mod.rs +++ b/runtimes/core/src/objects/s3/mod.rs @@ -13,21 +13,28 @@ pub struct Cluster { } impl Cluster { - pub fn new(cfg: &pb::BucketCluster) -> Self { - let region = s3::Region::Custom { - region: cfg.region.clone(), - endpoint: cfg.endpoint.clone(), + pub fn new(cfg: &pb::bucket_cluster::S3) -> Self { + let region = match cfg.endpoint.as_ref() { + Some(ep) => s3::Region::Custom { + region: cfg.region.clone(), + endpoint: ep.clone(), + }, + None => { + let region: s3::Region = cfg.region.parse().expect("unable to resolve S3 region"); + region + } }; - // TODO(andre): does this work? - let creds = s3::creds::Credentials::default().unwrap(); + let creds = s3::creds::Credentials::default() + .or_else(|_| s3::creds::Credentials::anonymous()) + .expect("unable to resolve S3 credentials"); Self { region, creds } } } impl objects::ClusterImpl for Cluster { - fn bucket(&self, cfg: &pb::Bucket) -> Arc { + fn bucket(self: Arc, cfg: &pb::Bucket) -> Arc { Arc::new(Bucket::new(self.region.clone(), self.creds.clone(), cfg)) } } diff --git a/runtimes/go/appruntime/exported/config/config.go b/runtimes/go/appruntime/exported/config/config.go index b9f4ca81ba..d7709601bd 100644 --- a/runtimes/go/appruntime/exported/config/config.go +++ b/runtimes/go/appruntime/exported/config/config.go @@ -57,6 +57,8 @@ type Runtime struct { PubsubTopics map[string]*PubsubTopic `json:"pubsub_topics,omitempty"` RedisServers []*RedisServer `json:"redis_servers,omitempty"` RedisDatabases []*RedisDatabase `json:"redis_databases,omitempty"` + BucketProviders []*BucketProvider `json:"bucket_providers,omitempty"` + Buckets []*Bucket `json:"buckets,omitempty"` Metrics *Metrics `json:"metrics,omitempty"` Gateways []Gateway `json:"gateways,omitempty"` // Gateways defines the gateways which should be served by the container HostedServices []string `json:"hosted_services,omitempty"` // List of services to be hosted within this container (zero length means all services, unless there's a gateway running) @@ -394,6 +396,26 @@ type RedisDatabase struct { KeyPrefix string `json:"key_prefix"` } +type BucketProvider struct { + S3 *S3BucketProvider `json:"s3,omitempty"` // set if the provider is S3 + GCS *GCSBucketProvider `json:"gcs,omitempty"` // set if the provider is GCS +} + +type S3BucketProvider struct { + Endpoint string `json:"endpoint"` + Region string `json:"region"` +} + +type GCSBucketProvider struct { + Endpoint string `json:"endpoint"` +} + +type Bucket struct { + ProviderID int `json:"cluster_id"` // the index into (*Runtime).BucketProviders + EncoreName string `json:"encore_name"` // the Encore name for the bucket + CloudName string `json:"cloud_name"` // the cloud name for the bucket +} + type Metrics struct { CollectionInterval time.Duration `json:"collection_interval,omitempty"` EncoreCloud *GCPCloudMonitoringProvider `json:"encore_cloud,omitempty"` diff --git a/runtimes/js/src/objects.rs b/runtimes/js/src/objects.rs index 7cf8567a6b..169abac052 100644 --- a/runtimes/js/src/objects.rs +++ b/runtimes/js/src/objects.rs @@ -31,7 +31,7 @@ impl BucketObject { } #[napi] - pub async fn exists(&self) -> bool { - self.obj.exists().await + pub async fn exists(&self) -> napi::Result { + self.obj.exists().await.map_err(napi::Error::from) } } diff --git a/tsparser/src/parser/usageparser/mod.rs b/tsparser/src/parser/usageparser/mod.rs index 96b5fa30dc..37ffd73eae 100644 --- a/tsparser/src/parser/usageparser/mod.rs +++ b/tsparser/src/parser/usageparser/mod.rs @@ -254,6 +254,11 @@ impl UsageResolver<'_> { usages.push(u) } } + Resource::Bucket(bkt) => { + if let Some(u) = infra::objects::resolve_bucket_usage(&data, bkt.clone())? { + usages.push(u) + } + } _ => {} } } From 6d550ebb06642929fe9b11aafb91976c5529b5e2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Eriksson?= Date: Mon, 21 Oct 2024 17:32:46 +0200 Subject: [PATCH 06/37] Add more object storage operations --- Cargo.lock | 1 + cli/daemon/objects/objects.go | 11 +- runtimes/core/Cargo.toml | 1 + runtimes/core/src/objects/gcs/bucket.rs | 136 +++++++++++++++++- runtimes/core/src/objects/manager.rs | 37 +---- runtimes/core/src/objects/mod.rs | 125 +++++++++++++++- runtimes/core/src/objects/noop/mod.rs | 36 ++++- runtimes/core/src/objects/s3/bucket.rs | 39 ++++- .../js/encore.dev/storage/objects/bucket.ts | 40 +++++- runtimes/js/encore.dev/storage/objects/mod.ts | 4 +- runtimes/js/src/objects.rs | 106 +++++++++++++- 11 files changed, 486 insertions(+), 50 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 57c4d80259..fc4a0b7e22 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1523,6 +1523,7 @@ dependencies = [ "sha2", "sha3", "subtle", + "thiserror", "tokio", "tokio-nsq", "tokio-postgres", diff --git a/cli/daemon/objects/objects.go b/cli/daemon/objects/objects.go index 70cb565217..5f8a93ec61 100644 --- a/cli/daemon/objects/objects.go +++ b/cli/daemon/objects/objects.go @@ -26,7 +26,16 @@ type Server struct { func New() *Server { return &Server{ // TODO set up dir storage - emu: gcsemu.NewGcsEmu(gcsemu.Options{}), + emu: gcsemu.NewGcsEmu(gcsemu.Options{ + Verbose: true, + Log: func(err error, fmt string, args ...interface{}) { + if err != nil { + log.Error().Err(err).Msgf(fmt, args...) + } else { + log.Info().Msgf(fmt, args...) + } + }, + }), } } diff --git a/runtimes/core/Cargo.toml b/runtimes/core/Cargo.toml index 11ca146731..db3a58912f 100644 --- a/runtimes/core/Cargo.toml +++ b/runtimes/core/Cargo.toml @@ -88,6 +88,7 @@ google-cloud-storage = "0.22.1" serde_path_to_error = "0.1.16" tracing = "0.1.40" tracing-subscriber = { version = "0.3.18", features = ["alloc", "ansi", "env-filter", "fmt", "matchers", "nu-ansi-term", "once_cell", "regex", "registry", "sharded-slab", "smallvec", "std", "thread_local", "tracing"], default-features = false } +thiserror = "1.0.64" [build-dependencies] prost-build = "0.12.3" diff --git a/runtimes/core/src/objects/gcs/bucket.rs b/runtimes/core/src/objects/gcs/bucket.rs index 50b0267721..5d7664f0ac 100644 --- a/runtimes/core/src/objects/gcs/bucket.rs +++ b/runtimes/core/src/objects/gcs/bucket.rs @@ -1,9 +1,15 @@ -use anyhow::Result; +use futures::TryStreamExt; +use google_cloud_storage::http::objects::download::Range; +use google_cloud_storage::http::objects::get::GetObjectRequest; +use google_cloud_storage::http::objects::upload::{Media, UploadObjectRequest, UploadType}; +use std::borrow::Cow; use std::future::Future; use std::pin::Pin; use std::sync::Arc; +use tokio::io::AsyncRead; use crate::encore::runtime::v1 as pb; +use crate::objects::{DownloadError, DownloadStream, Error, ObjectAttrs, UploadOptions}; use crate::{objects, CloudName}; use google_cloud_storage as gcs; @@ -37,7 +43,45 @@ struct Object { } impl objects::ObjectImpl for Object { - fn exists(self: Arc) -> Pin> + Send>> { + fn attrs(self: Arc) -> Pin> + Send>> { + Box::pin(async move { + match self.bkt.client.get().await { + Ok(client) => { + use gcs::http::{error::ErrorResponse, Error as GCSError}; + let req = &gcs::http::objects::get::GetObjectRequest { + bucket: self.bkt.name.to_string(), + object: self.name.clone(), + ..Default::default() + }; + + match client.get_object(req).await { + Ok(obj) => Ok(ObjectAttrs { + name: obj.name, + version: obj.generation.to_string(), + size: obj.size as u64, + content_type: obj.content_type, + etag: obj.etag, + }), + Err(GCSError::Response(ErrorResponse { code: 404, .. })) => { + Err(Error::NotFound) + } + Err(GCSError::HttpClient(err)) + if err.status().is_some_and(|v| v.as_u16() == 404) => + { + Err(Error::NotFound) + } + + Err(err) => Err(Error::Other(err.into())), + } + } + Err(err) => Err(Error::Internal(anyhow::anyhow!( + "unable to resolve client: {}", + err + ))), + } + }) + } + fn exists(self: Arc) -> Pin> + Send>> { Box::pin(async move { match self.bkt.client.get().await { Ok(client) => { @@ -64,4 +108,92 @@ impl objects::ObjectImpl for Object { } }) } + + fn upload( + self: Arc, + data: Box, + opts: objects::UploadOptions, + ) -> Pin> + Send>> { + Box::pin(async move { + match self.bkt.client.get().await { + Ok(client) => { + let mut req = UploadObjectRequest { + bucket: self.bkt.name.to_string(), + ..Default::default() + }; + let mut media = Media::new(self.name.clone()); + + apply_upload_opts(opts, &mut req, &mut media); + + let upload_type = UploadType::Simple(media); + let stream = tokio_util::io::ReaderStream::new(data); + + match client + .upload_streamed_object(&req, stream, &upload_type) + .await + { + Ok(obj) => Ok(ObjectAttrs { + name: obj.name, + version: obj.generation.to_string(), + size: obj.size as u64, + content_type: obj.content_type, + etag: obj.etag, + }), + Err(err) => Err(err.into()), + } + } + Err(err) => Err(anyhow::anyhow!("unable to resolve client: {}", err)), + } + }) + } + + fn download( + self: Arc, + ) -> Pin> + Send>> { + fn convert_err(err: gcs::http::Error) -> DownloadError { + use gcs::http::error::ErrorResponse; + use gcs::http::Error; + match err { + Error::Response(ErrorResponse { code: 404, .. }) => DownloadError::NotFound, + Error::HttpClient(err) if err.status().map(|s| s.as_u16()) == Some(404) => { + DownloadError::NotFound + } + err => DownloadError::Other(err.into()), + } + } + + Box::pin(async move { + match self.bkt.client.get().await { + Ok(client) => { + let req = GetObjectRequest { + bucket: self.bkt.name.to_string(), + object: self.name.clone(), + ..Default::default() + }; + let resp = client + .download_streamed_object(&req, &Range::default()) + .await; + + let stream = resp.map_err(convert_err)?; + let stream: DownloadStream = Box::new(stream.map_err(convert_err)); + Ok(stream) + } + Err(err) => Err(DownloadError::Internal(anyhow::anyhow!( + "unable to resolve client: {}", + err + ))), + } + }) + } +} + +fn apply_upload_opts(opts: UploadOptions, req: &mut UploadObjectRequest, media: &mut Media) { + if let Some(content_type) = opts.content_type { + media.content_type = Cow::Owned(content_type); + } + if let Some(pre) = opts.preconditions { + if pre.not_exists == Some(true) { + req.if_generation_match = Some(0); + } + } } diff --git a/runtimes/core/src/objects/manager.rs b/runtimes/core/src/objects/manager.rs index bddb2a1b2e..e67766728a 100644 --- a/runtimes/core/src/objects/manager.rs +++ b/runtimes/core/src/objects/manager.rs @@ -4,9 +4,11 @@ use std::sync::{Arc, RwLock}; use crate::encore::parser::meta::v1 as meta; use crate::encore::runtime::v1 as pb; use crate::names::EncoreName; -use crate::objects::{gcs, noop, s3, BucketImpl, ClusterImpl, ObjectImpl}; +use crate::objects::{gcs, noop, s3, BucketImpl, ClusterImpl}; use crate::trace::Tracer; +use super::Bucket; + pub struct Manager { tracer: Tracer, bucket_cfg: HashMap, pb::Bucket)>, @@ -14,33 +16,6 @@ pub struct Manager { buckets: Arc>>>, } -#[derive(Debug)] -pub struct Bucket { - tracer: Tracer, - imp: Arc, -} - -impl Bucket { - pub fn object(&self, name: String) -> Object { - Object { - imp: self.imp.clone().object(name), - _tracer: self.tracer.clone(), - } - } -} - -#[derive(Debug)] -pub struct Object { - _tracer: Tracer, - imp: Arc, -} - -impl Object { - pub async fn exists(&self) -> anyhow::Result { - self.imp.clone().exists().await - } -} - impl Manager { pub fn new(tracer: Tracer, clusters: Vec, md: &meta::Data) -> Self { let bucket_cfg = make_cfg_maps(clusters, md); @@ -105,9 +80,7 @@ fn new_cluster(cluster: &pb::BucketCluster) -> Arc { }; match provider { - pb::bucket_cluster::Provider::S3(s3cfg) => return Arc::new(s3::Cluster::new(s3cfg)), - pb::bucket_cluster::Provider::Gcs(gcscfg) => { - return Arc::new(gcs::Cluster::new(gcscfg.clone())) - } + pb::bucket_cluster::Provider::S3(s3cfg) => Arc::new(s3::Cluster::new(s3cfg)), + pb::bucket_cluster::Provider::Gcs(gcscfg) => Arc::new(gcs::Cluster::new(gcscfg.clone())), } } diff --git a/runtimes/core/src/objects/mod.rs b/runtimes/core/src/objects/mod.rs index bae16dd5ab..858ac3ea26 100644 --- a/runtimes/core/src/objects/mod.rs +++ b/runtimes/core/src/objects/mod.rs @@ -1,11 +1,14 @@ -use anyhow::Result; +use bytes::Bytes; +use futures::{Stream, StreamExt}; use std::future::Future; use std::sync::Arc; use std::{fmt::Debug, pin::Pin}; +use tokio::io::AsyncRead; -pub use manager::{Bucket, Manager, Object}; +pub use manager::Manager; use crate::encore::runtime::v1 as pb; +use crate::trace::Tracer; mod gcs; mod manager; @@ -21,5 +24,121 @@ trait BucketImpl: Debug + Send + Sync { } trait ObjectImpl: Debug + Send + Sync { - fn exists(self: Arc) -> Pin> + Send>>; + fn exists(self: Arc) -> Pin> + Send>>; + + fn upload( + self: Arc, + data: Box, + options: UploadOptions, + ) -> Pin> + Send>>; + + fn download( + self: Arc, + ) -> Pin> + Send>>; + + fn attrs(self: Arc) -> Pin> + Send>>; +} + +#[derive(Debug)] +pub struct Bucket { + tracer: Tracer, + imp: Arc, +} + +impl Bucket { + pub fn object(&self, name: String) -> Object { + Object { + imp: self.imp.clone().object(name), + _tracer: self.tracer.clone(), + } + } +} + +#[derive(Debug)] +pub struct Object { + _tracer: Tracer, + imp: Arc, +} + +impl Object { + pub async fn exists(&self) -> anyhow::Result { + self.imp.clone().exists().await + } + + pub fn upload( + &self, + data: Box, + options: UploadOptions, + ) -> impl Future> + Send + 'static { + self.imp.clone().upload(data, options) + } + + pub fn download_stream( + &self, + ) -> impl Future> + Send + 'static { + self.imp.clone().download() + } + + pub fn download_all( + &self, + ) -> impl Future, DownloadError>> + Send + 'static { + let stream = self.imp.clone().download(); + async move { + let mut bytes = Vec::new(); + let mut stream = stream.await?; + while let Some(chunk) = stream.next().await { + bytes.extend_from_slice(&chunk?); + } + Ok(bytes) + } + } + + pub async fn attrs(&self) -> Result { + self.imp.clone().attrs().await + } +} + +#[derive(thiserror::Error, Debug)] +pub enum Error { + #[error("object not found")] + NotFound, + + #[error("internal error: {0:?}")] + Internal(anyhow::Error), + + #[error(transparent)] + Other(#[from] anyhow::Error), +} + +#[derive(thiserror::Error, Debug)] +pub enum DownloadError { + #[error("object not found")] + NotFound, + + #[error("internal error: {0:?}")] + Internal(anyhow::Error), + + #[error(transparent)] + Other(#[from] anyhow::Error), +} + +pub type DownloadStream = Box> + Unpin + Send>; + +pub struct ObjectAttrs { + pub name: String, + pub version: String, + pub size: u64, + pub content_type: Option, + pub etag: String, +} + +#[derive(Debug, Default)] +pub struct UploadOptions { + pub content_type: Option, + pub preconditions: Option, +} + +#[derive(Debug, Default)] +pub struct UploadPreconditions { + pub not_exists: Option, } diff --git a/runtimes/core/src/objects/noop/mod.rs b/runtimes/core/src/objects/noop/mod.rs index e354ff37ac..a471a28107 100644 --- a/runtimes/core/src/objects/noop/mod.rs +++ b/runtimes/core/src/objects/noop/mod.rs @@ -3,6 +3,7 @@ use std::pin::Pin; use std::sync::Arc; use futures::future; +use tokio::io::AsyncRead; use crate::encore::runtime::v1 as pb; use crate::objects; @@ -29,9 +30,38 @@ impl objects::BucketImpl for Bucket { } impl objects::ObjectImpl for Object { - fn exists( + fn attrs( self: Arc, - ) -> Pin> + Send + 'static>> { - Box::pin(future::ready(Ok(false))) + ) -> Pin> + Send>> { + Box::pin(future::ready(Err(objects::Error::Internal( + anyhow::anyhow!("noop bucket does not support attrs"), + )))) + } + + fn exists(self: Arc) -> Pin> + Send>> { + Box::pin(future::ready(Err(anyhow::anyhow!( + "noop bucket does not support exists" + )))) + } + + fn upload( + self: Arc, + _data: Box, + _options: objects::UploadOptions, + ) -> Pin> + Send>> { + Box::pin(future::ready(Err(anyhow::anyhow!( + "noop bucket does not support upload" + )))) + } + + fn download( + self: Arc, + ) -> Pin> + Send>> + { + Box::pin(async move { + Err(objects::DownloadError::Internal(anyhow::anyhow!( + "noop bucket does not support download" + ))) + }) } } diff --git a/runtimes/core/src/objects/s3/bucket.rs b/runtimes/core/src/objects/s3/bucket.rs index c6dc7b13aa..6e0ab08313 100644 --- a/runtimes/core/src/objects/s3/bucket.rs +++ b/runtimes/core/src/objects/s3/bucket.rs @@ -1,9 +1,10 @@ use std::future::Future; use std::pin::Pin; use std::sync::Arc; +use tokio::io::AsyncRead; use crate::encore::runtime::v1 as pb; -use crate::objects; +use crate::objects::{self, Error, ObjectAttrs}; #[derive(Debug)] pub struct Bucket { @@ -36,6 +37,23 @@ struct Object { } impl objects::ObjectImpl for Object { + fn attrs(self: Arc) -> Pin> + Send>> { + Box::pin(async move { + let res = self.client.head_object(&self.name).await; + match res { + Ok((obj, _)) => Ok(ObjectAttrs { + name: self.name.clone(), + version: obj.version_id.unwrap_or_default(), + size: obj.content_length.unwrap_or_default() as u64, + content_type: obj.content_type, + etag: obj.e_tag.unwrap_or_default(), + }), + Err(s3::error::S3Error::HttpFailWithBody(404, _)) => Err(Error::NotFound), + Err(err) => Err(Error::Other(err.into())), + } + }) + } + fn exists(self: Arc) -> Pin> + Send>> { Box::pin(async move { let res = self.client.head_object(&self.name).await; @@ -46,4 +64,23 @@ impl objects::ObjectImpl for Object { } }) } + + fn upload( + self: Arc, + _data: Box, + _options: objects::UploadOptions, + ) -> Pin> + Send>> { + Box::pin(async move { Err(anyhow::anyhow!("not yet implemented")) }) + } + + fn download( + self: Arc, + ) -> Pin> + Send>> + { + Box::pin(async move { + Err(objects::DownloadError::Internal(anyhow::anyhow!( + "not yet implemented" + ))) + }) + } } diff --git a/runtimes/js/encore.dev/storage/objects/bucket.ts b/runtimes/js/encore.dev/storage/objects/bucket.ts index beea518c9c..cb300dd3a1 100644 --- a/runtimes/js/encore.dev/storage/objects/bucket.ts +++ b/runtimes/js/encore.dev/storage/objects/bucket.ts @@ -1,8 +1,8 @@ -import { getCurrentRequest } from "../../internal/reqtrack/mod"; import * as runtime from "../../internal/runtime/mod"; import { StringLiteral } from "../../internal/utils/constraints"; export interface BucketConfig { + public?: boolean; } /** @@ -15,7 +15,7 @@ export class Bucket { * Creates a new bucket with the given name and configuration */ // eslint-disable-next-line @typescript-eslint/no-unused-vars - constructor(name: string, cfg: BucketConfig) { + constructor(name: string, cfg?: BucketConfig) { this.impl = runtime.RT.bucket(name); } @@ -27,13 +27,13 @@ export class Bucket { return new Bucket(name, {}); } - object(name: string): BucketObject { + object(name: string): ObjectHandle { const impl = this.impl.object(name); - return new BucketObject(impl, this, name); + return new ObjectHandle(impl, this, name); } } -export class BucketObject { +export class ObjectHandle { private impl: runtime.BucketObject; public readonly bucket: Bucket; public readonly name: string; @@ -51,4 +51,34 @@ export class BucketObject { async exists(): Promise { return this.impl.exists(); } + + /** + * Returns the object's attributes. + * Throws an error if the object does not exist. + */ + async attrs(): Promise { + return this.impl.attrs(); + } + + async upload(data: Buffer, options?: UploadOptions): Promise { + return this.impl.upload(data, options); + } + + async download(): Promise { + return this.impl.downloadAll(); + } +} + +export interface ObjectAttrs { + name: string; + contentType?: string; + size: number; + version: string; +} + +export interface UploadOptions { + contentType?: string; + preconditions?: { + notExists?: boolean; + }, } diff --git a/runtimes/js/encore.dev/storage/objects/mod.ts b/runtimes/js/encore.dev/storage/objects/mod.ts index cba84cfc7b..51d54da47b 100644 --- a/runtimes/js/encore.dev/storage/objects/mod.ts +++ b/runtimes/js/encore.dev/storage/objects/mod.ts @@ -1,2 +1,2 @@ -export { Bucket } from "./bucket"; -export type { BucketConfig } from "./bucket"; +export { Bucket, ObjectHandle } from "./bucket"; +export type { BucketConfig, ObjectAttrs, UploadOptions } from "./bucket"; diff --git a/runtimes/js/src/objects.rs b/runtimes/js/src/objects.rs index 169abac052..3a5db80100 100644 --- a/runtimes/js/src/objects.rs +++ b/runtimes/js/src/objects.rs @@ -1,6 +1,11 @@ +use napi::bindgen_prelude::Buffer; +use napi::{Env, JsBuffer, JsObject}; use napi_derive::napi; -use encore_runtime_core::objects::{Bucket as CoreBucket, Object as CoreObject}; +use encore_runtime_core::objects::{ + Bucket as CoreBucket, DownloadError, Object as CoreObject, ObjectAttrs as CoreAttrs, + UploadOptions as CoreUploadOptions, UploadPreconditions as CoreUploadPreconditions, +}; #[napi] pub struct Bucket { @@ -30,8 +35,107 @@ impl BucketObject { Self { obj } } + #[napi] + pub async fn attrs(&self) -> napi::Result { + self.obj + .attrs() + .await + .map(ObjectAttrs::from) + .map_err(map_objects_err) + } + #[napi] pub async fn exists(&self) -> napi::Result { self.obj.exists().await.map_err(napi::Error::from) } + + #[napi(ts_return_type = "Promise")] + pub fn upload( + &self, + env: Env, + data: JsBuffer, + opts: Option, + ) -> napi::Result { + // TODO: reference the data via a Ref, so that we can keep it alive throughout the upload. + let data = data.into_value()?.as_ref().to_vec(); + + let cursor = std::io::Cursor::new(data); + let opts = opts.unwrap_or_default().into(); + let fut = self.obj.upload(Box::new(cursor), opts); + + // We need to always execute the handler below so that we can decrement the ref count. + // To do so, we need the future to be a napi::Result::Ok. So wrap the result inside that + // so that the handler gets called regardless of result. + let fut = async move { Ok(fut.await) }; + + env.execute_tokio_future(fut, move |&mut _env, result| { + // TODO: Decrement the ref count on the data buffer. + result.map(ObjectAttrs::from).map_err(napi::Error::from) + }) + } + + #[napi] + pub async fn download_all(&self) -> napi::Result { + let buf = self.obj.download_all().await.map_err(map_download_err)?; + Ok(buf.into()) + } +} + +#[napi] +pub struct ObjectAttrs { + pub name: String, + pub version: String, + pub size: i64, + pub content_type: Option, + pub etag: String, +} + +impl From for ObjectAttrs { + fn from(value: CoreAttrs) -> Self { + Self { + name: value.name, + version: value.version, + size: value.size as i64, + content_type: value.content_type, + etag: value.etag, + } + } +} + +#[napi(object)] +#[derive(Debug, Default)] +pub struct UploadOptions { + pub content_type: Option, + pub preconditions: Option, +} + +#[napi(object)] +#[derive(Debug, Default)] +pub struct UploadPreconditions { + pub not_exists: Option, +} + +impl From for CoreUploadOptions { + fn from(value: UploadOptions) -> Self { + Self { + content_type: value.content_type, + preconditions: value.preconditions.map(|p| p.into()), + } + } +} + +impl From for CoreUploadPreconditions { + fn from(value: UploadPreconditions) -> Self { + Self { + not_exists: value.not_exists, + } + } +} + +fn map_objects_err(err: encore_runtime_core::objects::Error) -> napi::Error { + napi::Error::new(napi::Status::GenericFailure, err) +} + +fn map_download_err(err: DownloadError) -> napi::Error { + napi::Error::new(napi::Status::GenericFailure, err) } From 8428eef2aa18855986e79e92d769bdf38646f658 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Eriksson?= Date: Thu, 24 Oct 2024 17:32:15 +0200 Subject: [PATCH 07/37] Add delete, usage parsing --- Cargo.lock | 9 +- proto/encore/parser/meta/v1/meta.pb.go | 1700 +++++++++-------- proto/encore/parser/meta/v1/meta.proto | 32 +- runtimes/core/Cargo.toml | 1 + runtimes/core/src/objects/gcs/bucket.rs | 80 + runtimes/core/src/objects/gcs/mod.rs | 6 +- runtimes/core/src/objects/mod.rs | 16 + runtimes/core/src/objects/noop/mod.rs | 17 + runtimes/core/src/objects/s3/bucket.rs | 22 + .../js/encore.dev/storage/objects/bucket.ts | 64 +- runtimes/js/encore.dev/storage/objects/mod.ts | 2 +- runtimes/js/src/objects.rs | 48 +- tsparser/src/legacymeta/mod.rs | 41 +- .../src/parser/resources/infra/objects.rs | 60 +- tsparser/src/parser/usageparser/mod.rs | 2 +- 15 files changed, 1278 insertions(+), 822 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index fc4a0b7e22..29ff624a5f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -229,9 +229,9 @@ dependencies = [ [[package]] name = "async-stream" -version = "0.3.5" +version = "0.3.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd56dd203fef61ac097dd65721a419ddccb106b2d2b70ba60a6b529f03961a51" +checksum = "0b5a71a6f37880a80d1d7f19efd781e4b5de42c88f0722cc13bcb6cc2cfe8476" dependencies = [ "async-stream-impl", "futures-core", @@ -240,9 +240,9 @@ dependencies = [ [[package]] name = "async-stream-impl" -version = "0.3.5" +version = "0.3.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "16e62a023e7c117e27523144c5d2459f4397fcc3cab0085af8e2224f643a0193" +checksum = "c7c24de15d275a1ecfd47a380fb4d5ec9bfe0933f309ed5e705b775596a3574d" dependencies = [ "proc-macro2", "quote", @@ -1463,6 +1463,7 @@ version = "0.1.0" dependencies = [ "anyhow", "assert_matches", + "async-stream", "aws-config", "aws-sdk-sns", "aws-sdk-sqs", diff --git a/proto/encore/parser/meta/v1/meta.pb.go b/proto/encore/parser/meta/v1/meta.pb.go index 841f94668d..4aadf5ab84 100644 --- a/proto/encore/parser/meta/v1/meta.pb.go +++ b/proto/encore/parser/meta/v1/meta.pb.go @@ -69,6 +69,73 @@ func (Lang) EnumDescriptor() ([]byte, []int) { return file_encore_parser_meta_v1_meta_proto_rawDescGZIP(), []int{0} } +type BucketUsage_Operation int32 + +const ( + BucketUsage_UNKNOWN BucketUsage_Operation = 0 + // Listing objects and accessing their metadata during listing. + BucketUsage_LIST_OBJECTS BucketUsage_Operation = 1 + // Reading the contents of an object. + BucketUsage_READ_OBJECT_CONTENTS BucketUsage_Operation = 2 + // Creating or updating an object, with contents and metadata. + BucketUsage_WRITE_OBJECT BucketUsage_Operation = 3 + // Updating the metadata of an object, without reading or writing its contents. + BucketUsage_UPDATE_OBJECT_METADATA BucketUsage_Operation = 4 + // Reading the metadata of an object, or checking for its existence. + BucketUsage_GET_OBJECT_METADATA BucketUsage_Operation = 5 + // Deleting an object. + BucketUsage_DELETE_OBJECT BucketUsage_Operation = 6 +) + +// Enum value maps for BucketUsage_Operation. +var ( + BucketUsage_Operation_name = map[int32]string{ + 0: "UNKNOWN", + 1: "LIST_OBJECTS", + 2: "READ_OBJECT_CONTENTS", + 3: "WRITE_OBJECT", + 4: "UPDATE_OBJECT_METADATA", + 5: "GET_OBJECT_METADATA", + 6: "DELETE_OBJECT", + } + BucketUsage_Operation_value = map[string]int32{ + "UNKNOWN": 0, + "LIST_OBJECTS": 1, + "READ_OBJECT_CONTENTS": 2, + "WRITE_OBJECT": 3, + "UPDATE_OBJECT_METADATA": 4, + "GET_OBJECT_METADATA": 5, + "DELETE_OBJECT": 6, + } +) + +func (x BucketUsage_Operation) Enum() *BucketUsage_Operation { + p := new(BucketUsage_Operation) + *p = x + return p +} + +func (x BucketUsage_Operation) String() string { + return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) +} + +func (BucketUsage_Operation) Descriptor() protoreflect.EnumDescriptor { + return file_encore_parser_meta_v1_meta_proto_enumTypes[1].Descriptor() +} + +func (BucketUsage_Operation) Type() protoreflect.EnumType { + return &file_encore_parser_meta_v1_meta_proto_enumTypes[1] +} + +func (x BucketUsage_Operation) Number() protoreflect.EnumNumber { + return protoreflect.EnumNumber(x) +} + +// Deprecated: Use BucketUsage_Operation.Descriptor instead. +func (BucketUsage_Operation) EnumDescriptor() ([]byte, []int) { + return file_encore_parser_meta_v1_meta_proto_rawDescGZIP(), []int{4, 0} +} + type Selector_Type int32 const ( @@ -102,11 +169,11 @@ func (x Selector_Type) String() string { } func (Selector_Type) Descriptor() protoreflect.EnumDescriptor { - return file_encore_parser_meta_v1_meta_proto_enumTypes[1].Descriptor() + return file_encore_parser_meta_v1_meta_proto_enumTypes[2].Descriptor() } func (Selector_Type) Type() protoreflect.EnumType { - return &file_encore_parser_meta_v1_meta_proto_enumTypes[1] + return &file_encore_parser_meta_v1_meta_proto_enumTypes[2] } func (x Selector_Type) Number() protoreflect.EnumNumber { @@ -115,7 +182,7 @@ func (x Selector_Type) Number() protoreflect.EnumNumber { // Deprecated: Use Selector_Type.Descriptor instead. func (Selector_Type) EnumDescriptor() ([]byte, []int) { - return file_encore_parser_meta_v1_meta_proto_rawDescGZIP(), []int{4, 0} + return file_encore_parser_meta_v1_meta_proto_rawDescGZIP(), []int{5, 0} } type RPC_AccessType int32 @@ -151,11 +218,11 @@ func (x RPC_AccessType) String() string { } func (RPC_AccessType) Descriptor() protoreflect.EnumDescriptor { - return file_encore_parser_meta_v1_meta_proto_enumTypes[2].Descriptor() + return file_encore_parser_meta_v1_meta_proto_enumTypes[3].Descriptor() } func (RPC_AccessType) Type() protoreflect.EnumType { - return &file_encore_parser_meta_v1_meta_proto_enumTypes[2] + return &file_encore_parser_meta_v1_meta_proto_enumTypes[3] } func (x RPC_AccessType) Number() protoreflect.EnumNumber { @@ -164,7 +231,7 @@ func (x RPC_AccessType) Number() protoreflect.EnumNumber { // Deprecated: Use RPC_AccessType.Descriptor instead. func (RPC_AccessType) EnumDescriptor() ([]byte, []int) { - return file_encore_parser_meta_v1_meta_proto_rawDescGZIP(), []int{5, 0} + return file_encore_parser_meta_v1_meta_proto_rawDescGZIP(), []int{6, 0} } type RPC_Protocol int32 @@ -197,11 +264,11 @@ func (x RPC_Protocol) String() string { } func (RPC_Protocol) Descriptor() protoreflect.EnumDescriptor { - return file_encore_parser_meta_v1_meta_proto_enumTypes[3].Descriptor() + return file_encore_parser_meta_v1_meta_proto_enumTypes[4].Descriptor() } func (RPC_Protocol) Type() protoreflect.EnumType { - return &file_encore_parser_meta_v1_meta_proto_enumTypes[3] + return &file_encore_parser_meta_v1_meta_proto_enumTypes[4] } func (x RPC_Protocol) Number() protoreflect.EnumNumber { @@ -210,7 +277,7 @@ func (x RPC_Protocol) Number() protoreflect.EnumNumber { // Deprecated: Use RPC_Protocol.Descriptor instead. func (RPC_Protocol) EnumDescriptor() ([]byte, []int) { - return file_encore_parser_meta_v1_meta_proto_rawDescGZIP(), []int{5, 1} + return file_encore_parser_meta_v1_meta_proto_rawDescGZIP(), []int{6, 1} } type StaticCallNode_Package int32 @@ -246,11 +313,11 @@ func (x StaticCallNode_Package) String() string { } func (StaticCallNode_Package) Descriptor() protoreflect.EnumDescriptor { - return file_encore_parser_meta_v1_meta_proto_enumTypes[4].Descriptor() + return file_encore_parser_meta_v1_meta_proto_enumTypes[5].Descriptor() } func (StaticCallNode_Package) Type() protoreflect.EnumType { - return &file_encore_parser_meta_v1_meta_proto_enumTypes[4] + return &file_encore_parser_meta_v1_meta_proto_enumTypes[5] } func (x StaticCallNode_Package) Number() protoreflect.EnumNumber { @@ -259,7 +326,7 @@ func (x StaticCallNode_Package) Number() protoreflect.EnumNumber { // Deprecated: Use StaticCallNode_Package.Descriptor instead. func (StaticCallNode_Package) EnumDescriptor() ([]byte, []int) { - return file_encore_parser_meta_v1_meta_proto_rawDescGZIP(), []int{11, 0} + return file_encore_parser_meta_v1_meta_proto_rawDescGZIP(), []int{12, 0} } type Path_Type int32 @@ -292,11 +359,11 @@ func (x Path_Type) String() string { } func (Path_Type) Descriptor() protoreflect.EnumDescriptor { - return file_encore_parser_meta_v1_meta_proto_enumTypes[5].Descriptor() + return file_encore_parser_meta_v1_meta_proto_enumTypes[6].Descriptor() } func (Path_Type) Type() protoreflect.EnumType { - return &file_encore_parser_meta_v1_meta_proto_enumTypes[5] + return &file_encore_parser_meta_v1_meta_proto_enumTypes[6] } func (x Path_Type) Number() protoreflect.EnumNumber { @@ -305,7 +372,7 @@ func (x Path_Type) Number() protoreflect.EnumNumber { // Deprecated: Use Path_Type.Descriptor instead. func (Path_Type) EnumDescriptor() ([]byte, []int) { - return file_encore_parser_meta_v1_meta_proto_rawDescGZIP(), []int{19, 0} + return file_encore_parser_meta_v1_meta_proto_rawDescGZIP(), []int{20, 0} } type PathSegment_SegmentType int32 @@ -344,11 +411,11 @@ func (x PathSegment_SegmentType) String() string { } func (PathSegment_SegmentType) Descriptor() protoreflect.EnumDescriptor { - return file_encore_parser_meta_v1_meta_proto_enumTypes[6].Descriptor() + return file_encore_parser_meta_v1_meta_proto_enumTypes[7].Descriptor() } func (PathSegment_SegmentType) Type() protoreflect.EnumType { - return &file_encore_parser_meta_v1_meta_proto_enumTypes[6] + return &file_encore_parser_meta_v1_meta_proto_enumTypes[7] } func (x PathSegment_SegmentType) Number() protoreflect.EnumNumber { @@ -357,7 +424,7 @@ func (x PathSegment_SegmentType) Number() protoreflect.EnumNumber { // Deprecated: Use PathSegment_SegmentType.Descriptor instead. func (PathSegment_SegmentType) EnumDescriptor() ([]byte, []int) { - return file_encore_parser_meta_v1_meta_proto_rawDescGZIP(), []int{20, 0} + return file_encore_parser_meta_v1_meta_proto_rawDescGZIP(), []int{21, 0} } type PathSegment_ParamType int32 @@ -423,11 +490,11 @@ func (x PathSegment_ParamType) String() string { } func (PathSegment_ParamType) Descriptor() protoreflect.EnumDescriptor { - return file_encore_parser_meta_v1_meta_proto_enumTypes[7].Descriptor() + return file_encore_parser_meta_v1_meta_proto_enumTypes[8].Descriptor() } func (PathSegment_ParamType) Type() protoreflect.EnumType { - return &file_encore_parser_meta_v1_meta_proto_enumTypes[7] + return &file_encore_parser_meta_v1_meta_proto_enumTypes[8] } func (x PathSegment_ParamType) Number() protoreflect.EnumNumber { @@ -436,7 +503,7 @@ func (x PathSegment_ParamType) Number() protoreflect.EnumNumber { // Deprecated: Use PathSegment_ParamType.Descriptor instead. func (PathSegment_ParamType) EnumDescriptor() ([]byte, []int) { - return file_encore_parser_meta_v1_meta_proto_rawDescGZIP(), []int{20, 1} + return file_encore_parser_meta_v1_meta_proto_rawDescGZIP(), []int{21, 1} } type PubSubTopic_DeliveryGuarantee int32 @@ -469,11 +536,11 @@ func (x PubSubTopic_DeliveryGuarantee) String() string { } func (PubSubTopic_DeliveryGuarantee) Descriptor() protoreflect.EnumDescriptor { - return file_encore_parser_meta_v1_meta_proto_enumTypes[8].Descriptor() + return file_encore_parser_meta_v1_meta_proto_enumTypes[9].Descriptor() } func (PubSubTopic_DeliveryGuarantee) Type() protoreflect.EnumType { - return &file_encore_parser_meta_v1_meta_proto_enumTypes[8] + return &file_encore_parser_meta_v1_meta_proto_enumTypes[9] } func (x PubSubTopic_DeliveryGuarantee) Number() protoreflect.EnumNumber { @@ -482,7 +549,7 @@ func (x PubSubTopic_DeliveryGuarantee) Number() protoreflect.EnumNumber { // Deprecated: Use PubSubTopic_DeliveryGuarantee.Descriptor instead. func (PubSubTopic_DeliveryGuarantee) EnumDescriptor() ([]byte, []int) { - return file_encore_parser_meta_v1_meta_proto_rawDescGZIP(), []int{26, 0} + return file_encore_parser_meta_v1_meta_proto_rawDescGZIP(), []int{27, 0} } type Metric_MetricKind int32 @@ -518,11 +585,11 @@ func (x Metric_MetricKind) String() string { } func (Metric_MetricKind) Descriptor() protoreflect.EnumDescriptor { - return file_encore_parser_meta_v1_meta_proto_enumTypes[9].Descriptor() + return file_encore_parser_meta_v1_meta_proto_enumTypes[10].Descriptor() } func (Metric_MetricKind) Type() protoreflect.EnumType { - return &file_encore_parser_meta_v1_meta_proto_enumTypes[9] + return &file_encore_parser_meta_v1_meta_proto_enumTypes[10] } func (x Metric_MetricKind) Number() protoreflect.EnumNumber { @@ -531,7 +598,7 @@ func (x Metric_MetricKind) Number() protoreflect.EnumNumber { // Deprecated: Use Metric_MetricKind.Descriptor instead. func (Metric_MetricKind) EnumDescriptor() ([]byte, []int) { - return file_encore_parser_meta_v1_meta_proto_rawDescGZIP(), []int{28, 0} + return file_encore_parser_meta_v1_meta_proto_rawDescGZIP(), []int{29, 0} } // Data is the metadata associated with an app version. @@ -874,7 +941,7 @@ type Service struct { Migrations []*DBMigration `protobuf:"bytes,4,rep,name=migrations,proto3" json:"migrations,omitempty"` Databases []string `protobuf:"bytes,5,rep,name=databases,proto3" json:"databases,omitempty"` // databases this service connects to HasConfig bool `protobuf:"varint,6,opt,name=has_config,json=hasConfig,proto3" json:"has_config,omitempty"` // true if the service has uses config - Buckets []string `protobuf:"bytes,7,rep,name=buckets,proto3" json:"buckets,omitempty"` // buckets this service uses + Buckets []*BucketUsage `protobuf:"bytes,7,rep,name=buckets,proto3" json:"buckets,omitempty"` // buckets this service uses } func (x *Service) Reset() { @@ -951,13 +1018,70 @@ func (x *Service) GetHasConfig() bool { return false } -func (x *Service) GetBuckets() []string { +func (x *Service) GetBuckets() []*BucketUsage { if x != nil { return x.Buckets } return nil } +type BucketUsage struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // The encore name of the bucket. + Bucket string `protobuf:"bytes,1,opt,name=bucket,proto3" json:"bucket,omitempty"` + // Recorded operations. + Operations []BucketUsage_Operation `protobuf:"varint,2,rep,packed,name=operations,proto3,enum=encore.parser.meta.v1.BucketUsage_Operation" json:"operations,omitempty"` +} + +func (x *BucketUsage) Reset() { + *x = BucketUsage{} + if protoimpl.UnsafeEnabled { + mi := &file_encore_parser_meta_v1_meta_proto_msgTypes[4] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *BucketUsage) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*BucketUsage) ProtoMessage() {} + +func (x *BucketUsage) ProtoReflect() protoreflect.Message { + mi := &file_encore_parser_meta_v1_meta_proto_msgTypes[4] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use BucketUsage.ProtoReflect.Descriptor instead. +func (*BucketUsage) Descriptor() ([]byte, []int) { + return file_encore_parser_meta_v1_meta_proto_rawDescGZIP(), []int{4} +} + +func (x *BucketUsage) GetBucket() string { + if x != nil { + return x.Bucket + } + return "" +} + +func (x *BucketUsage) GetOperations() []BucketUsage_Operation { + if x != nil { + return x.Operations + } + return nil +} + type Selector struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -970,7 +1094,7 @@ type Selector struct { func (x *Selector) Reset() { *x = Selector{} if protoimpl.UnsafeEnabled { - mi := &file_encore_parser_meta_v1_meta_proto_msgTypes[4] + mi := &file_encore_parser_meta_v1_meta_proto_msgTypes[5] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -983,7 +1107,7 @@ func (x *Selector) String() string { func (*Selector) ProtoMessage() {} func (x *Selector) ProtoReflect() protoreflect.Message { - mi := &file_encore_parser_meta_v1_meta_proto_msgTypes[4] + mi := &file_encore_parser_meta_v1_meta_proto_msgTypes[5] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -996,7 +1120,7 @@ func (x *Selector) ProtoReflect() protoreflect.Message { // Deprecated: Use Selector.ProtoReflect.Descriptor instead. func (*Selector) Descriptor() ([]byte, []int) { - return file_encore_parser_meta_v1_meta_proto_rawDescGZIP(), []int{4} + return file_encore_parser_meta_v1_meta_proto_rawDescGZIP(), []int{5} } func (x *Selector) GetType() Selector_Type { @@ -1050,7 +1174,7 @@ type RPC struct { func (x *RPC) Reset() { *x = RPC{} if protoimpl.UnsafeEnabled { - mi := &file_encore_parser_meta_v1_meta_proto_msgTypes[5] + mi := &file_encore_parser_meta_v1_meta_proto_msgTypes[6] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1063,7 +1187,7 @@ func (x *RPC) String() string { func (*RPC) ProtoMessage() {} func (x *RPC) ProtoReflect() protoreflect.Message { - mi := &file_encore_parser_meta_v1_meta_proto_msgTypes[5] + mi := &file_encore_parser_meta_v1_meta_proto_msgTypes[6] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1076,7 +1200,7 @@ func (x *RPC) ProtoReflect() protoreflect.Message { // Deprecated: Use RPC.ProtoReflect.Descriptor instead. func (*RPC) Descriptor() ([]byte, []int) { - return file_encore_parser_meta_v1_meta_proto_rawDescGZIP(), []int{5} + return file_encore_parser_meta_v1_meta_proto_rawDescGZIP(), []int{6} } func (x *RPC) GetName() string { @@ -1230,7 +1354,7 @@ type AuthHandler struct { func (x *AuthHandler) Reset() { *x = AuthHandler{} if protoimpl.UnsafeEnabled { - mi := &file_encore_parser_meta_v1_meta_proto_msgTypes[6] + mi := &file_encore_parser_meta_v1_meta_proto_msgTypes[7] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1243,7 +1367,7 @@ func (x *AuthHandler) String() string { func (*AuthHandler) ProtoMessage() {} func (x *AuthHandler) ProtoReflect() protoreflect.Message { - mi := &file_encore_parser_meta_v1_meta_proto_msgTypes[6] + mi := &file_encore_parser_meta_v1_meta_proto_msgTypes[7] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1256,7 +1380,7 @@ func (x *AuthHandler) ProtoReflect() protoreflect.Message { // Deprecated: Use AuthHandler.ProtoReflect.Descriptor instead. func (*AuthHandler) Descriptor() ([]byte, []int) { - return file_encore_parser_meta_v1_meta_proto_rawDescGZIP(), []int{6} + return file_encore_parser_meta_v1_meta_proto_rawDescGZIP(), []int{7} } func (x *AuthHandler) GetName() string { @@ -1331,7 +1455,7 @@ type Middleware struct { func (x *Middleware) Reset() { *x = Middleware{} if protoimpl.UnsafeEnabled { - mi := &file_encore_parser_meta_v1_meta_proto_msgTypes[7] + mi := &file_encore_parser_meta_v1_meta_proto_msgTypes[8] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1344,7 +1468,7 @@ func (x *Middleware) String() string { func (*Middleware) ProtoMessage() {} func (x *Middleware) ProtoReflect() protoreflect.Message { - mi := &file_encore_parser_meta_v1_meta_proto_msgTypes[7] + mi := &file_encore_parser_meta_v1_meta_proto_msgTypes[8] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1357,7 +1481,7 @@ func (x *Middleware) ProtoReflect() protoreflect.Message { // Deprecated: Use Middleware.ProtoReflect.Descriptor instead. func (*Middleware) Descriptor() ([]byte, []int) { - return file_encore_parser_meta_v1_meta_proto_rawDescGZIP(), []int{7} + return file_encore_parser_meta_v1_meta_proto_rawDescGZIP(), []int{8} } func (x *Middleware) GetName() *QualifiedName { @@ -1433,7 +1557,7 @@ type TraceNode struct { func (x *TraceNode) Reset() { *x = TraceNode{} if protoimpl.UnsafeEnabled { - mi := &file_encore_parser_meta_v1_meta_proto_msgTypes[8] + mi := &file_encore_parser_meta_v1_meta_proto_msgTypes[9] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1446,7 +1570,7 @@ func (x *TraceNode) String() string { func (*TraceNode) ProtoMessage() {} func (x *TraceNode) ProtoReflect() protoreflect.Message { - mi := &file_encore_parser_meta_v1_meta_proto_msgTypes[8] + mi := &file_encore_parser_meta_v1_meta_proto_msgTypes[9] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1459,7 +1583,7 @@ func (x *TraceNode) ProtoReflect() protoreflect.Message { // Deprecated: Use TraceNode.ProtoReflect.Descriptor instead. func (*TraceNode) Descriptor() ([]byte, []int) { - return file_encore_parser_meta_v1_meta_proto_rawDescGZIP(), []int{8} + return file_encore_parser_meta_v1_meta_proto_rawDescGZIP(), []int{9} } func (x *TraceNode) GetId() int32 { @@ -1672,7 +1796,7 @@ type RPCDefNode struct { func (x *RPCDefNode) Reset() { *x = RPCDefNode{} if protoimpl.UnsafeEnabled { - mi := &file_encore_parser_meta_v1_meta_proto_msgTypes[9] + mi := &file_encore_parser_meta_v1_meta_proto_msgTypes[10] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1685,7 +1809,7 @@ func (x *RPCDefNode) String() string { func (*RPCDefNode) ProtoMessage() {} func (x *RPCDefNode) ProtoReflect() protoreflect.Message { - mi := &file_encore_parser_meta_v1_meta_proto_msgTypes[9] + mi := &file_encore_parser_meta_v1_meta_proto_msgTypes[10] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1698,7 +1822,7 @@ func (x *RPCDefNode) ProtoReflect() protoreflect.Message { // Deprecated: Use RPCDefNode.ProtoReflect.Descriptor instead. func (*RPCDefNode) Descriptor() ([]byte, []int) { - return file_encore_parser_meta_v1_meta_proto_rawDescGZIP(), []int{9} + return file_encore_parser_meta_v1_meta_proto_rawDescGZIP(), []int{10} } func (x *RPCDefNode) GetServiceName() string { @@ -1735,7 +1859,7 @@ type RPCCallNode struct { func (x *RPCCallNode) Reset() { *x = RPCCallNode{} if protoimpl.UnsafeEnabled { - mi := &file_encore_parser_meta_v1_meta_proto_msgTypes[10] + mi := &file_encore_parser_meta_v1_meta_proto_msgTypes[11] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1748,7 +1872,7 @@ func (x *RPCCallNode) String() string { func (*RPCCallNode) ProtoMessage() {} func (x *RPCCallNode) ProtoReflect() protoreflect.Message { - mi := &file_encore_parser_meta_v1_meta_proto_msgTypes[10] + mi := &file_encore_parser_meta_v1_meta_proto_msgTypes[11] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1761,7 +1885,7 @@ func (x *RPCCallNode) ProtoReflect() protoreflect.Message { // Deprecated: Use RPCCallNode.ProtoReflect.Descriptor instead. func (*RPCCallNode) Descriptor() ([]byte, []int) { - return file_encore_parser_meta_v1_meta_proto_rawDescGZIP(), []int{10} + return file_encore_parser_meta_v1_meta_proto_rawDescGZIP(), []int{11} } func (x *RPCCallNode) GetServiceName() string { @@ -1798,7 +1922,7 @@ type StaticCallNode struct { func (x *StaticCallNode) Reset() { *x = StaticCallNode{} if protoimpl.UnsafeEnabled { - mi := &file_encore_parser_meta_v1_meta_proto_msgTypes[11] + mi := &file_encore_parser_meta_v1_meta_proto_msgTypes[12] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1811,7 +1935,7 @@ func (x *StaticCallNode) String() string { func (*StaticCallNode) ProtoMessage() {} func (x *StaticCallNode) ProtoReflect() protoreflect.Message { - mi := &file_encore_parser_meta_v1_meta_proto_msgTypes[11] + mi := &file_encore_parser_meta_v1_meta_proto_msgTypes[12] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1824,7 +1948,7 @@ func (x *StaticCallNode) ProtoReflect() protoreflect.Message { // Deprecated: Use StaticCallNode.ProtoReflect.Descriptor instead. func (*StaticCallNode) Descriptor() ([]byte, []int) { - return file_encore_parser_meta_v1_meta_proto_rawDescGZIP(), []int{11} + return file_encore_parser_meta_v1_meta_proto_rawDescGZIP(), []int{12} } func (x *StaticCallNode) GetPackage() StaticCallNode_Package { @@ -1861,7 +1985,7 @@ type AuthHandlerDefNode struct { func (x *AuthHandlerDefNode) Reset() { *x = AuthHandlerDefNode{} if protoimpl.UnsafeEnabled { - mi := &file_encore_parser_meta_v1_meta_proto_msgTypes[12] + mi := &file_encore_parser_meta_v1_meta_proto_msgTypes[13] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1874,7 +1998,7 @@ func (x *AuthHandlerDefNode) String() string { func (*AuthHandlerDefNode) ProtoMessage() {} func (x *AuthHandlerDefNode) ProtoReflect() protoreflect.Message { - mi := &file_encore_parser_meta_v1_meta_proto_msgTypes[12] + mi := &file_encore_parser_meta_v1_meta_proto_msgTypes[13] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1887,7 +2011,7 @@ func (x *AuthHandlerDefNode) ProtoReflect() protoreflect.Message { // Deprecated: Use AuthHandlerDefNode.ProtoReflect.Descriptor instead. func (*AuthHandlerDefNode) Descriptor() ([]byte, []int) { - return file_encore_parser_meta_v1_meta_proto_rawDescGZIP(), []int{12} + return file_encore_parser_meta_v1_meta_proto_rawDescGZIP(), []int{13} } func (x *AuthHandlerDefNode) GetServiceName() string { @@ -1923,7 +2047,7 @@ type PubSubTopicDefNode struct { func (x *PubSubTopicDefNode) Reset() { *x = PubSubTopicDefNode{} if protoimpl.UnsafeEnabled { - mi := &file_encore_parser_meta_v1_meta_proto_msgTypes[13] + mi := &file_encore_parser_meta_v1_meta_proto_msgTypes[14] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1936,7 +2060,7 @@ func (x *PubSubTopicDefNode) String() string { func (*PubSubTopicDefNode) ProtoMessage() {} func (x *PubSubTopicDefNode) ProtoReflect() protoreflect.Message { - mi := &file_encore_parser_meta_v1_meta_proto_msgTypes[13] + mi := &file_encore_parser_meta_v1_meta_proto_msgTypes[14] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1949,7 +2073,7 @@ func (x *PubSubTopicDefNode) ProtoReflect() protoreflect.Message { // Deprecated: Use PubSubTopicDefNode.ProtoReflect.Descriptor instead. func (*PubSubTopicDefNode) Descriptor() ([]byte, []int) { - return file_encore_parser_meta_v1_meta_proto_rawDescGZIP(), []int{13} + return file_encore_parser_meta_v1_meta_proto_rawDescGZIP(), []int{14} } func (x *PubSubTopicDefNode) GetTopicName() string { @@ -1978,7 +2102,7 @@ type PubSubPublishNode struct { func (x *PubSubPublishNode) Reset() { *x = PubSubPublishNode{} if protoimpl.UnsafeEnabled { - mi := &file_encore_parser_meta_v1_meta_proto_msgTypes[14] + mi := &file_encore_parser_meta_v1_meta_proto_msgTypes[15] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1991,7 +2115,7 @@ func (x *PubSubPublishNode) String() string { func (*PubSubPublishNode) ProtoMessage() {} func (x *PubSubPublishNode) ProtoReflect() protoreflect.Message { - mi := &file_encore_parser_meta_v1_meta_proto_msgTypes[14] + mi := &file_encore_parser_meta_v1_meta_proto_msgTypes[15] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2004,7 +2128,7 @@ func (x *PubSubPublishNode) ProtoReflect() protoreflect.Message { // Deprecated: Use PubSubPublishNode.ProtoReflect.Descriptor instead. func (*PubSubPublishNode) Descriptor() ([]byte, []int) { - return file_encore_parser_meta_v1_meta_proto_rawDescGZIP(), []int{14} + return file_encore_parser_meta_v1_meta_proto_rawDescGZIP(), []int{15} } func (x *PubSubPublishNode) GetTopicName() string { @@ -2035,7 +2159,7 @@ type PubSubSubscriberNode struct { func (x *PubSubSubscriberNode) Reset() { *x = PubSubSubscriberNode{} if protoimpl.UnsafeEnabled { - mi := &file_encore_parser_meta_v1_meta_proto_msgTypes[15] + mi := &file_encore_parser_meta_v1_meta_proto_msgTypes[16] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2048,7 +2172,7 @@ func (x *PubSubSubscriberNode) String() string { func (*PubSubSubscriberNode) ProtoMessage() {} func (x *PubSubSubscriberNode) ProtoReflect() protoreflect.Message { - mi := &file_encore_parser_meta_v1_meta_proto_msgTypes[15] + mi := &file_encore_parser_meta_v1_meta_proto_msgTypes[16] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2061,7 +2185,7 @@ func (x *PubSubSubscriberNode) ProtoReflect() protoreflect.Message { // Deprecated: Use PubSubSubscriberNode.ProtoReflect.Descriptor instead. func (*PubSubSubscriberNode) Descriptor() ([]byte, []int) { - return file_encore_parser_meta_v1_meta_proto_rawDescGZIP(), []int{15} + return file_encore_parser_meta_v1_meta_proto_rawDescGZIP(), []int{16} } func (x *PubSubSubscriberNode) GetTopicName() string { @@ -2105,7 +2229,7 @@ type ServiceInitNode struct { func (x *ServiceInitNode) Reset() { *x = ServiceInitNode{} if protoimpl.UnsafeEnabled { - mi := &file_encore_parser_meta_v1_meta_proto_msgTypes[16] + mi := &file_encore_parser_meta_v1_meta_proto_msgTypes[17] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2118,7 +2242,7 @@ func (x *ServiceInitNode) String() string { func (*ServiceInitNode) ProtoMessage() {} func (x *ServiceInitNode) ProtoReflect() protoreflect.Message { - mi := &file_encore_parser_meta_v1_meta_proto_msgTypes[16] + mi := &file_encore_parser_meta_v1_meta_proto_msgTypes[17] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2131,7 +2255,7 @@ func (x *ServiceInitNode) ProtoReflect() protoreflect.Message { // Deprecated: Use ServiceInitNode.ProtoReflect.Descriptor instead. func (*ServiceInitNode) Descriptor() ([]byte, []int) { - return file_encore_parser_meta_v1_meta_proto_rawDescGZIP(), []int{16} + return file_encore_parser_meta_v1_meta_proto_rawDescGZIP(), []int{17} } func (x *ServiceInitNode) GetServiceName() string { @@ -2169,7 +2293,7 @@ type MiddlewareDefNode struct { func (x *MiddlewareDefNode) Reset() { *x = MiddlewareDefNode{} if protoimpl.UnsafeEnabled { - mi := &file_encore_parser_meta_v1_meta_proto_msgTypes[17] + mi := &file_encore_parser_meta_v1_meta_proto_msgTypes[18] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2182,7 +2306,7 @@ func (x *MiddlewareDefNode) String() string { func (*MiddlewareDefNode) ProtoMessage() {} func (x *MiddlewareDefNode) ProtoReflect() protoreflect.Message { - mi := &file_encore_parser_meta_v1_meta_proto_msgTypes[17] + mi := &file_encore_parser_meta_v1_meta_proto_msgTypes[18] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2195,7 +2319,7 @@ func (x *MiddlewareDefNode) ProtoReflect() protoreflect.Message { // Deprecated: Use MiddlewareDefNode.ProtoReflect.Descriptor instead. func (*MiddlewareDefNode) Descriptor() ([]byte, []int) { - return file_encore_parser_meta_v1_meta_proto_rawDescGZIP(), []int{17} + return file_encore_parser_meta_v1_meta_proto_rawDescGZIP(), []int{18} } func (x *MiddlewareDefNode) GetPkgRelPath() string { @@ -2240,7 +2364,7 @@ type CacheKeyspaceDefNode struct { func (x *CacheKeyspaceDefNode) Reset() { *x = CacheKeyspaceDefNode{} if protoimpl.UnsafeEnabled { - mi := &file_encore_parser_meta_v1_meta_proto_msgTypes[18] + mi := &file_encore_parser_meta_v1_meta_proto_msgTypes[19] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2253,7 +2377,7 @@ func (x *CacheKeyspaceDefNode) String() string { func (*CacheKeyspaceDefNode) ProtoMessage() {} func (x *CacheKeyspaceDefNode) ProtoReflect() protoreflect.Message { - mi := &file_encore_parser_meta_v1_meta_proto_msgTypes[18] + mi := &file_encore_parser_meta_v1_meta_proto_msgTypes[19] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2266,7 +2390,7 @@ func (x *CacheKeyspaceDefNode) ProtoReflect() protoreflect.Message { // Deprecated: Use CacheKeyspaceDefNode.ProtoReflect.Descriptor instead. func (*CacheKeyspaceDefNode) Descriptor() ([]byte, []int) { - return file_encore_parser_meta_v1_meta_proto_rawDescGZIP(), []int{18} + return file_encore_parser_meta_v1_meta_proto_rawDescGZIP(), []int{19} } func (x *CacheKeyspaceDefNode) GetPkgRelPath() string { @@ -2309,7 +2433,7 @@ type Path struct { func (x *Path) Reset() { *x = Path{} if protoimpl.UnsafeEnabled { - mi := &file_encore_parser_meta_v1_meta_proto_msgTypes[19] + mi := &file_encore_parser_meta_v1_meta_proto_msgTypes[20] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2322,7 +2446,7 @@ func (x *Path) String() string { func (*Path) ProtoMessage() {} func (x *Path) ProtoReflect() protoreflect.Message { - mi := &file_encore_parser_meta_v1_meta_proto_msgTypes[19] + mi := &file_encore_parser_meta_v1_meta_proto_msgTypes[20] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2335,7 +2459,7 @@ func (x *Path) ProtoReflect() protoreflect.Message { // Deprecated: Use Path.ProtoReflect.Descriptor instead. func (*Path) Descriptor() ([]byte, []int) { - return file_encore_parser_meta_v1_meta_proto_rawDescGZIP(), []int{19} + return file_encore_parser_meta_v1_meta_proto_rawDescGZIP(), []int{20} } func (x *Path) GetSegments() []*PathSegment { @@ -2365,7 +2489,7 @@ type PathSegment struct { func (x *PathSegment) Reset() { *x = PathSegment{} if protoimpl.UnsafeEnabled { - mi := &file_encore_parser_meta_v1_meta_proto_msgTypes[20] + mi := &file_encore_parser_meta_v1_meta_proto_msgTypes[21] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2378,7 +2502,7 @@ func (x *PathSegment) String() string { func (*PathSegment) ProtoMessage() {} func (x *PathSegment) ProtoReflect() protoreflect.Message { - mi := &file_encore_parser_meta_v1_meta_proto_msgTypes[20] + mi := &file_encore_parser_meta_v1_meta_proto_msgTypes[21] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2391,7 +2515,7 @@ func (x *PathSegment) ProtoReflect() protoreflect.Message { // Deprecated: Use PathSegment.ProtoReflect.Descriptor instead. func (*PathSegment) Descriptor() ([]byte, []int) { - return file_encore_parser_meta_v1_meta_proto_rawDescGZIP(), []int{20} + return file_encore_parser_meta_v1_meta_proto_rawDescGZIP(), []int{21} } func (x *PathSegment) GetType() PathSegment_SegmentType { @@ -2428,7 +2552,7 @@ type Gateway struct { func (x *Gateway) Reset() { *x = Gateway{} if protoimpl.UnsafeEnabled { - mi := &file_encore_parser_meta_v1_meta_proto_msgTypes[21] + mi := &file_encore_parser_meta_v1_meta_proto_msgTypes[22] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2441,7 +2565,7 @@ func (x *Gateway) String() string { func (*Gateway) ProtoMessage() {} func (x *Gateway) ProtoReflect() protoreflect.Message { - mi := &file_encore_parser_meta_v1_meta_proto_msgTypes[21] + mi := &file_encore_parser_meta_v1_meta_proto_msgTypes[22] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2454,7 +2578,7 @@ func (x *Gateway) ProtoReflect() protoreflect.Message { // Deprecated: Use Gateway.ProtoReflect.Descriptor instead. func (*Gateway) Descriptor() ([]byte, []int) { - return file_encore_parser_meta_v1_meta_proto_rawDescGZIP(), []int{21} + return file_encore_parser_meta_v1_meta_proto_rawDescGZIP(), []int{22} } func (x *Gateway) GetEncoreName() string { @@ -2486,7 +2610,7 @@ type CronJob struct { func (x *CronJob) Reset() { *x = CronJob{} if protoimpl.UnsafeEnabled { - mi := &file_encore_parser_meta_v1_meta_proto_msgTypes[22] + mi := &file_encore_parser_meta_v1_meta_proto_msgTypes[23] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2499,7 +2623,7 @@ func (x *CronJob) String() string { func (*CronJob) ProtoMessage() {} func (x *CronJob) ProtoReflect() protoreflect.Message { - mi := &file_encore_parser_meta_v1_meta_proto_msgTypes[22] + mi := &file_encore_parser_meta_v1_meta_proto_msgTypes[23] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2512,7 +2636,7 @@ func (x *CronJob) ProtoReflect() protoreflect.Message { // Deprecated: Use CronJob.ProtoReflect.Descriptor instead. func (*CronJob) Descriptor() ([]byte, []int) { - return file_encore_parser_meta_v1_meta_proto_rawDescGZIP(), []int{22} + return file_encore_parser_meta_v1_meta_proto_rawDescGZIP(), []int{23} } func (x *CronJob) GetId() string { @@ -2567,7 +2691,7 @@ type SQLDatabase struct { func (x *SQLDatabase) Reset() { *x = SQLDatabase{} if protoimpl.UnsafeEnabled { - mi := &file_encore_parser_meta_v1_meta_proto_msgTypes[23] + mi := &file_encore_parser_meta_v1_meta_proto_msgTypes[24] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2580,7 +2704,7 @@ func (x *SQLDatabase) String() string { func (*SQLDatabase) ProtoMessage() {} func (x *SQLDatabase) ProtoReflect() protoreflect.Message { - mi := &file_encore_parser_meta_v1_meta_proto_msgTypes[23] + mi := &file_encore_parser_meta_v1_meta_proto_msgTypes[24] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2593,7 +2717,7 @@ func (x *SQLDatabase) ProtoReflect() protoreflect.Message { // Deprecated: Use SQLDatabase.ProtoReflect.Descriptor instead. func (*SQLDatabase) Descriptor() ([]byte, []int) { - return file_encore_parser_meta_v1_meta_proto_rawDescGZIP(), []int{23} + return file_encore_parser_meta_v1_meta_proto_rawDescGZIP(), []int{24} } func (x *SQLDatabase) GetName() string { @@ -2644,7 +2768,7 @@ type DBMigration struct { func (x *DBMigration) Reset() { *x = DBMigration{} if protoimpl.UnsafeEnabled { - mi := &file_encore_parser_meta_v1_meta_proto_msgTypes[24] + mi := &file_encore_parser_meta_v1_meta_proto_msgTypes[25] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2657,7 +2781,7 @@ func (x *DBMigration) String() string { func (*DBMigration) ProtoMessage() {} func (x *DBMigration) ProtoReflect() protoreflect.Message { - mi := &file_encore_parser_meta_v1_meta_proto_msgTypes[24] + mi := &file_encore_parser_meta_v1_meta_proto_msgTypes[25] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2670,7 +2794,7 @@ func (x *DBMigration) ProtoReflect() protoreflect.Message { // Deprecated: Use DBMigration.ProtoReflect.Descriptor instead. func (*DBMigration) Descriptor() ([]byte, []int) { - return file_encore_parser_meta_v1_meta_proto_rawDescGZIP(), []int{24} + return file_encore_parser_meta_v1_meta_proto_rawDescGZIP(), []int{25} } func (x *DBMigration) GetFilename() string { @@ -2706,7 +2830,7 @@ type Bucket struct { func (x *Bucket) Reset() { *x = Bucket{} if protoimpl.UnsafeEnabled { - mi := &file_encore_parser_meta_v1_meta_proto_msgTypes[25] + mi := &file_encore_parser_meta_v1_meta_proto_msgTypes[26] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2719,7 +2843,7 @@ func (x *Bucket) String() string { func (*Bucket) ProtoMessage() {} func (x *Bucket) ProtoReflect() protoreflect.Message { - mi := &file_encore_parser_meta_v1_meta_proto_msgTypes[25] + mi := &file_encore_parser_meta_v1_meta_proto_msgTypes[26] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2732,7 +2856,7 @@ func (x *Bucket) ProtoReflect() protoreflect.Message { // Deprecated: Use Bucket.ProtoReflect.Descriptor instead. func (*Bucket) Descriptor() ([]byte, []int) { - return file_encore_parser_meta_v1_meta_proto_rawDescGZIP(), []int{25} + return file_encore_parser_meta_v1_meta_proto_rawDescGZIP(), []int{26} } func (x *Bucket) GetName() string { @@ -2766,7 +2890,7 @@ type PubSubTopic struct { func (x *PubSubTopic) Reset() { *x = PubSubTopic{} if protoimpl.UnsafeEnabled { - mi := &file_encore_parser_meta_v1_meta_proto_msgTypes[26] + mi := &file_encore_parser_meta_v1_meta_proto_msgTypes[27] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2779,7 +2903,7 @@ func (x *PubSubTopic) String() string { func (*PubSubTopic) ProtoMessage() {} func (x *PubSubTopic) ProtoReflect() protoreflect.Message { - mi := &file_encore_parser_meta_v1_meta_proto_msgTypes[26] + mi := &file_encore_parser_meta_v1_meta_proto_msgTypes[27] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2792,7 +2916,7 @@ func (x *PubSubTopic) ProtoReflect() protoreflect.Message { // Deprecated: Use PubSubTopic.ProtoReflect.Descriptor instead. func (*PubSubTopic) Descriptor() ([]byte, []int) { - return file_encore_parser_meta_v1_meta_proto_rawDescGZIP(), []int{26} + return file_encore_parser_meta_v1_meta_proto_rawDescGZIP(), []int{27} } func (x *PubSubTopic) GetName() string { @@ -2858,7 +2982,7 @@ type CacheCluster struct { func (x *CacheCluster) Reset() { *x = CacheCluster{} if protoimpl.UnsafeEnabled { - mi := &file_encore_parser_meta_v1_meta_proto_msgTypes[27] + mi := &file_encore_parser_meta_v1_meta_proto_msgTypes[28] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2871,7 +2995,7 @@ func (x *CacheCluster) String() string { func (*CacheCluster) ProtoMessage() {} func (x *CacheCluster) ProtoReflect() protoreflect.Message { - mi := &file_encore_parser_meta_v1_meta_proto_msgTypes[27] + mi := &file_encore_parser_meta_v1_meta_proto_msgTypes[28] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2884,7 +3008,7 @@ func (x *CacheCluster) ProtoReflect() protoreflect.Message { // Deprecated: Use CacheCluster.ProtoReflect.Descriptor instead. func (*CacheCluster) Descriptor() ([]byte, []int) { - return file_encore_parser_meta_v1_meta_proto_rawDescGZIP(), []int{27} + return file_encore_parser_meta_v1_meta_proto_rawDescGZIP(), []int{28} } func (x *CacheCluster) GetName() string { @@ -2931,7 +3055,7 @@ type Metric struct { func (x *Metric) Reset() { *x = Metric{} if protoimpl.UnsafeEnabled { - mi := &file_encore_parser_meta_v1_meta_proto_msgTypes[28] + mi := &file_encore_parser_meta_v1_meta_proto_msgTypes[29] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2944,7 +3068,7 @@ func (x *Metric) String() string { func (*Metric) ProtoMessage() {} func (x *Metric) ProtoReflect() protoreflect.Message { - mi := &file_encore_parser_meta_v1_meta_proto_msgTypes[28] + mi := &file_encore_parser_meta_v1_meta_proto_msgTypes[29] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2957,7 +3081,7 @@ func (x *Metric) ProtoReflect() protoreflect.Message { // Deprecated: Use Metric.ProtoReflect.Descriptor instead. func (*Metric) Descriptor() ([]byte, []int) { - return file_encore_parser_meta_v1_meta_proto_rawDescGZIP(), []int{28} + return file_encore_parser_meta_v1_meta_proto_rawDescGZIP(), []int{29} } func (x *Metric) GetName() string { @@ -3011,7 +3135,7 @@ type RPC_ExposeOptions struct { func (x *RPC_ExposeOptions) Reset() { *x = RPC_ExposeOptions{} if protoimpl.UnsafeEnabled { - mi := &file_encore_parser_meta_v1_meta_proto_msgTypes[30] + mi := &file_encore_parser_meta_v1_meta_proto_msgTypes[31] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -3024,7 +3148,7 @@ func (x *RPC_ExposeOptions) String() string { func (*RPC_ExposeOptions) ProtoMessage() {} func (x *RPC_ExposeOptions) ProtoReflect() protoreflect.Message { - mi := &file_encore_parser_meta_v1_meta_proto_msgTypes[30] + mi := &file_encore_parser_meta_v1_meta_proto_msgTypes[31] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3037,7 +3161,7 @@ func (x *RPC_ExposeOptions) ProtoReflect() protoreflect.Message { // Deprecated: Use RPC_ExposeOptions.ProtoReflect.Descriptor instead. func (*RPC_ExposeOptions) Descriptor() ([]byte, []int) { - return file_encore_parser_meta_v1_meta_proto_rawDescGZIP(), []int{5, 1} + return file_encore_parser_meta_v1_meta_proto_rawDescGZIP(), []int{6, 1} } type RPC_StaticAssets struct { @@ -3056,7 +3180,7 @@ type RPC_StaticAssets struct { func (x *RPC_StaticAssets) Reset() { *x = RPC_StaticAssets{} if protoimpl.UnsafeEnabled { - mi := &file_encore_parser_meta_v1_meta_proto_msgTypes[31] + mi := &file_encore_parser_meta_v1_meta_proto_msgTypes[32] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -3069,7 +3193,7 @@ func (x *RPC_StaticAssets) String() string { func (*RPC_StaticAssets) ProtoMessage() {} func (x *RPC_StaticAssets) ProtoReflect() protoreflect.Message { - mi := &file_encore_parser_meta_v1_meta_proto_msgTypes[31] + mi := &file_encore_parser_meta_v1_meta_proto_msgTypes[32] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3082,7 +3206,7 @@ func (x *RPC_StaticAssets) ProtoReflect() protoreflect.Message { // Deprecated: Use RPC_StaticAssets.ProtoReflect.Descriptor instead. func (*RPC_StaticAssets) Descriptor() ([]byte, []int) { - return file_encore_parser_meta_v1_meta_proto_rawDescGZIP(), []int{5, 2} + return file_encore_parser_meta_v1_meta_proto_rawDescGZIP(), []int{6, 2} } func (x *RPC_StaticAssets) GetDirRelPath() string { @@ -3112,7 +3236,7 @@ type Gateway_Explicit struct { func (x *Gateway_Explicit) Reset() { *x = Gateway_Explicit{} if protoimpl.UnsafeEnabled { - mi := &file_encore_parser_meta_v1_meta_proto_msgTypes[32] + mi := &file_encore_parser_meta_v1_meta_proto_msgTypes[33] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -3125,7 +3249,7 @@ func (x *Gateway_Explicit) String() string { func (*Gateway_Explicit) ProtoMessage() {} func (x *Gateway_Explicit) ProtoReflect() protoreflect.Message { - mi := &file_encore_parser_meta_v1_meta_proto_msgTypes[32] + mi := &file_encore_parser_meta_v1_meta_proto_msgTypes[33] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3138,7 +3262,7 @@ func (x *Gateway_Explicit) ProtoReflect() protoreflect.Message { // Deprecated: Use Gateway_Explicit.ProtoReflect.Descriptor instead. func (*Gateway_Explicit) Descriptor() ([]byte, []int) { - return file_encore_parser_meta_v1_meta_proto_rawDescGZIP(), []int{21, 0} + return file_encore_parser_meta_v1_meta_proto_rawDescGZIP(), []int{22, 0} } func (x *Gateway_Explicit) GetServiceName() string { @@ -3166,7 +3290,7 @@ type PubSubTopic_Publisher struct { func (x *PubSubTopic_Publisher) Reset() { *x = PubSubTopic_Publisher{} if protoimpl.UnsafeEnabled { - mi := &file_encore_parser_meta_v1_meta_proto_msgTypes[33] + mi := &file_encore_parser_meta_v1_meta_proto_msgTypes[34] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -3179,7 +3303,7 @@ func (x *PubSubTopic_Publisher) String() string { func (*PubSubTopic_Publisher) ProtoMessage() {} func (x *PubSubTopic_Publisher) ProtoReflect() protoreflect.Message { - mi := &file_encore_parser_meta_v1_meta_proto_msgTypes[33] + mi := &file_encore_parser_meta_v1_meta_proto_msgTypes[34] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3192,7 +3316,7 @@ func (x *PubSubTopic_Publisher) ProtoReflect() protoreflect.Message { // Deprecated: Use PubSubTopic_Publisher.ProtoReflect.Descriptor instead. func (*PubSubTopic_Publisher) Descriptor() ([]byte, []int) { - return file_encore_parser_meta_v1_meta_proto_rawDescGZIP(), []int{26, 0} + return file_encore_parser_meta_v1_meta_proto_rawDescGZIP(), []int{27, 0} } func (x *PubSubTopic_Publisher) GetServiceName() string { @@ -3220,7 +3344,7 @@ type PubSubTopic_Subscription struct { func (x *PubSubTopic_Subscription) Reset() { *x = PubSubTopic_Subscription{} if protoimpl.UnsafeEnabled { - mi := &file_encore_parser_meta_v1_meta_proto_msgTypes[34] + mi := &file_encore_parser_meta_v1_meta_proto_msgTypes[35] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -3233,7 +3357,7 @@ func (x *PubSubTopic_Subscription) String() string { func (*PubSubTopic_Subscription) ProtoMessage() {} func (x *PubSubTopic_Subscription) ProtoReflect() protoreflect.Message { - mi := &file_encore_parser_meta_v1_meta_proto_msgTypes[34] + mi := &file_encore_parser_meta_v1_meta_proto_msgTypes[35] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3246,7 +3370,7 @@ func (x *PubSubTopic_Subscription) ProtoReflect() protoreflect.Message { // Deprecated: Use PubSubTopic_Subscription.ProtoReflect.Descriptor instead. func (*PubSubTopic_Subscription) Descriptor() ([]byte, []int) { - return file_encore_parser_meta_v1_meta_proto_rawDescGZIP(), []int{26, 1} + return file_encore_parser_meta_v1_meta_proto_rawDescGZIP(), []int{27, 1} } func (x *PubSubTopic_Subscription) GetName() string { @@ -3304,7 +3428,7 @@ type PubSubTopic_RetryPolicy struct { func (x *PubSubTopic_RetryPolicy) Reset() { *x = PubSubTopic_RetryPolicy{} if protoimpl.UnsafeEnabled { - mi := &file_encore_parser_meta_v1_meta_proto_msgTypes[35] + mi := &file_encore_parser_meta_v1_meta_proto_msgTypes[36] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -3317,7 +3441,7 @@ func (x *PubSubTopic_RetryPolicy) String() string { func (*PubSubTopic_RetryPolicy) ProtoMessage() {} func (x *PubSubTopic_RetryPolicy) ProtoReflect() protoreflect.Message { - mi := &file_encore_parser_meta_v1_meta_proto_msgTypes[35] + mi := &file_encore_parser_meta_v1_meta_proto_msgTypes[36] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3330,7 +3454,7 @@ func (x *PubSubTopic_RetryPolicy) ProtoReflect() protoreflect.Message { // Deprecated: Use PubSubTopic_RetryPolicy.ProtoReflect.Descriptor instead. func (*PubSubTopic_RetryPolicy) Descriptor() ([]byte, []int) { - return file_encore_parser_meta_v1_meta_proto_rawDescGZIP(), []int{26, 2} + return file_encore_parser_meta_v1_meta_proto_rawDescGZIP(), []int{27, 2} } func (x *PubSubTopic_RetryPolicy) GetMinBackoff() int64 { @@ -3369,7 +3493,7 @@ type CacheCluster_Keyspace struct { func (x *CacheCluster_Keyspace) Reset() { *x = CacheCluster_Keyspace{} if protoimpl.UnsafeEnabled { - mi := &file_encore_parser_meta_v1_meta_proto_msgTypes[36] + mi := &file_encore_parser_meta_v1_meta_proto_msgTypes[37] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -3382,7 +3506,7 @@ func (x *CacheCluster_Keyspace) String() string { func (*CacheCluster_Keyspace) ProtoMessage() {} func (x *CacheCluster_Keyspace) ProtoReflect() protoreflect.Message { - mi := &file_encore_parser_meta_v1_meta_proto_msgTypes[36] + mi := &file_encore_parser_meta_v1_meta_proto_msgTypes[37] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3395,7 +3519,7 @@ func (x *CacheCluster_Keyspace) ProtoReflect() protoreflect.Message { // Deprecated: Use CacheCluster_Keyspace.ProtoReflect.Descriptor instead. func (*CacheCluster_Keyspace) Descriptor() ([]byte, []int) { - return file_encore_parser_meta_v1_meta_proto_rawDescGZIP(), []int{27, 0} + return file_encore_parser_meta_v1_meta_proto_rawDescGZIP(), []int{28, 0} } func (x *CacheCluster_Keyspace) GetKeyType() *v1.Type { @@ -3446,7 +3570,7 @@ type Metric_Label struct { func (x *Metric_Label) Reset() { *x = Metric_Label{} if protoimpl.UnsafeEnabled { - mi := &file_encore_parser_meta_v1_meta_proto_msgTypes[37] + mi := &file_encore_parser_meta_v1_meta_proto_msgTypes[38] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -3459,7 +3583,7 @@ func (x *Metric_Label) String() string { func (*Metric_Label) ProtoMessage() {} func (x *Metric_Label) ProtoReflect() protoreflect.Message { - mi := &file_encore_parser_meta_v1_meta_proto_msgTypes[37] + mi := &file_encore_parser_meta_v1_meta_proto_msgTypes[38] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3472,7 +3596,7 @@ func (x *Metric_Label) ProtoReflect() protoreflect.Message { // Deprecated: Use Metric_Label.ProtoReflect.Descriptor instead. func (*Metric_Label) Descriptor() ([]byte, []int) { - return file_encore_parser_meta_v1_meta_proto_rawDescGZIP(), []int{28, 0} + return file_encore_parser_meta_v1_meta_proto_rawDescGZIP(), []int{29, 0} } func (x *Metric_Label) GetKey() string { @@ -3587,7 +3711,7 @@ var file_encore_parser_meta_v1_meta_proto_rawDesc = []byte{ 0x73, 0x18, 0x07, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x20, 0x2e, 0x65, 0x6e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x70, 0x61, 0x72, 0x73, 0x65, 0x72, 0x2e, 0x6d, 0x65, 0x74, 0x61, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x72, 0x61, 0x63, 0x65, 0x4e, 0x6f, 0x64, 0x65, 0x52, 0x0a, 0x74, 0x72, 0x61, 0x63, 0x65, - 0x4e, 0x6f, 0x64, 0x65, 0x73, 0x22, 0x83, 0x02, 0x0a, 0x07, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, + 0x4e, 0x6f, 0x64, 0x65, 0x73, 0x22, 0xa7, 0x02, 0x0a, 0x07, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x19, 0x0a, 0x08, 0x72, 0x65, 0x6c, 0x5f, 0x70, 0x61, 0x74, 0x68, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x72, 0x65, 0x6c, 0x50, 0x61, 0x74, 0x68, @@ -3602,496 +3726,516 @@ var file_encore_parser_meta_v1_meta_proto_rawDesc = []byte{ 0x73, 0x18, 0x05, 0x20, 0x03, 0x28, 0x09, 0x52, 0x09, 0x64, 0x61, 0x74, 0x61, 0x62, 0x61, 0x73, 0x65, 0x73, 0x12, 0x1d, 0x0a, 0x0a, 0x68, 0x61, 0x73, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x18, 0x06, 0x20, 0x01, 0x28, 0x08, 0x52, 0x09, 0x68, 0x61, 0x73, 0x43, 0x6f, 0x6e, 0x66, 0x69, - 0x67, 0x12, 0x18, 0x0a, 0x07, 0x62, 0x75, 0x63, 0x6b, 0x65, 0x74, 0x73, 0x18, 0x07, 0x20, 0x03, - 0x28, 0x09, 0x52, 0x07, 0x62, 0x75, 0x63, 0x6b, 0x65, 0x74, 0x73, 0x22, 0x81, 0x01, 0x0a, 0x08, - 0x53, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x12, 0x38, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x24, 0x2e, 0x65, 0x6e, 0x63, 0x6f, 0x72, 0x65, 0x2e, - 0x70, 0x61, 0x72, 0x73, 0x65, 0x72, 0x2e, 0x6d, 0x65, 0x74, 0x61, 0x2e, 0x76, 0x31, 0x2e, 0x53, - 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x2e, 0x54, 0x79, 0x70, 0x65, 0x52, 0x04, 0x74, 0x79, - 0x70, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x22, 0x25, 0x0a, 0x04, 0x54, 0x79, 0x70, 0x65, - 0x12, 0x0b, 0x0a, 0x07, 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x10, 0x00, 0x12, 0x07, 0x0a, - 0x03, 0x41, 0x4c, 0x4c, 0x10, 0x01, 0x12, 0x07, 0x0a, 0x03, 0x54, 0x41, 0x47, 0x10, 0x02, 0x22, - 0x85, 0x0b, 0x0a, 0x03, 0x52, 0x50, 0x43, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x15, 0x0a, 0x03, 0x64, - 0x6f, 0x63, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x48, 0x00, 0x52, 0x03, 0x64, 0x6f, 0x63, 0x88, - 0x01, 0x01, 0x12, 0x21, 0x0a, 0x0c, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x5f, 0x6e, 0x61, - 0x6d, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, - 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x46, 0x0a, 0x0b, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x5f, - 0x74, 0x79, 0x70, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x25, 0x2e, 0x65, 0x6e, 0x63, - 0x6f, 0x72, 0x65, 0x2e, 0x70, 0x61, 0x72, 0x73, 0x65, 0x72, 0x2e, 0x6d, 0x65, 0x74, 0x61, 0x2e, - 0x76, 0x31, 0x2e, 0x52, 0x50, 0x43, 0x2e, 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, 0x54, 0x79, 0x70, - 0x65, 0x52, 0x0a, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x54, 0x79, 0x70, 0x65, 0x12, 0x49, 0x0a, - 0x0e, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x5f, 0x73, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x18, - 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x65, 0x6e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x70, - 0x61, 0x72, 0x73, 0x65, 0x72, 0x2e, 0x73, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x2e, 0x76, 0x31, 0x2e, - 0x54, 0x79, 0x70, 0x65, 0x48, 0x01, 0x52, 0x0d, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x53, - 0x63, 0x68, 0x65, 0x6d, 0x61, 0x88, 0x01, 0x01, 0x12, 0x4b, 0x0a, 0x0f, 0x72, 0x65, 0x73, 0x70, - 0x6f, 0x6e, 0x73, 0x65, 0x5f, 0x73, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x18, 0x06, 0x20, 0x01, 0x28, + 0x67, 0x12, 0x3c, 0x0a, 0x07, 0x62, 0x75, 0x63, 0x6b, 0x65, 0x74, 0x73, 0x18, 0x07, 0x20, 0x03, + 0x28, 0x0b, 0x32, 0x22, 0x2e, 0x65, 0x6e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x70, 0x61, 0x72, 0x73, + 0x65, 0x72, 0x2e, 0x6d, 0x65, 0x74, 0x61, 0x2e, 0x76, 0x31, 0x2e, 0x42, 0x75, 0x63, 0x6b, 0x65, + 0x74, 0x55, 0x73, 0x61, 0x67, 0x65, 0x52, 0x07, 0x62, 0x75, 0x63, 0x6b, 0x65, 0x74, 0x73, 0x22, + 0x94, 0x02, 0x0a, 0x0b, 0x42, 0x75, 0x63, 0x6b, 0x65, 0x74, 0x55, 0x73, 0x61, 0x67, 0x65, 0x12, + 0x16, 0x0a, 0x06, 0x62, 0x75, 0x63, 0x6b, 0x65, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x06, 0x62, 0x75, 0x63, 0x6b, 0x65, 0x74, 0x12, 0x4c, 0x0a, 0x0a, 0x6f, 0x70, 0x65, 0x72, 0x61, + 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0e, 0x32, 0x2c, 0x2e, 0x65, 0x6e, + 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x70, 0x61, 0x72, 0x73, 0x65, 0x72, 0x2e, 0x6d, 0x65, 0x74, 0x61, + 0x2e, 0x76, 0x31, 0x2e, 0x42, 0x75, 0x63, 0x6b, 0x65, 0x74, 0x55, 0x73, 0x61, 0x67, 0x65, 0x2e, + 0x4f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x0a, 0x6f, 0x70, 0x65, 0x72, 0x61, + 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x22, 0x9e, 0x01, 0x0a, 0x09, 0x4f, 0x70, 0x65, 0x72, 0x61, 0x74, + 0x69, 0x6f, 0x6e, 0x12, 0x0b, 0x0a, 0x07, 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x10, 0x00, + 0x12, 0x10, 0x0a, 0x0c, 0x4c, 0x49, 0x53, 0x54, 0x5f, 0x4f, 0x42, 0x4a, 0x45, 0x43, 0x54, 0x53, + 0x10, 0x01, 0x12, 0x18, 0x0a, 0x14, 0x52, 0x45, 0x41, 0x44, 0x5f, 0x4f, 0x42, 0x4a, 0x45, 0x43, + 0x54, 0x5f, 0x43, 0x4f, 0x4e, 0x54, 0x45, 0x4e, 0x54, 0x53, 0x10, 0x02, 0x12, 0x10, 0x0a, 0x0c, + 0x57, 0x52, 0x49, 0x54, 0x45, 0x5f, 0x4f, 0x42, 0x4a, 0x45, 0x43, 0x54, 0x10, 0x03, 0x12, 0x1a, + 0x0a, 0x16, 0x55, 0x50, 0x44, 0x41, 0x54, 0x45, 0x5f, 0x4f, 0x42, 0x4a, 0x45, 0x43, 0x54, 0x5f, + 0x4d, 0x45, 0x54, 0x41, 0x44, 0x41, 0x54, 0x41, 0x10, 0x04, 0x12, 0x17, 0x0a, 0x13, 0x47, 0x45, + 0x54, 0x5f, 0x4f, 0x42, 0x4a, 0x45, 0x43, 0x54, 0x5f, 0x4d, 0x45, 0x54, 0x41, 0x44, 0x41, 0x54, + 0x41, 0x10, 0x05, 0x12, 0x11, 0x0a, 0x0d, 0x44, 0x45, 0x4c, 0x45, 0x54, 0x45, 0x5f, 0x4f, 0x42, + 0x4a, 0x45, 0x43, 0x54, 0x10, 0x06, 0x22, 0x81, 0x01, 0x0a, 0x08, 0x53, 0x65, 0x6c, 0x65, 0x63, + 0x74, 0x6f, 0x72, 0x12, 0x38, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x0e, 0x32, 0x24, 0x2e, 0x65, 0x6e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x70, 0x61, 0x72, 0x73, 0x65, + 0x72, 0x2e, 0x6d, 0x65, 0x74, 0x61, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x6c, 0x65, 0x63, 0x74, + 0x6f, 0x72, 0x2e, 0x54, 0x79, 0x70, 0x65, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x14, 0x0a, + 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, + 0x6c, 0x75, 0x65, 0x22, 0x25, 0x0a, 0x04, 0x54, 0x79, 0x70, 0x65, 0x12, 0x0b, 0x0a, 0x07, 0x55, + 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x10, 0x00, 0x12, 0x07, 0x0a, 0x03, 0x41, 0x4c, 0x4c, 0x10, + 0x01, 0x12, 0x07, 0x0a, 0x03, 0x54, 0x41, 0x47, 0x10, 0x02, 0x22, 0x85, 0x0b, 0x0a, 0x03, 0x52, + 0x50, 0x43, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x15, 0x0a, 0x03, 0x64, 0x6f, 0x63, 0x18, 0x02, 0x20, + 0x01, 0x28, 0x09, 0x48, 0x00, 0x52, 0x03, 0x64, 0x6f, 0x63, 0x88, 0x01, 0x01, 0x12, 0x21, 0x0a, + 0x0c, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x03, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x0b, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x4e, 0x61, 0x6d, 0x65, + 0x12, 0x46, 0x0a, 0x0b, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, + 0x04, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x25, 0x2e, 0x65, 0x6e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x70, + 0x61, 0x72, 0x73, 0x65, 0x72, 0x2e, 0x6d, 0x65, 0x74, 0x61, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x50, + 0x43, 0x2e, 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, 0x54, 0x79, 0x70, 0x65, 0x52, 0x0a, 0x61, 0x63, + 0x63, 0x65, 0x73, 0x73, 0x54, 0x79, 0x70, 0x65, 0x12, 0x49, 0x0a, 0x0e, 0x72, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x5f, 0x73, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, + 0x32, 0x1d, 0x2e, 0x65, 0x6e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x70, 0x61, 0x72, 0x73, 0x65, 0x72, + 0x2e, 0x73, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x79, 0x70, 0x65, 0x48, + 0x01, 0x52, 0x0d, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, + 0x88, 0x01, 0x01, 0x12, 0x4b, 0x0a, 0x0f, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x5f, + 0x73, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x65, + 0x6e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x70, 0x61, 0x72, 0x73, 0x65, 0x72, 0x2e, 0x73, 0x63, 0x68, + 0x65, 0x6d, 0x61, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x79, 0x70, 0x65, 0x48, 0x02, 0x52, 0x0e, 0x72, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x88, 0x01, 0x01, + 0x12, 0x39, 0x0a, 0x05, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0e, 0x32, + 0x23, 0x2e, 0x65, 0x6e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x70, 0x61, 0x72, 0x73, 0x65, 0x72, 0x2e, + 0x6d, 0x65, 0x74, 0x61, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x50, 0x43, 0x2e, 0x50, 0x72, 0x6f, 0x74, + 0x6f, 0x63, 0x6f, 0x6c, 0x52, 0x05, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x2e, 0x0a, 0x03, 0x6c, + 0x6f, 0x63, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x65, 0x6e, 0x63, 0x6f, 0x72, + 0x65, 0x2e, 0x70, 0x61, 0x72, 0x73, 0x65, 0x72, 0x2e, 0x73, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x2e, + 0x76, 0x31, 0x2e, 0x4c, 0x6f, 0x63, 0x52, 0x03, 0x6c, 0x6f, 0x63, 0x12, 0x2f, 0x0a, 0x04, 0x70, + 0x61, 0x74, 0x68, 0x18, 0x09, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x65, 0x6e, 0x63, 0x6f, + 0x72, 0x65, 0x2e, 0x70, 0x61, 0x72, 0x73, 0x65, 0x72, 0x2e, 0x6d, 0x65, 0x74, 0x61, 0x2e, 0x76, + 0x31, 0x2e, 0x50, 0x61, 0x74, 0x68, 0x52, 0x04, 0x70, 0x61, 0x74, 0x68, 0x12, 0x21, 0x0a, 0x0c, + 0x68, 0x74, 0x74, 0x70, 0x5f, 0x6d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x73, 0x18, 0x0a, 0x20, 0x03, + 0x28, 0x09, 0x52, 0x0b, 0x68, 0x74, 0x74, 0x70, 0x4d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x73, 0x12, + 0x33, 0x0a, 0x04, 0x74, 0x61, 0x67, 0x73, 0x18, 0x0b, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1f, 0x2e, + 0x65, 0x6e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x70, 0x61, 0x72, 0x73, 0x65, 0x72, 0x2e, 0x6d, 0x65, + 0x74, 0x61, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x52, 0x04, + 0x74, 0x61, 0x67, 0x73, 0x12, 0x1c, 0x0a, 0x09, 0x73, 0x65, 0x6e, 0x73, 0x69, 0x74, 0x69, 0x76, + 0x65, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x08, 0x52, 0x09, 0x73, 0x65, 0x6e, 0x73, 0x69, 0x74, 0x69, + 0x76, 0x65, 0x12, 0x33, 0x0a, 0x15, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x5f, 0x75, 0x6e, 0x61, 0x75, + 0x74, 0x68, 0x65, 0x6e, 0x74, 0x69, 0x63, 0x61, 0x74, 0x65, 0x64, 0x18, 0x0d, 0x20, 0x01, 0x28, + 0x08, 0x52, 0x14, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x55, 0x6e, 0x61, 0x75, 0x74, 0x68, 0x65, 0x6e, + 0x74, 0x69, 0x63, 0x61, 0x74, 0x65, 0x64, 0x12, 0x3e, 0x0a, 0x06, 0x65, 0x78, 0x70, 0x6f, 0x73, + 0x65, 0x18, 0x0e, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x26, 0x2e, 0x65, 0x6e, 0x63, 0x6f, 0x72, 0x65, + 0x2e, 0x70, 0x61, 0x72, 0x73, 0x65, 0x72, 0x2e, 0x6d, 0x65, 0x74, 0x61, 0x2e, 0x76, 0x31, 0x2e, + 0x52, 0x50, 0x43, 0x2e, 0x45, 0x78, 0x70, 0x6f, 0x73, 0x65, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, + 0x06, 0x65, 0x78, 0x70, 0x6f, 0x73, 0x65, 0x12, 0x22, 0x0a, 0x0a, 0x62, 0x6f, 0x64, 0x79, 0x5f, + 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x18, 0x0f, 0x20, 0x01, 0x28, 0x04, 0x48, 0x03, 0x52, 0x09, 0x62, + 0x6f, 0x64, 0x79, 0x4c, 0x69, 0x6d, 0x69, 0x74, 0x88, 0x01, 0x01, 0x12, 0x2b, 0x0a, 0x11, 0x73, + 0x74, 0x72, 0x65, 0x61, 0x6d, 0x69, 0x6e, 0x67, 0x5f, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x18, 0x10, 0x20, 0x01, 0x28, 0x08, 0x52, 0x10, 0x73, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x69, 0x6e, + 0x67, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x2d, 0x0a, 0x12, 0x73, 0x74, 0x72, 0x65, + 0x61, 0x6d, 0x69, 0x6e, 0x67, 0x5f, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x18, 0x11, + 0x20, 0x01, 0x28, 0x08, 0x52, 0x11, 0x73, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x69, 0x6e, 0x67, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x4d, 0x0a, 0x10, 0x68, 0x61, 0x6e, 0x64, 0x73, + 0x68, 0x61, 0x6b, 0x65, 0x5f, 0x73, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x18, 0x12, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x65, 0x6e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x70, 0x61, 0x72, 0x73, 0x65, 0x72, 0x2e, 0x73, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x79, 0x70, 0x65, - 0x48, 0x02, 0x52, 0x0e, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x53, 0x63, 0x68, 0x65, - 0x6d, 0x61, 0x88, 0x01, 0x01, 0x12, 0x39, 0x0a, 0x05, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x18, 0x07, - 0x20, 0x01, 0x28, 0x0e, 0x32, 0x23, 0x2e, 0x65, 0x6e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x70, 0x61, - 0x72, 0x73, 0x65, 0x72, 0x2e, 0x6d, 0x65, 0x74, 0x61, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x50, 0x43, - 0x2e, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x52, 0x05, 0x70, 0x72, 0x6f, 0x74, 0x6f, - 0x12, 0x2e, 0x0a, 0x03, 0x6c, 0x6f, 0x63, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e, - 0x65, 0x6e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x70, 0x61, 0x72, 0x73, 0x65, 0x72, 0x2e, 0x73, 0x63, - 0x68, 0x65, 0x6d, 0x61, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x6f, 0x63, 0x52, 0x03, 0x6c, 0x6f, 0x63, - 0x12, 0x2f, 0x0a, 0x04, 0x70, 0x61, 0x74, 0x68, 0x18, 0x09, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, - 0x2e, 0x65, 0x6e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x70, 0x61, 0x72, 0x73, 0x65, 0x72, 0x2e, 0x6d, - 0x65, 0x74, 0x61, 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x61, 0x74, 0x68, 0x52, 0x04, 0x70, 0x61, 0x74, - 0x68, 0x12, 0x21, 0x0a, 0x0c, 0x68, 0x74, 0x74, 0x70, 0x5f, 0x6d, 0x65, 0x74, 0x68, 0x6f, 0x64, - 0x73, 0x18, 0x0a, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0b, 0x68, 0x74, 0x74, 0x70, 0x4d, 0x65, 0x74, - 0x68, 0x6f, 0x64, 0x73, 0x12, 0x33, 0x0a, 0x04, 0x74, 0x61, 0x67, 0x73, 0x18, 0x0b, 0x20, 0x03, + 0x48, 0x04, 0x52, 0x0f, 0x68, 0x61, 0x6e, 0x64, 0x73, 0x68, 0x61, 0x6b, 0x65, 0x53, 0x63, 0x68, + 0x65, 0x6d, 0x61, 0x88, 0x01, 0x01, 0x12, 0x51, 0x0a, 0x0d, 0x73, 0x74, 0x61, 0x74, 0x69, 0x63, + 0x5f, 0x61, 0x73, 0x73, 0x65, 0x74, 0x73, 0x18, 0x13, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x27, 0x2e, + 0x65, 0x6e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x70, 0x61, 0x72, 0x73, 0x65, 0x72, 0x2e, 0x6d, 0x65, + 0x74, 0x61, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x50, 0x43, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x69, 0x63, + 0x41, 0x73, 0x73, 0x65, 0x74, 0x73, 0x48, 0x05, 0x52, 0x0c, 0x73, 0x74, 0x61, 0x74, 0x69, 0x63, + 0x41, 0x73, 0x73, 0x65, 0x74, 0x73, 0x88, 0x01, 0x01, 0x1a, 0x63, 0x0a, 0x0b, 0x45, 0x78, 0x70, + 0x6f, 0x73, 0x65, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x3e, 0x0a, 0x05, 0x76, 0x61, + 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x28, 0x2e, 0x65, 0x6e, 0x63, 0x6f, + 0x72, 0x65, 0x2e, 0x70, 0x61, 0x72, 0x73, 0x65, 0x72, 0x2e, 0x6d, 0x65, 0x74, 0x61, 0x2e, 0x76, + 0x31, 0x2e, 0x52, 0x50, 0x43, 0x2e, 0x45, 0x78, 0x70, 0x6f, 0x73, 0x65, 0x4f, 0x70, 0x74, 0x69, + 0x6f, 0x6e, 0x73, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x1a, 0x0f, + 0x0a, 0x0d, 0x45, 0x78, 0x70, 0x6f, 0x73, 0x65, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x1a, + 0x79, 0x0a, 0x0c, 0x53, 0x74, 0x61, 0x74, 0x69, 0x63, 0x41, 0x73, 0x73, 0x65, 0x74, 0x73, 0x12, + 0x20, 0x0a, 0x0c, 0x64, 0x69, 0x72, 0x5f, 0x72, 0x65, 0x6c, 0x5f, 0x70, 0x61, 0x74, 0x68, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x64, 0x69, 0x72, 0x52, 0x65, 0x6c, 0x50, 0x61, 0x74, + 0x68, 0x12, 0x30, 0x0a, 0x12, 0x6e, 0x6f, 0x74, 0x5f, 0x66, 0x6f, 0x75, 0x6e, 0x64, 0x5f, 0x72, + 0x65, 0x6c, 0x5f, 0x70, 0x61, 0x74, 0x68, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x48, 0x00, 0x52, + 0x0f, 0x6e, 0x6f, 0x74, 0x46, 0x6f, 0x75, 0x6e, 0x64, 0x52, 0x65, 0x6c, 0x50, 0x61, 0x74, 0x68, + 0x88, 0x01, 0x01, 0x42, 0x15, 0x0a, 0x13, 0x5f, 0x6e, 0x6f, 0x74, 0x5f, 0x66, 0x6f, 0x75, 0x6e, + 0x64, 0x5f, 0x72, 0x65, 0x6c, 0x5f, 0x70, 0x61, 0x74, 0x68, 0x22, 0x2f, 0x0a, 0x0a, 0x41, 0x63, + 0x63, 0x65, 0x73, 0x73, 0x54, 0x79, 0x70, 0x65, 0x12, 0x0b, 0x0a, 0x07, 0x50, 0x52, 0x49, 0x56, + 0x41, 0x54, 0x45, 0x10, 0x00, 0x12, 0x0a, 0x0a, 0x06, 0x50, 0x55, 0x42, 0x4c, 0x49, 0x43, 0x10, + 0x01, 0x12, 0x08, 0x0a, 0x04, 0x41, 0x55, 0x54, 0x48, 0x10, 0x02, 0x22, 0x20, 0x0a, 0x08, 0x50, + 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x12, 0x0b, 0x0a, 0x07, 0x52, 0x45, 0x47, 0x55, 0x4c, + 0x41, 0x52, 0x10, 0x00, 0x12, 0x07, 0x0a, 0x03, 0x52, 0x41, 0x57, 0x10, 0x01, 0x42, 0x06, 0x0a, + 0x04, 0x5f, 0x64, 0x6f, 0x63, 0x42, 0x11, 0x0a, 0x0f, 0x5f, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x5f, 0x73, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x42, 0x12, 0x0a, 0x10, 0x5f, 0x72, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x5f, 0x73, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x42, 0x0d, 0x0a, 0x0b, + 0x5f, 0x62, 0x6f, 0x64, 0x79, 0x5f, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x42, 0x13, 0x0a, 0x11, 0x5f, + 0x68, 0x61, 0x6e, 0x64, 0x73, 0x68, 0x61, 0x6b, 0x65, 0x5f, 0x73, 0x63, 0x68, 0x65, 0x6d, 0x61, + 0x42, 0x10, 0x0a, 0x0e, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x69, 0x63, 0x5f, 0x61, 0x73, 0x73, 0x65, + 0x74, 0x73, 0x22, 0xd2, 0x02, 0x0a, 0x0b, 0x41, 0x75, 0x74, 0x68, 0x48, 0x61, 0x6e, 0x64, 0x6c, + 0x65, 0x72, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x64, 0x6f, 0x63, 0x18, 0x02, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x03, 0x64, 0x6f, 0x63, 0x12, 0x19, 0x0a, 0x08, 0x70, 0x6b, 0x67, 0x5f, + 0x70, 0x61, 0x74, 0x68, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x70, 0x6b, 0x67, 0x50, + 0x61, 0x74, 0x68, 0x12, 0x19, 0x0a, 0x08, 0x70, 0x6b, 0x67, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, + 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x70, 0x6b, 0x67, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x2e, + 0x0a, 0x03, 0x6c, 0x6f, 0x63, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x65, 0x6e, + 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x70, 0x61, 0x72, 0x73, 0x65, 0x72, 0x2e, 0x73, 0x63, 0x68, 0x65, + 0x6d, 0x61, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x6f, 0x63, 0x52, 0x03, 0x6c, 0x6f, 0x63, 0x12, 0x3f, + 0x0a, 0x09, 0x61, 0x75, 0x74, 0x68, 0x5f, 0x64, 0x61, 0x74, 0x61, 0x18, 0x06, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x1d, 0x2e, 0x65, 0x6e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x70, 0x61, 0x72, 0x73, 0x65, + 0x72, 0x2e, 0x73, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x79, 0x70, 0x65, + 0x48, 0x00, 0x52, 0x08, 0x61, 0x75, 0x74, 0x68, 0x44, 0x61, 0x74, 0x61, 0x88, 0x01, 0x01, 0x12, + 0x3a, 0x0a, 0x06, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x1d, 0x2e, 0x65, 0x6e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x70, 0x61, 0x72, 0x73, 0x65, 0x72, 0x2e, + 0x73, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x79, 0x70, 0x65, 0x48, 0x01, + 0x52, 0x06, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x88, 0x01, 0x01, 0x12, 0x21, 0x0a, 0x0c, 0x73, + 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x08, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x0b, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x42, 0x0c, + 0x0a, 0x0a, 0x5f, 0x61, 0x75, 0x74, 0x68, 0x5f, 0x64, 0x61, 0x74, 0x61, 0x42, 0x09, 0x0a, 0x07, + 0x5f, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x22, 0x92, 0x02, 0x0a, 0x0a, 0x4d, 0x69, 0x64, 0x64, + 0x6c, 0x65, 0x77, 0x61, 0x72, 0x65, 0x12, 0x38, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x0b, 0x32, 0x24, 0x2e, 0x65, 0x6e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x70, 0x61, + 0x72, 0x73, 0x65, 0x72, 0x2e, 0x6d, 0x65, 0x74, 0x61, 0x2e, 0x76, 0x31, 0x2e, 0x51, 0x75, 0x61, + 0x6c, 0x69, 0x66, 0x69, 0x65, 0x64, 0x4e, 0x61, 0x6d, 0x65, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, + 0x12, 0x10, 0x0a, 0x03, 0x64, 0x6f, 0x63, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x64, + 0x6f, 0x63, 0x12, 0x2e, 0x0a, 0x03, 0x6c, 0x6f, 0x63, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x1c, 0x2e, 0x65, 0x6e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x70, 0x61, 0x72, 0x73, 0x65, 0x72, 0x2e, + 0x73, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x6f, 0x63, 0x52, 0x03, 0x6c, + 0x6f, 0x63, 0x12, 0x16, 0x0a, 0x06, 0x67, 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x18, 0x04, 0x20, 0x01, + 0x28, 0x08, 0x52, 0x06, 0x67, 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x12, 0x26, 0x0a, 0x0c, 0x73, 0x65, + 0x72, 0x76, 0x69, 0x63, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, + 0x48, 0x00, 0x52, 0x0b, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x88, + 0x01, 0x01, 0x12, 0x37, 0x0a, 0x06, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x18, 0x06, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x65, 0x6e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x70, 0x61, 0x72, 0x73, 0x65, 0x72, 0x2e, 0x6d, 0x65, 0x74, 0x61, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x6c, 0x65, 0x63, - 0x74, 0x6f, 0x72, 0x52, 0x04, 0x74, 0x61, 0x67, 0x73, 0x12, 0x1c, 0x0a, 0x09, 0x73, 0x65, 0x6e, - 0x73, 0x69, 0x74, 0x69, 0x76, 0x65, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x08, 0x52, 0x09, 0x73, 0x65, - 0x6e, 0x73, 0x69, 0x74, 0x69, 0x76, 0x65, 0x12, 0x33, 0x0a, 0x15, 0x61, 0x6c, 0x6c, 0x6f, 0x77, - 0x5f, 0x75, 0x6e, 0x61, 0x75, 0x74, 0x68, 0x65, 0x6e, 0x74, 0x69, 0x63, 0x61, 0x74, 0x65, 0x64, - 0x18, 0x0d, 0x20, 0x01, 0x28, 0x08, 0x52, 0x14, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x55, 0x6e, 0x61, - 0x75, 0x74, 0x68, 0x65, 0x6e, 0x74, 0x69, 0x63, 0x61, 0x74, 0x65, 0x64, 0x12, 0x3e, 0x0a, 0x06, - 0x65, 0x78, 0x70, 0x6f, 0x73, 0x65, 0x18, 0x0e, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x26, 0x2e, 0x65, - 0x6e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x70, 0x61, 0x72, 0x73, 0x65, 0x72, 0x2e, 0x6d, 0x65, 0x74, - 0x61, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x50, 0x43, 0x2e, 0x45, 0x78, 0x70, 0x6f, 0x73, 0x65, 0x45, - 0x6e, 0x74, 0x72, 0x79, 0x52, 0x06, 0x65, 0x78, 0x70, 0x6f, 0x73, 0x65, 0x12, 0x22, 0x0a, 0x0a, - 0x62, 0x6f, 0x64, 0x79, 0x5f, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x18, 0x0f, 0x20, 0x01, 0x28, 0x04, - 0x48, 0x03, 0x52, 0x09, 0x62, 0x6f, 0x64, 0x79, 0x4c, 0x69, 0x6d, 0x69, 0x74, 0x88, 0x01, 0x01, - 0x12, 0x2b, 0x0a, 0x11, 0x73, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x69, 0x6e, 0x67, 0x5f, 0x72, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x18, 0x10, 0x20, 0x01, 0x28, 0x08, 0x52, 0x10, 0x73, 0x74, 0x72, - 0x65, 0x61, 0x6d, 0x69, 0x6e, 0x67, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x2d, 0x0a, - 0x12, 0x73, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x69, 0x6e, 0x67, 0x5f, 0x72, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x18, 0x11, 0x20, 0x01, 0x28, 0x08, 0x52, 0x11, 0x73, 0x74, 0x72, 0x65, 0x61, - 0x6d, 0x69, 0x6e, 0x67, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x4d, 0x0a, 0x10, - 0x68, 0x61, 0x6e, 0x64, 0x73, 0x68, 0x61, 0x6b, 0x65, 0x5f, 0x73, 0x63, 0x68, 0x65, 0x6d, 0x61, - 0x18, 0x12, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x65, 0x6e, 0x63, 0x6f, 0x72, 0x65, 0x2e, - 0x70, 0x61, 0x72, 0x73, 0x65, 0x72, 0x2e, 0x73, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x2e, 0x76, 0x31, - 0x2e, 0x54, 0x79, 0x70, 0x65, 0x48, 0x04, 0x52, 0x0f, 0x68, 0x61, 0x6e, 0x64, 0x73, 0x68, 0x61, - 0x6b, 0x65, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x88, 0x01, 0x01, 0x12, 0x51, 0x0a, 0x0d, 0x73, - 0x74, 0x61, 0x74, 0x69, 0x63, 0x5f, 0x61, 0x73, 0x73, 0x65, 0x74, 0x73, 0x18, 0x13, 0x20, 0x01, - 0x28, 0x0b, 0x32, 0x27, 0x2e, 0x65, 0x6e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x70, 0x61, 0x72, 0x73, - 0x65, 0x72, 0x2e, 0x6d, 0x65, 0x74, 0x61, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x50, 0x43, 0x2e, 0x53, - 0x74, 0x61, 0x74, 0x69, 0x63, 0x41, 0x73, 0x73, 0x65, 0x74, 0x73, 0x48, 0x05, 0x52, 0x0c, 0x73, - 0x74, 0x61, 0x74, 0x69, 0x63, 0x41, 0x73, 0x73, 0x65, 0x74, 0x73, 0x88, 0x01, 0x01, 0x1a, 0x63, - 0x0a, 0x0b, 0x45, 0x78, 0x70, 0x6f, 0x73, 0x65, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, - 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, - 0x3e, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x28, - 0x2e, 0x65, 0x6e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x70, 0x61, 0x72, 0x73, 0x65, 0x72, 0x2e, 0x6d, - 0x65, 0x74, 0x61, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x50, 0x43, 0x2e, 0x45, 0x78, 0x70, 0x6f, 0x73, - 0x65, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, - 0x02, 0x38, 0x01, 0x1a, 0x0f, 0x0a, 0x0d, 0x45, 0x78, 0x70, 0x6f, 0x73, 0x65, 0x4f, 0x70, 0x74, - 0x69, 0x6f, 0x6e, 0x73, 0x1a, 0x79, 0x0a, 0x0c, 0x53, 0x74, 0x61, 0x74, 0x69, 0x63, 0x41, 0x73, - 0x73, 0x65, 0x74, 0x73, 0x12, 0x20, 0x0a, 0x0c, 0x64, 0x69, 0x72, 0x5f, 0x72, 0x65, 0x6c, 0x5f, - 0x70, 0x61, 0x74, 0x68, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x64, 0x69, 0x72, 0x52, - 0x65, 0x6c, 0x50, 0x61, 0x74, 0x68, 0x12, 0x30, 0x0a, 0x12, 0x6e, 0x6f, 0x74, 0x5f, 0x66, 0x6f, - 0x75, 0x6e, 0x64, 0x5f, 0x72, 0x65, 0x6c, 0x5f, 0x70, 0x61, 0x74, 0x68, 0x18, 0x02, 0x20, 0x01, - 0x28, 0x09, 0x48, 0x00, 0x52, 0x0f, 0x6e, 0x6f, 0x74, 0x46, 0x6f, 0x75, 0x6e, 0x64, 0x52, 0x65, - 0x6c, 0x50, 0x61, 0x74, 0x68, 0x88, 0x01, 0x01, 0x42, 0x15, 0x0a, 0x13, 0x5f, 0x6e, 0x6f, 0x74, - 0x5f, 0x66, 0x6f, 0x75, 0x6e, 0x64, 0x5f, 0x72, 0x65, 0x6c, 0x5f, 0x70, 0x61, 0x74, 0x68, 0x22, - 0x2f, 0x0a, 0x0a, 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, 0x54, 0x79, 0x70, 0x65, 0x12, 0x0b, 0x0a, - 0x07, 0x50, 0x52, 0x49, 0x56, 0x41, 0x54, 0x45, 0x10, 0x00, 0x12, 0x0a, 0x0a, 0x06, 0x50, 0x55, - 0x42, 0x4c, 0x49, 0x43, 0x10, 0x01, 0x12, 0x08, 0x0a, 0x04, 0x41, 0x55, 0x54, 0x48, 0x10, 0x02, - 0x22, 0x20, 0x0a, 0x08, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x12, 0x0b, 0x0a, 0x07, - 0x52, 0x45, 0x47, 0x55, 0x4c, 0x41, 0x52, 0x10, 0x00, 0x12, 0x07, 0x0a, 0x03, 0x52, 0x41, 0x57, - 0x10, 0x01, 0x42, 0x06, 0x0a, 0x04, 0x5f, 0x64, 0x6f, 0x63, 0x42, 0x11, 0x0a, 0x0f, 0x5f, 0x72, - 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x5f, 0x73, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x42, 0x12, 0x0a, - 0x10, 0x5f, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x5f, 0x73, 0x63, 0x68, 0x65, 0x6d, - 0x61, 0x42, 0x0d, 0x0a, 0x0b, 0x5f, 0x62, 0x6f, 0x64, 0x79, 0x5f, 0x6c, 0x69, 0x6d, 0x69, 0x74, - 0x42, 0x13, 0x0a, 0x11, 0x5f, 0x68, 0x61, 0x6e, 0x64, 0x73, 0x68, 0x61, 0x6b, 0x65, 0x5f, 0x73, - 0x63, 0x68, 0x65, 0x6d, 0x61, 0x42, 0x10, 0x0a, 0x0e, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x69, 0x63, - 0x5f, 0x61, 0x73, 0x73, 0x65, 0x74, 0x73, 0x22, 0xd2, 0x02, 0x0a, 0x0b, 0x41, 0x75, 0x74, 0x68, - 0x48, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x72, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x64, - 0x6f, 0x63, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x64, 0x6f, 0x63, 0x12, 0x19, 0x0a, - 0x08, 0x70, 0x6b, 0x67, 0x5f, 0x70, 0x61, 0x74, 0x68, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x07, 0x70, 0x6b, 0x67, 0x50, 0x61, 0x74, 0x68, 0x12, 0x19, 0x0a, 0x08, 0x70, 0x6b, 0x67, 0x5f, - 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x70, 0x6b, 0x67, 0x4e, - 0x61, 0x6d, 0x65, 0x12, 0x2e, 0x0a, 0x03, 0x6c, 0x6f, 0x63, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, - 0x32, 0x1c, 0x2e, 0x65, 0x6e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x70, 0x61, 0x72, 0x73, 0x65, 0x72, - 0x2e, 0x73, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x6f, 0x63, 0x52, 0x03, - 0x6c, 0x6f, 0x63, 0x12, 0x3f, 0x0a, 0x09, 0x61, 0x75, 0x74, 0x68, 0x5f, 0x64, 0x61, 0x74, 0x61, - 0x18, 0x06, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x65, 0x6e, 0x63, 0x6f, 0x72, 0x65, 0x2e, - 0x70, 0x61, 0x72, 0x73, 0x65, 0x72, 0x2e, 0x73, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x2e, 0x76, 0x31, - 0x2e, 0x54, 0x79, 0x70, 0x65, 0x48, 0x00, 0x52, 0x08, 0x61, 0x75, 0x74, 0x68, 0x44, 0x61, 0x74, - 0x61, 0x88, 0x01, 0x01, 0x12, 0x3a, 0x0a, 0x06, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x18, 0x07, - 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x65, 0x6e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x70, 0x61, - 0x72, 0x73, 0x65, 0x72, 0x2e, 0x73, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x2e, 0x76, 0x31, 0x2e, 0x54, - 0x79, 0x70, 0x65, 0x48, 0x01, 0x52, 0x06, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x88, 0x01, 0x01, - 0x12, 0x21, 0x0a, 0x0c, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, - 0x18, 0x08, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x4e, - 0x61, 0x6d, 0x65, 0x42, 0x0c, 0x0a, 0x0a, 0x5f, 0x61, 0x75, 0x74, 0x68, 0x5f, 0x64, 0x61, 0x74, - 0x61, 0x42, 0x09, 0x0a, 0x07, 0x5f, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x22, 0x92, 0x02, 0x0a, - 0x0a, 0x4d, 0x69, 0x64, 0x64, 0x6c, 0x65, 0x77, 0x61, 0x72, 0x65, 0x12, 0x38, 0x0a, 0x04, 0x6e, - 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x24, 0x2e, 0x65, 0x6e, 0x63, 0x6f, - 0x72, 0x65, 0x2e, 0x70, 0x61, 0x72, 0x73, 0x65, 0x72, 0x2e, 0x6d, 0x65, 0x74, 0x61, 0x2e, 0x76, - 0x31, 0x2e, 0x51, 0x75, 0x61, 0x6c, 0x69, 0x66, 0x69, 0x65, 0x64, 0x4e, 0x61, 0x6d, 0x65, 0x52, - 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x64, 0x6f, 0x63, 0x18, 0x02, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x03, 0x64, 0x6f, 0x63, 0x12, 0x2e, 0x0a, 0x03, 0x6c, 0x6f, 0x63, 0x18, 0x03, - 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x65, 0x6e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x70, 0x61, - 0x72, 0x73, 0x65, 0x72, 0x2e, 0x73, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x2e, 0x76, 0x31, 0x2e, 0x4c, - 0x6f, 0x63, 0x52, 0x03, 0x6c, 0x6f, 0x63, 0x12, 0x16, 0x0a, 0x06, 0x67, 0x6c, 0x6f, 0x62, 0x61, - 0x6c, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x06, 0x67, 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x12, - 0x26, 0x0a, 0x0c, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, - 0x05, 0x20, 0x01, 0x28, 0x09, 0x48, 0x00, 0x52, 0x0b, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, - 0x4e, 0x61, 0x6d, 0x65, 0x88, 0x01, 0x01, 0x12, 0x37, 0x0a, 0x06, 0x74, 0x61, 0x72, 0x67, 0x65, - 0x74, 0x18, 0x06, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x65, 0x6e, 0x63, 0x6f, 0x72, 0x65, - 0x2e, 0x70, 0x61, 0x72, 0x73, 0x65, 0x72, 0x2e, 0x6d, 0x65, 0x74, 0x61, 0x2e, 0x76, 0x31, 0x2e, - 0x53, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x52, 0x06, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, - 0x42, 0x0f, 0x0a, 0x0d, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x5f, 0x6e, 0x61, 0x6d, - 0x65, 0x22, 0xa0, 0x08, 0x0a, 0x09, 0x54, 0x72, 0x61, 0x63, 0x65, 0x4e, 0x6f, 0x64, 0x65, 0x12, - 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x02, 0x69, 0x64, 0x12, - 0x1a, 0x0a, 0x08, 0x66, 0x69, 0x6c, 0x65, 0x70, 0x61, 0x74, 0x68, 0x18, 0x02, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x08, 0x66, 0x69, 0x6c, 0x65, 0x70, 0x61, 0x74, 0x68, 0x12, 0x1b, 0x0a, 0x09, 0x73, - 0x74, 0x61, 0x72, 0x74, 0x5f, 0x70, 0x6f, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x05, 0x52, 0x08, - 0x73, 0x74, 0x61, 0x72, 0x74, 0x50, 0x6f, 0x73, 0x12, 0x17, 0x0a, 0x07, 0x65, 0x6e, 0x64, 0x5f, - 0x70, 0x6f, 0x73, 0x18, 0x05, 0x20, 0x01, 0x28, 0x05, 0x52, 0x06, 0x65, 0x6e, 0x64, 0x50, 0x6f, - 0x73, 0x12, 0x24, 0x0a, 0x0e, 0x73, 0x72, 0x63, 0x5f, 0x6c, 0x69, 0x6e, 0x65, 0x5f, 0x73, 0x74, - 0x61, 0x72, 0x74, 0x18, 0x06, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0c, 0x73, 0x72, 0x63, 0x4c, 0x69, - 0x6e, 0x65, 0x53, 0x74, 0x61, 0x72, 0x74, 0x12, 0x20, 0x0a, 0x0c, 0x73, 0x72, 0x63, 0x5f, 0x6c, - 0x69, 0x6e, 0x65, 0x5f, 0x65, 0x6e, 0x64, 0x18, 0x07, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0a, 0x73, - 0x72, 0x63, 0x4c, 0x69, 0x6e, 0x65, 0x45, 0x6e, 0x64, 0x12, 0x22, 0x0a, 0x0d, 0x73, 0x72, 0x63, - 0x5f, 0x63, 0x6f, 0x6c, 0x5f, 0x73, 0x74, 0x61, 0x72, 0x74, 0x18, 0x08, 0x20, 0x01, 0x28, 0x05, - 0x52, 0x0b, 0x73, 0x72, 0x63, 0x43, 0x6f, 0x6c, 0x53, 0x74, 0x61, 0x72, 0x74, 0x12, 0x1e, 0x0a, - 0x0b, 0x73, 0x72, 0x63, 0x5f, 0x63, 0x6f, 0x6c, 0x5f, 0x65, 0x6e, 0x64, 0x18, 0x09, 0x20, 0x01, - 0x28, 0x05, 0x52, 0x09, 0x73, 0x72, 0x63, 0x43, 0x6f, 0x6c, 0x45, 0x6e, 0x64, 0x12, 0x3c, 0x0a, - 0x07, 0x72, 0x70, 0x63, 0x5f, 0x64, 0x65, 0x66, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x21, - 0x2e, 0x65, 0x6e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x70, 0x61, 0x72, 0x73, 0x65, 0x72, 0x2e, 0x6d, - 0x65, 0x74, 0x61, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x50, 0x43, 0x44, 0x65, 0x66, 0x4e, 0x6f, 0x64, - 0x65, 0x48, 0x00, 0x52, 0x06, 0x72, 0x70, 0x63, 0x44, 0x65, 0x66, 0x12, 0x3f, 0x0a, 0x08, 0x72, - 0x70, 0x63, 0x5f, 0x63, 0x61, 0x6c, 0x6c, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x22, 0x2e, - 0x65, 0x6e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x70, 0x61, 0x72, 0x73, 0x65, 0x72, 0x2e, 0x6d, 0x65, - 0x74, 0x61, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x50, 0x43, 0x43, 0x61, 0x6c, 0x6c, 0x4e, 0x6f, 0x64, - 0x65, 0x48, 0x00, 0x52, 0x07, 0x72, 0x70, 0x63, 0x43, 0x61, 0x6c, 0x6c, 0x12, 0x48, 0x0a, 0x0b, - 0x73, 0x74, 0x61, 0x74, 0x69, 0x63, 0x5f, 0x63, 0x61, 0x6c, 0x6c, 0x18, 0x0c, 0x20, 0x01, 0x28, - 0x0b, 0x32, 0x25, 0x2e, 0x65, 0x6e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x70, 0x61, 0x72, 0x73, 0x65, - 0x72, 0x2e, 0x6d, 0x65, 0x74, 0x61, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x69, 0x63, - 0x43, 0x61, 0x6c, 0x6c, 0x4e, 0x6f, 0x64, 0x65, 0x48, 0x00, 0x52, 0x0a, 0x73, 0x74, 0x61, 0x74, - 0x69, 0x63, 0x43, 0x61, 0x6c, 0x6c, 0x12, 0x55, 0x0a, 0x10, 0x61, 0x75, 0x74, 0x68, 0x5f, 0x68, - 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x72, 0x5f, 0x64, 0x65, 0x66, 0x18, 0x0d, 0x20, 0x01, 0x28, 0x0b, - 0x32, 0x29, 0x2e, 0x65, 0x6e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x70, 0x61, 0x72, 0x73, 0x65, 0x72, - 0x2e, 0x6d, 0x65, 0x74, 0x61, 0x2e, 0x76, 0x31, 0x2e, 0x41, 0x75, 0x74, 0x68, 0x48, 0x61, 0x6e, - 0x64, 0x6c, 0x65, 0x72, 0x44, 0x65, 0x66, 0x4e, 0x6f, 0x64, 0x65, 0x48, 0x00, 0x52, 0x0e, 0x61, - 0x75, 0x74, 0x68, 0x48, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x72, 0x44, 0x65, 0x66, 0x12, 0x55, 0x0a, - 0x10, 0x70, 0x75, 0x62, 0x73, 0x75, 0x62, 0x5f, 0x74, 0x6f, 0x70, 0x69, 0x63, 0x5f, 0x64, 0x65, - 0x66, 0x18, 0x0e, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x29, 0x2e, 0x65, 0x6e, 0x63, 0x6f, 0x72, 0x65, - 0x2e, 0x70, 0x61, 0x72, 0x73, 0x65, 0x72, 0x2e, 0x6d, 0x65, 0x74, 0x61, 0x2e, 0x76, 0x31, 0x2e, - 0x50, 0x75, 0x62, 0x53, 0x75, 0x62, 0x54, 0x6f, 0x70, 0x69, 0x63, 0x44, 0x65, 0x66, 0x4e, 0x6f, - 0x64, 0x65, 0x48, 0x00, 0x52, 0x0e, 0x70, 0x75, 0x62, 0x73, 0x75, 0x62, 0x54, 0x6f, 0x70, 0x69, - 0x63, 0x44, 0x65, 0x66, 0x12, 0x51, 0x0a, 0x0e, 0x70, 0x75, 0x62, 0x73, 0x75, 0x62, 0x5f, 0x70, - 0x75, 0x62, 0x6c, 0x69, 0x73, 0x68, 0x18, 0x0f, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x28, 0x2e, 0x65, - 0x6e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x70, 0x61, 0x72, 0x73, 0x65, 0x72, 0x2e, 0x6d, 0x65, 0x74, - 0x61, 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x75, 0x62, 0x53, 0x75, 0x62, 0x50, 0x75, 0x62, 0x6c, 0x69, - 0x73, 0x68, 0x4e, 0x6f, 0x64, 0x65, 0x48, 0x00, 0x52, 0x0d, 0x70, 0x75, 0x62, 0x73, 0x75, 0x62, - 0x50, 0x75, 0x62, 0x6c, 0x69, 0x73, 0x68, 0x12, 0x5a, 0x0a, 0x11, 0x70, 0x75, 0x62, 0x73, 0x75, - 0x62, 0x5f, 0x73, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x62, 0x65, 0x72, 0x18, 0x10, 0x20, 0x01, - 0x28, 0x0b, 0x32, 0x2b, 0x2e, 0x65, 0x6e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x70, 0x61, 0x72, 0x73, - 0x65, 0x72, 0x2e, 0x6d, 0x65, 0x74, 0x61, 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x75, 0x62, 0x53, 0x75, - 0x62, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x62, 0x65, 0x72, 0x4e, 0x6f, 0x64, 0x65, 0x48, - 0x00, 0x52, 0x10, 0x70, 0x75, 0x62, 0x73, 0x75, 0x62, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, - 0x62, 0x65, 0x72, 0x12, 0x4b, 0x0a, 0x0c, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x5f, 0x69, - 0x6e, 0x69, 0x74, 0x18, 0x11, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x26, 0x2e, 0x65, 0x6e, 0x63, 0x6f, + 0x74, 0x6f, 0x72, 0x52, 0x06, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x42, 0x0f, 0x0a, 0x0d, 0x5f, + 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x22, 0xa0, 0x08, 0x0a, + 0x09, 0x54, 0x72, 0x61, 0x63, 0x65, 0x4e, 0x6f, 0x64, 0x65, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x02, 0x69, 0x64, 0x12, 0x1a, 0x0a, 0x08, 0x66, 0x69, + 0x6c, 0x65, 0x70, 0x61, 0x74, 0x68, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x66, 0x69, + 0x6c, 0x65, 0x70, 0x61, 0x74, 0x68, 0x12, 0x1b, 0x0a, 0x09, 0x73, 0x74, 0x61, 0x72, 0x74, 0x5f, + 0x70, 0x6f, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x05, 0x52, 0x08, 0x73, 0x74, 0x61, 0x72, 0x74, + 0x50, 0x6f, 0x73, 0x12, 0x17, 0x0a, 0x07, 0x65, 0x6e, 0x64, 0x5f, 0x70, 0x6f, 0x73, 0x18, 0x05, + 0x20, 0x01, 0x28, 0x05, 0x52, 0x06, 0x65, 0x6e, 0x64, 0x50, 0x6f, 0x73, 0x12, 0x24, 0x0a, 0x0e, + 0x73, 0x72, 0x63, 0x5f, 0x6c, 0x69, 0x6e, 0x65, 0x5f, 0x73, 0x74, 0x61, 0x72, 0x74, 0x18, 0x06, + 0x20, 0x01, 0x28, 0x05, 0x52, 0x0c, 0x73, 0x72, 0x63, 0x4c, 0x69, 0x6e, 0x65, 0x53, 0x74, 0x61, + 0x72, 0x74, 0x12, 0x20, 0x0a, 0x0c, 0x73, 0x72, 0x63, 0x5f, 0x6c, 0x69, 0x6e, 0x65, 0x5f, 0x65, + 0x6e, 0x64, 0x18, 0x07, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0a, 0x73, 0x72, 0x63, 0x4c, 0x69, 0x6e, + 0x65, 0x45, 0x6e, 0x64, 0x12, 0x22, 0x0a, 0x0d, 0x73, 0x72, 0x63, 0x5f, 0x63, 0x6f, 0x6c, 0x5f, + 0x73, 0x74, 0x61, 0x72, 0x74, 0x18, 0x08, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0b, 0x73, 0x72, 0x63, + 0x43, 0x6f, 0x6c, 0x53, 0x74, 0x61, 0x72, 0x74, 0x12, 0x1e, 0x0a, 0x0b, 0x73, 0x72, 0x63, 0x5f, + 0x63, 0x6f, 0x6c, 0x5f, 0x65, 0x6e, 0x64, 0x18, 0x09, 0x20, 0x01, 0x28, 0x05, 0x52, 0x09, 0x73, + 0x72, 0x63, 0x43, 0x6f, 0x6c, 0x45, 0x6e, 0x64, 0x12, 0x3c, 0x0a, 0x07, 0x72, 0x70, 0x63, 0x5f, + 0x64, 0x65, 0x66, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x21, 0x2e, 0x65, 0x6e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x70, 0x61, 0x72, 0x73, 0x65, 0x72, 0x2e, 0x6d, 0x65, 0x74, 0x61, 0x2e, 0x76, - 0x31, 0x2e, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x49, 0x6e, 0x69, 0x74, 0x4e, 0x6f, 0x64, - 0x65, 0x48, 0x00, 0x52, 0x0b, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x49, 0x6e, 0x69, 0x74, - 0x12, 0x51, 0x0a, 0x0e, 0x6d, 0x69, 0x64, 0x64, 0x6c, 0x65, 0x77, 0x61, 0x72, 0x65, 0x5f, 0x64, - 0x65, 0x66, 0x18, 0x12, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x28, 0x2e, 0x65, 0x6e, 0x63, 0x6f, 0x72, + 0x31, 0x2e, 0x52, 0x50, 0x43, 0x44, 0x65, 0x66, 0x4e, 0x6f, 0x64, 0x65, 0x48, 0x00, 0x52, 0x06, + 0x72, 0x70, 0x63, 0x44, 0x65, 0x66, 0x12, 0x3f, 0x0a, 0x08, 0x72, 0x70, 0x63, 0x5f, 0x63, 0x61, + 0x6c, 0x6c, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x22, 0x2e, 0x65, 0x6e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x70, 0x61, 0x72, 0x73, 0x65, 0x72, 0x2e, 0x6d, 0x65, 0x74, 0x61, 0x2e, 0x76, 0x31, - 0x2e, 0x4d, 0x69, 0x64, 0x64, 0x6c, 0x65, 0x77, 0x61, 0x72, 0x65, 0x44, 0x65, 0x66, 0x4e, 0x6f, - 0x64, 0x65, 0x48, 0x00, 0x52, 0x0d, 0x6d, 0x69, 0x64, 0x64, 0x6c, 0x65, 0x77, 0x61, 0x72, 0x65, - 0x44, 0x65, 0x66, 0x12, 0x54, 0x0a, 0x0e, 0x63, 0x61, 0x63, 0x68, 0x65, 0x5f, 0x6b, 0x65, 0x79, - 0x73, 0x70, 0x61, 0x63, 0x65, 0x18, 0x13, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2b, 0x2e, 0x65, 0x6e, + 0x2e, 0x52, 0x50, 0x43, 0x43, 0x61, 0x6c, 0x6c, 0x4e, 0x6f, 0x64, 0x65, 0x48, 0x00, 0x52, 0x07, + 0x72, 0x70, 0x63, 0x43, 0x61, 0x6c, 0x6c, 0x12, 0x48, 0x0a, 0x0b, 0x73, 0x74, 0x61, 0x74, 0x69, + 0x63, 0x5f, 0x63, 0x61, 0x6c, 0x6c, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x25, 0x2e, 0x65, + 0x6e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x70, 0x61, 0x72, 0x73, 0x65, 0x72, 0x2e, 0x6d, 0x65, 0x74, + 0x61, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x69, 0x63, 0x43, 0x61, 0x6c, 0x6c, 0x4e, + 0x6f, 0x64, 0x65, 0x48, 0x00, 0x52, 0x0a, 0x73, 0x74, 0x61, 0x74, 0x69, 0x63, 0x43, 0x61, 0x6c, + 0x6c, 0x12, 0x55, 0x0a, 0x10, 0x61, 0x75, 0x74, 0x68, 0x5f, 0x68, 0x61, 0x6e, 0x64, 0x6c, 0x65, + 0x72, 0x5f, 0x64, 0x65, 0x66, 0x18, 0x0d, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x29, 0x2e, 0x65, 0x6e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x70, 0x61, 0x72, 0x73, 0x65, 0x72, 0x2e, 0x6d, 0x65, 0x74, 0x61, - 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x61, 0x63, 0x68, 0x65, 0x4b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, - 0x65, 0x44, 0x65, 0x66, 0x4e, 0x6f, 0x64, 0x65, 0x48, 0x00, 0x52, 0x0d, 0x63, 0x61, 0x63, 0x68, - 0x65, 0x4b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x42, 0x09, 0x0a, 0x07, 0x63, 0x6f, 0x6e, - 0x74, 0x65, 0x78, 0x74, 0x22, 0x64, 0x0a, 0x0a, 0x52, 0x50, 0x43, 0x44, 0x65, 0x66, 0x4e, 0x6f, - 0x64, 0x65, 0x12, 0x21, 0x0a, 0x0c, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x5f, 0x6e, 0x61, - 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, - 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x19, 0x0a, 0x08, 0x72, 0x70, 0x63, 0x5f, 0x6e, 0x61, 0x6d, - 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x72, 0x70, 0x63, 0x4e, 0x61, 0x6d, 0x65, - 0x12, 0x18, 0x0a, 0x07, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x07, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x22, 0x65, 0x0a, 0x0b, 0x52, 0x50, - 0x43, 0x43, 0x61, 0x6c, 0x6c, 0x4e, 0x6f, 0x64, 0x65, 0x12, 0x21, 0x0a, 0x0c, 0x73, 0x65, 0x72, - 0x76, 0x69, 0x63, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x0b, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x19, 0x0a, 0x08, - 0x72, 0x70, 0x63, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, - 0x72, 0x70, 0x63, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x63, 0x6f, 0x6e, 0x74, 0x65, - 0x78, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x78, - 0x74, 0x22, 0xb4, 0x01, 0x0a, 0x0e, 0x53, 0x74, 0x61, 0x74, 0x69, 0x63, 0x43, 0x61, 0x6c, 0x6c, - 0x4e, 0x6f, 0x64, 0x65, 0x12, 0x47, 0x0a, 0x07, 0x70, 0x61, 0x63, 0x6b, 0x61, 0x67, 0x65, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x2d, 0x2e, 0x65, 0x6e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x70, - 0x61, 0x72, 0x73, 0x65, 0x72, 0x2e, 0x6d, 0x65, 0x74, 0x61, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x74, - 0x61, 0x74, 0x69, 0x63, 0x43, 0x61, 0x6c, 0x6c, 0x4e, 0x6f, 0x64, 0x65, 0x2e, 0x50, 0x61, 0x63, - 0x6b, 0x61, 0x67, 0x65, 0x52, 0x07, 0x70, 0x61, 0x63, 0x6b, 0x61, 0x67, 0x65, 0x12, 0x12, 0x0a, - 0x04, 0x66, 0x75, 0x6e, 0x63, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x66, 0x75, 0x6e, - 0x63, 0x12, 0x18, 0x0a, 0x07, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x18, 0x03, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x07, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x22, 0x2b, 0x0a, 0x07, 0x50, - 0x61, 0x63, 0x6b, 0x61, 0x67, 0x65, 0x12, 0x0b, 0x0a, 0x07, 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, - 0x4e, 0x10, 0x00, 0x12, 0x09, 0x0a, 0x05, 0x53, 0x51, 0x4c, 0x44, 0x42, 0x10, 0x01, 0x12, 0x08, - 0x0a, 0x04, 0x52, 0x4c, 0x4f, 0x47, 0x10, 0x02, 0x22, 0x65, 0x0a, 0x12, 0x41, 0x75, 0x74, 0x68, - 0x48, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x72, 0x44, 0x65, 0x66, 0x4e, 0x6f, 0x64, 0x65, 0x12, 0x21, - 0x0a, 0x0c, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x4e, 0x61, 0x6d, - 0x65, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, - 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x22, - 0x4d, 0x0a, 0x12, 0x50, 0x75, 0x62, 0x53, 0x75, 0x62, 0x54, 0x6f, 0x70, 0x69, 0x63, 0x44, 0x65, - 0x66, 0x4e, 0x6f, 0x64, 0x65, 0x12, 0x1d, 0x0a, 0x0a, 0x74, 0x6f, 0x70, 0x69, 0x63, 0x5f, 0x6e, - 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x74, 0x6f, 0x70, 0x69, 0x63, - 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x18, - 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x22, 0x4c, - 0x0a, 0x11, 0x50, 0x75, 0x62, 0x53, 0x75, 0x62, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x73, 0x68, 0x4e, - 0x6f, 0x64, 0x65, 0x12, 0x1d, 0x0a, 0x0a, 0x74, 0x6f, 0x70, 0x69, 0x63, 0x5f, 0x6e, 0x61, 0x6d, - 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x74, 0x6f, 0x70, 0x69, 0x63, 0x4e, 0x61, - 0x6d, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x18, 0x02, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x07, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x22, 0x9b, 0x01, 0x0a, - 0x14, 0x50, 0x75, 0x62, 0x53, 0x75, 0x62, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x62, 0x65, - 0x72, 0x4e, 0x6f, 0x64, 0x65, 0x12, 0x1d, 0x0a, 0x0a, 0x74, 0x6f, 0x70, 0x69, 0x63, 0x5f, 0x6e, - 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x74, 0x6f, 0x70, 0x69, 0x63, - 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x27, 0x0a, 0x0f, 0x73, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x62, - 0x65, 0x72, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x73, - 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x62, 0x65, 0x72, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x21, 0x0a, - 0x0c, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x03, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x0b, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x4e, 0x61, 0x6d, 0x65, - 0x12, 0x18, 0x0a, 0x07, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x07, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x22, 0x76, 0x0a, 0x0f, 0x53, 0x65, - 0x72, 0x76, 0x69, 0x63, 0x65, 0x49, 0x6e, 0x69, 0x74, 0x4e, 0x6f, 0x64, 0x65, 0x12, 0x21, 0x0a, + 0x2e, 0x76, 0x31, 0x2e, 0x41, 0x75, 0x74, 0x68, 0x48, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x72, 0x44, + 0x65, 0x66, 0x4e, 0x6f, 0x64, 0x65, 0x48, 0x00, 0x52, 0x0e, 0x61, 0x75, 0x74, 0x68, 0x48, 0x61, + 0x6e, 0x64, 0x6c, 0x65, 0x72, 0x44, 0x65, 0x66, 0x12, 0x55, 0x0a, 0x10, 0x70, 0x75, 0x62, 0x73, + 0x75, 0x62, 0x5f, 0x74, 0x6f, 0x70, 0x69, 0x63, 0x5f, 0x64, 0x65, 0x66, 0x18, 0x0e, 0x20, 0x01, + 0x28, 0x0b, 0x32, 0x29, 0x2e, 0x65, 0x6e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x70, 0x61, 0x72, 0x73, + 0x65, 0x72, 0x2e, 0x6d, 0x65, 0x74, 0x61, 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x75, 0x62, 0x53, 0x75, + 0x62, 0x54, 0x6f, 0x70, 0x69, 0x63, 0x44, 0x65, 0x66, 0x4e, 0x6f, 0x64, 0x65, 0x48, 0x00, 0x52, + 0x0e, 0x70, 0x75, 0x62, 0x73, 0x75, 0x62, 0x54, 0x6f, 0x70, 0x69, 0x63, 0x44, 0x65, 0x66, 0x12, + 0x51, 0x0a, 0x0e, 0x70, 0x75, 0x62, 0x73, 0x75, 0x62, 0x5f, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x73, + 0x68, 0x18, 0x0f, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x28, 0x2e, 0x65, 0x6e, 0x63, 0x6f, 0x72, 0x65, + 0x2e, 0x70, 0x61, 0x72, 0x73, 0x65, 0x72, 0x2e, 0x6d, 0x65, 0x74, 0x61, 0x2e, 0x76, 0x31, 0x2e, + 0x50, 0x75, 0x62, 0x53, 0x75, 0x62, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x73, 0x68, 0x4e, 0x6f, 0x64, + 0x65, 0x48, 0x00, 0x52, 0x0d, 0x70, 0x75, 0x62, 0x73, 0x75, 0x62, 0x50, 0x75, 0x62, 0x6c, 0x69, + 0x73, 0x68, 0x12, 0x5a, 0x0a, 0x11, 0x70, 0x75, 0x62, 0x73, 0x75, 0x62, 0x5f, 0x73, 0x75, 0x62, + 0x73, 0x63, 0x72, 0x69, 0x62, 0x65, 0x72, 0x18, 0x10, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2b, 0x2e, + 0x65, 0x6e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x70, 0x61, 0x72, 0x73, 0x65, 0x72, 0x2e, 0x6d, 0x65, + 0x74, 0x61, 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x75, 0x62, 0x53, 0x75, 0x62, 0x53, 0x75, 0x62, 0x73, + 0x63, 0x72, 0x69, 0x62, 0x65, 0x72, 0x4e, 0x6f, 0x64, 0x65, 0x48, 0x00, 0x52, 0x10, 0x70, 0x75, + 0x62, 0x73, 0x75, 0x62, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x62, 0x65, 0x72, 0x12, 0x4b, + 0x0a, 0x0c, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x5f, 0x69, 0x6e, 0x69, 0x74, 0x18, 0x11, + 0x20, 0x01, 0x28, 0x0b, 0x32, 0x26, 0x2e, 0x65, 0x6e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x70, 0x61, + 0x72, 0x73, 0x65, 0x72, 0x2e, 0x6d, 0x65, 0x74, 0x61, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x72, + 0x76, 0x69, 0x63, 0x65, 0x49, 0x6e, 0x69, 0x74, 0x4e, 0x6f, 0x64, 0x65, 0x48, 0x00, 0x52, 0x0b, + 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x49, 0x6e, 0x69, 0x74, 0x12, 0x51, 0x0a, 0x0e, 0x6d, + 0x69, 0x64, 0x64, 0x6c, 0x65, 0x77, 0x61, 0x72, 0x65, 0x5f, 0x64, 0x65, 0x66, 0x18, 0x12, 0x20, + 0x01, 0x28, 0x0b, 0x32, 0x28, 0x2e, 0x65, 0x6e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x70, 0x61, 0x72, + 0x73, 0x65, 0x72, 0x2e, 0x6d, 0x65, 0x74, 0x61, 0x2e, 0x76, 0x31, 0x2e, 0x4d, 0x69, 0x64, 0x64, + 0x6c, 0x65, 0x77, 0x61, 0x72, 0x65, 0x44, 0x65, 0x66, 0x4e, 0x6f, 0x64, 0x65, 0x48, 0x00, 0x52, + 0x0d, 0x6d, 0x69, 0x64, 0x64, 0x6c, 0x65, 0x77, 0x61, 0x72, 0x65, 0x44, 0x65, 0x66, 0x12, 0x54, + 0x0a, 0x0e, 0x63, 0x61, 0x63, 0x68, 0x65, 0x5f, 0x6b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, + 0x18, 0x13, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2b, 0x2e, 0x65, 0x6e, 0x63, 0x6f, 0x72, 0x65, 0x2e, + 0x70, 0x61, 0x72, 0x73, 0x65, 0x72, 0x2e, 0x6d, 0x65, 0x74, 0x61, 0x2e, 0x76, 0x31, 0x2e, 0x43, + 0x61, 0x63, 0x68, 0x65, 0x4b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x44, 0x65, 0x66, 0x4e, + 0x6f, 0x64, 0x65, 0x48, 0x00, 0x52, 0x0d, 0x63, 0x61, 0x63, 0x68, 0x65, 0x4b, 0x65, 0x79, 0x73, + 0x70, 0x61, 0x63, 0x65, 0x42, 0x09, 0x0a, 0x07, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x22, + 0x64, 0x0a, 0x0a, 0x52, 0x50, 0x43, 0x44, 0x65, 0x66, 0x4e, 0x6f, 0x64, 0x65, 0x12, 0x21, 0x0a, 0x0c, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x4e, 0x61, 0x6d, 0x65, - 0x12, 0x26, 0x0a, 0x0f, 0x73, 0x65, 0x74, 0x75, 0x70, 0x5f, 0x66, 0x75, 0x6e, 0x63, 0x5f, 0x6e, - 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x73, 0x65, 0x74, 0x75, 0x70, - 0x46, 0x75, 0x6e, 0x63, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x63, 0x6f, 0x6e, 0x74, - 0x65, 0x78, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x63, 0x6f, 0x6e, 0x74, 0x65, - 0x78, 0x74, 0x22, 0x9c, 0x01, 0x0a, 0x11, 0x4d, 0x69, 0x64, 0x64, 0x6c, 0x65, 0x77, 0x61, 0x72, - 0x65, 0x44, 0x65, 0x66, 0x4e, 0x6f, 0x64, 0x65, 0x12, 0x20, 0x0a, 0x0c, 0x70, 0x6b, 0x67, 0x5f, - 0x72, 0x65, 0x6c, 0x5f, 0x70, 0x61, 0x74, 0x68, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, - 0x70, 0x6b, 0x67, 0x52, 0x65, 0x6c, 0x50, 0x61, 0x74, 0x68, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, - 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x18, - 0x0a, 0x07, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x07, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x12, 0x37, 0x0a, 0x06, 0x74, 0x61, 0x72, 0x67, - 0x65, 0x74, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x65, 0x6e, 0x63, 0x6f, 0x72, - 0x65, 0x2e, 0x70, 0x61, 0x72, 0x73, 0x65, 0x72, 0x2e, 0x6d, 0x65, 0x74, 0x61, 0x2e, 0x76, 0x31, - 0x2e, 0x53, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x52, 0x06, 0x74, 0x61, 0x72, 0x67, 0x65, - 0x74, 0x22, 0x90, 0x01, 0x0a, 0x14, 0x43, 0x61, 0x63, 0x68, 0x65, 0x4b, 0x65, 0x79, 0x73, 0x70, - 0x61, 0x63, 0x65, 0x44, 0x65, 0x66, 0x4e, 0x6f, 0x64, 0x65, 0x12, 0x20, 0x0a, 0x0c, 0x70, 0x6b, - 0x67, 0x5f, 0x72, 0x65, 0x6c, 0x5f, 0x70, 0x61, 0x74, 0x68, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x0a, 0x70, 0x6b, 0x67, 0x52, 0x65, 0x6c, 0x50, 0x61, 0x74, 0x68, 0x12, 0x19, 0x0a, 0x08, - 0x76, 0x61, 0x72, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, - 0x76, 0x61, 0x72, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x21, 0x0a, 0x0c, 0x63, 0x6c, 0x75, 0x73, 0x74, - 0x65, 0x72, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x63, - 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x63, 0x6f, - 0x6e, 0x74, 0x65, 0x78, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x63, 0x6f, 0x6e, - 0x74, 0x65, 0x78, 0x74, 0x22, 0xa1, 0x01, 0x0a, 0x04, 0x50, 0x61, 0x74, 0x68, 0x12, 0x3e, 0x0a, - 0x08, 0x73, 0x65, 0x67, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, - 0x22, 0x2e, 0x65, 0x6e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x70, 0x61, 0x72, 0x73, 0x65, 0x72, 0x2e, - 0x6d, 0x65, 0x74, 0x61, 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x61, 0x74, 0x68, 0x53, 0x65, 0x67, 0x6d, - 0x65, 0x6e, 0x74, 0x52, 0x08, 0x73, 0x65, 0x67, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x12, 0x34, 0x0a, - 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x20, 0x2e, 0x65, 0x6e, - 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x70, 0x61, 0x72, 0x73, 0x65, 0x72, 0x2e, 0x6d, 0x65, 0x74, 0x61, - 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x61, 0x74, 0x68, 0x2e, 0x54, 0x79, 0x70, 0x65, 0x52, 0x04, 0x74, - 0x79, 0x70, 0x65, 0x22, 0x23, 0x0a, 0x04, 0x54, 0x79, 0x70, 0x65, 0x12, 0x07, 0x0a, 0x03, 0x55, - 0x52, 0x4c, 0x10, 0x00, 0x12, 0x12, 0x0a, 0x0e, 0x43, 0x41, 0x43, 0x48, 0x45, 0x5f, 0x4b, 0x45, - 0x59, 0x53, 0x50, 0x41, 0x43, 0x45, 0x10, 0x01, 0x22, 0x92, 0x03, 0x0a, 0x0b, 0x50, 0x61, 0x74, - 0x68, 0x53, 0x65, 0x67, 0x6d, 0x65, 0x6e, 0x74, 0x12, 0x42, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x2e, 0x2e, 0x65, 0x6e, 0x63, 0x6f, 0x72, 0x65, 0x2e, - 0x70, 0x61, 0x72, 0x73, 0x65, 0x72, 0x2e, 0x6d, 0x65, 0x74, 0x61, 0x2e, 0x76, 0x31, 0x2e, 0x50, - 0x61, 0x74, 0x68, 0x53, 0x65, 0x67, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x53, 0x65, 0x67, 0x6d, 0x65, - 0x6e, 0x74, 0x54, 0x79, 0x70, 0x65, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x14, 0x0a, 0x05, - 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, - 0x75, 0x65, 0x12, 0x4b, 0x0a, 0x0a, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x5f, 0x74, 0x79, 0x70, 0x65, - 0x18, 0x03, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x2c, 0x2e, 0x65, 0x6e, 0x63, 0x6f, 0x72, 0x65, 0x2e, + 0x12, 0x19, 0x0a, 0x08, 0x72, 0x70, 0x63, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x07, 0x72, 0x70, 0x63, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x63, + 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x63, 0x6f, + 0x6e, 0x74, 0x65, 0x78, 0x74, 0x22, 0x65, 0x0a, 0x0b, 0x52, 0x50, 0x43, 0x43, 0x61, 0x6c, 0x6c, + 0x4e, 0x6f, 0x64, 0x65, 0x12, 0x21, 0x0a, 0x0c, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x5f, + 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x73, 0x65, 0x72, 0x76, + 0x69, 0x63, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x19, 0x0a, 0x08, 0x72, 0x70, 0x63, 0x5f, 0x6e, + 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x72, 0x70, 0x63, 0x4e, 0x61, + 0x6d, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x18, 0x03, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x07, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x22, 0xb4, 0x01, 0x0a, + 0x0e, 0x53, 0x74, 0x61, 0x74, 0x69, 0x63, 0x43, 0x61, 0x6c, 0x6c, 0x4e, 0x6f, 0x64, 0x65, 0x12, + 0x47, 0x0a, 0x07, 0x70, 0x61, 0x63, 0x6b, 0x61, 0x67, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, + 0x32, 0x2d, 0x2e, 0x65, 0x6e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x70, 0x61, 0x72, 0x73, 0x65, 0x72, + 0x2e, 0x6d, 0x65, 0x74, 0x61, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x69, 0x63, 0x43, + 0x61, 0x6c, 0x6c, 0x4e, 0x6f, 0x64, 0x65, 0x2e, 0x50, 0x61, 0x63, 0x6b, 0x61, 0x67, 0x65, 0x52, + 0x07, 0x70, 0x61, 0x63, 0x6b, 0x61, 0x67, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x66, 0x75, 0x6e, 0x63, + 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x66, 0x75, 0x6e, 0x63, 0x12, 0x18, 0x0a, 0x07, + 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x63, + 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x22, 0x2b, 0x0a, 0x07, 0x50, 0x61, 0x63, 0x6b, 0x61, 0x67, + 0x65, 0x12, 0x0b, 0x0a, 0x07, 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x10, 0x00, 0x12, 0x09, + 0x0a, 0x05, 0x53, 0x51, 0x4c, 0x44, 0x42, 0x10, 0x01, 0x12, 0x08, 0x0a, 0x04, 0x52, 0x4c, 0x4f, + 0x47, 0x10, 0x02, 0x22, 0x65, 0x0a, 0x12, 0x41, 0x75, 0x74, 0x68, 0x48, 0x61, 0x6e, 0x64, 0x6c, + 0x65, 0x72, 0x44, 0x65, 0x66, 0x4e, 0x6f, 0x64, 0x65, 0x12, 0x21, 0x0a, 0x0c, 0x73, 0x65, 0x72, + 0x76, 0x69, 0x63, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x0b, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x12, 0x0a, 0x04, + 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, + 0x12, 0x18, 0x0a, 0x07, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x07, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x22, 0x4d, 0x0a, 0x12, 0x50, 0x75, + 0x62, 0x53, 0x75, 0x62, 0x54, 0x6f, 0x70, 0x69, 0x63, 0x44, 0x65, 0x66, 0x4e, 0x6f, 0x64, 0x65, + 0x12, 0x1d, 0x0a, 0x0a, 0x74, 0x6f, 0x70, 0x69, 0x63, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x74, 0x6f, 0x70, 0x69, 0x63, 0x4e, 0x61, 0x6d, 0x65, 0x12, + 0x18, 0x0a, 0x07, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x07, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x22, 0x4c, 0x0a, 0x11, 0x50, 0x75, 0x62, + 0x53, 0x75, 0x62, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x73, 0x68, 0x4e, 0x6f, 0x64, 0x65, 0x12, 0x1d, + 0x0a, 0x0a, 0x74, 0x6f, 0x70, 0x69, 0x63, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x09, 0x74, 0x6f, 0x70, 0x69, 0x63, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x18, 0x0a, + 0x07, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, + 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x22, 0x9b, 0x01, 0x0a, 0x14, 0x50, 0x75, 0x62, 0x53, + 0x75, 0x62, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x62, 0x65, 0x72, 0x4e, 0x6f, 0x64, 0x65, + 0x12, 0x1d, 0x0a, 0x0a, 0x74, 0x6f, 0x70, 0x69, 0x63, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x74, 0x6f, 0x70, 0x69, 0x63, 0x4e, 0x61, 0x6d, 0x65, 0x12, + 0x27, 0x0a, 0x0f, 0x73, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x62, 0x65, 0x72, 0x5f, 0x6e, 0x61, + 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x73, 0x75, 0x62, 0x73, 0x63, 0x72, + 0x69, 0x62, 0x65, 0x72, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x21, 0x0a, 0x0c, 0x73, 0x65, 0x72, 0x76, + 0x69, 0x63, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, + 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x63, + 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x63, 0x6f, + 0x6e, 0x74, 0x65, 0x78, 0x74, 0x22, 0x76, 0x0a, 0x0f, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, + 0x49, 0x6e, 0x69, 0x74, 0x4e, 0x6f, 0x64, 0x65, 0x12, 0x21, 0x0a, 0x0c, 0x73, 0x65, 0x72, 0x76, + 0x69, 0x63, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, + 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x26, 0x0a, 0x0f, 0x73, + 0x65, 0x74, 0x75, 0x70, 0x5f, 0x66, 0x75, 0x6e, 0x63, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x73, 0x65, 0x74, 0x75, 0x70, 0x46, 0x75, 0x6e, 0x63, 0x4e, + 0x61, 0x6d, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x18, 0x03, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x22, 0x9c, 0x01, + 0x0a, 0x11, 0x4d, 0x69, 0x64, 0x64, 0x6c, 0x65, 0x77, 0x61, 0x72, 0x65, 0x44, 0x65, 0x66, 0x4e, + 0x6f, 0x64, 0x65, 0x12, 0x20, 0x0a, 0x0c, 0x70, 0x6b, 0x67, 0x5f, 0x72, 0x65, 0x6c, 0x5f, 0x70, + 0x61, 0x74, 0x68, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x70, 0x6b, 0x67, 0x52, 0x65, + 0x6c, 0x50, 0x61, 0x74, 0x68, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x63, 0x6f, 0x6e, + 0x74, 0x65, 0x78, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x63, 0x6f, 0x6e, 0x74, + 0x65, 0x78, 0x74, 0x12, 0x37, 0x0a, 0x06, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x18, 0x04, 0x20, + 0x03, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x65, 0x6e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x70, 0x61, 0x72, + 0x73, 0x65, 0x72, 0x2e, 0x6d, 0x65, 0x74, 0x61, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x6c, 0x65, + 0x63, 0x74, 0x6f, 0x72, 0x52, 0x06, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x22, 0x90, 0x01, 0x0a, + 0x14, 0x43, 0x61, 0x63, 0x68, 0x65, 0x4b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x44, 0x65, + 0x66, 0x4e, 0x6f, 0x64, 0x65, 0x12, 0x20, 0x0a, 0x0c, 0x70, 0x6b, 0x67, 0x5f, 0x72, 0x65, 0x6c, + 0x5f, 0x70, 0x61, 0x74, 0x68, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x70, 0x6b, 0x67, + 0x52, 0x65, 0x6c, 0x50, 0x61, 0x74, 0x68, 0x12, 0x19, 0x0a, 0x08, 0x76, 0x61, 0x72, 0x5f, 0x6e, + 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x76, 0x61, 0x72, 0x4e, 0x61, + 0x6d, 0x65, 0x12, 0x21, 0x0a, 0x0c, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x5f, 0x6e, 0x61, + 0x6d, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, + 0x72, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, + 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x22, + 0xa1, 0x01, 0x0a, 0x04, 0x50, 0x61, 0x74, 0x68, 0x12, 0x3e, 0x0a, 0x08, 0x73, 0x65, 0x67, 0x6d, + 0x65, 0x6e, 0x74, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x22, 0x2e, 0x65, 0x6e, 0x63, + 0x6f, 0x72, 0x65, 0x2e, 0x70, 0x61, 0x72, 0x73, 0x65, 0x72, 0x2e, 0x6d, 0x65, 0x74, 0x61, 0x2e, + 0x76, 0x31, 0x2e, 0x50, 0x61, 0x74, 0x68, 0x53, 0x65, 0x67, 0x6d, 0x65, 0x6e, 0x74, 0x52, 0x08, + 0x73, 0x65, 0x67, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x12, 0x34, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, + 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x20, 0x2e, 0x65, 0x6e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x70, 0x61, 0x72, 0x73, 0x65, 0x72, 0x2e, 0x6d, 0x65, 0x74, 0x61, 0x2e, 0x76, 0x31, 0x2e, 0x50, - 0x61, 0x74, 0x68, 0x53, 0x65, 0x67, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x50, 0x61, 0x72, 0x61, 0x6d, - 0x54, 0x79, 0x70, 0x65, 0x52, 0x09, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x54, 0x79, 0x70, 0x65, 0x22, - 0x41, 0x0a, 0x0b, 0x53, 0x65, 0x67, 0x6d, 0x65, 0x6e, 0x74, 0x54, 0x79, 0x70, 0x65, 0x12, 0x0b, - 0x0a, 0x07, 0x4c, 0x49, 0x54, 0x45, 0x52, 0x41, 0x4c, 0x10, 0x00, 0x12, 0x09, 0x0a, 0x05, 0x50, - 0x41, 0x52, 0x41, 0x4d, 0x10, 0x01, 0x12, 0x0c, 0x0a, 0x08, 0x57, 0x49, 0x4c, 0x44, 0x43, 0x41, - 0x52, 0x44, 0x10, 0x02, 0x12, 0x0c, 0x0a, 0x08, 0x46, 0x41, 0x4c, 0x4c, 0x42, 0x41, 0x43, 0x4b, - 0x10, 0x03, 0x22, 0x98, 0x01, 0x0a, 0x09, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x54, 0x79, 0x70, 0x65, - 0x12, 0x0a, 0x0a, 0x06, 0x53, 0x54, 0x52, 0x49, 0x4e, 0x47, 0x10, 0x00, 0x12, 0x08, 0x0a, 0x04, - 0x42, 0x4f, 0x4f, 0x4c, 0x10, 0x01, 0x12, 0x08, 0x0a, 0x04, 0x49, 0x4e, 0x54, 0x38, 0x10, 0x02, - 0x12, 0x09, 0x0a, 0x05, 0x49, 0x4e, 0x54, 0x31, 0x36, 0x10, 0x03, 0x12, 0x09, 0x0a, 0x05, 0x49, - 0x4e, 0x54, 0x33, 0x32, 0x10, 0x04, 0x12, 0x09, 0x0a, 0x05, 0x49, 0x4e, 0x54, 0x36, 0x34, 0x10, - 0x05, 0x12, 0x07, 0x0a, 0x03, 0x49, 0x4e, 0x54, 0x10, 0x06, 0x12, 0x09, 0x0a, 0x05, 0x55, 0x49, - 0x4e, 0x54, 0x38, 0x10, 0x07, 0x12, 0x0a, 0x0a, 0x06, 0x55, 0x49, 0x4e, 0x54, 0x31, 0x36, 0x10, - 0x08, 0x12, 0x0a, 0x0a, 0x06, 0x55, 0x49, 0x4e, 0x54, 0x33, 0x32, 0x10, 0x09, 0x12, 0x0a, 0x0a, - 0x06, 0x55, 0x49, 0x4e, 0x54, 0x36, 0x34, 0x10, 0x0a, 0x12, 0x08, 0x0a, 0x04, 0x55, 0x49, 0x4e, - 0x54, 0x10, 0x0b, 0x12, 0x08, 0x0a, 0x04, 0x55, 0x55, 0x49, 0x44, 0x10, 0x0c, 0x22, 0x8e, 0x02, - 0x0a, 0x07, 0x47, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x12, 0x1f, 0x0a, 0x0b, 0x65, 0x6e, 0x63, - 0x6f, 0x72, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, - 0x65, 0x6e, 0x63, 0x6f, 0x72, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x48, 0x0a, 0x08, 0x65, 0x78, - 0x70, 0x6c, 0x69, 0x63, 0x69, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x27, 0x2e, 0x65, - 0x6e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x70, 0x61, 0x72, 0x73, 0x65, 0x72, 0x2e, 0x6d, 0x65, 0x74, - 0x61, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x2e, 0x45, 0x78, 0x70, - 0x6c, 0x69, 0x63, 0x69, 0x74, 0x48, 0x00, 0x52, 0x08, 0x65, 0x78, 0x70, 0x6c, 0x69, 0x63, 0x69, - 0x74, 0x88, 0x01, 0x01, 0x1a, 0x8a, 0x01, 0x0a, 0x08, 0x45, 0x78, 0x70, 0x6c, 0x69, 0x63, 0x69, - 0x74, 0x12, 0x21, 0x0a, 0x0c, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x5f, 0x6e, 0x61, 0x6d, - 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, - 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x4a, 0x0a, 0x0c, 0x61, 0x75, 0x74, 0x68, 0x5f, 0x68, 0x61, 0x6e, - 0x64, 0x6c, 0x65, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x22, 0x2e, 0x65, 0x6e, 0x63, + 0x61, 0x74, 0x68, 0x2e, 0x54, 0x79, 0x70, 0x65, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x22, 0x23, + 0x0a, 0x04, 0x54, 0x79, 0x70, 0x65, 0x12, 0x07, 0x0a, 0x03, 0x55, 0x52, 0x4c, 0x10, 0x00, 0x12, + 0x12, 0x0a, 0x0e, 0x43, 0x41, 0x43, 0x48, 0x45, 0x5f, 0x4b, 0x45, 0x59, 0x53, 0x50, 0x41, 0x43, + 0x45, 0x10, 0x01, 0x22, 0x92, 0x03, 0x0a, 0x0b, 0x50, 0x61, 0x74, 0x68, 0x53, 0x65, 0x67, 0x6d, + 0x65, 0x6e, 0x74, 0x12, 0x42, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x0e, 0x32, 0x2e, 0x2e, 0x65, 0x6e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x70, 0x61, 0x72, 0x73, 0x65, + 0x72, 0x2e, 0x6d, 0x65, 0x74, 0x61, 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x61, 0x74, 0x68, 0x53, 0x65, + 0x67, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x53, 0x65, 0x67, 0x6d, 0x65, 0x6e, 0x74, 0x54, 0x79, 0x70, + 0x65, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, + 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x4b, 0x0a, + 0x0a, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, + 0x0e, 0x32, 0x2c, 0x2e, 0x65, 0x6e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x70, 0x61, 0x72, 0x73, 0x65, + 0x72, 0x2e, 0x6d, 0x65, 0x74, 0x61, 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x61, 0x74, 0x68, 0x53, 0x65, + 0x67, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x54, 0x79, 0x70, 0x65, 0x52, + 0x09, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x54, 0x79, 0x70, 0x65, 0x22, 0x41, 0x0a, 0x0b, 0x53, 0x65, + 0x67, 0x6d, 0x65, 0x6e, 0x74, 0x54, 0x79, 0x70, 0x65, 0x12, 0x0b, 0x0a, 0x07, 0x4c, 0x49, 0x54, + 0x45, 0x52, 0x41, 0x4c, 0x10, 0x00, 0x12, 0x09, 0x0a, 0x05, 0x50, 0x41, 0x52, 0x41, 0x4d, 0x10, + 0x01, 0x12, 0x0c, 0x0a, 0x08, 0x57, 0x49, 0x4c, 0x44, 0x43, 0x41, 0x52, 0x44, 0x10, 0x02, 0x12, + 0x0c, 0x0a, 0x08, 0x46, 0x41, 0x4c, 0x4c, 0x42, 0x41, 0x43, 0x4b, 0x10, 0x03, 0x22, 0x98, 0x01, + 0x0a, 0x09, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x54, 0x79, 0x70, 0x65, 0x12, 0x0a, 0x0a, 0x06, 0x53, + 0x54, 0x52, 0x49, 0x4e, 0x47, 0x10, 0x00, 0x12, 0x08, 0x0a, 0x04, 0x42, 0x4f, 0x4f, 0x4c, 0x10, + 0x01, 0x12, 0x08, 0x0a, 0x04, 0x49, 0x4e, 0x54, 0x38, 0x10, 0x02, 0x12, 0x09, 0x0a, 0x05, 0x49, + 0x4e, 0x54, 0x31, 0x36, 0x10, 0x03, 0x12, 0x09, 0x0a, 0x05, 0x49, 0x4e, 0x54, 0x33, 0x32, 0x10, + 0x04, 0x12, 0x09, 0x0a, 0x05, 0x49, 0x4e, 0x54, 0x36, 0x34, 0x10, 0x05, 0x12, 0x07, 0x0a, 0x03, + 0x49, 0x4e, 0x54, 0x10, 0x06, 0x12, 0x09, 0x0a, 0x05, 0x55, 0x49, 0x4e, 0x54, 0x38, 0x10, 0x07, + 0x12, 0x0a, 0x0a, 0x06, 0x55, 0x49, 0x4e, 0x54, 0x31, 0x36, 0x10, 0x08, 0x12, 0x0a, 0x0a, 0x06, + 0x55, 0x49, 0x4e, 0x54, 0x33, 0x32, 0x10, 0x09, 0x12, 0x0a, 0x0a, 0x06, 0x55, 0x49, 0x4e, 0x54, + 0x36, 0x34, 0x10, 0x0a, 0x12, 0x08, 0x0a, 0x04, 0x55, 0x49, 0x4e, 0x54, 0x10, 0x0b, 0x12, 0x08, + 0x0a, 0x04, 0x55, 0x55, 0x49, 0x44, 0x10, 0x0c, 0x22, 0x8e, 0x02, 0x0a, 0x07, 0x47, 0x61, 0x74, + 0x65, 0x77, 0x61, 0x79, 0x12, 0x1f, 0x0a, 0x0b, 0x65, 0x6e, 0x63, 0x6f, 0x72, 0x65, 0x5f, 0x6e, + 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x65, 0x6e, 0x63, 0x6f, 0x72, + 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x48, 0x0a, 0x08, 0x65, 0x78, 0x70, 0x6c, 0x69, 0x63, 0x69, + 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x27, 0x2e, 0x65, 0x6e, 0x63, 0x6f, 0x72, 0x65, + 0x2e, 0x70, 0x61, 0x72, 0x73, 0x65, 0x72, 0x2e, 0x6d, 0x65, 0x74, 0x61, 0x2e, 0x76, 0x31, 0x2e, + 0x47, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x2e, 0x45, 0x78, 0x70, 0x6c, 0x69, 0x63, 0x69, 0x74, + 0x48, 0x00, 0x52, 0x08, 0x65, 0x78, 0x70, 0x6c, 0x69, 0x63, 0x69, 0x74, 0x88, 0x01, 0x01, 0x1a, + 0x8a, 0x01, 0x0a, 0x08, 0x45, 0x78, 0x70, 0x6c, 0x69, 0x63, 0x69, 0x74, 0x12, 0x21, 0x0a, 0x0c, + 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x0b, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x12, + 0x4a, 0x0a, 0x0c, 0x61, 0x75, 0x74, 0x68, 0x5f, 0x68, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x72, 0x18, + 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x22, 0x2e, 0x65, 0x6e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x70, + 0x61, 0x72, 0x73, 0x65, 0x72, 0x2e, 0x6d, 0x65, 0x74, 0x61, 0x2e, 0x76, 0x31, 0x2e, 0x41, 0x75, + 0x74, 0x68, 0x48, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x72, 0x48, 0x00, 0x52, 0x0b, 0x61, 0x75, 0x74, + 0x68, 0x48, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x72, 0x88, 0x01, 0x01, 0x42, 0x0f, 0x0a, 0x0d, 0x5f, + 0x61, 0x75, 0x74, 0x68, 0x5f, 0x68, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x72, 0x42, 0x0b, 0x0a, 0x09, + 0x5f, 0x65, 0x78, 0x70, 0x6c, 0x69, 0x63, 0x69, 0x74, 0x22, 0xac, 0x01, 0x0a, 0x07, 0x43, 0x72, + 0x6f, 0x6e, 0x4a, 0x6f, 0x62, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x14, 0x0a, 0x05, 0x74, 0x69, 0x74, 0x6c, 0x65, 0x18, 0x02, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x74, 0x69, 0x74, 0x6c, 0x65, 0x12, 0x15, 0x0a, 0x03, 0x64, + 0x6f, 0x63, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x48, 0x00, 0x52, 0x03, 0x64, 0x6f, 0x63, 0x88, + 0x01, 0x01, 0x12, 0x1a, 0x0a, 0x08, 0x73, 0x63, 0x68, 0x65, 0x64, 0x75, 0x6c, 0x65, 0x18, 0x04, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x73, 0x63, 0x68, 0x65, 0x64, 0x75, 0x6c, 0x65, 0x12, 0x40, + 0x0a, 0x08, 0x65, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, + 0x32, 0x24, 0x2e, 0x65, 0x6e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x70, 0x61, 0x72, 0x73, 0x65, 0x72, + 0x2e, 0x6d, 0x65, 0x74, 0x61, 0x2e, 0x76, 0x31, 0x2e, 0x51, 0x75, 0x61, 0x6c, 0x69, 0x66, 0x69, + 0x65, 0x64, 0x4e, 0x61, 0x6d, 0x65, 0x52, 0x08, 0x65, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, + 0x42, 0x06, 0x0a, 0x04, 0x5f, 0x64, 0x6f, 0x63, 0x22, 0x95, 0x02, 0x0a, 0x0b, 0x53, 0x51, 0x4c, + 0x44, 0x61, 0x74, 0x61, 0x62, 0x61, 0x73, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x15, 0x0a, 0x03, + 0x64, 0x6f, 0x63, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x48, 0x00, 0x52, 0x03, 0x64, 0x6f, 0x63, + 0x88, 0x01, 0x01, 0x12, 0x31, 0x0a, 0x12, 0x6d, 0x69, 0x67, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, + 0x5f, 0x72, 0x65, 0x6c, 0x5f, 0x70, 0x61, 0x74, 0x68, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x48, + 0x01, 0x52, 0x10, 0x6d, 0x69, 0x67, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x6c, 0x50, + 0x61, 0x74, 0x68, 0x88, 0x01, 0x01, 0x12, 0x42, 0x0a, 0x0a, 0x6d, 0x69, 0x67, 0x72, 0x61, 0x74, + 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x22, 0x2e, 0x65, 0x6e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x70, 0x61, 0x72, 0x73, 0x65, 0x72, 0x2e, 0x6d, 0x65, 0x74, 0x61, 0x2e, - 0x76, 0x31, 0x2e, 0x41, 0x75, 0x74, 0x68, 0x48, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x72, 0x48, 0x00, - 0x52, 0x0b, 0x61, 0x75, 0x74, 0x68, 0x48, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x72, 0x88, 0x01, 0x01, - 0x42, 0x0f, 0x0a, 0x0d, 0x5f, 0x61, 0x75, 0x74, 0x68, 0x5f, 0x68, 0x61, 0x6e, 0x64, 0x6c, 0x65, - 0x72, 0x42, 0x0b, 0x0a, 0x09, 0x5f, 0x65, 0x78, 0x70, 0x6c, 0x69, 0x63, 0x69, 0x74, 0x22, 0xac, - 0x01, 0x0a, 0x07, 0x43, 0x72, 0x6f, 0x6e, 0x4a, 0x6f, 0x62, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x14, 0x0a, 0x05, 0x74, 0x69, - 0x74, 0x6c, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x74, 0x69, 0x74, 0x6c, 0x65, - 0x12, 0x15, 0x0a, 0x03, 0x64, 0x6f, 0x63, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x48, 0x00, 0x52, - 0x03, 0x64, 0x6f, 0x63, 0x88, 0x01, 0x01, 0x12, 0x1a, 0x0a, 0x08, 0x73, 0x63, 0x68, 0x65, 0x64, - 0x75, 0x6c, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x73, 0x63, 0x68, 0x65, 0x64, - 0x75, 0x6c, 0x65, 0x12, 0x40, 0x0a, 0x08, 0x65, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x18, - 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x24, 0x2e, 0x65, 0x6e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x70, - 0x61, 0x72, 0x73, 0x65, 0x72, 0x2e, 0x6d, 0x65, 0x74, 0x61, 0x2e, 0x76, 0x31, 0x2e, 0x51, 0x75, - 0x61, 0x6c, 0x69, 0x66, 0x69, 0x65, 0x64, 0x4e, 0x61, 0x6d, 0x65, 0x52, 0x08, 0x65, 0x6e, 0x64, - 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x42, 0x06, 0x0a, 0x04, 0x5f, 0x64, 0x6f, 0x63, 0x22, 0x95, 0x02, - 0x0a, 0x0b, 0x53, 0x51, 0x4c, 0x44, 0x61, 0x74, 0x61, 0x62, 0x61, 0x73, 0x65, 0x12, 0x12, 0x0a, - 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, - 0x65, 0x12, 0x15, 0x0a, 0x03, 0x64, 0x6f, 0x63, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x48, 0x00, - 0x52, 0x03, 0x64, 0x6f, 0x63, 0x88, 0x01, 0x01, 0x12, 0x31, 0x0a, 0x12, 0x6d, 0x69, 0x67, 0x72, - 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x72, 0x65, 0x6c, 0x5f, 0x70, 0x61, 0x74, 0x68, 0x18, 0x03, - 0x20, 0x01, 0x28, 0x09, 0x48, 0x01, 0x52, 0x10, 0x6d, 0x69, 0x67, 0x72, 0x61, 0x74, 0x69, 0x6f, - 0x6e, 0x52, 0x65, 0x6c, 0x50, 0x61, 0x74, 0x68, 0x88, 0x01, 0x01, 0x12, 0x42, 0x0a, 0x0a, 0x6d, - 0x69, 0x67, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, - 0x22, 0x2e, 0x65, 0x6e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x70, 0x61, 0x72, 0x73, 0x65, 0x72, 0x2e, - 0x6d, 0x65, 0x74, 0x61, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x42, 0x4d, 0x69, 0x67, 0x72, 0x61, 0x74, - 0x69, 0x6f, 0x6e, 0x52, 0x0a, 0x6d, 0x69, 0x67, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, - 0x45, 0x0a, 0x1f, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x5f, 0x6e, 0x6f, 0x6e, 0x5f, 0x73, 0x65, 0x71, - 0x75, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x5f, 0x6d, 0x69, 0x67, 0x72, 0x61, 0x74, 0x69, 0x6f, - 0x6e, 0x73, 0x18, 0x05, 0x20, 0x01, 0x28, 0x08, 0x52, 0x1c, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x4e, - 0x6f, 0x6e, 0x53, 0x65, 0x71, 0x75, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x4d, 0x69, 0x67, 0x72, - 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x42, 0x06, 0x0a, 0x04, 0x5f, 0x64, 0x6f, 0x63, 0x42, 0x15, - 0x0a, 0x13, 0x5f, 0x6d, 0x69, 0x67, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x72, 0x65, 0x6c, - 0x5f, 0x70, 0x61, 0x74, 0x68, 0x22, 0x63, 0x0a, 0x0b, 0x44, 0x42, 0x4d, 0x69, 0x67, 0x72, 0x61, - 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x1a, 0x0a, 0x08, 0x66, 0x69, 0x6c, 0x65, 0x6e, 0x61, 0x6d, 0x65, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x66, 0x69, 0x6c, 0x65, 0x6e, 0x61, 0x6d, 0x65, - 0x12, 0x16, 0x0a, 0x06, 0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, - 0x52, 0x06, 0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x12, 0x20, 0x0a, 0x0b, 0x64, 0x65, 0x73, 0x63, - 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x64, - 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x22, 0x3b, 0x0a, 0x06, 0x42, 0x75, - 0x63, 0x6b, 0x65, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x15, 0x0a, 0x03, 0x64, 0x6f, 0x63, 0x18, - 0x02, 0x20, 0x01, 0x28, 0x09, 0x48, 0x00, 0x52, 0x03, 0x64, 0x6f, 0x63, 0x88, 0x01, 0x01, 0x42, - 0x06, 0x0a, 0x04, 0x5f, 0x64, 0x6f, 0x63, 0x22, 0xb8, 0x07, 0x0a, 0x0b, 0x50, 0x75, 0x62, 0x53, - 0x75, 0x62, 0x54, 0x6f, 0x70, 0x69, 0x63, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x15, 0x0a, 0x03, 0x64, - 0x6f, 0x63, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x48, 0x00, 0x52, 0x03, 0x64, 0x6f, 0x63, 0x88, - 0x01, 0x01, 0x12, 0x40, 0x0a, 0x0c, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x5f, 0x74, 0x79, - 0x70, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x65, 0x6e, 0x63, 0x6f, 0x72, - 0x65, 0x2e, 0x70, 0x61, 0x72, 0x73, 0x65, 0x72, 0x2e, 0x73, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x2e, - 0x76, 0x31, 0x2e, 0x54, 0x79, 0x70, 0x65, 0x52, 0x0b, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, - 0x54, 0x79, 0x70, 0x65, 0x12, 0x63, 0x0a, 0x12, 0x64, 0x65, 0x6c, 0x69, 0x76, 0x65, 0x72, 0x79, - 0x5f, 0x67, 0x75, 0x61, 0x72, 0x61, 0x6e, 0x74, 0x65, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0e, - 0x32, 0x34, 0x2e, 0x65, 0x6e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x70, 0x61, 0x72, 0x73, 0x65, 0x72, - 0x2e, 0x6d, 0x65, 0x74, 0x61, 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x75, 0x62, 0x53, 0x75, 0x62, 0x54, - 0x6f, 0x70, 0x69, 0x63, 0x2e, 0x44, 0x65, 0x6c, 0x69, 0x76, 0x65, 0x72, 0x79, 0x47, 0x75, 0x61, - 0x72, 0x61, 0x6e, 0x74, 0x65, 0x65, 0x52, 0x11, 0x64, 0x65, 0x6c, 0x69, 0x76, 0x65, 0x72, 0x79, - 0x47, 0x75, 0x61, 0x72, 0x61, 0x6e, 0x74, 0x65, 0x65, 0x12, 0x21, 0x0a, 0x0c, 0x6f, 0x72, 0x64, - 0x65, 0x72, 0x69, 0x6e, 0x67, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x0b, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x69, 0x6e, 0x67, 0x4b, 0x65, 0x79, 0x12, 0x4c, 0x0a, 0x0a, - 0x70, 0x75, 0x62, 0x6c, 0x69, 0x73, 0x68, 0x65, 0x72, 0x73, 0x18, 0x06, 0x20, 0x03, 0x28, 0x0b, - 0x32, 0x2c, 0x2e, 0x65, 0x6e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x70, 0x61, 0x72, 0x73, 0x65, 0x72, - 0x2e, 0x6d, 0x65, 0x74, 0x61, 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x75, 0x62, 0x53, 0x75, 0x62, 0x54, - 0x6f, 0x70, 0x69, 0x63, 0x2e, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x73, 0x68, 0x65, 0x72, 0x52, 0x0a, - 0x70, 0x75, 0x62, 0x6c, 0x69, 0x73, 0x68, 0x65, 0x72, 0x73, 0x12, 0x55, 0x0a, 0x0d, 0x73, 0x75, - 0x62, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x07, 0x20, 0x03, 0x28, - 0x0b, 0x32, 0x2f, 0x2e, 0x65, 0x6e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x70, 0x61, 0x72, 0x73, 0x65, - 0x72, 0x2e, 0x6d, 0x65, 0x74, 0x61, 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x75, 0x62, 0x53, 0x75, 0x62, - 0x54, 0x6f, 0x70, 0x69, 0x63, 0x2e, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, - 0x6f, 0x6e, 0x52, 0x0d, 0x73, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, - 0x73, 0x1a, 0x2e, 0x0a, 0x09, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x73, 0x68, 0x65, 0x72, 0x12, 0x21, - 0x0a, 0x0c, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x4e, 0x61, 0x6d, - 0x65, 0x1a, 0xaa, 0x02, 0x0a, 0x0c, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, - 0x6f, 0x6e, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x21, 0x0a, 0x0c, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, - 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x73, 0x65, - 0x72, 0x76, 0x69, 0x63, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x21, 0x0a, 0x0c, 0x61, 0x63, 0x6b, - 0x5f, 0x64, 0x65, 0x61, 0x64, 0x6c, 0x69, 0x6e, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x03, 0x52, - 0x0b, 0x61, 0x63, 0x6b, 0x44, 0x65, 0x61, 0x64, 0x6c, 0x69, 0x6e, 0x65, 0x12, 0x2b, 0x0a, 0x11, - 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x5f, 0x72, 0x65, 0x74, 0x65, 0x6e, 0x74, 0x69, 0x6f, - 0x6e, 0x18, 0x04, 0x20, 0x01, 0x28, 0x03, 0x52, 0x10, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, - 0x52, 0x65, 0x74, 0x65, 0x6e, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x51, 0x0a, 0x0c, 0x72, 0x65, 0x74, - 0x72, 0x79, 0x5f, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, - 0x2e, 0x2e, 0x65, 0x6e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x70, 0x61, 0x72, 0x73, 0x65, 0x72, 0x2e, - 0x6d, 0x65, 0x74, 0x61, 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x75, 0x62, 0x53, 0x75, 0x62, 0x54, 0x6f, - 0x70, 0x69, 0x63, 0x2e, 0x52, 0x65, 0x74, 0x72, 0x79, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x52, - 0x0b, 0x72, 0x65, 0x74, 0x72, 0x79, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x12, 0x2c, 0x0a, 0x0f, - 0x6d, 0x61, 0x78, 0x5f, 0x63, 0x6f, 0x6e, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x18, - 0x06, 0x20, 0x01, 0x28, 0x05, 0x48, 0x00, 0x52, 0x0e, 0x6d, 0x61, 0x78, 0x43, 0x6f, 0x6e, 0x63, - 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x88, 0x01, 0x01, 0x42, 0x12, 0x0a, 0x10, 0x5f, 0x6d, - 0x61, 0x78, 0x5f, 0x63, 0x6f, 0x6e, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x1a, 0x70, - 0x0a, 0x0b, 0x52, 0x65, 0x74, 0x72, 0x79, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x12, 0x1f, 0x0a, - 0x0b, 0x6d, 0x69, 0x6e, 0x5f, 0x62, 0x61, 0x63, 0x6b, 0x6f, 0x66, 0x66, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x03, 0x52, 0x0a, 0x6d, 0x69, 0x6e, 0x42, 0x61, 0x63, 0x6b, 0x6f, 0x66, 0x66, 0x12, 0x1f, - 0x0a, 0x0b, 0x6d, 0x61, 0x78, 0x5f, 0x62, 0x61, 0x63, 0x6b, 0x6f, 0x66, 0x66, 0x18, 0x02, 0x20, - 0x01, 0x28, 0x03, 0x52, 0x0a, 0x6d, 0x61, 0x78, 0x42, 0x61, 0x63, 0x6b, 0x6f, 0x66, 0x66, 0x12, - 0x1f, 0x0a, 0x0b, 0x6d, 0x61, 0x78, 0x5f, 0x72, 0x65, 0x74, 0x72, 0x69, 0x65, 0x73, 0x18, 0x03, - 0x20, 0x01, 0x28, 0x03, 0x52, 0x0a, 0x6d, 0x61, 0x78, 0x52, 0x65, 0x74, 0x72, 0x69, 0x65, 0x73, - 0x22, 0x38, 0x0a, 0x11, 0x44, 0x65, 0x6c, 0x69, 0x76, 0x65, 0x72, 0x79, 0x47, 0x75, 0x61, 0x72, - 0x61, 0x6e, 0x74, 0x65, 0x65, 0x12, 0x11, 0x0a, 0x0d, 0x41, 0x54, 0x5f, 0x4c, 0x45, 0x41, 0x53, - 0x54, 0x5f, 0x4f, 0x4e, 0x43, 0x45, 0x10, 0x00, 0x12, 0x10, 0x0a, 0x0c, 0x45, 0x58, 0x41, 0x43, - 0x54, 0x4c, 0x59, 0x5f, 0x4f, 0x4e, 0x43, 0x45, 0x10, 0x01, 0x42, 0x06, 0x0a, 0x04, 0x5f, 0x64, - 0x6f, 0x63, 0x22, 0x9a, 0x03, 0x0a, 0x0c, 0x43, 0x61, 0x63, 0x68, 0x65, 0x43, 0x6c, 0x75, 0x73, - 0x74, 0x65, 0x72, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x64, 0x6f, 0x63, 0x18, 0x02, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x64, 0x6f, 0x63, 0x12, 0x4a, 0x0a, 0x09, 0x6b, 0x65, 0x79, - 0x73, 0x70, 0x61, 0x63, 0x65, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2c, 0x2e, 0x65, + 0x76, 0x31, 0x2e, 0x44, 0x42, 0x4d, 0x69, 0x67, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x0a, + 0x6d, 0x69, 0x67, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x45, 0x0a, 0x1f, 0x61, 0x6c, + 0x6c, 0x6f, 0x77, 0x5f, 0x6e, 0x6f, 0x6e, 0x5f, 0x73, 0x65, 0x71, 0x75, 0x65, 0x6e, 0x74, 0x69, + 0x61, 0x6c, 0x5f, 0x6d, 0x69, 0x67, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x05, 0x20, + 0x01, 0x28, 0x08, 0x52, 0x1c, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x4e, 0x6f, 0x6e, 0x53, 0x65, 0x71, + 0x75, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x4d, 0x69, 0x67, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, + 0x73, 0x42, 0x06, 0x0a, 0x04, 0x5f, 0x64, 0x6f, 0x63, 0x42, 0x15, 0x0a, 0x13, 0x5f, 0x6d, 0x69, + 0x67, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x72, 0x65, 0x6c, 0x5f, 0x70, 0x61, 0x74, 0x68, + 0x22, 0x63, 0x0a, 0x0b, 0x44, 0x42, 0x4d, 0x69, 0x67, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, + 0x1a, 0x0a, 0x08, 0x66, 0x69, 0x6c, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x08, 0x66, 0x69, 0x6c, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x6e, + 0x75, 0x6d, 0x62, 0x65, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, 0x06, 0x6e, 0x75, 0x6d, + 0x62, 0x65, 0x72, 0x12, 0x20, 0x0a, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, + 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, + 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x22, 0x3b, 0x0a, 0x06, 0x42, 0x75, 0x63, 0x6b, 0x65, 0x74, 0x12, + 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, + 0x61, 0x6d, 0x65, 0x12, 0x15, 0x0a, 0x03, 0x64, 0x6f, 0x63, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, + 0x48, 0x00, 0x52, 0x03, 0x64, 0x6f, 0x63, 0x88, 0x01, 0x01, 0x42, 0x06, 0x0a, 0x04, 0x5f, 0x64, + 0x6f, 0x63, 0x22, 0xb8, 0x07, 0x0a, 0x0b, 0x50, 0x75, 0x62, 0x53, 0x75, 0x62, 0x54, 0x6f, 0x70, + 0x69, 0x63, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x15, 0x0a, 0x03, 0x64, 0x6f, 0x63, 0x18, 0x02, 0x20, + 0x01, 0x28, 0x09, 0x48, 0x00, 0x52, 0x03, 0x64, 0x6f, 0x63, 0x88, 0x01, 0x01, 0x12, 0x40, 0x0a, + 0x0c, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x03, 0x20, + 0x01, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x65, 0x6e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x70, 0x61, 0x72, + 0x73, 0x65, 0x72, 0x2e, 0x73, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x79, + 0x70, 0x65, 0x52, 0x0b, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x54, 0x79, 0x70, 0x65, 0x12, + 0x63, 0x0a, 0x12, 0x64, 0x65, 0x6c, 0x69, 0x76, 0x65, 0x72, 0x79, 0x5f, 0x67, 0x75, 0x61, 0x72, + 0x61, 0x6e, 0x74, 0x65, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x34, 0x2e, 0x65, 0x6e, + 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x70, 0x61, 0x72, 0x73, 0x65, 0x72, 0x2e, 0x6d, 0x65, 0x74, 0x61, + 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x75, 0x62, 0x53, 0x75, 0x62, 0x54, 0x6f, 0x70, 0x69, 0x63, 0x2e, + 0x44, 0x65, 0x6c, 0x69, 0x76, 0x65, 0x72, 0x79, 0x47, 0x75, 0x61, 0x72, 0x61, 0x6e, 0x74, 0x65, + 0x65, 0x52, 0x11, 0x64, 0x65, 0x6c, 0x69, 0x76, 0x65, 0x72, 0x79, 0x47, 0x75, 0x61, 0x72, 0x61, + 0x6e, 0x74, 0x65, 0x65, 0x12, 0x21, 0x0a, 0x0c, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x69, 0x6e, 0x67, + 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x6f, 0x72, 0x64, 0x65, + 0x72, 0x69, 0x6e, 0x67, 0x4b, 0x65, 0x79, 0x12, 0x4c, 0x0a, 0x0a, 0x70, 0x75, 0x62, 0x6c, 0x69, + 0x73, 0x68, 0x65, 0x72, 0x73, 0x18, 0x06, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2c, 0x2e, 0x65, 0x6e, + 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x70, 0x61, 0x72, 0x73, 0x65, 0x72, 0x2e, 0x6d, 0x65, 0x74, 0x61, + 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x75, 0x62, 0x53, 0x75, 0x62, 0x54, 0x6f, 0x70, 0x69, 0x63, 0x2e, + 0x50, 0x75, 0x62, 0x6c, 0x69, 0x73, 0x68, 0x65, 0x72, 0x52, 0x0a, 0x70, 0x75, 0x62, 0x6c, 0x69, + 0x73, 0x68, 0x65, 0x72, 0x73, 0x12, 0x55, 0x0a, 0x0d, 0x73, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, + 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x07, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2f, 0x2e, 0x65, 0x6e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x70, 0x61, 0x72, 0x73, 0x65, 0x72, 0x2e, 0x6d, 0x65, 0x74, - 0x61, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x61, 0x63, 0x68, 0x65, 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, - 0x72, 0x2e, 0x4b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x52, 0x09, 0x6b, 0x65, 0x79, 0x73, - 0x70, 0x61, 0x63, 0x65, 0x73, 0x12, 0x27, 0x0a, 0x0f, 0x65, 0x76, 0x69, 0x63, 0x74, 0x69, 0x6f, - 0x6e, 0x5f, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, - 0x65, 0x76, 0x69, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x1a, 0xee, - 0x01, 0x0a, 0x08, 0x4b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x12, 0x38, 0x0a, 0x08, 0x6b, - 0x65, 0x79, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1d, 0x2e, - 0x65, 0x6e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x70, 0x61, 0x72, 0x73, 0x65, 0x72, 0x2e, 0x73, 0x63, - 0x68, 0x65, 0x6d, 0x61, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x79, 0x70, 0x65, 0x52, 0x07, 0x6b, 0x65, - 0x79, 0x54, 0x79, 0x70, 0x65, 0x12, 0x3c, 0x0a, 0x0a, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x5f, 0x74, - 0x79, 0x70, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x65, 0x6e, 0x63, 0x6f, - 0x72, 0x65, 0x2e, 0x70, 0x61, 0x72, 0x73, 0x65, 0x72, 0x2e, 0x73, 0x63, 0x68, 0x65, 0x6d, 0x61, - 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x79, 0x70, 0x65, 0x52, 0x09, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x54, - 0x79, 0x70, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x18, 0x03, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x10, 0x0a, - 0x03, 0x64, 0x6f, 0x63, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x64, 0x6f, 0x63, 0x12, - 0x3e, 0x0a, 0x0c, 0x70, 0x61, 0x74, 0x68, 0x5f, 0x70, 0x61, 0x74, 0x74, 0x65, 0x72, 0x6e, 0x18, - 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x65, 0x6e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x70, - 0x61, 0x72, 0x73, 0x65, 0x72, 0x2e, 0x6d, 0x65, 0x74, 0x61, 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x61, - 0x74, 0x68, 0x52, 0x0b, 0x70, 0x61, 0x74, 0x68, 0x50, 0x61, 0x74, 0x74, 0x65, 0x72, 0x6e, 0x22, - 0xbb, 0x03, 0x0a, 0x06, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, - 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x3f, - 0x0a, 0x0a, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x02, 0x20, 0x01, - 0x28, 0x0e, 0x32, 0x20, 0x2e, 0x65, 0x6e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x70, 0x61, 0x72, 0x73, - 0x65, 0x72, 0x2e, 0x73, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x2e, 0x76, 0x31, 0x2e, 0x42, 0x75, 0x69, - 0x6c, 0x74, 0x69, 0x6e, 0x52, 0x09, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x54, 0x79, 0x70, 0x65, 0x12, - 0x10, 0x0a, 0x03, 0x64, 0x6f, 0x63, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x64, 0x6f, - 0x63, 0x12, 0x3c, 0x0a, 0x04, 0x6b, 0x69, 0x6e, 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0e, 0x32, - 0x28, 0x2e, 0x65, 0x6e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x70, 0x61, 0x72, 0x73, 0x65, 0x72, 0x2e, - 0x6d, 0x65, 0x74, 0x61, 0x2e, 0x76, 0x31, 0x2e, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x2e, 0x4d, - 0x65, 0x74, 0x72, 0x69, 0x63, 0x4b, 0x69, 0x6e, 0x64, 0x52, 0x04, 0x6b, 0x69, 0x6e, 0x64, 0x12, - 0x26, 0x0a, 0x0c, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, - 0x05, 0x20, 0x01, 0x28, 0x09, 0x48, 0x00, 0x52, 0x0b, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, - 0x4e, 0x61, 0x6d, 0x65, 0x88, 0x01, 0x01, 0x12, 0x3b, 0x0a, 0x06, 0x6c, 0x61, 0x62, 0x65, 0x6c, - 0x73, 0x18, 0x06, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x23, 0x2e, 0x65, 0x6e, 0x63, 0x6f, 0x72, 0x65, + 0x61, 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x75, 0x62, 0x53, 0x75, 0x62, 0x54, 0x6f, 0x70, 0x69, 0x63, + 0x2e, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x0d, 0x73, + 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x1a, 0x2e, 0x0a, 0x09, + 0x50, 0x75, 0x62, 0x6c, 0x69, 0x73, 0x68, 0x65, 0x72, 0x12, 0x21, 0x0a, 0x0c, 0x73, 0x65, 0x72, + 0x76, 0x69, 0x63, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x0b, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x1a, 0xaa, 0x02, 0x0a, + 0x0c, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x12, 0x0a, + 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, + 0x65, 0x12, 0x21, 0x0a, 0x0c, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x5f, 0x6e, 0x61, 0x6d, + 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, + 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x21, 0x0a, 0x0c, 0x61, 0x63, 0x6b, 0x5f, 0x64, 0x65, 0x61, 0x64, + 0x6c, 0x69, 0x6e, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0b, 0x61, 0x63, 0x6b, 0x44, + 0x65, 0x61, 0x64, 0x6c, 0x69, 0x6e, 0x65, 0x12, 0x2b, 0x0a, 0x11, 0x6d, 0x65, 0x73, 0x73, 0x61, + 0x67, 0x65, 0x5f, 0x72, 0x65, 0x74, 0x65, 0x6e, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x04, 0x20, 0x01, + 0x28, 0x03, 0x52, 0x10, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x52, 0x65, 0x74, 0x65, 0x6e, + 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x51, 0x0a, 0x0c, 0x72, 0x65, 0x74, 0x72, 0x79, 0x5f, 0x70, 0x6f, + 0x6c, 0x69, 0x63, 0x79, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2e, 0x2e, 0x65, 0x6e, 0x63, + 0x6f, 0x72, 0x65, 0x2e, 0x70, 0x61, 0x72, 0x73, 0x65, 0x72, 0x2e, 0x6d, 0x65, 0x74, 0x61, 0x2e, + 0x76, 0x31, 0x2e, 0x50, 0x75, 0x62, 0x53, 0x75, 0x62, 0x54, 0x6f, 0x70, 0x69, 0x63, 0x2e, 0x52, + 0x65, 0x74, 0x72, 0x79, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x52, 0x0b, 0x72, 0x65, 0x74, 0x72, + 0x79, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x12, 0x2c, 0x0a, 0x0f, 0x6d, 0x61, 0x78, 0x5f, 0x63, + 0x6f, 0x6e, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x18, 0x06, 0x20, 0x01, 0x28, 0x05, + 0x48, 0x00, 0x52, 0x0e, 0x6d, 0x61, 0x78, 0x43, 0x6f, 0x6e, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, + 0x63, 0x79, 0x88, 0x01, 0x01, 0x42, 0x12, 0x0a, 0x10, 0x5f, 0x6d, 0x61, 0x78, 0x5f, 0x63, 0x6f, + 0x6e, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x1a, 0x70, 0x0a, 0x0b, 0x52, 0x65, 0x74, + 0x72, 0x79, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x12, 0x1f, 0x0a, 0x0b, 0x6d, 0x69, 0x6e, 0x5f, + 0x62, 0x61, 0x63, 0x6b, 0x6f, 0x66, 0x66, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0a, 0x6d, + 0x69, 0x6e, 0x42, 0x61, 0x63, 0x6b, 0x6f, 0x66, 0x66, 0x12, 0x1f, 0x0a, 0x0b, 0x6d, 0x61, 0x78, + 0x5f, 0x62, 0x61, 0x63, 0x6b, 0x6f, 0x66, 0x66, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0a, + 0x6d, 0x61, 0x78, 0x42, 0x61, 0x63, 0x6b, 0x6f, 0x66, 0x66, 0x12, 0x1f, 0x0a, 0x0b, 0x6d, 0x61, + 0x78, 0x5f, 0x72, 0x65, 0x74, 0x72, 0x69, 0x65, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x03, 0x52, + 0x0a, 0x6d, 0x61, 0x78, 0x52, 0x65, 0x74, 0x72, 0x69, 0x65, 0x73, 0x22, 0x38, 0x0a, 0x11, 0x44, + 0x65, 0x6c, 0x69, 0x76, 0x65, 0x72, 0x79, 0x47, 0x75, 0x61, 0x72, 0x61, 0x6e, 0x74, 0x65, 0x65, + 0x12, 0x11, 0x0a, 0x0d, 0x41, 0x54, 0x5f, 0x4c, 0x45, 0x41, 0x53, 0x54, 0x5f, 0x4f, 0x4e, 0x43, + 0x45, 0x10, 0x00, 0x12, 0x10, 0x0a, 0x0c, 0x45, 0x58, 0x41, 0x43, 0x54, 0x4c, 0x59, 0x5f, 0x4f, + 0x4e, 0x43, 0x45, 0x10, 0x01, 0x42, 0x06, 0x0a, 0x04, 0x5f, 0x64, 0x6f, 0x63, 0x22, 0x9a, 0x03, + 0x0a, 0x0c, 0x43, 0x61, 0x63, 0x68, 0x65, 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x12, 0x12, + 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, + 0x6d, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x64, 0x6f, 0x63, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x03, 0x64, 0x6f, 0x63, 0x12, 0x4a, 0x0a, 0x09, 0x6b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, + 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2c, 0x2e, 0x65, 0x6e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x70, 0x61, 0x72, 0x73, 0x65, 0x72, 0x2e, 0x6d, 0x65, 0x74, 0x61, 0x2e, 0x76, 0x31, 0x2e, - 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x2e, 0x4c, 0x61, 0x62, 0x65, 0x6c, 0x52, 0x06, 0x6c, 0x61, - 0x62, 0x65, 0x6c, 0x73, 0x1a, 0x61, 0x0a, 0x05, 0x4c, 0x61, 0x62, 0x65, 0x6c, 0x12, 0x10, 0x0a, - 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, - 0x34, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x20, 0x2e, + 0x43, 0x61, 0x63, 0x68, 0x65, 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x2e, 0x4b, 0x65, 0x79, + 0x73, 0x70, 0x61, 0x63, 0x65, 0x52, 0x09, 0x6b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x73, + 0x12, 0x27, 0x0a, 0x0f, 0x65, 0x76, 0x69, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x70, 0x6f, 0x6c, + 0x69, 0x63, 0x79, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x65, 0x76, 0x69, 0x63, 0x74, + 0x69, 0x6f, 0x6e, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x1a, 0xee, 0x01, 0x0a, 0x08, 0x4b, 0x65, + 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x12, 0x38, 0x0a, 0x08, 0x6b, 0x65, 0x79, 0x5f, 0x74, 0x79, + 0x70, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x65, 0x6e, 0x63, 0x6f, 0x72, + 0x65, 0x2e, 0x70, 0x61, 0x72, 0x73, 0x65, 0x72, 0x2e, 0x73, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x2e, + 0x76, 0x31, 0x2e, 0x54, 0x79, 0x70, 0x65, 0x52, 0x07, 0x6b, 0x65, 0x79, 0x54, 0x79, 0x70, 0x65, + 0x12, 0x3c, 0x0a, 0x0a, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x02, + 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x65, 0x6e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x70, 0x61, + 0x72, 0x73, 0x65, 0x72, 0x2e, 0x73, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x2e, 0x76, 0x31, 0x2e, 0x54, + 0x79, 0x70, 0x65, 0x52, 0x09, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x54, 0x79, 0x70, 0x65, 0x12, 0x18, + 0x0a, 0x07, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x07, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x64, 0x6f, 0x63, 0x18, + 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x64, 0x6f, 0x63, 0x12, 0x3e, 0x0a, 0x0c, 0x70, 0x61, + 0x74, 0x68, 0x5f, 0x70, 0x61, 0x74, 0x74, 0x65, 0x72, 0x6e, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, + 0x32, 0x1b, 0x2e, 0x65, 0x6e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x70, 0x61, 0x72, 0x73, 0x65, 0x72, + 0x2e, 0x6d, 0x65, 0x74, 0x61, 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x61, 0x74, 0x68, 0x52, 0x0b, 0x70, + 0x61, 0x74, 0x68, 0x50, 0x61, 0x74, 0x74, 0x65, 0x72, 0x6e, 0x22, 0xbb, 0x03, 0x0a, 0x06, 0x4d, + 0x65, 0x74, 0x72, 0x69, 0x63, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x3f, 0x0a, 0x0a, 0x76, 0x61, 0x6c, + 0x75, 0x65, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x20, 0x2e, 0x65, 0x6e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x70, 0x61, 0x72, 0x73, 0x65, 0x72, 0x2e, 0x73, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x2e, 0x76, 0x31, 0x2e, 0x42, 0x75, 0x69, 0x6c, 0x74, 0x69, 0x6e, 0x52, - 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x64, 0x6f, 0x63, 0x18, 0x03, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x03, 0x64, 0x6f, 0x63, 0x22, 0x33, 0x0a, 0x0a, 0x4d, 0x65, 0x74, 0x72, 0x69, - 0x63, 0x4b, 0x69, 0x6e, 0x64, 0x12, 0x0b, 0x0a, 0x07, 0x43, 0x4f, 0x55, 0x4e, 0x54, 0x45, 0x52, - 0x10, 0x00, 0x12, 0x09, 0x0a, 0x05, 0x47, 0x41, 0x55, 0x47, 0x45, 0x10, 0x01, 0x12, 0x0d, 0x0a, - 0x09, 0x48, 0x49, 0x53, 0x54, 0x4f, 0x47, 0x52, 0x41, 0x4d, 0x10, 0x02, 0x42, 0x0f, 0x0a, 0x0d, - 0x5f, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x2a, 0x1e, 0x0a, - 0x04, 0x4c, 0x61, 0x6e, 0x67, 0x12, 0x06, 0x0a, 0x02, 0x47, 0x4f, 0x10, 0x00, 0x12, 0x0e, 0x0a, - 0x0a, 0x54, 0x59, 0x50, 0x45, 0x53, 0x43, 0x52, 0x49, 0x50, 0x54, 0x10, 0x01, 0x42, 0x26, 0x5a, - 0x24, 0x65, 0x6e, 0x63, 0x72, 0x2e, 0x64, 0x65, 0x76, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, - 0x65, 0x6e, 0x63, 0x6f, 0x72, 0x65, 0x2f, 0x70, 0x61, 0x72, 0x73, 0x65, 0x72, 0x2f, 0x6d, 0x65, - 0x74, 0x61, 0x2f, 0x76, 0x31, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x09, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x54, 0x79, 0x70, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x64, 0x6f, + 0x63, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x64, 0x6f, 0x63, 0x12, 0x3c, 0x0a, 0x04, + 0x6b, 0x69, 0x6e, 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x28, 0x2e, 0x65, 0x6e, 0x63, + 0x6f, 0x72, 0x65, 0x2e, 0x70, 0x61, 0x72, 0x73, 0x65, 0x72, 0x2e, 0x6d, 0x65, 0x74, 0x61, 0x2e, + 0x76, 0x31, 0x2e, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x2e, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, + 0x4b, 0x69, 0x6e, 0x64, 0x52, 0x04, 0x6b, 0x69, 0x6e, 0x64, 0x12, 0x26, 0x0a, 0x0c, 0x73, 0x65, + 0x72, 0x76, 0x69, 0x63, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, + 0x48, 0x00, 0x52, 0x0b, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x88, + 0x01, 0x01, 0x12, 0x3b, 0x0a, 0x06, 0x6c, 0x61, 0x62, 0x65, 0x6c, 0x73, 0x18, 0x06, 0x20, 0x03, + 0x28, 0x0b, 0x32, 0x23, 0x2e, 0x65, 0x6e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x70, 0x61, 0x72, 0x73, + 0x65, 0x72, 0x2e, 0x6d, 0x65, 0x74, 0x61, 0x2e, 0x76, 0x31, 0x2e, 0x4d, 0x65, 0x74, 0x72, 0x69, + 0x63, 0x2e, 0x4c, 0x61, 0x62, 0x65, 0x6c, 0x52, 0x06, 0x6c, 0x61, 0x62, 0x65, 0x6c, 0x73, 0x1a, + 0x61, 0x0a, 0x05, 0x4c, 0x61, 0x62, 0x65, 0x6c, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x34, 0x0a, 0x04, 0x74, 0x79, + 0x70, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x20, 0x2e, 0x65, 0x6e, 0x63, 0x6f, 0x72, + 0x65, 0x2e, 0x70, 0x61, 0x72, 0x73, 0x65, 0x72, 0x2e, 0x73, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x2e, + 0x76, 0x31, 0x2e, 0x42, 0x75, 0x69, 0x6c, 0x74, 0x69, 0x6e, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, + 0x12, 0x10, 0x0a, 0x03, 0x64, 0x6f, 0x63, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x64, + 0x6f, 0x63, 0x22, 0x33, 0x0a, 0x0a, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x4b, 0x69, 0x6e, 0x64, + 0x12, 0x0b, 0x0a, 0x07, 0x43, 0x4f, 0x55, 0x4e, 0x54, 0x45, 0x52, 0x10, 0x00, 0x12, 0x09, 0x0a, + 0x05, 0x47, 0x41, 0x55, 0x47, 0x45, 0x10, 0x01, 0x12, 0x0d, 0x0a, 0x09, 0x48, 0x49, 0x53, 0x54, + 0x4f, 0x47, 0x52, 0x41, 0x4d, 0x10, 0x02, 0x42, 0x0f, 0x0a, 0x0d, 0x5f, 0x73, 0x65, 0x72, 0x76, + 0x69, 0x63, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x2a, 0x1e, 0x0a, 0x04, 0x4c, 0x61, 0x6e, 0x67, + 0x12, 0x06, 0x0a, 0x02, 0x47, 0x4f, 0x10, 0x00, 0x12, 0x0e, 0x0a, 0x0a, 0x54, 0x59, 0x50, 0x45, + 0x53, 0x43, 0x52, 0x49, 0x50, 0x54, 0x10, 0x01, 0x42, 0x26, 0x5a, 0x24, 0x65, 0x6e, 0x63, 0x72, + 0x2e, 0x64, 0x65, 0x76, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x65, 0x6e, 0x63, 0x6f, 0x72, + 0x65, 0x2f, 0x70, 0x61, 0x72, 0x73, 0x65, 0x72, 0x2f, 0x6d, 0x65, 0x74, 0x61, 0x2f, 0x76, 0x31, + 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( @@ -4106,136 +4250,140 @@ func file_encore_parser_meta_v1_meta_proto_rawDescGZIP() []byte { return file_encore_parser_meta_v1_meta_proto_rawDescData } -var file_encore_parser_meta_v1_meta_proto_enumTypes = make([]protoimpl.EnumInfo, 10) -var file_encore_parser_meta_v1_meta_proto_msgTypes = make([]protoimpl.MessageInfo, 38) +var file_encore_parser_meta_v1_meta_proto_enumTypes = make([]protoimpl.EnumInfo, 11) +var file_encore_parser_meta_v1_meta_proto_msgTypes = make([]protoimpl.MessageInfo, 39) var file_encore_parser_meta_v1_meta_proto_goTypes = []interface{}{ (Lang)(0), // 0: encore.parser.meta.v1.Lang - (Selector_Type)(0), // 1: encore.parser.meta.v1.Selector.Type - (RPC_AccessType)(0), // 2: encore.parser.meta.v1.RPC.AccessType - (RPC_Protocol)(0), // 3: encore.parser.meta.v1.RPC.Protocol - (StaticCallNode_Package)(0), // 4: encore.parser.meta.v1.StaticCallNode.Package - (Path_Type)(0), // 5: encore.parser.meta.v1.Path.Type - (PathSegment_SegmentType)(0), // 6: encore.parser.meta.v1.PathSegment.SegmentType - (PathSegment_ParamType)(0), // 7: encore.parser.meta.v1.PathSegment.ParamType - (PubSubTopic_DeliveryGuarantee)(0), // 8: encore.parser.meta.v1.PubSubTopic.DeliveryGuarantee - (Metric_MetricKind)(0), // 9: encore.parser.meta.v1.Metric.MetricKind - (*Data)(nil), // 10: encore.parser.meta.v1.Data - (*QualifiedName)(nil), // 11: encore.parser.meta.v1.QualifiedName - (*Package)(nil), // 12: encore.parser.meta.v1.Package - (*Service)(nil), // 13: encore.parser.meta.v1.Service - (*Selector)(nil), // 14: encore.parser.meta.v1.Selector - (*RPC)(nil), // 15: encore.parser.meta.v1.RPC - (*AuthHandler)(nil), // 16: encore.parser.meta.v1.AuthHandler - (*Middleware)(nil), // 17: encore.parser.meta.v1.Middleware - (*TraceNode)(nil), // 18: encore.parser.meta.v1.TraceNode - (*RPCDefNode)(nil), // 19: encore.parser.meta.v1.RPCDefNode - (*RPCCallNode)(nil), // 20: encore.parser.meta.v1.RPCCallNode - (*StaticCallNode)(nil), // 21: encore.parser.meta.v1.StaticCallNode - (*AuthHandlerDefNode)(nil), // 22: encore.parser.meta.v1.AuthHandlerDefNode - (*PubSubTopicDefNode)(nil), // 23: encore.parser.meta.v1.PubSubTopicDefNode - (*PubSubPublishNode)(nil), // 24: encore.parser.meta.v1.PubSubPublishNode - (*PubSubSubscriberNode)(nil), // 25: encore.parser.meta.v1.PubSubSubscriberNode - (*ServiceInitNode)(nil), // 26: encore.parser.meta.v1.ServiceInitNode - (*MiddlewareDefNode)(nil), // 27: encore.parser.meta.v1.MiddlewareDefNode - (*CacheKeyspaceDefNode)(nil), // 28: encore.parser.meta.v1.CacheKeyspaceDefNode - (*Path)(nil), // 29: encore.parser.meta.v1.Path - (*PathSegment)(nil), // 30: encore.parser.meta.v1.PathSegment - (*Gateway)(nil), // 31: encore.parser.meta.v1.Gateway - (*CronJob)(nil), // 32: encore.parser.meta.v1.CronJob - (*SQLDatabase)(nil), // 33: encore.parser.meta.v1.SQLDatabase - (*DBMigration)(nil), // 34: encore.parser.meta.v1.DBMigration - (*Bucket)(nil), // 35: encore.parser.meta.v1.Bucket - (*PubSubTopic)(nil), // 36: encore.parser.meta.v1.PubSubTopic - (*CacheCluster)(nil), // 37: encore.parser.meta.v1.CacheCluster - (*Metric)(nil), // 38: encore.parser.meta.v1.Metric - nil, // 39: encore.parser.meta.v1.RPC.ExposeEntry - (*RPC_ExposeOptions)(nil), // 40: encore.parser.meta.v1.RPC.ExposeOptions - (*RPC_StaticAssets)(nil), // 41: encore.parser.meta.v1.RPC.StaticAssets - (*Gateway_Explicit)(nil), // 42: encore.parser.meta.v1.Gateway.Explicit - (*PubSubTopic_Publisher)(nil), // 43: encore.parser.meta.v1.PubSubTopic.Publisher - (*PubSubTopic_Subscription)(nil), // 44: encore.parser.meta.v1.PubSubTopic.Subscription - (*PubSubTopic_RetryPolicy)(nil), // 45: encore.parser.meta.v1.PubSubTopic.RetryPolicy - (*CacheCluster_Keyspace)(nil), // 46: encore.parser.meta.v1.CacheCluster.Keyspace - (*Metric_Label)(nil), // 47: encore.parser.meta.v1.Metric.Label - (*v1.Decl)(nil), // 48: encore.parser.schema.v1.Decl - (*v1.Type)(nil), // 49: encore.parser.schema.v1.Type - (*v1.Loc)(nil), // 50: encore.parser.schema.v1.Loc - (v1.Builtin)(0), // 51: encore.parser.schema.v1.Builtin + (BucketUsage_Operation)(0), // 1: encore.parser.meta.v1.BucketUsage.Operation + (Selector_Type)(0), // 2: encore.parser.meta.v1.Selector.Type + (RPC_AccessType)(0), // 3: encore.parser.meta.v1.RPC.AccessType + (RPC_Protocol)(0), // 4: encore.parser.meta.v1.RPC.Protocol + (StaticCallNode_Package)(0), // 5: encore.parser.meta.v1.StaticCallNode.Package + (Path_Type)(0), // 6: encore.parser.meta.v1.Path.Type + (PathSegment_SegmentType)(0), // 7: encore.parser.meta.v1.PathSegment.SegmentType + (PathSegment_ParamType)(0), // 8: encore.parser.meta.v1.PathSegment.ParamType + (PubSubTopic_DeliveryGuarantee)(0), // 9: encore.parser.meta.v1.PubSubTopic.DeliveryGuarantee + (Metric_MetricKind)(0), // 10: encore.parser.meta.v1.Metric.MetricKind + (*Data)(nil), // 11: encore.parser.meta.v1.Data + (*QualifiedName)(nil), // 12: encore.parser.meta.v1.QualifiedName + (*Package)(nil), // 13: encore.parser.meta.v1.Package + (*Service)(nil), // 14: encore.parser.meta.v1.Service + (*BucketUsage)(nil), // 15: encore.parser.meta.v1.BucketUsage + (*Selector)(nil), // 16: encore.parser.meta.v1.Selector + (*RPC)(nil), // 17: encore.parser.meta.v1.RPC + (*AuthHandler)(nil), // 18: encore.parser.meta.v1.AuthHandler + (*Middleware)(nil), // 19: encore.parser.meta.v1.Middleware + (*TraceNode)(nil), // 20: encore.parser.meta.v1.TraceNode + (*RPCDefNode)(nil), // 21: encore.parser.meta.v1.RPCDefNode + (*RPCCallNode)(nil), // 22: encore.parser.meta.v1.RPCCallNode + (*StaticCallNode)(nil), // 23: encore.parser.meta.v1.StaticCallNode + (*AuthHandlerDefNode)(nil), // 24: encore.parser.meta.v1.AuthHandlerDefNode + (*PubSubTopicDefNode)(nil), // 25: encore.parser.meta.v1.PubSubTopicDefNode + (*PubSubPublishNode)(nil), // 26: encore.parser.meta.v1.PubSubPublishNode + (*PubSubSubscriberNode)(nil), // 27: encore.parser.meta.v1.PubSubSubscriberNode + (*ServiceInitNode)(nil), // 28: encore.parser.meta.v1.ServiceInitNode + (*MiddlewareDefNode)(nil), // 29: encore.parser.meta.v1.MiddlewareDefNode + (*CacheKeyspaceDefNode)(nil), // 30: encore.parser.meta.v1.CacheKeyspaceDefNode + (*Path)(nil), // 31: encore.parser.meta.v1.Path + (*PathSegment)(nil), // 32: encore.parser.meta.v1.PathSegment + (*Gateway)(nil), // 33: encore.parser.meta.v1.Gateway + (*CronJob)(nil), // 34: encore.parser.meta.v1.CronJob + (*SQLDatabase)(nil), // 35: encore.parser.meta.v1.SQLDatabase + (*DBMigration)(nil), // 36: encore.parser.meta.v1.DBMigration + (*Bucket)(nil), // 37: encore.parser.meta.v1.Bucket + (*PubSubTopic)(nil), // 38: encore.parser.meta.v1.PubSubTopic + (*CacheCluster)(nil), // 39: encore.parser.meta.v1.CacheCluster + (*Metric)(nil), // 40: encore.parser.meta.v1.Metric + nil, // 41: encore.parser.meta.v1.RPC.ExposeEntry + (*RPC_ExposeOptions)(nil), // 42: encore.parser.meta.v1.RPC.ExposeOptions + (*RPC_StaticAssets)(nil), // 43: encore.parser.meta.v1.RPC.StaticAssets + (*Gateway_Explicit)(nil), // 44: encore.parser.meta.v1.Gateway.Explicit + (*PubSubTopic_Publisher)(nil), // 45: encore.parser.meta.v1.PubSubTopic.Publisher + (*PubSubTopic_Subscription)(nil), // 46: encore.parser.meta.v1.PubSubTopic.Subscription + (*PubSubTopic_RetryPolicy)(nil), // 47: encore.parser.meta.v1.PubSubTopic.RetryPolicy + (*CacheCluster_Keyspace)(nil), // 48: encore.parser.meta.v1.CacheCluster.Keyspace + (*Metric_Label)(nil), // 49: encore.parser.meta.v1.Metric.Label + (*v1.Decl)(nil), // 50: encore.parser.schema.v1.Decl + (*v1.Type)(nil), // 51: encore.parser.schema.v1.Type + (*v1.Loc)(nil), // 52: encore.parser.schema.v1.Loc + (v1.Builtin)(0), // 53: encore.parser.schema.v1.Builtin } var file_encore_parser_meta_v1_meta_proto_depIdxs = []int32{ - 48, // 0: encore.parser.meta.v1.Data.decls:type_name -> encore.parser.schema.v1.Decl - 12, // 1: encore.parser.meta.v1.Data.pkgs:type_name -> encore.parser.meta.v1.Package - 13, // 2: encore.parser.meta.v1.Data.svcs:type_name -> encore.parser.meta.v1.Service - 16, // 3: encore.parser.meta.v1.Data.auth_handler:type_name -> encore.parser.meta.v1.AuthHandler - 32, // 4: encore.parser.meta.v1.Data.cron_jobs:type_name -> encore.parser.meta.v1.CronJob - 36, // 5: encore.parser.meta.v1.Data.pubsub_topics:type_name -> encore.parser.meta.v1.PubSubTopic - 17, // 6: encore.parser.meta.v1.Data.middleware:type_name -> encore.parser.meta.v1.Middleware - 37, // 7: encore.parser.meta.v1.Data.cache_clusters:type_name -> encore.parser.meta.v1.CacheCluster - 38, // 8: encore.parser.meta.v1.Data.metrics:type_name -> encore.parser.meta.v1.Metric - 33, // 9: encore.parser.meta.v1.Data.sql_databases:type_name -> encore.parser.meta.v1.SQLDatabase - 31, // 10: encore.parser.meta.v1.Data.gateways:type_name -> encore.parser.meta.v1.Gateway + 50, // 0: encore.parser.meta.v1.Data.decls:type_name -> encore.parser.schema.v1.Decl + 13, // 1: encore.parser.meta.v1.Data.pkgs:type_name -> encore.parser.meta.v1.Package + 14, // 2: encore.parser.meta.v1.Data.svcs:type_name -> encore.parser.meta.v1.Service + 18, // 3: encore.parser.meta.v1.Data.auth_handler:type_name -> encore.parser.meta.v1.AuthHandler + 34, // 4: encore.parser.meta.v1.Data.cron_jobs:type_name -> encore.parser.meta.v1.CronJob + 38, // 5: encore.parser.meta.v1.Data.pubsub_topics:type_name -> encore.parser.meta.v1.PubSubTopic + 19, // 6: encore.parser.meta.v1.Data.middleware:type_name -> encore.parser.meta.v1.Middleware + 39, // 7: encore.parser.meta.v1.Data.cache_clusters:type_name -> encore.parser.meta.v1.CacheCluster + 40, // 8: encore.parser.meta.v1.Data.metrics:type_name -> encore.parser.meta.v1.Metric + 35, // 9: encore.parser.meta.v1.Data.sql_databases:type_name -> encore.parser.meta.v1.SQLDatabase + 33, // 10: encore.parser.meta.v1.Data.gateways:type_name -> encore.parser.meta.v1.Gateway 0, // 11: encore.parser.meta.v1.Data.language:type_name -> encore.parser.meta.v1.Lang - 35, // 12: encore.parser.meta.v1.Data.buckets:type_name -> encore.parser.meta.v1.Bucket - 11, // 13: encore.parser.meta.v1.Package.rpc_calls:type_name -> encore.parser.meta.v1.QualifiedName - 18, // 14: encore.parser.meta.v1.Package.trace_nodes:type_name -> encore.parser.meta.v1.TraceNode - 15, // 15: encore.parser.meta.v1.Service.rpcs:type_name -> encore.parser.meta.v1.RPC - 34, // 16: encore.parser.meta.v1.Service.migrations:type_name -> encore.parser.meta.v1.DBMigration - 1, // 17: encore.parser.meta.v1.Selector.type:type_name -> encore.parser.meta.v1.Selector.Type - 2, // 18: encore.parser.meta.v1.RPC.access_type:type_name -> encore.parser.meta.v1.RPC.AccessType - 49, // 19: encore.parser.meta.v1.RPC.request_schema:type_name -> encore.parser.schema.v1.Type - 49, // 20: encore.parser.meta.v1.RPC.response_schema:type_name -> encore.parser.schema.v1.Type - 3, // 21: encore.parser.meta.v1.RPC.proto:type_name -> encore.parser.meta.v1.RPC.Protocol - 50, // 22: encore.parser.meta.v1.RPC.loc:type_name -> encore.parser.schema.v1.Loc - 29, // 23: encore.parser.meta.v1.RPC.path:type_name -> encore.parser.meta.v1.Path - 14, // 24: encore.parser.meta.v1.RPC.tags:type_name -> encore.parser.meta.v1.Selector - 39, // 25: encore.parser.meta.v1.RPC.expose:type_name -> encore.parser.meta.v1.RPC.ExposeEntry - 49, // 26: encore.parser.meta.v1.RPC.handshake_schema:type_name -> encore.parser.schema.v1.Type - 41, // 27: encore.parser.meta.v1.RPC.static_assets:type_name -> encore.parser.meta.v1.RPC.StaticAssets - 50, // 28: encore.parser.meta.v1.AuthHandler.loc:type_name -> encore.parser.schema.v1.Loc - 49, // 29: encore.parser.meta.v1.AuthHandler.auth_data:type_name -> encore.parser.schema.v1.Type - 49, // 30: encore.parser.meta.v1.AuthHandler.params:type_name -> encore.parser.schema.v1.Type - 11, // 31: encore.parser.meta.v1.Middleware.name:type_name -> encore.parser.meta.v1.QualifiedName - 50, // 32: encore.parser.meta.v1.Middleware.loc:type_name -> encore.parser.schema.v1.Loc - 14, // 33: encore.parser.meta.v1.Middleware.target:type_name -> encore.parser.meta.v1.Selector - 19, // 34: encore.parser.meta.v1.TraceNode.rpc_def:type_name -> encore.parser.meta.v1.RPCDefNode - 20, // 35: encore.parser.meta.v1.TraceNode.rpc_call:type_name -> encore.parser.meta.v1.RPCCallNode - 21, // 36: encore.parser.meta.v1.TraceNode.static_call:type_name -> encore.parser.meta.v1.StaticCallNode - 22, // 37: encore.parser.meta.v1.TraceNode.auth_handler_def:type_name -> encore.parser.meta.v1.AuthHandlerDefNode - 23, // 38: encore.parser.meta.v1.TraceNode.pubsub_topic_def:type_name -> encore.parser.meta.v1.PubSubTopicDefNode - 24, // 39: encore.parser.meta.v1.TraceNode.pubsub_publish:type_name -> encore.parser.meta.v1.PubSubPublishNode - 25, // 40: encore.parser.meta.v1.TraceNode.pubsub_subscriber:type_name -> encore.parser.meta.v1.PubSubSubscriberNode - 26, // 41: encore.parser.meta.v1.TraceNode.service_init:type_name -> encore.parser.meta.v1.ServiceInitNode - 27, // 42: encore.parser.meta.v1.TraceNode.middleware_def:type_name -> encore.parser.meta.v1.MiddlewareDefNode - 28, // 43: encore.parser.meta.v1.TraceNode.cache_keyspace:type_name -> encore.parser.meta.v1.CacheKeyspaceDefNode - 4, // 44: encore.parser.meta.v1.StaticCallNode.package:type_name -> encore.parser.meta.v1.StaticCallNode.Package - 14, // 45: encore.parser.meta.v1.MiddlewareDefNode.target:type_name -> encore.parser.meta.v1.Selector - 30, // 46: encore.parser.meta.v1.Path.segments:type_name -> encore.parser.meta.v1.PathSegment - 5, // 47: encore.parser.meta.v1.Path.type:type_name -> encore.parser.meta.v1.Path.Type - 6, // 48: encore.parser.meta.v1.PathSegment.type:type_name -> encore.parser.meta.v1.PathSegment.SegmentType - 7, // 49: encore.parser.meta.v1.PathSegment.value_type:type_name -> encore.parser.meta.v1.PathSegment.ParamType - 42, // 50: encore.parser.meta.v1.Gateway.explicit:type_name -> encore.parser.meta.v1.Gateway.Explicit - 11, // 51: encore.parser.meta.v1.CronJob.endpoint:type_name -> encore.parser.meta.v1.QualifiedName - 34, // 52: encore.parser.meta.v1.SQLDatabase.migrations:type_name -> encore.parser.meta.v1.DBMigration - 49, // 53: encore.parser.meta.v1.PubSubTopic.message_type:type_name -> encore.parser.schema.v1.Type - 8, // 54: encore.parser.meta.v1.PubSubTopic.delivery_guarantee:type_name -> encore.parser.meta.v1.PubSubTopic.DeliveryGuarantee - 43, // 55: encore.parser.meta.v1.PubSubTopic.publishers:type_name -> encore.parser.meta.v1.PubSubTopic.Publisher - 44, // 56: encore.parser.meta.v1.PubSubTopic.subscriptions:type_name -> encore.parser.meta.v1.PubSubTopic.Subscription - 46, // 57: encore.parser.meta.v1.CacheCluster.keyspaces:type_name -> encore.parser.meta.v1.CacheCluster.Keyspace - 51, // 58: encore.parser.meta.v1.Metric.value_type:type_name -> encore.parser.schema.v1.Builtin - 9, // 59: encore.parser.meta.v1.Metric.kind:type_name -> encore.parser.meta.v1.Metric.MetricKind - 47, // 60: encore.parser.meta.v1.Metric.labels:type_name -> encore.parser.meta.v1.Metric.Label - 40, // 61: encore.parser.meta.v1.RPC.ExposeEntry.value:type_name -> encore.parser.meta.v1.RPC.ExposeOptions - 16, // 62: encore.parser.meta.v1.Gateway.Explicit.auth_handler:type_name -> encore.parser.meta.v1.AuthHandler - 45, // 63: encore.parser.meta.v1.PubSubTopic.Subscription.retry_policy:type_name -> encore.parser.meta.v1.PubSubTopic.RetryPolicy - 49, // 64: encore.parser.meta.v1.CacheCluster.Keyspace.key_type:type_name -> encore.parser.schema.v1.Type - 49, // 65: encore.parser.meta.v1.CacheCluster.Keyspace.value_type:type_name -> encore.parser.schema.v1.Type - 29, // 66: encore.parser.meta.v1.CacheCluster.Keyspace.path_pattern:type_name -> encore.parser.meta.v1.Path - 51, // 67: encore.parser.meta.v1.Metric.Label.type:type_name -> encore.parser.schema.v1.Builtin - 68, // [68:68] is the sub-list for method output_type - 68, // [68:68] is the sub-list for method input_type - 68, // [68:68] is the sub-list for extension type_name - 68, // [68:68] is the sub-list for extension extendee - 0, // [0:68] is the sub-list for field type_name + 37, // 12: encore.parser.meta.v1.Data.buckets:type_name -> encore.parser.meta.v1.Bucket + 12, // 13: encore.parser.meta.v1.Package.rpc_calls:type_name -> encore.parser.meta.v1.QualifiedName + 20, // 14: encore.parser.meta.v1.Package.trace_nodes:type_name -> encore.parser.meta.v1.TraceNode + 17, // 15: encore.parser.meta.v1.Service.rpcs:type_name -> encore.parser.meta.v1.RPC + 36, // 16: encore.parser.meta.v1.Service.migrations:type_name -> encore.parser.meta.v1.DBMigration + 15, // 17: encore.parser.meta.v1.Service.buckets:type_name -> encore.parser.meta.v1.BucketUsage + 1, // 18: encore.parser.meta.v1.BucketUsage.operations:type_name -> encore.parser.meta.v1.BucketUsage.Operation + 2, // 19: encore.parser.meta.v1.Selector.type:type_name -> encore.parser.meta.v1.Selector.Type + 3, // 20: encore.parser.meta.v1.RPC.access_type:type_name -> encore.parser.meta.v1.RPC.AccessType + 51, // 21: encore.parser.meta.v1.RPC.request_schema:type_name -> encore.parser.schema.v1.Type + 51, // 22: encore.parser.meta.v1.RPC.response_schema:type_name -> encore.parser.schema.v1.Type + 4, // 23: encore.parser.meta.v1.RPC.proto:type_name -> encore.parser.meta.v1.RPC.Protocol + 52, // 24: encore.parser.meta.v1.RPC.loc:type_name -> encore.parser.schema.v1.Loc + 31, // 25: encore.parser.meta.v1.RPC.path:type_name -> encore.parser.meta.v1.Path + 16, // 26: encore.parser.meta.v1.RPC.tags:type_name -> encore.parser.meta.v1.Selector + 41, // 27: encore.parser.meta.v1.RPC.expose:type_name -> encore.parser.meta.v1.RPC.ExposeEntry + 51, // 28: encore.parser.meta.v1.RPC.handshake_schema:type_name -> encore.parser.schema.v1.Type + 43, // 29: encore.parser.meta.v1.RPC.static_assets:type_name -> encore.parser.meta.v1.RPC.StaticAssets + 52, // 30: encore.parser.meta.v1.AuthHandler.loc:type_name -> encore.parser.schema.v1.Loc + 51, // 31: encore.parser.meta.v1.AuthHandler.auth_data:type_name -> encore.parser.schema.v1.Type + 51, // 32: encore.parser.meta.v1.AuthHandler.params:type_name -> encore.parser.schema.v1.Type + 12, // 33: encore.parser.meta.v1.Middleware.name:type_name -> encore.parser.meta.v1.QualifiedName + 52, // 34: encore.parser.meta.v1.Middleware.loc:type_name -> encore.parser.schema.v1.Loc + 16, // 35: encore.parser.meta.v1.Middleware.target:type_name -> encore.parser.meta.v1.Selector + 21, // 36: encore.parser.meta.v1.TraceNode.rpc_def:type_name -> encore.parser.meta.v1.RPCDefNode + 22, // 37: encore.parser.meta.v1.TraceNode.rpc_call:type_name -> encore.parser.meta.v1.RPCCallNode + 23, // 38: encore.parser.meta.v1.TraceNode.static_call:type_name -> encore.parser.meta.v1.StaticCallNode + 24, // 39: encore.parser.meta.v1.TraceNode.auth_handler_def:type_name -> encore.parser.meta.v1.AuthHandlerDefNode + 25, // 40: encore.parser.meta.v1.TraceNode.pubsub_topic_def:type_name -> encore.parser.meta.v1.PubSubTopicDefNode + 26, // 41: encore.parser.meta.v1.TraceNode.pubsub_publish:type_name -> encore.parser.meta.v1.PubSubPublishNode + 27, // 42: encore.parser.meta.v1.TraceNode.pubsub_subscriber:type_name -> encore.parser.meta.v1.PubSubSubscriberNode + 28, // 43: encore.parser.meta.v1.TraceNode.service_init:type_name -> encore.parser.meta.v1.ServiceInitNode + 29, // 44: encore.parser.meta.v1.TraceNode.middleware_def:type_name -> encore.parser.meta.v1.MiddlewareDefNode + 30, // 45: encore.parser.meta.v1.TraceNode.cache_keyspace:type_name -> encore.parser.meta.v1.CacheKeyspaceDefNode + 5, // 46: encore.parser.meta.v1.StaticCallNode.package:type_name -> encore.parser.meta.v1.StaticCallNode.Package + 16, // 47: encore.parser.meta.v1.MiddlewareDefNode.target:type_name -> encore.parser.meta.v1.Selector + 32, // 48: encore.parser.meta.v1.Path.segments:type_name -> encore.parser.meta.v1.PathSegment + 6, // 49: encore.parser.meta.v1.Path.type:type_name -> encore.parser.meta.v1.Path.Type + 7, // 50: encore.parser.meta.v1.PathSegment.type:type_name -> encore.parser.meta.v1.PathSegment.SegmentType + 8, // 51: encore.parser.meta.v1.PathSegment.value_type:type_name -> encore.parser.meta.v1.PathSegment.ParamType + 44, // 52: encore.parser.meta.v1.Gateway.explicit:type_name -> encore.parser.meta.v1.Gateway.Explicit + 12, // 53: encore.parser.meta.v1.CronJob.endpoint:type_name -> encore.parser.meta.v1.QualifiedName + 36, // 54: encore.parser.meta.v1.SQLDatabase.migrations:type_name -> encore.parser.meta.v1.DBMigration + 51, // 55: encore.parser.meta.v1.PubSubTopic.message_type:type_name -> encore.parser.schema.v1.Type + 9, // 56: encore.parser.meta.v1.PubSubTopic.delivery_guarantee:type_name -> encore.parser.meta.v1.PubSubTopic.DeliveryGuarantee + 45, // 57: encore.parser.meta.v1.PubSubTopic.publishers:type_name -> encore.parser.meta.v1.PubSubTopic.Publisher + 46, // 58: encore.parser.meta.v1.PubSubTopic.subscriptions:type_name -> encore.parser.meta.v1.PubSubTopic.Subscription + 48, // 59: encore.parser.meta.v1.CacheCluster.keyspaces:type_name -> encore.parser.meta.v1.CacheCluster.Keyspace + 53, // 60: encore.parser.meta.v1.Metric.value_type:type_name -> encore.parser.schema.v1.Builtin + 10, // 61: encore.parser.meta.v1.Metric.kind:type_name -> encore.parser.meta.v1.Metric.MetricKind + 49, // 62: encore.parser.meta.v1.Metric.labels:type_name -> encore.parser.meta.v1.Metric.Label + 42, // 63: encore.parser.meta.v1.RPC.ExposeEntry.value:type_name -> encore.parser.meta.v1.RPC.ExposeOptions + 18, // 64: encore.parser.meta.v1.Gateway.Explicit.auth_handler:type_name -> encore.parser.meta.v1.AuthHandler + 47, // 65: encore.parser.meta.v1.PubSubTopic.Subscription.retry_policy:type_name -> encore.parser.meta.v1.PubSubTopic.RetryPolicy + 51, // 66: encore.parser.meta.v1.CacheCluster.Keyspace.key_type:type_name -> encore.parser.schema.v1.Type + 51, // 67: encore.parser.meta.v1.CacheCluster.Keyspace.value_type:type_name -> encore.parser.schema.v1.Type + 31, // 68: encore.parser.meta.v1.CacheCluster.Keyspace.path_pattern:type_name -> encore.parser.meta.v1.Path + 53, // 69: encore.parser.meta.v1.Metric.Label.type:type_name -> encore.parser.schema.v1.Builtin + 70, // [70:70] is the sub-list for method output_type + 70, // [70:70] is the sub-list for method input_type + 70, // [70:70] is the sub-list for extension type_name + 70, // [70:70] is the sub-list for extension extendee + 0, // [0:70] is the sub-list for field type_name } func init() { file_encore_parser_meta_v1_meta_proto_init() } @@ -4293,7 +4441,7 @@ func file_encore_parser_meta_v1_meta_proto_init() { } } file_encore_parser_meta_v1_meta_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*Selector); i { + switch v := v.(*BucketUsage); i { case 0: return &v.state case 1: @@ -4305,7 +4453,7 @@ func file_encore_parser_meta_v1_meta_proto_init() { } } file_encore_parser_meta_v1_meta_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*RPC); i { + switch v := v.(*Selector); i { case 0: return &v.state case 1: @@ -4317,7 +4465,7 @@ func file_encore_parser_meta_v1_meta_proto_init() { } } file_encore_parser_meta_v1_meta_proto_msgTypes[6].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*AuthHandler); i { + switch v := v.(*RPC); i { case 0: return &v.state case 1: @@ -4329,7 +4477,7 @@ func file_encore_parser_meta_v1_meta_proto_init() { } } file_encore_parser_meta_v1_meta_proto_msgTypes[7].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*Middleware); i { + switch v := v.(*AuthHandler); i { case 0: return &v.state case 1: @@ -4341,7 +4489,7 @@ func file_encore_parser_meta_v1_meta_proto_init() { } } file_encore_parser_meta_v1_meta_proto_msgTypes[8].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*TraceNode); i { + switch v := v.(*Middleware); i { case 0: return &v.state case 1: @@ -4353,7 +4501,7 @@ func file_encore_parser_meta_v1_meta_proto_init() { } } file_encore_parser_meta_v1_meta_proto_msgTypes[9].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*RPCDefNode); i { + switch v := v.(*TraceNode); i { case 0: return &v.state case 1: @@ -4365,7 +4513,7 @@ func file_encore_parser_meta_v1_meta_proto_init() { } } file_encore_parser_meta_v1_meta_proto_msgTypes[10].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*RPCCallNode); i { + switch v := v.(*RPCDefNode); i { case 0: return &v.state case 1: @@ -4377,7 +4525,7 @@ func file_encore_parser_meta_v1_meta_proto_init() { } } file_encore_parser_meta_v1_meta_proto_msgTypes[11].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*StaticCallNode); i { + switch v := v.(*RPCCallNode); i { case 0: return &v.state case 1: @@ -4389,7 +4537,7 @@ func file_encore_parser_meta_v1_meta_proto_init() { } } file_encore_parser_meta_v1_meta_proto_msgTypes[12].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*AuthHandlerDefNode); i { + switch v := v.(*StaticCallNode); i { case 0: return &v.state case 1: @@ -4401,7 +4549,7 @@ func file_encore_parser_meta_v1_meta_proto_init() { } } file_encore_parser_meta_v1_meta_proto_msgTypes[13].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*PubSubTopicDefNode); i { + switch v := v.(*AuthHandlerDefNode); i { case 0: return &v.state case 1: @@ -4413,7 +4561,7 @@ func file_encore_parser_meta_v1_meta_proto_init() { } } file_encore_parser_meta_v1_meta_proto_msgTypes[14].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*PubSubPublishNode); i { + switch v := v.(*PubSubTopicDefNode); i { case 0: return &v.state case 1: @@ -4425,7 +4573,7 @@ func file_encore_parser_meta_v1_meta_proto_init() { } } file_encore_parser_meta_v1_meta_proto_msgTypes[15].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*PubSubSubscriberNode); i { + switch v := v.(*PubSubPublishNode); i { case 0: return &v.state case 1: @@ -4437,7 +4585,7 @@ func file_encore_parser_meta_v1_meta_proto_init() { } } file_encore_parser_meta_v1_meta_proto_msgTypes[16].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*ServiceInitNode); i { + switch v := v.(*PubSubSubscriberNode); i { case 0: return &v.state case 1: @@ -4449,7 +4597,7 @@ func file_encore_parser_meta_v1_meta_proto_init() { } } file_encore_parser_meta_v1_meta_proto_msgTypes[17].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*MiddlewareDefNode); i { + switch v := v.(*ServiceInitNode); i { case 0: return &v.state case 1: @@ -4461,7 +4609,7 @@ func file_encore_parser_meta_v1_meta_proto_init() { } } file_encore_parser_meta_v1_meta_proto_msgTypes[18].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*CacheKeyspaceDefNode); i { + switch v := v.(*MiddlewareDefNode); i { case 0: return &v.state case 1: @@ -4473,7 +4621,7 @@ func file_encore_parser_meta_v1_meta_proto_init() { } } file_encore_parser_meta_v1_meta_proto_msgTypes[19].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*Path); i { + switch v := v.(*CacheKeyspaceDefNode); i { case 0: return &v.state case 1: @@ -4485,7 +4633,7 @@ func file_encore_parser_meta_v1_meta_proto_init() { } } file_encore_parser_meta_v1_meta_proto_msgTypes[20].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*PathSegment); i { + switch v := v.(*Path); i { case 0: return &v.state case 1: @@ -4497,7 +4645,7 @@ func file_encore_parser_meta_v1_meta_proto_init() { } } file_encore_parser_meta_v1_meta_proto_msgTypes[21].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*Gateway); i { + switch v := v.(*PathSegment); i { case 0: return &v.state case 1: @@ -4509,7 +4657,7 @@ func file_encore_parser_meta_v1_meta_proto_init() { } } file_encore_parser_meta_v1_meta_proto_msgTypes[22].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*CronJob); i { + switch v := v.(*Gateway); i { case 0: return &v.state case 1: @@ -4521,7 +4669,7 @@ func file_encore_parser_meta_v1_meta_proto_init() { } } file_encore_parser_meta_v1_meta_proto_msgTypes[23].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*SQLDatabase); i { + switch v := v.(*CronJob); i { case 0: return &v.state case 1: @@ -4533,7 +4681,7 @@ func file_encore_parser_meta_v1_meta_proto_init() { } } file_encore_parser_meta_v1_meta_proto_msgTypes[24].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*DBMigration); i { + switch v := v.(*SQLDatabase); i { case 0: return &v.state case 1: @@ -4545,7 +4693,7 @@ func file_encore_parser_meta_v1_meta_proto_init() { } } file_encore_parser_meta_v1_meta_proto_msgTypes[25].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*Bucket); i { + switch v := v.(*DBMigration); i { case 0: return &v.state case 1: @@ -4557,7 +4705,7 @@ func file_encore_parser_meta_v1_meta_proto_init() { } } file_encore_parser_meta_v1_meta_proto_msgTypes[26].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*PubSubTopic); i { + switch v := v.(*Bucket); i { case 0: return &v.state case 1: @@ -4569,7 +4717,7 @@ func file_encore_parser_meta_v1_meta_proto_init() { } } file_encore_parser_meta_v1_meta_proto_msgTypes[27].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*CacheCluster); i { + switch v := v.(*PubSubTopic); i { case 0: return &v.state case 1: @@ -4581,6 +4729,18 @@ func file_encore_parser_meta_v1_meta_proto_init() { } } file_encore_parser_meta_v1_meta_proto_msgTypes[28].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*CacheCluster); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_encore_parser_meta_v1_meta_proto_msgTypes[29].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*Metric); i { case 0: return &v.state @@ -4592,7 +4752,7 @@ func file_encore_parser_meta_v1_meta_proto_init() { return nil } } - file_encore_parser_meta_v1_meta_proto_msgTypes[30].Exporter = func(v interface{}, i int) interface{} { + file_encore_parser_meta_v1_meta_proto_msgTypes[31].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*RPC_ExposeOptions); i { case 0: return &v.state @@ -4604,7 +4764,7 @@ func file_encore_parser_meta_v1_meta_proto_init() { return nil } } - file_encore_parser_meta_v1_meta_proto_msgTypes[31].Exporter = func(v interface{}, i int) interface{} { + file_encore_parser_meta_v1_meta_proto_msgTypes[32].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*RPC_StaticAssets); i { case 0: return &v.state @@ -4616,7 +4776,7 @@ func file_encore_parser_meta_v1_meta_proto_init() { return nil } } - file_encore_parser_meta_v1_meta_proto_msgTypes[32].Exporter = func(v interface{}, i int) interface{} { + file_encore_parser_meta_v1_meta_proto_msgTypes[33].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*Gateway_Explicit); i { case 0: return &v.state @@ -4628,7 +4788,7 @@ func file_encore_parser_meta_v1_meta_proto_init() { return nil } } - file_encore_parser_meta_v1_meta_proto_msgTypes[33].Exporter = func(v interface{}, i int) interface{} { + file_encore_parser_meta_v1_meta_proto_msgTypes[34].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*PubSubTopic_Publisher); i { case 0: return &v.state @@ -4640,7 +4800,7 @@ func file_encore_parser_meta_v1_meta_proto_init() { return nil } } - file_encore_parser_meta_v1_meta_proto_msgTypes[34].Exporter = func(v interface{}, i int) interface{} { + file_encore_parser_meta_v1_meta_proto_msgTypes[35].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*PubSubTopic_Subscription); i { case 0: return &v.state @@ -4652,7 +4812,7 @@ func file_encore_parser_meta_v1_meta_proto_init() { return nil } } - file_encore_parser_meta_v1_meta_proto_msgTypes[35].Exporter = func(v interface{}, i int) interface{} { + file_encore_parser_meta_v1_meta_proto_msgTypes[36].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*PubSubTopic_RetryPolicy); i { case 0: return &v.state @@ -4664,7 +4824,7 @@ func file_encore_parser_meta_v1_meta_proto_init() { return nil } } - file_encore_parser_meta_v1_meta_proto_msgTypes[36].Exporter = func(v interface{}, i int) interface{} { + file_encore_parser_meta_v1_meta_proto_msgTypes[37].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*CacheCluster_Keyspace); i { case 0: return &v.state @@ -4676,7 +4836,7 @@ func file_encore_parser_meta_v1_meta_proto_init() { return nil } } - file_encore_parser_meta_v1_meta_proto_msgTypes[37].Exporter = func(v interface{}, i int) interface{} { + file_encore_parser_meta_v1_meta_proto_msgTypes[38].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*Metric_Label); i { case 0: return &v.state @@ -4690,10 +4850,10 @@ func file_encore_parser_meta_v1_meta_proto_init() { } } file_encore_parser_meta_v1_meta_proto_msgTypes[0].OneofWrappers = []interface{}{} - file_encore_parser_meta_v1_meta_proto_msgTypes[5].OneofWrappers = []interface{}{} file_encore_parser_meta_v1_meta_proto_msgTypes[6].OneofWrappers = []interface{}{} file_encore_parser_meta_v1_meta_proto_msgTypes[7].OneofWrappers = []interface{}{} - file_encore_parser_meta_v1_meta_proto_msgTypes[8].OneofWrappers = []interface{}{ + file_encore_parser_meta_v1_meta_proto_msgTypes[8].OneofWrappers = []interface{}{} + file_encore_parser_meta_v1_meta_proto_msgTypes[9].OneofWrappers = []interface{}{ (*TraceNode_RpcDef)(nil), (*TraceNode_RpcCall)(nil), (*TraceNode_StaticCall)(nil), @@ -4705,22 +4865,22 @@ func file_encore_parser_meta_v1_meta_proto_init() { (*TraceNode_MiddlewareDef)(nil), (*TraceNode_CacheKeyspace)(nil), } - file_encore_parser_meta_v1_meta_proto_msgTypes[21].OneofWrappers = []interface{}{} file_encore_parser_meta_v1_meta_proto_msgTypes[22].OneofWrappers = []interface{}{} file_encore_parser_meta_v1_meta_proto_msgTypes[23].OneofWrappers = []interface{}{} - file_encore_parser_meta_v1_meta_proto_msgTypes[25].OneofWrappers = []interface{}{} + file_encore_parser_meta_v1_meta_proto_msgTypes[24].OneofWrappers = []interface{}{} file_encore_parser_meta_v1_meta_proto_msgTypes[26].OneofWrappers = []interface{}{} - file_encore_parser_meta_v1_meta_proto_msgTypes[28].OneofWrappers = []interface{}{} - file_encore_parser_meta_v1_meta_proto_msgTypes[31].OneofWrappers = []interface{}{} + file_encore_parser_meta_v1_meta_proto_msgTypes[27].OneofWrappers = []interface{}{} + file_encore_parser_meta_v1_meta_proto_msgTypes[29].OneofWrappers = []interface{}{} file_encore_parser_meta_v1_meta_proto_msgTypes[32].OneofWrappers = []interface{}{} - file_encore_parser_meta_v1_meta_proto_msgTypes[34].OneofWrappers = []interface{}{} + file_encore_parser_meta_v1_meta_proto_msgTypes[33].OneofWrappers = []interface{}{} + file_encore_parser_meta_v1_meta_proto_msgTypes[35].OneofWrappers = []interface{}{} type x struct{} out := protoimpl.TypeBuilder{ File: protoimpl.DescBuilder{ GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_encore_parser_meta_v1_meta_proto_rawDesc, - NumEnums: 10, - NumMessages: 38, + NumEnums: 11, + NumMessages: 39, NumExtensions: 0, NumServices: 0, }, diff --git a/proto/encore/parser/meta/v1/meta.proto b/proto/encore/parser/meta/v1/meta.proto index 29bb335901..2bd4fd721d 100644 --- a/proto/encore/parser/meta/v1/meta.proto +++ b/proto/encore/parser/meta/v1/meta.proto @@ -59,7 +59,37 @@ message Service { repeated DBMigration migrations = 4; repeated string databases = 5; // databases this service connects to bool has_config = 6; // true if the service has uses config - repeated string buckets = 7; // buckets this service uses + repeated BucketUsage buckets = 7; // buckets this service uses +} + +message BucketUsage { + // The encore name of the bucket. + string bucket = 1; + + // Recorded operations. + repeated Operation operations = 2; + + enum Operation { + UNKNOWN = 0; + + // Listing objects and accessing their metadata during listing. + LIST_OBJECTS = 1; + + // Reading the contents of an object. + READ_OBJECT_CONTENTS = 2; + + // Creating or updating an object, with contents and metadata. + WRITE_OBJECT = 3; + + // Updating the metadata of an object, without reading or writing its contents. + UPDATE_OBJECT_METADATA = 4; + + // Reading the metadata of an object, or checking for its existence. + GET_OBJECT_METADATA = 5; + + // Deleting an object. + DELETE_OBJECT = 6; + } } message Selector { diff --git a/runtimes/core/Cargo.toml b/runtimes/core/Cargo.toml index db3a58912f..9cf12a9229 100644 --- a/runtimes/core/Cargo.toml +++ b/runtimes/core/Cargo.toml @@ -89,6 +89,7 @@ serde_path_to_error = "0.1.16" tracing = "0.1.40" tracing-subscriber = { version = "0.3.18", features = ["alloc", "ansi", "env-filter", "fmt", "matchers", "nu-ansi-term", "once_cell", "regex", "registry", "sharded-slab", "smallvec", "std", "thread_local", "tracing"], default-features = false } thiserror = "1.0.64" +async-stream = "0.3.6" [build-dependencies] prost-build = "0.12.3" diff --git a/runtimes/core/src/objects/gcs/bucket.rs b/runtimes/core/src/objects/gcs/bucket.rs index 5d7664f0ac..bfec34ecd5 100644 --- a/runtimes/core/src/objects/gcs/bucket.rs +++ b/runtimes/core/src/objects/gcs/bucket.rs @@ -1,3 +1,4 @@ +use async_stream::try_stream; use futures::TryStreamExt; use google_cloud_storage::http::objects::download::Range; use google_cloud_storage::http::objects::get::GetObjectRequest; @@ -34,6 +35,53 @@ impl objects::BucketImpl for Bucket { fn object(self: Arc, name: String) -> Arc { Arc::new(Object { bkt: self, name }) } + + fn list( + self: Arc, + ) -> Pin> + Send + 'static>> + { + Box::pin(async move { + match self.client.get().await { + Ok(client) => { + let client = client.clone(); + let s: objects::ListStream = Box::new(try_stream! { + let mut req = gcs::http::objects::list::ListObjectsRequest { + bucket: self.name.to_string(), + ..Default::default() + }; + + loop { + let resp = client.list_objects(&req).await.map_err(|e| Error::Other(e.into()))?; + if let Some(items) = resp.items { + for obj in items { + let attrs = ObjectAttrs { + name: obj.name, + version: obj.generation.to_string(), + size: obj.size as u64, + content_type: obj.content_type, + etag: obj.etag, + }; + yield attrs; + } + } + + req.page_token = resp.next_page_token; + if req.page_token.is_none() { + break; + } + } + }); + + Ok(s) + } + + Err(err) => Err(Error::Internal(anyhow::anyhow!( + "unable to resolve client: {}", + err + ))), + } + }) + } } #[derive(Debug)] @@ -81,6 +129,7 @@ impl objects::ObjectImpl for Object { } }) } + fn exists(self: Arc) -> Pin> + Send>> { Box::pin(async move { match self.bkt.client.get().await { @@ -185,6 +234,37 @@ impl objects::ObjectImpl for Object { } }) } + + fn delete(self: Arc) -> Pin> + Send>> { + Box::pin(async move { + match self.bkt.client.get().await { + Ok(client) => { + use gcs::http::{error::ErrorResponse, Error as GCSError}; + let req = &gcs::http::objects::delete::DeleteObjectRequest { + bucket: self.bkt.name.to_string(), + object: self.name.clone(), + ..Default::default() + }; + + match client.delete_object(req).await { + Ok(_) => Ok(()), + Err(GCSError::Response(ErrorResponse { code: 404, .. })) => Ok(()), + Err(GCSError::HttpClient(err)) + if err.status().is_some_and(|v| v.as_u16() == 404) => + { + Ok(()) + } + + Err(err) => Err(Error::Other(err.into())), + } + } + Err(err) => Err(Error::Internal(anyhow::anyhow!( + "unable to resolve client: {}", + err + ))), + } + }) + } } fn apply_upload_opts(opts: UploadOptions, req: &mut UploadObjectRequest, media: &mut Media) { diff --git a/runtimes/core/src/objects/gcs/mod.rs b/runtimes/core/src/objects/gcs/mod.rs index f1845a8f3f..e6b9d19099 100644 --- a/runtimes/core/src/objects/gcs/mod.rs +++ b/runtimes/core/src/objects/gcs/mod.rs @@ -29,7 +29,7 @@ impl objects::ClusterImpl for Cluster { struct LazyGCSClient { cfg: pb::bucket_cluster::Gcs, - cell: tokio::sync::OnceCell>, + cell: tokio::sync::OnceCell>>, } impl Debug for LazyGCSClient { @@ -46,7 +46,7 @@ impl LazyGCSClient { } } - async fn get(&self) -> &anyhow::Result { + async fn get(&self) -> &anyhow::Result> { self.cell .get_or_init(|| async { let mut config = gcs::client::ClientConfig::default() @@ -58,7 +58,7 @@ impl LazyGCSClient { config.storage_endpoint.clone_from(endpoint); } - Ok(gcs::client::Client::new(config)) + Ok(Arc::new(gcs::client::Client::new(config))) }) .await } diff --git a/runtimes/core/src/objects/mod.rs b/runtimes/core/src/objects/mod.rs index 858ac3ea26..4dfaebb39f 100644 --- a/runtimes/core/src/objects/mod.rs +++ b/runtimes/core/src/objects/mod.rs @@ -21,8 +21,14 @@ trait ClusterImpl: Debug + Send + Sync { trait BucketImpl: Debug + Send + Sync { fn object(self: Arc, name: String) -> Arc; + + fn list( + self: Arc, + ) -> Pin> + Send + 'static>>; } +pub type ListStream = Box> + Send>; + trait ObjectImpl: Debug + Send + Sync { fn exists(self: Arc) -> Pin> + Send>>; @@ -37,6 +43,8 @@ trait ObjectImpl: Debug + Send + Sync { ) -> Pin> + Send>>; fn attrs(self: Arc) -> Pin> + Send>>; + + fn delete(self: Arc) -> Pin> + Send>>; } #[derive(Debug)] @@ -52,6 +60,10 @@ impl Bucket { _tracer: self.tracer.clone(), } } + + pub async fn list(&self) -> Result { + self.imp.clone().list().await + } } #[derive(Debug)] @@ -96,6 +108,10 @@ impl Object { pub async fn attrs(&self) -> Result { self.imp.clone().attrs().await } + + pub async fn delete(&self) -> Result<(), Error> { + self.imp.clone().delete().await + } } #[derive(thiserror::Error, Debug)] diff --git a/runtimes/core/src/objects/noop/mod.rs b/runtimes/core/src/objects/noop/mod.rs index a471a28107..df83a20c1c 100644 --- a/runtimes/core/src/objects/noop/mod.rs +++ b/runtimes/core/src/objects/noop/mod.rs @@ -27,6 +27,17 @@ impl objects::BucketImpl for Bucket { fn object(self: Arc, _name: String) -> Arc { Arc::new(Object) } + + fn list( + self: Arc, + ) -> Pin> + Send + 'static>> + { + Box::pin(async move { + Err(objects::Error::Internal(anyhow::anyhow!( + "noop bucket does not support list" + ))) + }) + } } impl objects::ObjectImpl for Object { @@ -64,4 +75,10 @@ impl objects::ObjectImpl for Object { ))) }) } + + fn delete(self: Arc) -> Pin> + Send>> { + Box::pin(future::ready(Err(objects::Error::Internal( + anyhow::anyhow!("noop bucket does not support delete"), + )))) + } } diff --git a/runtimes/core/src/objects/s3/bucket.rs b/runtimes/core/src/objects/s3/bucket.rs index 6e0ab08313..0bef81e7d2 100644 --- a/runtimes/core/src/objects/s3/bucket.rs +++ b/runtimes/core/src/objects/s3/bucket.rs @@ -28,6 +28,17 @@ impl objects::BucketImpl for Bucket { name, }) } + + fn list( + self: Arc, + ) -> Pin> + Send + 'static>> + { + Box::pin(async move { + Err(objects::Error::Internal(anyhow::anyhow!( + "not yet implemented" + ))) + }) + } } #[derive(Debug)] @@ -83,4 +94,15 @@ impl objects::ObjectImpl for Object { ))) }) } + + fn delete(self: Arc) -> Pin> + Send>> { + Box::pin(async move { + let res = self.client.delete_object(&self.name).await; + match res { + Ok(_) => Ok(()), + Err(s3::error::S3Error::HttpFailWithBody(404, _)) => Ok(()), + Err(err) => Err(Error::Other(err.into())), + } + }) + } } diff --git a/runtimes/js/encore.dev/storage/objects/bucket.ts b/runtimes/js/encore.dev/storage/objects/bucket.ts index cb300dd3a1..446bc93476 100644 --- a/runtimes/js/encore.dev/storage/objects/bucket.ts +++ b/runtimes/js/encore.dev/storage/objects/bucket.ts @@ -27,53 +27,69 @@ export class Bucket { return new Bucket(name, {}); } - object(name: string): ObjectHandle { - const impl = this.impl.object(name); - return new ObjectHandle(impl, this, name); - } -} - -export class ObjectHandle { - private impl: runtime.BucketObject; - public readonly bucket: Bucket; - public readonly name: string; - - constructor(impl: runtime.BucketObject, bucket: Bucket, name: string) { - this.impl = impl; - this.bucket = bucket; - this.name = name; + async *list(options: ListOptions): AsyncGenerator { + const iter = await this.impl.list(); + while (true) { + const attrs = await iter.next(); + if (attrs === null) { + break; + } + yield attrs; + } } /** * Returns whether the object exists in the bucket. * Throws an error on network failure. */ - async exists(): Promise { - return this.impl.exists(); + async exists(name: string): Promise { + const impl = this.impl.object(name); + return impl.exists(); } /** * Returns the object's attributes. * Throws an error if the object does not exist. */ - async attrs(): Promise { - return this.impl.attrs(); + async attrs(name: string): Promise { + const impl = this.impl.object(name); + return impl.attrs(); } - async upload(data: Buffer, options?: UploadOptions): Promise { - return this.impl.upload(data, options); + /** + * Uploads an object to the bucket. + */ + async upload(name: string, data: Buffer, options?: UploadOptions): Promise { + const impl = this.impl.object(name); + return impl.upload(data, options); } - async download(): Promise { - return this.impl.downloadAll(); + /** + * Downloads an object from the bucket and returns its contents. + */ + async download(name: string): Promise { + const impl = this.impl.object(name); + return impl.downloadAll(); } + + /** + * Removes an object from the bucket. + * Throws an error on network failure. + */ + async remove(name: string): Promise { + const impl = this.impl.object(name); + return impl.delete(); + } +} + +export interface ListOptions { } export interface ObjectAttrs { name: string; - contentType?: string; size: number; version: string; + contentType?: string; } export interface UploadOptions { diff --git a/runtimes/js/encore.dev/storage/objects/mod.ts b/runtimes/js/encore.dev/storage/objects/mod.ts index 51d54da47b..746f05933d 100644 --- a/runtimes/js/encore.dev/storage/objects/mod.ts +++ b/runtimes/js/encore.dev/storage/objects/mod.ts @@ -1,2 +1,2 @@ -export { Bucket, ObjectHandle } from "./bucket"; +export { Bucket } from "./bucket"; export type { BucketConfig, ObjectAttrs, UploadOptions } from "./bucket"; diff --git a/runtimes/js/src/objects.rs b/runtimes/js/src/objects.rs index 3a5db80100..c2d9458d07 100644 --- a/runtimes/js/src/objects.rs +++ b/runtimes/js/src/objects.rs @@ -1,10 +1,13 @@ +use std::pin::Pin; + use napi::bindgen_prelude::Buffer; use napi::{Env, JsBuffer, JsObject}; use napi_derive::napi; use encore_runtime_core::objects::{ - Bucket as CoreBucket, DownloadError, Object as CoreObject, ObjectAttrs as CoreAttrs, - UploadOptions as CoreUploadOptions, UploadPreconditions as CoreUploadPreconditions, + Bucket as CoreBucket, DownloadError, ListStream, Object as CoreObject, + ObjectAttrs as CoreAttrs, UploadOptions as CoreUploadOptions, + UploadPreconditions as CoreUploadPreconditions, }; #[napi] @@ -22,6 +25,15 @@ impl Bucket { pub fn object(&self, name: String) -> BucketObject { BucketObject::new(self.bkt.object(name)) } + + #[napi] + pub async fn list(&self) -> napi::Result { + self.bkt + .list() + .await + .map_err(map_objects_err) + .map(ListIterator::new) + } } #[napi] @@ -79,6 +91,11 @@ impl BucketObject { let buf = self.obj.download_all().await.map_err(map_download_err)?; Ok(buf.into()) } + + #[napi] + pub async fn delete(&self) -> napi::Result<()> { + self.obj.delete().await.map_err(map_objects_err) + } } #[napi] @@ -139,3 +156,30 @@ fn map_objects_err(err: encore_runtime_core::objects::Error) -> napi::Error { fn map_download_err(err: DownloadError) -> napi::Error { napi::Error::new(napi::Status::GenericFailure, err) } + +#[napi] +pub struct ListIterator { + stream: tokio::sync::Mutex>, +} + +#[napi] +impl ListIterator { + fn new(stream: ListStream) -> Self { + Self { + stream: tokio::sync::Mutex::new(stream.into()), + } + } + + #[napi] + pub async fn next(&self) -> napi::Result> { + use futures::StreamExt; + let mut stream = self.stream.lock().await; + let row = stream + .next() + .await + .transpose() + .map_err(|e| napi::Error::new(napi::Status::GenericFailure, format!("{:#?}", e)))?; + + Ok(row.map(ObjectAttrs::from)) + } +} diff --git a/tsparser/src/legacymeta/mod.rs b/tsparser/src/legacymeta/mod.rs index c6dbb36187..ac071f2d2e 100644 --- a/tsparser/src/legacymeta/mod.rs +++ b/tsparser/src/legacymeta/mod.rs @@ -11,7 +11,7 @@ use crate::parser::parser::{ParseContext, ParseResult, Service}; use crate::parser::resourceparser::bind::{Bind, BindKind}; use crate::parser::resources::apis::{authhandler, gateway}; use crate::parser::resources::infra::cron::CronJobSchedule; -use crate::parser::resources::infra::{cron, pubsub_subscription, pubsub_topic, sqldb, objects}; +use crate::parser::resources::infra::{cron, objects, pubsub_subscription, pubsub_topic, sqldb}; use crate::parser::resources::Resource; use crate::parser::types::ObjectId; use crate::parser::usageparser::Usage; @@ -72,7 +72,7 @@ impl<'a> MetaBuilder<'a> { rel_path, rpcs: vec![], // filled in later databases: vec![], // filled in later - buckets: vec![], // filled in later + buckets: vec![], // filled in later has_config: false, // TODO change when config is supported // We no longer care about migrations in a service, so just set @@ -340,6 +340,8 @@ impl<'a> MetaBuilder<'a> { let mut seen_publishers = HashSet::new(); let mut seen_calls = HashSet::new(); + + let mut bucket_perms = HashMap::new(); for u in &self.parse.usages { match u { Usage::PublishTopic(publish) => { @@ -377,7 +379,7 @@ impl<'a> MetaBuilder<'a> { self.data.svcs[*idx].databases.push(access.db.name.clone()); } - Usage::AccessBucket(access) => { + Usage::Bucket(access) => { let Some(svc) = self.service_for_range(&access.range) else { HANDLER.with(|h| { h.span_err( @@ -388,8 +390,31 @@ impl<'a> MetaBuilder<'a> { continue; }; + use objects::Operation; + let op = match access.op { + Operation::DeleteObject => v1::bucket_usage::Operation::DeleteObject, + Operation::ListObjects => v1::bucket_usage::Operation::ListObjects, + Operation::ReadObjectContents => { + v1::bucket_usage::Operation::ReadObjectContents + } + Operation::WriteObject => v1::bucket_usage::Operation::WriteObject, + Operation::UpdateObjectMetadata => { + v1::bucket_usage::Operation::UpdateObjectMetadata + } + Operation::GetObjectMetadata => { + v1::bucket_usage::Operation::GetObjectMetadata + } + }; + let idx = svc_index.get(&svc.name).unwrap(); - self.data.svcs[*idx].buckets.push(access.bucket.name.clone()); + bucket_perms + .entry(*idx) + .or_insert(v1::BucketUsage { + bucket: access.bucket.name.clone(), + operations: vec![], + }) + .operations + .push(op as i32); } Usage::CallEndpoint(call) => { @@ -425,6 +450,14 @@ impl<'a> MetaBuilder<'a> { } } + // Add the computed bucket permissions to the services. + for (svc_idx, mut bucket_perm) in bucket_perms { + // Make the bucket perms sorted and unique. + bucket_perm.operations.sort(); + bucket_perm.operations.dedup(); + self.data.svcs[svc_idx].buckets.push(bucket_perm); + } + // Sort the packages for deterministic output. self.data.pkgs.sort_by(|a, b| a.name.cmp(&b.name)); diff --git a/tsparser/src/parser/resources/infra/objects.rs b/tsparser/src/parser/resources/infra/objects.rs index 1eb486803a..5aabece296 100644 --- a/tsparser/src/parser/resources/infra/objects.rs +++ b/tsparser/src/parser/resources/infra/objects.rs @@ -1,8 +1,7 @@ use anyhow::Result; use litparser_derive::LitParser; -use swc_common::errors::HANDLER; -use swc_ecma_ast as ast; use swc_common::sync::Lrc; +use swc_ecma_ast as ast; use crate::parser::resourceparser::bind::ResourceOrPath; use crate::parser::resourceparser::bind::{BindData, BindKind}; @@ -14,6 +13,7 @@ use crate::parser::resources::Resource; use crate::parser::resources::ResourcePath; use crate::parser::usageparser::{ResolveUsageData, Usage, UsageExprKind}; use crate::parser::Range; +use crate::span_err::ErrReporter; #[derive(Debug, Clone)] pub struct Bucket { @@ -90,24 +90,60 @@ pub const OBJECTS_PARSER: ResourceParser = ResourceParser { pub fn resolve_bucket_usage(data: &ResolveUsageData, bucket: Lrc) -> Result> { Ok(match &data.expr.kind { - UsageExprKind::MethodCall(_) - | UsageExprKind::FieldAccess(_) - | UsageExprKind::CallArg(_) - | UsageExprKind::ConstructorArg(_) => Some(Usage::AccessBucket(AccessBucketUsage { - range: data.expr.range, - bucket, - })), + UsageExprKind::MethodCall(call) => { + let op = match call.method.as_ref() { + "list" => Operation::ListObjects, + "exists" | "attrs" => Operation::GetObjectMetadata, + "upload" => Operation::WriteObject, + "download" => Operation::ReadObjectContents, + "remove" => Operation::DeleteObject, + _ => { + call.method.err("unsupported bucket operation"); + return Ok(None); + } + }; + + Some(Usage::Bucket(BucketUsage { + range: data.expr.range, + bucket, + op, + })) + } _ => { - HANDLER - .with(|h| h.span_err(data.expr.range.to_span(), "invalid use of bucket resource")); + data.expr + .range + .to_span() + .err("invalid use of bucket resource"); None } }) } #[derive(Debug)] -pub struct AccessBucketUsage { +pub struct BucketUsage { pub range: Range, pub bucket: Lrc, + pub op: Operation, +} + +#[derive(Debug)] +pub enum Operation { + /// Listing objects and accessing their metadata during list operations. + ListObjects, + + /// Reading the contents of an object. + ReadObjectContents, + + /// Creating or updating an object, with contents and metadata. + WriteObject, + + /// Updating the metadata of an object, without reading or writing its contents. + UpdateObjectMetadata, + + /// Reading the metadata of an object, or checking for its existence. + GetObjectMetadata, + + /// Deleting an object. + DeleteObject, } diff --git a/tsparser/src/parser/usageparser/mod.rs b/tsparser/src/parser/usageparser/mod.rs index 37ffd73eae..a8f9524080 100644 --- a/tsparser/src/parser/usageparser/mod.rs +++ b/tsparser/src/parser/usageparser/mod.rs @@ -214,7 +214,7 @@ pub enum Usage { CallEndpoint(apis::api::CallEndpointUsage), PublishTopic(infra::pubsub_topic::PublishUsage), AccessDatabase(infra::sqldb::AccessDatabaseUsage), - AccessBucket(infra::objects::AccessBucketUsage), + Bucket(infra::objects::BucketUsage), } pub struct ResolveUsageData<'a> { From 53bd618f1521d2a0fc9039186a4940d2687fa6a4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Eriksson?= Date: Thu, 24 Oct 2024 19:27:30 +0200 Subject: [PATCH 08/37] Add support for infra namespaces --- cli/cmd/encore/daemon/daemon.go | 5 ++++ cli/daemon/namespace/namespace.go | 2 ++ cli/daemon/objects/manager.go | 44 +++++++++++++++++++++++++++++++ cli/daemon/objects/objects.go | 31 ++++++++++++++-------- cli/daemon/run/exec_script.go | 2 +- cli/daemon/run/infra/infra.go | 42 ++++++++++++++++++++--------- cli/daemon/run/manager.go | 2 ++ cli/daemon/run/run.go | 2 +- cli/daemon/run/tests.go | 2 +- pkg/rtconfgen/infra_builder.go | 2 +- 10 files changed, 107 insertions(+), 27 deletions(-) create mode 100644 cli/daemon/objects/manager.go diff --git a/cli/cmd/encore/daemon/daemon.go b/cli/cmd/encore/daemon/daemon.go index bd20da447b..52604263f0 100644 --- a/cli/cmd/encore/daemon/daemon.go +++ b/cli/cmd/encore/daemon/daemon.go @@ -41,6 +41,7 @@ import ( "encr.dev/cli/daemon/engine/trace2" "encr.dev/cli/daemon/engine/trace2/sqlite" "encr.dev/cli/daemon/namespace" + "encr.dev/cli/daemon/objects" "encr.dev/cli/daemon/run" "encr.dev/cli/daemon/secret" "encr.dev/cli/daemon/sqldb" @@ -105,6 +106,7 @@ type Daemon struct { RunMgr *run.Manager NS *namespace.Manager ClusterMgr *sqldb.ClusterManager + ObjectsMgr *objects.ClusterManager Trace trace2.Store Server *daemon.Server @@ -144,6 +146,7 @@ func (d *Daemon) init(ctx context.Context) { d.NS = namespace.NewManager(d.EncoreDB) d.ClusterMgr = sqldb.NewClusterManager(sqldbDriver, d.Apps, d.NS) + d.ObjectsMgr = objects.NewClusterManager(d.NS) d.Trace = sqlite.New(ctx, d.EncoreDB) d.Secret = secret.New() @@ -153,11 +156,13 @@ func (d *Daemon) init(ctx context.Context) { DashBaseURL: fmt.Sprintf("http://%s", d.Dash.ClientAddr()), Secret: d.Secret, ClusterMgr: d.ClusterMgr, + ObjectsMgr: d.ObjectsMgr, } // Register namespace deletion handlers. d.NS.RegisterDeletionHandler(d.ClusterMgr) d.NS.RegisterDeletionHandler(d.RunMgr) + d.NS.RegisterDeletionHandler(d.ObjectsMgr) d.Server = daemon.New(d.Apps, d.RunMgr, d.ClusterMgr, d.Secret, d.NS) } diff --git a/cli/daemon/namespace/namespace.go b/cli/daemon/namespace/namespace.go index 88eee4a229..655b1f8acb 100644 --- a/cli/daemon/namespace/namespace.go +++ b/cli/daemon/namespace/namespace.go @@ -22,6 +22,8 @@ type ( Name string ) +func (id ID) String() string { return string(id) } + func NewManager(db *sql.DB) *Manager { return &Manager{db, nil} } diff --git a/cli/daemon/objects/manager.go b/cli/daemon/objects/manager.go new file mode 100644 index 0000000000..61254eeefc --- /dev/null +++ b/cli/daemon/objects/manager.go @@ -0,0 +1,44 @@ +package objects + +import ( + "context" + "os" + "path/filepath" + + "encr.dev/cli/daemon/apps" + "encr.dev/cli/daemon/namespace" +) + +// NewClusterManager creates a new ClusterManager. +func NewClusterManager(ns *namespace.Manager) *ClusterManager { + return &ClusterManager{ + ns: ns, + } +} + +type ClusterManager struct { + ns *namespace.Manager +} + +func (cm *ClusterManager) BaseDir(ctx context.Context, ns *namespace.Namespace) (string, error) { + cache, err := os.UserCacheDir() + if err != nil { + return "", err + } + + return filepath.Join(cache, "encore", "objects", ns.ID.String()), nil +} + +// CanDeleteNamespace implements namespace.DeletionHandler. +func (cm *ClusterManager) CanDeleteNamespace(ctx context.Context, app *apps.Instance, ns *namespace.Namespace) error { + return nil +} + +// DeleteNamespace implements namespace.DeletionHandler. +func (cm *ClusterManager) DeleteNamespace(ctx context.Context, app *apps.Instance, ns *namespace.Namespace) error { + baseDir, err := cm.BaseDir(ctx, ns) + if err == nil { + err = os.RemoveAll(baseDir) + } + return err +} diff --git a/cli/daemon/objects/objects.go b/cli/daemon/objects/objects.go index 5f8a93ec61..88549b15ce 100644 --- a/cli/daemon/objects/objects.go +++ b/cli/daemon/objects/objects.go @@ -16,6 +16,7 @@ import ( ) type Server struct { + cm *ClusterManager startOnce syncutil.Once cancel func() // set by Start emu *gcsemu.GcsEmu @@ -23,22 +24,32 @@ type Server struct { srv *http.Server } -func New() *Server { +func NewInMemoryServer() *Server { return &Server{ // TODO set up dir storage emu: gcsemu.NewGcsEmu(gcsemu.Options{ - Verbose: true, - Log: func(err error, fmt string, args ...interface{}) { - if err != nil { - log.Error().Err(err).Msgf(fmt, args...) - } else { - log.Info().Msgf(fmt, args...) - } - }, + Store: gcsemu.NewMemStore(), }), } } +func NewDirServer(baseDir string) *Server { + return &Server{ + emu: gcsemu.NewGcsEmu(gcsemu.Options{ + Store: gcsemu.NewFileStore(baseDir), + }), + } +} + +func (s *Server) Initialize(md *meta.Data) error { + for _, bucket := range md.Buckets { + if err := s.emu.InitBucket(bucket.Name); err != nil { + return errors.Wrap(err, "initialize object storage bucket") + } + } + return nil +} + func (s *Server) Start() error { return s.startOnce.Do(func() error { mux := http.NewServeMux() @@ -56,8 +67,6 @@ func (s *Server) Start() error { } }() - s.emu.InitBucket("test") - return nil }) } diff --git a/cli/daemon/run/exec_script.go b/cli/daemon/run/exec_script.go index 4f56e91d63..70e444717a 100644 --- a/cli/daemon/run/exec_script.go +++ b/cli/daemon/run/exec_script.go @@ -64,7 +64,7 @@ func (mgr *Manager) ExecScript(ctx context.Context, p ExecScriptParams) (err err return err } - rm := infra.NewResourceManager(p.App, mgr.ClusterMgr, p.NS, p.Environ, mgr.DBProxyPort, false) + rm := infra.NewResourceManager(p.App, mgr.ClusterMgr, mgr.ObjectsMgr, p.NS, p.Environ, mgr.DBProxyPort, false) defer rm.StopAll() tracker := p.OpTracker diff --git a/cli/daemon/run/infra/infra.go b/cli/daemon/run/infra/infra.go index 408ae46c7a..d5ece76bbb 100644 --- a/cli/daemon/run/infra/infra.go +++ b/cli/daemon/run/infra/infra.go @@ -48,6 +48,7 @@ type ResourceManager struct { app *apps.Instance dbProxyPort int sqlMgr *sqldb.ClusterManager + objectsMgr *objects.ClusterManager ns *namespace.Namespace environ environ.Environ log zerolog.Logger @@ -57,11 +58,12 @@ type ResourceManager struct { servers map[Type]Resource } -func NewResourceManager(app *apps.Instance, sqlMgr *sqldb.ClusterManager, ns *namespace.Namespace, environ environ.Environ, dbProxyPort int, forTests bool) *ResourceManager { +func NewResourceManager(app *apps.Instance, sqlMgr *sqldb.ClusterManager, objectsMgr *objects.ClusterManager, ns *namespace.Namespace, environ environ.Environ, dbProxyPort int, forTests bool) *ResourceManager { return &ResourceManager{ app: app, dbProxyPort: dbProxyPort, sqlMgr: sqlMgr, + objectsMgr: objectsMgr, ns: ns, environ: environ, forTests: forTests, @@ -103,7 +105,7 @@ func (rm *ResourceManager) StartRequiredServices(a *optracker.AsyncBuildJobs, md } if objects.IsUsed(md) && rm.GetObjects() == nil { - a.Go("Starting Object Storage server", true, 250*time.Millisecond, rm.StartObjects) + a.Go("Starting Object Storage server", true, 250*time.Millisecond, rm.StartObjects(md)) } } @@ -158,17 +160,33 @@ func (rm *ResourceManager) GetRedis() *redis.Server { } // StartObjects starts an Object Storage server. -func (rm *ResourceManager) StartObjects(ctx context.Context) error { - srv := objects.New() - err := srv.Start() - if err != nil { - return err - } +func (rm *ResourceManager) StartObjects(md *meta.Data) func(context.Context) error { + return func(ctx context.Context) error { + var srv *objects.Server + if rm.forTests { + srv = objects.NewInMemoryServer() + } else { + if rm.sqlMgr == nil { + return fmt.Errorf("StartObjects: no Object Storage cluster manager provided") + } + baseDir, err := rm.objectsMgr.BaseDir(ctx, rm.ns) + if err != nil { + return err + } + srv = objects.NewDirServer(baseDir) + } - rm.mutex.Lock() - rm.servers[Objects] = srv - rm.mutex.Unlock() - return nil + if err := srv.Initialize(md); err != nil { + return err + } else if err := srv.Start(); err != nil { + return err + } + + rm.mutex.Lock() + rm.servers[Objects] = srv + rm.mutex.Unlock() + return nil + } } // GetObjects returns the Object Storage server if it is running otherwise it returns nil diff --git a/cli/daemon/run/manager.go b/cli/daemon/run/manager.go index 07f96ffa40..2009ea80b2 100644 --- a/cli/daemon/run/manager.go +++ b/cli/daemon/run/manager.go @@ -12,6 +12,7 @@ import ( encore "encore.dev" "encore.dev/appruntime/exported/config" "encr.dev/cli/daemon/apps" + "encr.dev/cli/daemon/objects" "encr.dev/cli/daemon/run/infra" "encr.dev/cli/daemon/secret" "encr.dev/cli/daemon/sqldb" @@ -26,6 +27,7 @@ type Manager struct { DashBaseURL string // base url for the dev dashboard Secret *secret.Manager ClusterMgr *sqldb.ClusterManager + ObjectsMgr *objects.ClusterManager listeners []EventListener mu sync.Mutex diff --git a/cli/daemon/run/run.go b/cli/daemon/run/run.go index afcef5b3c5..d53470f204 100644 --- a/cli/daemon/run/run.go +++ b/cli/daemon/run/run.go @@ -147,7 +147,7 @@ func (mgr *Manager) Start(ctx context.Context, params StartParams) (run *Run, er ID: GenID(), App: params.App, NS: params.NS, - ResourceManager: infra.NewResourceManager(params.App, mgr.ClusterMgr, params.NS, params.Environ, mgr.DBProxyPort, false), + ResourceManager: infra.NewResourceManager(params.App, mgr.ClusterMgr, mgr.ObjectsMgr, params.NS, params.Environ, mgr.DBProxyPort, false), ListenAddr: params.ListenAddr, SvcProxy: svcProxy, log: logger, diff --git a/cli/daemon/run/tests.go b/cli/daemon/run/tests.go index d6a538642e..26eddc38a1 100644 --- a/cli/daemon/run/tests.go +++ b/cli/daemon/run/tests.go @@ -148,7 +148,7 @@ func (mgr *Manager) testSpec(ctx context.Context, bld builder.Impl, expSet *expe return nil, errors.Wrap(err, "cache metadata") } - rm := infra.NewResourceManager(params.App, mgr.ClusterMgr, params.NS, nil, mgr.DBProxyPort, true) + rm := infra.NewResourceManager(params.App, mgr.ClusterMgr, mgr.ObjectsMgr, params.NS, nil, mgr.DBProxyPort, true) jobs := optracker.NewAsyncBuildJobs(ctx, params.App.PlatformOrLocalID(), nil) rm.StartRequiredServices(jobs, parse.Meta) diff --git a/pkg/rtconfgen/infra_builder.go b/pkg/rtconfgen/infra_builder.go index 6cd4aa4a0c..3001367599 100644 --- a/pkg/rtconfgen/infra_builder.go +++ b/pkg/rtconfgen/infra_builder.go @@ -286,7 +286,7 @@ func reduceForServices(infra *runtimev1.Infrastructure, md *meta.Data, svcs []st continue } for _, bktName := range svc.Buckets { - bucketsToKeep[bktName] = true + bucketsToKeep[bktName.Bucket] = true } } From e1c9d373a4c7343a9691cb6fdd3518518c34af47 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Eriksson?= Date: Tue, 29 Oct 2024 13:01:22 +0100 Subject: [PATCH 09/37] Add key prefix support --- proto/encore/runtime/v1/infra.proto | 6 ++++ runtimes/core/src/objects/gcs/bucket.rs | 47 +++++++++++++++++++++---- runtimes/core/src/objects/s3/bucket.rs | 44 +++++++++++++++++++---- 3 files changed, 84 insertions(+), 13 deletions(-) diff --git a/proto/encore/runtime/v1/infra.proto b/proto/encore/runtime/v1/infra.proto index fabaa7a106..0ace7f8a66 100644 --- a/proto/encore/runtime/v1/infra.proto +++ b/proto/encore/runtime/v1/infra.proto @@ -340,6 +340,12 @@ message Bucket { // The cloud name of the bucket. string cloud_name = 3; + + // Optional key prefix to prepend to all bucket keys. + // + // Note: make sure it ends with a slash ("/") if you want + // to group objects within a certain folder. + optional string key_prefix = 4; } message Gateway { diff --git a/runtimes/core/src/objects/gcs/bucket.rs b/runtimes/core/src/objects/gcs/bucket.rs index bfec34ecd5..1ba0d44be6 100644 --- a/runtimes/core/src/objects/gcs/bucket.rs +++ b/runtimes/core/src/objects/gcs/bucket.rs @@ -20,6 +20,7 @@ use super::LazyGCSClient; pub struct Bucket { client: Arc, name: CloudName, + key_prefix: Option, } impl Bucket { @@ -27,6 +28,31 @@ impl Bucket { Self { client, name: cfg.cloud_name.clone().into(), + key_prefix: cfg.key_prefix.clone(), + } + } + + /// Computes the object name, including the key prefix if present. + fn obj_name<'a>(&'_ self, name: Cow<'a, str>) -> Cow<'a, str> { + match &self.key_prefix { + Some(prefix) => { + let mut key = prefix.to_owned(); + key.push_str(&name); + Cow::Owned(key) + } + None => name, + } + } + + /// Returns the name with the key prefix stripped, if present. + fn strip_prefix<'a>(&'_ self, name: Cow<'a, str>) -> Cow<'a, str> { + match &self.key_prefix { + Some(prefix) => name + .as_ref() + .strip_prefix(prefix) + .map(|s| Cow::Owned(s.to_string())) + .unwrap_or(name), + None => name, } } } @@ -50,12 +76,17 @@ impl objects::BucketImpl for Bucket { ..Default::default() }; + // Filter by key prefix, if provided. + if let Some(key_prefix) = &self.key_prefix { + req.prefix = Some(key_prefix.clone()); + } + loop { let resp = client.list_objects(&req).await.map_err(|e| Error::Other(e.into()))?; if let Some(items) = resp.items { for obj in items { let attrs = ObjectAttrs { - name: obj.name, + name: self.strip_prefix(Cow::Owned(obj.name)).into_owned(), version: obj.generation.to_string(), size: obj.size as u64, content_type: obj.content_type, @@ -98,7 +129,7 @@ impl objects::ObjectImpl for Object { use gcs::http::{error::ErrorResponse, Error as GCSError}; let req = &gcs::http::objects::get::GetObjectRequest { bucket: self.bkt.name.to_string(), - object: self.name.clone(), + object: self.bkt.obj_name(Cow::Borrowed(&self.name)).into_owned(), ..Default::default() }; @@ -137,7 +168,7 @@ impl objects::ObjectImpl for Object { use gcs::http::{error::ErrorResponse, Error}; let req = &gcs::http::objects::get::GetObjectRequest { bucket: self.bkt.name.to_string(), - object: self.name.clone(), + object: self.bkt.obj_name(Cow::Borrowed(&self.name)).into_owned(), ..Default::default() }; @@ -170,7 +201,9 @@ impl objects::ObjectImpl for Object { bucket: self.bkt.name.to_string(), ..Default::default() }; - let mut media = Media::new(self.name.clone()); + + let cloud_name = self.bkt.obj_name(Cow::Borrowed(&self.name)); + let mut media = Media::new(cloud_name.into_owned()); apply_upload_opts(opts, &mut req, &mut media); @@ -182,7 +215,7 @@ impl objects::ObjectImpl for Object { .await { Ok(obj) => Ok(ObjectAttrs { - name: obj.name, + name: self.name.clone(), version: obj.generation.to_string(), size: obj.size as u64, content_type: obj.content_type, @@ -216,7 +249,7 @@ impl objects::ObjectImpl for Object { Ok(client) => { let req = GetObjectRequest { bucket: self.bkt.name.to_string(), - object: self.name.clone(), + object: self.bkt.obj_name(Cow::Borrowed(&self.name)).into_owned(), ..Default::default() }; let resp = client @@ -242,7 +275,7 @@ impl objects::ObjectImpl for Object { use gcs::http::{error::ErrorResponse, Error as GCSError}; let req = &gcs::http::objects::delete::DeleteObjectRequest { bucket: self.bkt.name.to_string(), - object: self.name.clone(), + object: self.bkt.obj_name(Cow::Borrowed(&self.name)).into_owned(), ..Default::default() }; diff --git a/runtimes/core/src/objects/s3/bucket.rs b/runtimes/core/src/objects/s3/bucket.rs index 0bef81e7d2..e0ab773a6d 100644 --- a/runtimes/core/src/objects/s3/bucket.rs +++ b/runtimes/core/src/objects/s3/bucket.rs @@ -1,3 +1,4 @@ +use std::borrow::Cow; use std::future::Future; use std::pin::Pin; use std::sync::Arc; @@ -9,6 +10,7 @@ use crate::objects::{self, Error, ObjectAttrs}; #[derive(Debug)] pub struct Bucket { client: Arc, + key_prefix: Option, } impl Bucket { @@ -17,14 +19,41 @@ impl Bucket { .expect("unable to construct bucket client") .with_path_style(); let client = Arc::from(client); - Self { client } + Self { + client, + key_prefix: cfg.key_prefix.clone(), + } + } + + /// Computes the object name, including the key prefix if present. + fn obj_name<'a>(&'_ self, name: Cow<'a, str>) -> Cow<'a, str> { + match &self.key_prefix { + Some(prefix) => { + let mut key = prefix.to_owned(); + key.push_str(&name); + Cow::Owned(key) + } + None => name, + } + } + + /// Returns the name with the key prefix stripped, if present. + fn _strip_prefix<'a>(&'_ self, name: Cow<'a, str>) -> Cow<'a, str> { + match &self.key_prefix { + Some(prefix) => name + .as_ref() + .strip_prefix(prefix) + .map(|s| Cow::Owned(s.to_string())) + .unwrap_or(name), + None => name, + } } } impl objects::BucketImpl for Bucket { fn object(self: Arc, name: String) -> Arc { Arc::new(Object { - client: self.client.clone(), + bkt: self.clone(), name, }) } @@ -43,14 +72,15 @@ impl objects::BucketImpl for Bucket { #[derive(Debug)] struct Object { - client: Arc, + bkt: Arc, name: String, } impl objects::ObjectImpl for Object { fn attrs(self: Arc) -> Pin> + Send>> { Box::pin(async move { - let res = self.client.head_object(&self.name).await; + let cloud_name = self.bkt.obj_name(Cow::Borrowed(&self.name)); + let res = self.bkt.client.head_object(&cloud_name).await; match res { Ok((obj, _)) => Ok(ObjectAttrs { name: self.name.clone(), @@ -67,7 +97,8 @@ impl objects::ObjectImpl for Object { fn exists(self: Arc) -> Pin> + Send>> { Box::pin(async move { - let res = self.client.head_object(&self.name).await; + let cloud_name = self.bkt.obj_name(Cow::Borrowed(&self.name)); + let res = self.bkt.client.head_object(&cloud_name).await; match res { Ok(_) => Ok(true), Err(s3::error::S3Error::HttpFailWithBody(404, _)) => Ok(false), @@ -97,7 +128,8 @@ impl objects::ObjectImpl for Object { fn delete(self: Arc) -> Pin> + Send>> { Box::pin(async move { - let res = self.client.delete_object(&self.name).await; + let cloud_name = self.bkt.obj_name(Cow::Borrowed(&self.name)); + let res = self.bkt.client.delete_object(&cloud_name).await; match res { Ok(_) => Ok(()), Err(s3::error::S3Error::HttpFailWithBody(404, _)) => Ok(()), From 84aa985e463517390074cd399962eb53ec599a99 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Eriksson?= Date: Tue, 29 Oct 2024 18:00:26 +0100 Subject: [PATCH 10/37] Add s3 implementation --- Cargo.lock | 489 ++++++++++-------- runtimes/core/Cargo.toml | 4 +- runtimes/core/src/objects/gcs/bucket.rs | 20 +- runtimes/core/src/objects/mod.rs | 16 +- runtimes/core/src/objects/noop/mod.rs | 2 +- runtimes/core/src/objects/s3/bucket.rs | 320 ++++++++++-- runtimes/core/src/objects/s3/mod.rs | 60 ++- .../js/encore.dev/storage/objects/bucket.ts | 16 +- runtimes/js/src/objects.rs | 29 +- 9 files changed, 654 insertions(+), 302 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 29ff624a5f..75a0514afc 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -266,20 +266,6 @@ version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" -[[package]] -name = "attohttpc" -version = "0.28.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a13149d0cf3f7f9b9261fad4ec63b2efbf9a80665f52def86282d26255e6331" -dependencies = [ - "http 1.0.0", - "log", - "native-tls", - "serde", - "serde_json", - "url", -] - [[package]] name = "atty" version = "0.2.14" @@ -330,9 +316,9 @@ dependencies = [ [[package]] name = "aws-credential-types" -version = "1.1.8" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa8587ae17c8e967e4b05a62d495be2fb7701bec52a97f7acfe8a29f938384c8" +checksum = "60e8f6b615cb5fc60a98132268508ad104310f0cfb25a1c22eee76efdf9154da" dependencies = [ "aws-smithy-async", "aws-smithy-runtime-api", @@ -341,52 +327,63 @@ dependencies = [ ] [[package]] -name = "aws-creds" -version = "0.37.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f84143206b9c72b3c5cb65415de60c7539c79cd1559290fddec657939131be0" -dependencies = [ - "attohttpc", - "home", - "log", - "quick-xml", - "rust-ini", - "serde", - "thiserror", - "time", - "url", -] - -[[package]] -name = "aws-region" -version = "0.25.5" +name = "aws-runtime" +version = "1.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e9aed3f9c7eac9be28662fdb3b0f4d1951e812f7c64fed4f0327ba702f459b3b" +checksum = "a10d5c055aa540164d9561a0e2e74ad30f0dcf7393c3a92f6733ddf9c5762468" dependencies = [ - "thiserror", + "aws-credential-types", + "aws-sigv4", + "aws-smithy-async", + "aws-smithy-eventstream", + "aws-smithy-http", + "aws-smithy-runtime", + "aws-smithy-runtime-api", + "aws-smithy-types", + "aws-types", + "bytes", + "fastrand", + "http 0.2.11", + "http-body 0.4.6", + "once_cell", + "percent-encoding", + "pin-project-lite", + "tracing", + "uuid", ] [[package]] -name = "aws-runtime" -version = "1.1.9" +name = "aws-sdk-s3" +version = "1.58.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c4ee6903f9d0197510eb6b44c4d86b493011d08b4992938f7b9be0333b6685aa" +checksum = "0656a79cf5e6ab0d4bb2465cd750a7a2fd7ea26c062183ed94225f5782e22365" dependencies = [ "aws-credential-types", + "aws-runtime", "aws-sigv4", "aws-smithy-async", + "aws-smithy-checksums", + "aws-smithy-eventstream", "aws-smithy-http", + "aws-smithy-json", + "aws-smithy-runtime", "aws-smithy-runtime-api", "aws-smithy-types", + "aws-smithy-xml", "aws-types", "bytes", "fastrand", + "hex", + "hmac", "http 0.2.11", "http-body 0.4.6", + "lru", + "once_cell", "percent-encoding", - "pin-project-lite", + "regex-lite", + "sha2", "tracing", - "uuid", + "url", ] [[package]] @@ -503,25 +500,31 @@ dependencies = [ [[package]] name = "aws-sigv4" -version = "1.2.0" +version = "1.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "11d6f29688a4be9895c0ba8bef861ad0c0dac5c15e9618b9b7a6c233990fc263" +checksum = "5619742a0d8f253be760bfbb8e8e8368c69e3587e4637af5754e488a611499b1" dependencies = [ "aws-credential-types", + "aws-smithy-eventstream", "aws-smithy-http", "aws-smithy-runtime-api", "aws-smithy-types", "bytes", + "crypto-bigint 0.5.5", "form_urlencoded", "hex", "hmac", "http 0.2.11", "http 1.0.0", "once_cell", + "p256", "percent-encoding", + "ring 0.17.7", "sha2", + "subtle", "time", "tracing", + "zeroize", ] [[package]] @@ -535,12 +538,45 @@ dependencies = [ "tokio", ] +[[package]] +name = "aws-smithy-checksums" +version = "0.60.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba1a71073fca26775c8b5189175ea8863afb1c9ea2cceb02a5de5ad9dfbaa795" +dependencies = [ + "aws-smithy-http", + "aws-smithy-types", + "bytes", + "crc32c", + "crc32fast", + "hex", + "http 0.2.11", + "http-body 0.4.6", + "md-5", + "pin-project-lite", + "sha1", + "sha2", + "tracing", +] + +[[package]] +name = "aws-smithy-eventstream" +version = "0.60.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cef7d0a272725f87e51ba2bf89f8c21e4df61b9e49ae1ac367a6d69916ef7c90" +dependencies = [ + "aws-smithy-types", + "bytes", + "crc32fast", +] + [[package]] name = "aws-smithy-http" -version = "0.60.7" +version = "0.60.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f10fa66956f01540051b0aa7ad54574640f748f9839e843442d99b970d3aff9" +checksum = "5c8bc3e8fdc6b8d07d976e301c02fe553f72a39b7a9fea820e023268467d7ab6" dependencies = [ + "aws-smithy-eventstream", "aws-smithy-runtime-api", "aws-smithy-types", "bytes", @@ -576,9 +612,9 @@ dependencies = [ [[package]] name = "aws-smithy-runtime" -version = "1.2.1" +version = "1.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c53572b4cd934ee5e8461ad53caa36e9d246aaef42166e3ac539e206a925d330" +checksum = "be28bd063fa91fd871d131fc8b68d7cd4c5fa0869bea68daca50dcb1cbd76be2" dependencies = [ "aws-smithy-async", "aws-smithy-http", @@ -590,6 +626,7 @@ dependencies = [ "http 0.2.11", "http-body 0.4.6", "http-body 1.0.0", + "httparse", "hyper 0.14.28", "hyper-rustls", "once_cell", @@ -602,9 +639,9 @@ dependencies = [ [[package]] name = "aws-smithy-runtime-api" -version = "1.3.0" +version = "1.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ccb2b3a7030dc9a3c9a08ce0b25decea5130e9db19619d4dffbbff34f75fe850" +checksum = "e086682a53d3aa241192aa110fa8dfce98f2f5ac2ead0de84d41582c7e8fdb96" dependencies = [ "aws-smithy-async", "aws-smithy-types", @@ -619,9 +656,9 @@ dependencies = [ [[package]] name = "aws-smithy-types" -version = "1.1.8" +version = "1.2.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "abe14dceea1e70101d38fbf2a99e6a34159477c0fb95e68e05c66bd7ae4c3729" +checksum = "07c9cdc179e6afbf5d391ab08c85eac817b51c87e1892a5edb5f7bbdc64314b4" dependencies = [ "base64-simd", "bytes", @@ -645,24 +682,23 @@ dependencies = [ [[package]] name = "aws-smithy-xml" -version = "0.60.7" +version = "0.60.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "872c68cf019c0e4afc5de7753c4f7288ce4b71663212771bf5e4542eb9346ca9" +checksum = "ab0b0166827aa700d3dc519f72f8b3a91c35d0b8d042dc5d643a91e6f80648fc" dependencies = [ "xmlparser", ] [[package]] name = "aws-types" -version = "1.1.9" +version = "1.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "afb278e322f16f59630a83b6b2dc992a0b48aa74ed47b4130f193fae0053d713" +checksum = "5221b91b3e441e6675310829fd8984801b772cb1546ef6c0e54dec9f1ac13fef" dependencies = [ "aws-credential-types", "aws-smithy-async", "aws-smithy-runtime-api", "aws-smithy-types", - "http 0.2.11", "rustc_version", "tracing", ] @@ -795,6 +831,12 @@ dependencies = [ "rustc-demangle", ] +[[package]] +name = "base16ct" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "349a06037c7bf932dd7e7d1f653678b2038b9ad46a74102f1fc7bd7872678cce" + [[package]] name = "base32" version = "0.4.0" @@ -1117,26 +1159,6 @@ version = "0.9.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8" -[[package]] -name = "const-random" -version = "0.1.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87e00182fe74b066627d63b85fd550ac2998d4b0bd86bfed477a0ae4c7c71359" -dependencies = [ - "const-random-macro", -] - -[[package]] -name = "const-random-macro" -version = "0.1.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f9d839f2a20b0aee515dc581a6172f2321f96cab76c1a38a4c584a194955390e" -dependencies = [ - "getrandom 0.2.11", - "once_cell", - "tiny-keccak", -] - [[package]] name = "constant_time_eq" version = "0.3.0" @@ -1177,6 +1199,15 @@ dependencies = [ "libc", ] +[[package]] +name = "crc32c" +version = "0.6.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a47af21622d091a8f0fb295b88bc886ac74efcc613efc19f5d0b21de5c89e47" +dependencies = [ + "rustc_version", +] + [[package]] name = "crc32fast" version = "1.3.2" @@ -1242,10 +1273,26 @@ dependencies = [ ] [[package]] -name = "crunchy" -version = "0.2.2" +name = "crypto-bigint" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef2b4b23cddf68b89b8f8069890e8c270d54e2d5fe1b143820234805e4cb17ef" +dependencies = [ + "generic-array", + "rand_core 0.6.4", + "subtle", + "zeroize", +] + +[[package]] +name = "crypto-bigint" +version = "0.5.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" +checksum = "0dc92fb57ca44df6db8059111ab3af99a63d5d0f8375d9972e319a379c6bab76" +dependencies = [ + "rand_core 0.6.4", + "subtle", +] [[package]] name = "crypto-common" @@ -1330,6 +1377,16 @@ version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e8566979429cf69b49a5c740c60791108e86440e8be149bbea4fe54d2c32d6e2" +[[package]] +name = "der" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1a467a65c5e759bce6e65eaf91cc29f466cdc57cb65777bd646872a8a1fd4de" +dependencies = [ + "const-oid", + "zeroize", +] + [[package]] name = "der" version = "0.7.9" @@ -1380,15 +1437,6 @@ dependencies = [ "subtle", ] -[[package]] -name = "dlv-list" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "442039f5147480ba31067cb00ada1adae6892028e40e45fc5de7b7df6dcc1b5f" -dependencies = [ - "const-random", -] - [[package]] name = "doc-comment" version = "0.3.3" @@ -1413,12 +1461,44 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6fcc1d9ae294a15ed05aeae8e11ee5f2b3fe971c077d45a42fb20825fba6ee13" +[[package]] +name = "ecdsa" +version = "0.14.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "413301934810f597c1d19ca71c8710e99a3f1ba28a0d2ebc01551a2daeea3c5c" +dependencies = [ + "der 0.6.1", + "elliptic-curve", + "rfc6979", + "signature 1.6.4", +] + [[package]] name = "either" version = "1.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07" +[[package]] +name = "elliptic-curve" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7bb888ab5300a19b8e5bceef25ac745ad065f3c9f7efc6de1b91958110891d3" +dependencies = [ + "base16ct", + "crypto-bigint 0.4.9", + "der 0.6.1", + "digest", + "ff", + "generic-array", + "group", + "pkcs8 0.9.0", + "rand_core 0.6.4", + "sec1", + "subtle", + "zeroize", +] + [[package]] name = "encode_unicode" version = "0.3.6" @@ -1465,8 +1545,10 @@ dependencies = [ "assert_matches", "async-stream", "aws-config", + "aws-sdk-s3", "aws-sdk-sns", "aws-sdk-sqs", + "aws-smithy-types", "axum 0.7.5", "backtrace", "base32", @@ -1499,6 +1581,7 @@ dependencies = [ "jsonwebtoken 9.2.0", "log", "matchit", + "md5", "mime", "native-tls", "once_cell", @@ -1515,7 +1598,6 @@ dependencies = [ "rand 0.8.5", "reqwest 0.12.4", "rsa", - "rust-s3", "serde", "serde_json", "serde_path_to_error", @@ -1704,6 +1786,16 @@ version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "25cbce373ec4653f1a01a31e8a5e5ec0c622dc27ff9c4e6606eefef5cbbed4a5" +[[package]] +name = "ff" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d013fc25338cc558c5c2cfbad646908fb23591e2404481826742b651c9af7160" +dependencies = [ + "rand_core 0.6.4", + "subtle", +] + [[package]] name = "finl_unicode" version = "1.2.0" @@ -2093,7 +2185,7 @@ dependencies = [ "hex", "once_cell", "percent-encoding", - "pkcs8", + "pkcs8 0.10.2", "regex", "reqwest 0.12.4", "reqwest-middleware", @@ -2117,6 +2209,17 @@ dependencies = [ "async-trait", ] +[[package]] +name = "group" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5dfbfb3a6cfbd390d5c9564ab283a0349b9b9fcd46a706c1eb10e0db70bfbac7" +dependencies = [ + "ff", + "rand_core 0.6.4", + "subtle", +] + [[package]] name = "h2" version = "0.3.22" @@ -2831,17 +2934,6 @@ version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0e7465ac9959cc2b1404e8e2367b43684a6d13790fe23056cc8c6c5a6b7bcb94" -[[package]] -name = "maybe-async" -version = "0.2.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5cf92c10c7e361d6b99666ec1c6f9805b0bea2c3bd8c78dc6fe98ac5bd78db11" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.47", -] - [[package]] name = "md-5" version = "0.10.6" @@ -2889,15 +2981,6 @@ dependencies = [ "unicase", ] -[[package]] -name = "minidom" -version = "0.15.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f45614075738ce1b77a1768912a60c0227525971b03e09122a05b8a34a2a6278" -dependencies = [ - "rxml", -] - [[package]] name = "miniz_oxide" version = "0.7.1" @@ -3190,16 +3273,6 @@ dependencies = [ "vcpkg", ] -[[package]] -name = "ordered-multimap" -version = "0.7.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49203cdcae0030493bad186b28da2fa25645fa276a51b6fec8010d281e02ef79" -dependencies = [ - "dlv-list", - "hashbrown 0.14.3", -] - [[package]] name = "os_pipe" version = "1.1.5" @@ -3228,6 +3301,17 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39" +[[package]] +name = "p256" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51f44edd08f51e2ade572f141051021c5af22677e42b7dd28a88155151c33594" +dependencies = [ + "ecdsa", + "elliptic-curve", + "sha2", +] + [[package]] name = "parking_lot" version = "0.12.1" @@ -3718,9 +3802,19 @@ version = "0.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c8ffb9f10fa047879315e6625af03c164b16962a5368d724ed16323b68ace47f" dependencies = [ - "der", - "pkcs8", - "spki", + "der 0.7.9", + "pkcs8 0.10.2", + "spki 0.7.3", +] + +[[package]] +name = "pkcs8" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9eca2c590a5f85da82668fa685c09ce2888b9430e83299debf1f34b65fd4a4ba" +dependencies = [ + "der 0.6.1", + "spki 0.6.0", ] [[package]] @@ -3729,8 +3823,8 @@ version = "0.10.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f950b2377845cebe5cf8b5165cb3cc1a5e0fa5cfa3e1f7f55707d8fd82e0a7b7" dependencies = [ - "der", - "spki", + "der 0.7.9", + "spki 0.7.3", ] [[package]] @@ -3980,16 +4074,6 @@ version = "2.28.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "106dd99e98437432fed6519dedecfade6a06a73bb7b2a1e019fdd2bee5778d94" -[[package]] -name = "quick-xml" -version = "0.32.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d3a6e5838b60e0e8fa7a43f22ade549a37d61f8bdbe636d0d7816191de969c2" -dependencies = [ - "memchr", - "serde", -] - [[package]] name = "quickcheck" version = "1.0.3" @@ -4291,6 +4375,17 @@ dependencies = [ "tower-service", ] +[[package]] +name = "rfc6979" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7743f17af12fa0b03b803ba12cd6a8d9483a587e89c69445e3909655c0b9fabb" +dependencies = [ + "crypto-bigint 0.4.9", + "hmac", + "zeroize", +] + [[package]] name = "ring" version = "0.16.20" @@ -4354,62 +4449,14 @@ dependencies = [ "num-integer", "num-traits", "pkcs1", - "pkcs8", + "pkcs8 0.10.2", "rand_core 0.6.4", - "signature", - "spki", + "signature 2.2.0", + "spki 0.7.3", "subtle", "zeroize", ] -[[package]] -name = "rust-ini" -version = "0.21.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4e310ef0e1b6eeb79169a1171daf9abcb87a2e17c03bee2c4bb100b55c75409f" -dependencies = [ - "cfg-if", - "ordered-multimap", - "trim-in-place", -] - -[[package]] -name = "rust-s3" -version = "0.35.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3df3f353b1f4209dcf437d777cda90279c397ab15a0cd6fd06bd32c88591533" -dependencies = [ - "async-trait", - "aws-creds", - "aws-region", - "base64 0.22.1", - "bytes", - "cfg-if", - "futures", - "hex", - "hmac", - "http 0.2.11", - "hyper 0.14.28", - "hyper-tls 0.5.0", - "log", - "maybe-async", - "md5", - "minidom", - "native-tls", - "percent-encoding", - "quick-xml", - "serde", - "serde_derive", - "serde_json", - "sha2", - "thiserror", - "time", - "tokio", - "tokio-native-tls", - "tokio-stream", - "url", -] - [[package]] name = "rust_decimal" version = "1.35.0" @@ -4559,23 +4606,6 @@ version = "1.0.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7ffc183a10b4478d04cbbbfc96d0873219d962dd5accaff2ffbd4ceb7df837f4" -[[package]] -name = "rxml" -version = "0.9.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a98f186c7a2f3abbffb802984b7f1dfd65dac8be1aafdaabbca4137f53f0dff7" -dependencies = [ - "bytes", - "rxml_validation", - "smartstring", -] - -[[package]] -name = "rxml_validation" -version = "0.9.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22a197350ece202f19a166d1ad6d9d6de145e1d2a8ef47db299abe164dbd7530" - [[package]] name = "ryu" version = "1.0.16" @@ -4632,6 +4662,20 @@ dependencies = [ "untrusted 0.9.0", ] +[[package]] +name = "sec1" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3be24c1842290c45df0a7bf069e0c268a747ad05a192f2fd7dcfdbc1cba40928" +dependencies = [ + "base16ct", + "der 0.6.1", + "generic-array", + "pkcs8 0.9.0", + "subtle", + "zeroize", +] + [[package]] name = "security-framework" version = "2.9.2" @@ -4860,6 +4904,16 @@ dependencies = [ "libc", ] +[[package]] +name = "signature" +version = "1.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74233d3b3b2f6d4b006dc19dee745e73e2a6bfb6f93607cd3b02bd5b00797d7c" +dependencies = [ + "digest", + "rand_core 0.6.4", +] + [[package]] name = "signature" version = "2.2.0" @@ -4962,6 +5016,16 @@ version = "0.9.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" +[[package]] +name = "spki" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67cf02bbac7a337dc36e4f5a693db6c21e7863f45070f7064577eb4367a3212b" +dependencies = [ + "base64ct", + "der 0.6.1", +] + [[package]] name = "spki" version = "0.7.3" @@ -4969,7 +5033,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d91ed6c858b01f942cd56b37a94b3e0a1798290327d1236e4d9cf4eaca44d29d" dependencies = [ "base64ct", - "der", + "der 0.7.9", ] [[package]] @@ -5517,15 +5581,6 @@ dependencies = [ "time-core", ] -[[package]] -name = "tiny-keccak" -version = "2.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2c9d3793400a45f954c52e73d068316d76b6f4e36977e3fcebb13a2721e80237" -dependencies = [ - "crunchy", -] - [[package]] name = "tinyvec" version = "1.6.0" @@ -5942,12 +5997,6 @@ dependencies = [ "syn 1.0.109", ] -[[package]] -name = "trim-in-place" -version = "0.1.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "343e926fc669bc8cde4fa3129ab681c63671bae288b1f1081ceee6d9d37904fc" - [[package]] name = "try-lock" version = "0.2.5" diff --git a/runtimes/core/Cargo.toml b/runtimes/core/Cargo.toml index 9cf12a9229..64d8c4978d 100644 --- a/runtimes/core/Cargo.toml +++ b/runtimes/core/Cargo.toml @@ -83,13 +83,15 @@ rsa = { version = "0.9.6", features = ["pem"] } flate2 = "1.0.30" urlencoding = "2.1.3" tower-http = { version = "0.5.2", features = ["fs"] } -rust-s3 = "0.35.1" google-cloud-storage = "0.22.1" serde_path_to_error = "0.1.16" tracing = "0.1.40" tracing-subscriber = { version = "0.3.18", features = ["alloc", "ansi", "env-filter", "fmt", "matchers", "nu-ansi-term", "once_cell", "regex", "registry", "sharded-slab", "smallvec", "std", "thread_local", "tracing"], default-features = false } thiserror = "1.0.64" async-stream = "0.3.6" +md5 = "0.7.0" +aws-sdk-s3 = "1.58.0" +aws-smithy-types = { version = "1.2.8", features = ["byte-stream-poll-next", "rt-tokio"] } [build-dependencies] prost-build = "0.12.3" diff --git a/runtimes/core/src/objects/gcs/bucket.rs b/runtimes/core/src/objects/gcs/bucket.rs index 1ba0d44be6..0353681f38 100644 --- a/runtimes/core/src/objects/gcs/bucket.rs +++ b/runtimes/core/src/objects/gcs/bucket.rs @@ -10,7 +10,7 @@ use std::sync::Arc; use tokio::io::AsyncRead; use crate::encore::runtime::v1 as pb; -use crate::objects::{DownloadError, DownloadStream, Error, ObjectAttrs, UploadOptions}; +use crate::objects::{DownloadError, DownloadStream, Error, ListEntry, ObjectAttrs, UploadOptions}; use crate::{objects, CloudName}; use google_cloud_storage as gcs; @@ -85,14 +85,12 @@ impl objects::BucketImpl for Bucket { let resp = client.list_objects(&req).await.map_err(|e| Error::Other(e.into()))?; if let Some(items) = resp.items { for obj in items { - let attrs = ObjectAttrs { + let entry = ListEntry { name: self.strip_prefix(Cow::Owned(obj.name)).into_owned(), - version: obj.generation.to_string(), size: obj.size as u64, - content_type: obj.content_type, etag: obj.etag, }; - yield attrs; + yield entry; } } @@ -193,7 +191,7 @@ impl objects::ObjectImpl for Object { self: Arc, data: Box, opts: objects::UploadOptions, - ) -> Pin> + Send>> { + ) -> Pin> + Send>> { Box::pin(async move { match self.bkt.client.get().await { Ok(client) => { @@ -214,13 +212,7 @@ impl objects::ObjectImpl for Object { .upload_streamed_object(&req, stream, &upload_type) .await { - Ok(obj) => Ok(ObjectAttrs { - name: self.name.clone(), - version: obj.generation.to_string(), - size: obj.size as u64, - content_type: obj.content_type, - etag: obj.etag, - }), + Ok(_obj) => Ok(()), Err(err) => Err(err.into()), } } @@ -257,7 +249,7 @@ impl objects::ObjectImpl for Object { .await; let stream = resp.map_err(convert_err)?; - let stream: DownloadStream = Box::new(stream.map_err(convert_err)); + let stream: DownloadStream = Box::pin(stream.map_err(convert_err)); Ok(stream) } Err(err) => Err(DownloadError::Internal(anyhow::anyhow!( diff --git a/runtimes/core/src/objects/mod.rs b/runtimes/core/src/objects/mod.rs index 4dfaebb39f..10094d34bb 100644 --- a/runtimes/core/src/objects/mod.rs +++ b/runtimes/core/src/objects/mod.rs @@ -14,6 +14,7 @@ mod gcs; mod manager; mod noop; mod s3; +mod s3old; trait ClusterImpl: Debug + Send + Sync { fn bucket(self: Arc, cfg: &pb::Bucket) -> Arc; @@ -27,7 +28,7 @@ trait BucketImpl: Debug + Send + Sync { ) -> Pin> + Send + 'static>>; } -pub type ListStream = Box> + Send>; +pub type ListStream = Box> + Send>; trait ObjectImpl: Debug + Send + Sync { fn exists(self: Arc) -> Pin> + Send>>; @@ -36,7 +37,7 @@ trait ObjectImpl: Debug + Send + Sync { self: Arc, data: Box, options: UploadOptions, - ) -> Pin> + Send>>; + ) -> Pin> + Send>>; fn download( self: Arc, @@ -81,7 +82,7 @@ impl Object { &self, data: Box, options: UploadOptions, - ) -> impl Future> + Send + 'static { + ) -> impl Future> + Send + 'static { self.imp.clone().upload(data, options) } @@ -98,6 +99,7 @@ impl Object { async move { let mut bytes = Vec::new(); let mut stream = stream.await?; + while let Some(chunk) = stream.next().await { bytes.extend_from_slice(&chunk?); } @@ -138,7 +140,7 @@ pub enum DownloadError { Other(#[from] anyhow::Error), } -pub type DownloadStream = Box> + Unpin + Send>; +pub type DownloadStream = Pin> + Send>>; pub struct ObjectAttrs { pub name: String, @@ -148,6 +150,12 @@ pub struct ObjectAttrs { pub etag: String, } +pub struct ListEntry { + pub name: String, + pub size: u64, + pub etag: String, +} + #[derive(Debug, Default)] pub struct UploadOptions { pub content_type: Option, diff --git a/runtimes/core/src/objects/noop/mod.rs b/runtimes/core/src/objects/noop/mod.rs index df83a20c1c..5bc297390e 100644 --- a/runtimes/core/src/objects/noop/mod.rs +++ b/runtimes/core/src/objects/noop/mod.rs @@ -59,7 +59,7 @@ impl objects::ObjectImpl for Object { self: Arc, _data: Box, _options: objects::UploadOptions, - ) -> Pin> + Send>> { + ) -> Pin> + Send>> { Box::pin(future::ready(Err(anyhow::anyhow!( "noop bucket does not support upload" )))) diff --git a/runtimes/core/src/objects/s3/bucket.rs b/runtimes/core/src/objects/s3/bucket.rs index e0ab773a6d..3818d18b46 100644 --- a/runtimes/core/src/objects/s3/bucket.rs +++ b/runtimes/core/src/objects/s3/bucket.rs @@ -1,26 +1,37 @@ +use anyhow::Context; +use async_stream::{stream, try_stream}; +use aws_sdk_s3 as s3; +use aws_sdk_s3::error::SdkError; +use aws_smithy_types::byte_stream::ByteStream; +use base64::Engine; +use bytes::{Bytes, BytesMut}; +use futures::Stream; use std::borrow::Cow; use std::future::Future; use std::pin::Pin; use std::sync::Arc; -use tokio::io::AsyncRead; +use std::task::Poll; +use tokio::io::{AsyncRead, AsyncReadExt}; use crate::encore::runtime::v1 as pb; -use crate::objects::{self, Error, ObjectAttrs}; +use crate::objects::{self, Error, ListEntry, ObjectAttrs}; + +use super::LazyS3Client; + +const CHUNK_SIZE: usize = 8_388_608; // 8 Mebibytes, min is 5 (5_242_880); #[derive(Debug)] pub struct Bucket { - client: Arc, + client: Arc, + name: String, key_prefix: Option, } impl Bucket { - pub(super) fn new(region: s3::Region, creds: s3::creds::Credentials, cfg: &pb::Bucket) -> Self { - let client = s3::Bucket::new(&cfg.cloud_name, region, creds) - .expect("unable to construct bucket client") - .with_path_style(); - let client = Arc::from(client); + pub(super) fn new(client: Arc, cfg: &pb::Bucket) -> Self { Self { client, + name: cfg.cloud_name.clone(), key_prefix: cfg.key_prefix.clone(), } } @@ -38,7 +49,7 @@ impl Bucket { } /// Returns the name with the key prefix stripped, if present. - fn _strip_prefix<'a>(&'_ self, name: Cow<'a, str>) -> Cow<'a, str> { + fn strip_prefix<'a>(&'_ self, name: Cow<'a, str>) -> Cow<'a, str> { match &self.key_prefix { Some(prefix) => name .as_ref() @@ -54,7 +65,7 @@ impl objects::BucketImpl for Bucket { fn object(self: Arc, name: String) -> Arc { Arc::new(Object { bkt: self.clone(), - name, + cloud_name: name, }) } @@ -63,9 +74,30 @@ impl objects::BucketImpl for Bucket { ) -> Pin> + Send + 'static>> { Box::pin(async move { - Err(objects::Error::Internal(anyhow::anyhow!( - "not yet implemented" - ))) + let client = self.client.get().await.clone(); + let s: objects::ListStream = Box::new(try_stream! { + let mut req = client.list_objects_v2() + .bucket(self.name.clone()) + .max_keys(1000); + + if let Some(key_prefix) = self.key_prefix.clone() { + req = req.prefix(key_prefix); + } + + let mut stream = req.into_paginator().send(); + while let Some(resp) = stream.try_next().await.map_err(|e| Error::Other(e.into()))? { + for obj in resp.contents.unwrap_or_default() { + let entry = ListEntry { + name: self.strip_prefix(Cow::Owned(obj.key.unwrap_or_default())).into_owned(), + size: obj.size.unwrap_or_default() as u64, + etag: obj.e_tag.unwrap_or_default(), + }; + yield entry; + } + } + }); + + Ok(s) }) } } @@ -73,23 +105,32 @@ impl objects::BucketImpl for Bucket { #[derive(Debug)] struct Object { bkt: Arc, - name: String, + cloud_name: String, } impl objects::ObjectImpl for Object { fn attrs(self: Arc) -> Pin> + Send>> { Box::pin(async move { - let cloud_name = self.bkt.obj_name(Cow::Borrowed(&self.name)); - let res = self.bkt.client.head_object(&cloud_name).await; + let client = self.bkt.client.get().await.clone(); + let cloud_name = self.bkt.obj_name(Cow::Borrowed(&self.cloud_name)); + let res = client + .head_object() + .bucket(self.bkt.name.clone()) + .key(cloud_name) + .send() + .await; + match res { - Ok((obj, _)) => Ok(ObjectAttrs { - name: self.name.clone(), + Ok(obj) => Ok(ObjectAttrs { + name: self.cloud_name.clone(), version: obj.version_id.unwrap_or_default(), size: obj.content_length.unwrap_or_default() as u64, content_type: obj.content_type, etag: obj.e_tag.unwrap_or_default(), }), - Err(s3::error::S3Error::HttpFailWithBody(404, _)) => Err(Error::NotFound), + Err(SdkError::ServiceError(err)) if err.err().is_not_found() => { + Err(Error::NotFound) + } Err(err) => Err(Error::Other(err.into())), } }) @@ -97,11 +138,17 @@ impl objects::ObjectImpl for Object { fn exists(self: Arc) -> Pin> + Send>> { Box::pin(async move { - let cloud_name = self.bkt.obj_name(Cow::Borrowed(&self.name)); - let res = self.bkt.client.head_object(&cloud_name).await; + let client = self.bkt.client.get().await.clone(); + let cloud_name = self.bkt.obj_name(Cow::Borrowed(&self.cloud_name)); + let res = client + .head_object() + .bucket(&self.cloud_name) + .key(cloud_name) + .send() + .await; match res { Ok(_) => Ok(true), - Err(s3::error::S3Error::HttpFailWithBody(404, _)) => Ok(false), + Err(SdkError::ServiceError(err)) if err.err().is_not_found() => Ok(false), Err(err) => Err(err.into()), } }) @@ -109,10 +156,86 @@ impl objects::ObjectImpl for Object { fn upload( self: Arc, - _data: Box, - _options: objects::UploadOptions, - ) -> Pin> + Send>> { - Box::pin(async move { Err(anyhow::anyhow!("not yet implemented")) }) + mut data: Box, + options: objects::UploadOptions, + ) -> Pin> + Send>> { + Box::pin(async move { + let client = self.bkt.client.get().await.clone(); + let cloud_name = self.bkt.obj_name(Cow::Borrowed(&self.cloud_name)); + let first_chunk = read_chunk_async(&mut data) + .await + .context("unable to read from data source")?; + + match first_chunk { + Chunk::Complete(chunk) => { + // The file is small; do a regular upload. + let chunk = chunk.freeze(); + let total_size = chunk.len(); + let content_md5 = base64::engine::general_purpose::STANDARD + .encode(md5::compute(&chunk).as_ref()); + + let mut req = client + .put_object() + .bucket(&self.bkt.name) + .key(cloud_name) + .content_length(total_size as i64) + .content_md5(content_md5) + .set_content_type(options.content_type) + .body(ByteStream::from(chunk)); + + if let Some(precond) = options.preconditions { + if precond.not_exists == Some(true) { + req = req.if_none_match("*"); + } + } + + let _ = req.send().await?; + Ok(()) + } + + Chunk::Part(chunk) => { + // Large file; do a multipart upload. + let upload = client + .create_multipart_upload() + .bucket(&self.bkt.name) + .key(cloud_name.to_string()) + .set_content_type(options.content_type) + .send() + .await + .context("unable to begin streaming upload")?; + + let upload_id = upload + .upload_id + .context("missing upload_id in streaming upload")?; + + let res = upload_multipart_chunks( + &client, + &mut data, + chunk.freeze(), + &upload_id, + &self.bkt.name, + &cloud_name, + ) + .await; + + match res { + Ok(()) => Ok(()), + Err(err) => { + let fut = client + .abort_multipart_upload() + .bucket(&self.bkt.name) + .key(cloud_name) + .upload_id(upload_id) + .send(); + tokio::spawn(async move { + let _ = fut.await; + }); + return Err(err); + } + } + } + } + }) } fn download( @@ -120,21 +243,154 @@ impl objects::ObjectImpl for Object { ) -> Pin> + Send>> { Box::pin(async move { - Err(objects::DownloadError::Internal(anyhow::anyhow!( - "not yet implemented" - ))) + let client = self.bkt.client.get().await.clone(); + let cloud_name = self.bkt.obj_name(Cow::Borrowed(&self.cloud_name)); + let res = client + .get_object() + .bucket(&self.bkt.name) + .key(cloud_name.into_owned()) + .send() + .await; + + match res { + Ok(mut resp) => { + let result = stream! { + while let Some(chunk) = resp.body.next().await { + yield chunk.map_err(|e| objects::DownloadError::Other(e.into())); + } + }; + let result: objects::DownloadStream = Box::pin(result); + Ok(result) + } + Err(SdkError::ServiceError(err)) if err.err().is_no_such_key() => { + Err(objects::DownloadError::NotFound) + } + Err(err) => Err(objects::DownloadError::Other(err.into())), + } }) } fn delete(self: Arc) -> Pin> + Send>> { Box::pin(async move { - let cloud_name = self.bkt.obj_name(Cow::Borrowed(&self.name)); - let res = self.bkt.client.delete_object(&cloud_name).await; + let client = self.bkt.client.get().await.clone(); + let cloud_name = self.bkt.obj_name(Cow::Borrowed(&self.cloud_name)); + let res = client + .delete_object() + .bucket(&self.bkt.name) + .key(cloud_name.into_owned()) + .send() + .await; match res { Ok(_) => Ok(()), - Err(s3::error::S3Error::HttpFailWithBody(404, _)) => Ok(()), + Err(SdkError::ServiceError(err)) if err.raw().status().as_u16() == 404 => Ok(()), Err(err) => Err(Error::Other(err.into())), } }) } } + +enum Chunk { + Part(BytesMut), + Complete(BytesMut), +} + +impl Chunk { + fn into_bytes(self) -> BytesMut { + match self { + Chunk::Part(buf) => buf, + Chunk::Complete(buf) => buf, + } + } +} + +async fn read_chunk_async(reader: &mut R) -> std::io::Result { + // Use an initial capacity of 10KiB. + let mut buf = BytesMut::with_capacity(10 * 1024); + while buf.len() < CHUNK_SIZE { + // If the buf has no available capacity, we need to allocate more. + if buf.len() == buf.capacity() { + buf.reserve(buf.capacity()); + } + + let n = reader.read_buf(&mut buf).await?; + if n == 0 { + // We've reached the end of the stream. + // This is guaranteed to be the case since in the case + // where the buffer was full we reserved + // additional capacity in the buffer before reading. + return Ok(Chunk::Complete(buf)); + } + } + + Ok(Chunk::Part(buf)) +} + +async fn upload_multipart_chunks( + client: &s3::Client, + reader: &mut R, + first_chunk: Bytes, + upload_id: &str, + bucket: &str, + key: &str, +) -> anyhow::Result<()> { + let mut handles = Vec::new(); + let mut part_number = 0; + let mut upload_part = |chunk: Bytes| { + part_number += 1; + let content_md5 = + base64::engine::general_purpose::STANDARD.encode(md5::compute(&chunk).as_ref()); + let handle = client + .upload_part() + .bucket(bucket) + .key(key) + .upload_id(upload_id) + .part_number(part_number) + .content_length(chunk.len() as i64) + .content_md5(content_md5) + .body(ByteStream::from(chunk)) + .send(); + handles.push(handle); + }; + + upload_part(first_chunk); + loop { + let bytes = read_chunk_async(reader).await?.into_bytes(); + if bytes.is_empty() { + break; + } + upload_part(bytes.freeze()); + } + + // Wait for all the parts to finish uploading. + let responses = futures::future::join_all(handles).await; + + // Check for errors. + for res in responses { + let _ = res?; + } + + Ok(()) +} + +struct ObjectStream { + inner: ByteStream, +} + +impl Stream for ObjectStream { + type Item = Result; + + fn poll_next( + self: Pin<&mut Self>, + cx: &mut std::task::Context, + ) -> std::task::Poll> { + let stream = Pin::new(&mut self.get_mut().inner); + match stream.poll_next(cx) { + Poll::Ready(Some(Err(err))) => { + Poll::Ready(Some(Err(objects::DownloadError::Other(err.into())))) + } + Poll::Ready(Some(Ok(data))) => Poll::Ready(Some(Ok(data))), + Poll::Ready(None) => Poll::Ready(None), + Poll::Pending => Poll::Pending, + } + } +} diff --git a/runtimes/core/src/objects/s3/mod.rs b/runtimes/core/src/objects/s3/mod.rs index 3e8a07ee06..a9885f2f09 100644 --- a/runtimes/core/src/objects/s3/mod.rs +++ b/runtimes/core/src/objects/s3/mod.rs @@ -3,38 +3,60 @@ use std::sync::Arc; use crate::encore::runtime::v1 as pb; use crate::objects; use crate::objects::s3::bucket::Bucket; +use aws_sdk_s3 as s3; mod bucket; #[derive(Debug)] pub struct Cluster { - region: s3::Region, - creds: s3::creds::Credentials, + client: Arc, } impl Cluster { pub fn new(cfg: &pb::bucket_cluster::S3) -> Self { - let region = match cfg.endpoint.as_ref() { - Some(ep) => s3::Region::Custom { - region: cfg.region.clone(), - endpoint: ep.clone(), - }, - None => { - let region: s3::Region = cfg.region.parse().expect("unable to resolve S3 region"); - region - } - }; - - let creds = s3::creds::Credentials::default() - .or_else(|_| s3::creds::Credentials::anonymous()) - .expect("unable to resolve S3 credentials"); - - Self { region, creds } + let client = Arc::new(LazyS3Client::new(cfg.clone())); + Self { client } } } impl objects::ClusterImpl for Cluster { fn bucket(self: Arc, cfg: &pb::Bucket) -> Arc { - Arc::new(Bucket::new(self.region.clone(), self.creds.clone(), cfg)) + Arc::new(Bucket::new(self.client.clone(), cfg)) + } +} + +struct LazyS3Client { + cfg: pb::bucket_cluster::S3, + cell: tokio::sync::OnceCell>, +} + +impl std::fmt::Debug for LazyS3Client { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("LazyS3Client").finish() + } +} + +impl LazyS3Client { + fn new(cfg: pb::bucket_cluster::S3) -> Self { + Self { + cfg, + cell: tokio::sync::OnceCell::new(), + } + } + + async fn get(&self) -> &Arc { + self.cell + .get_or_init(|| async { + let region = aws_config::Region::new(self.cfg.region.clone()); + let mut builder = + aws_config::defaults(aws_config::BehaviorVersion::v2024_03_28()).region(region); + if let Some(endpoint) = self.cfg.endpoint.as_ref() { + builder = builder.endpoint_url(endpoint.clone()); + } + + let cfg = builder.load().await; + Arc::new(s3::Client::new(&cfg)) + }) + .await } } diff --git a/runtimes/js/encore.dev/storage/objects/bucket.ts b/runtimes/js/encore.dev/storage/objects/bucket.ts index 446bc93476..962d2191a3 100644 --- a/runtimes/js/encore.dev/storage/objects/bucket.ts +++ b/runtimes/js/encore.dev/storage/objects/bucket.ts @@ -2,7 +2,6 @@ import * as runtime from "../../internal/runtime/mod"; import { StringLiteral } from "../../internal/utils/constraints"; export interface BucketConfig { - public?: boolean; } /** @@ -27,14 +26,14 @@ export class Bucket { return new Bucket(name, {}); } - async *list(options: ListOptions): AsyncGenerator { + async *list(options: ListOptions): AsyncGenerator { const iter = await this.impl.list(); while (true) { - const attrs = await iter.next(); - if (attrs === null) { + const entry = await iter.next(); + if (entry === null) { break; } - yield attrs; + yield entry; } } @@ -89,9 +88,16 @@ export interface ObjectAttrs { name: string; size: number; version: string; + etag: string; contentType?: string; } +export interface ListEntry { + name: string; + size: number; + etag: string; +} + export interface UploadOptions { contentType?: string; preconditions?: { diff --git a/runtimes/js/src/objects.rs b/runtimes/js/src/objects.rs index c2d9458d07..1133bac881 100644 --- a/runtimes/js/src/objects.rs +++ b/runtimes/js/src/objects.rs @@ -5,8 +5,8 @@ use napi::{Env, JsBuffer, JsObject}; use napi_derive::napi; use encore_runtime_core::objects::{ - Bucket as CoreBucket, DownloadError, ListStream, Object as CoreObject, - ObjectAttrs as CoreAttrs, UploadOptions as CoreUploadOptions, + Bucket as CoreBucket, DownloadError, ListEntry as CoreListEntry, ListStream, + Object as CoreObject, ObjectAttrs as CoreAttrs, UploadOptions as CoreUploadOptions, UploadPreconditions as CoreUploadPreconditions, }; @@ -80,9 +80,9 @@ impl BucketObject { // so that the handler gets called regardless of result. let fut = async move { Ok(fut.await) }; - env.execute_tokio_future(fut, move |&mut _env, result| { + env.execute_tokio_future(fut, move |&mut env, result| { // TODO: Decrement the ref count on the data buffer. - result.map(ObjectAttrs::from).map_err(napi::Error::from) + result.map_err(napi::Error::from) }) } @@ -119,6 +119,23 @@ impl From for ObjectAttrs { } } +#[napi] +pub struct ListEntry { + pub name: String, + pub size: i64, + pub etag: String, +} + +impl From for ListEntry { + fn from(value: CoreListEntry) -> Self { + Self { + name: value.name, + size: value.size as i64, + etag: value.etag, + } + } +} + #[napi(object)] #[derive(Debug, Default)] pub struct UploadOptions { @@ -171,7 +188,7 @@ impl ListIterator { } #[napi] - pub async fn next(&self) -> napi::Result> { + pub async fn next(&self) -> napi::Result> { use futures::StreamExt; let mut stream = self.stream.lock().await; let row = stream @@ -180,6 +197,6 @@ impl ListIterator { .transpose() .map_err(|e| napi::Error::new(napi::Status::GenericFailure, format!("{:#?}", e)))?; - Ok(row.map(ObjectAttrs::from)) + Ok(row.map(ListEntry::from)) } } From 264ea677df91934297d8b4c71aab52edc44ee66f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Eriksson?= Date: Wed, 30 Oct 2024 13:30:31 +0100 Subject: [PATCH 11/37] runtimes/js: improve errors, fix upload return type --- runtimes/core/src/objects/mod.rs | 8 ++++---- runtimes/js/encore.dev/storage/objects/bucket.ts | 2 +- runtimes/js/src/objects.rs | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/runtimes/core/src/objects/mod.rs b/runtimes/core/src/objects/mod.rs index 10094d34bb..0af1464630 100644 --- a/runtimes/core/src/objects/mod.rs +++ b/runtimes/core/src/objects/mod.rs @@ -124,8 +124,8 @@ pub enum Error { #[error("internal error: {0:?}")] Internal(anyhow::Error), - #[error(transparent)] - Other(#[from] anyhow::Error), + #[error("{0:?}")] + Other(anyhow::Error), } #[derive(thiserror::Error, Debug)] @@ -136,8 +136,8 @@ pub enum DownloadError { #[error("internal error: {0:?}")] Internal(anyhow::Error), - #[error(transparent)] - Other(#[from] anyhow::Error), + #[error("{0:?}")] + Other(anyhow::Error), } pub type DownloadStream = Pin> + Send>>; diff --git a/runtimes/js/encore.dev/storage/objects/bucket.ts b/runtimes/js/encore.dev/storage/objects/bucket.ts index 962d2191a3..6bede1c050 100644 --- a/runtimes/js/encore.dev/storage/objects/bucket.ts +++ b/runtimes/js/encore.dev/storage/objects/bucket.ts @@ -58,7 +58,7 @@ export class Bucket { /** * Uploads an object to the bucket. */ - async upload(name: string, data: Buffer, options?: UploadOptions): Promise { + async upload(name: string, data: Buffer, options?: UploadOptions): Promise { const impl = this.impl.object(name); return impl.upload(data, options); } diff --git a/runtimes/js/src/objects.rs b/runtimes/js/src/objects.rs index 1133bac881..4d49231650 100644 --- a/runtimes/js/src/objects.rs +++ b/runtimes/js/src/objects.rs @@ -61,7 +61,7 @@ impl BucketObject { self.obj.exists().await.map_err(napi::Error::from) } - #[napi(ts_return_type = "Promise")] + #[napi(ts_return_type = "Promise")] pub fn upload( &self, env: Env, From 932a6467df86627dd602e3362b0023eff03ff7bd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Eriksson?= Date: Wed, 30 Oct 2024 13:52:56 +0100 Subject: [PATCH 12/37] Fix s3 exists --- runtimes/core/src/objects/s3/bucket.rs | 2 +- runtimes/js/src/objects.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/runtimes/core/src/objects/s3/bucket.rs b/runtimes/core/src/objects/s3/bucket.rs index 3818d18b46..bb72eacded 100644 --- a/runtimes/core/src/objects/s3/bucket.rs +++ b/runtimes/core/src/objects/s3/bucket.rs @@ -142,7 +142,7 @@ impl objects::ObjectImpl for Object { let cloud_name = self.bkt.obj_name(Cow::Borrowed(&self.cloud_name)); let res = client .head_object() - .bucket(&self.cloud_name) + .bucket(&self.bkt.name) .key(cloud_name) .send() .await; diff --git a/runtimes/js/src/objects.rs b/runtimes/js/src/objects.rs index 4d49231650..2c305ea3fe 100644 --- a/runtimes/js/src/objects.rs +++ b/runtimes/js/src/objects.rs @@ -80,7 +80,7 @@ impl BucketObject { // so that the handler gets called regardless of result. let fut = async move { Ok(fut.await) }; - env.execute_tokio_future(fut, move |&mut env, result| { + env.execute_tokio_future(fut, move |&mut _env, result| { // TODO: Decrement the ref count on the data buffer. result.map_err(napi::Error::from) }) From 0b6b6a9a769085b3e3305445fdf844e7846cc94d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Eriksson?= Date: Wed, 30 Oct 2024 16:56:22 +0100 Subject: [PATCH 13/37] Vendor gcsemu with minor tweaks --- cli/daemon/objects/objects.go | 2 +- go.mod | 10 +- go.sum | 3 - pkg/emulators/storage/LICENSE | 21 + pkg/emulators/storage/gcsemu/batch.go | 107 +++ pkg/emulators/storage/gcsemu/client.go | 40 + pkg/emulators/storage/gcsemu/errors.go | 42 + pkg/emulators/storage/gcsemu/filestore.go | 263 ++++++ .../storage/gcsemu/filestore_test.go | 67 ++ pkg/emulators/storage/gcsemu/gcsemu.go | 830 ++++++++++++++++++ pkg/emulators/storage/gcsemu/gcsemu_test.go | 593 +++++++++++++ pkg/emulators/storage/gcsemu/http_wrappers.go | 35 + pkg/emulators/storage/gcsemu/memstore.go | 215 +++++ pkg/emulators/storage/gcsemu/memstore_test.go | 61 ++ pkg/emulators/storage/gcsemu/meta.go | 84 ++ pkg/emulators/storage/gcsemu/multipart.go | 65 ++ pkg/emulators/storage/gcsemu/parse.go | 65 ++ pkg/emulators/storage/gcsemu/range.go | 52 ++ pkg/emulators/storage/gcsemu/range_test.go | 23 + pkg/emulators/storage/gcsemu/raw_http_test.go | 299 +++++++ pkg/emulators/storage/gcsemu/remote_test.go | 50 ++ pkg/emulators/storage/gcsemu/server.go | 44 + pkg/emulators/storage/gcsemu/store.go | 41 + pkg/emulators/storage/gcsemu/util.go | 141 +++ pkg/emulators/storage/gcsemu/walk.go | 139 +++ pkg/emulators/storage/gcsutil/counted_lock.go | 51 ++ pkg/emulators/storage/gcsutil/doc.go | 3 + pkg/emulators/storage/gcsutil/gcspagetoken.go | 37 + .../storage/gcsutil/gcspagetoken.pb.go | 146 +++ .../storage/gcsutil/gcspagetoken.proto | 11 + .../storage/gcsutil/gcspagetoken_test.go | 103 +++ .../storage/gcsutil/transient_lock_map.go | 94 ++ .../gcsutil/transient_lock_map_test.go | 293 +++++++ 33 files changed, 4021 insertions(+), 9 deletions(-) create mode 100644 pkg/emulators/storage/LICENSE create mode 100644 pkg/emulators/storage/gcsemu/batch.go create mode 100644 pkg/emulators/storage/gcsemu/client.go create mode 100644 pkg/emulators/storage/gcsemu/errors.go create mode 100644 pkg/emulators/storage/gcsemu/filestore.go create mode 100644 pkg/emulators/storage/gcsemu/filestore_test.go create mode 100644 pkg/emulators/storage/gcsemu/gcsemu.go create mode 100644 pkg/emulators/storage/gcsemu/gcsemu_test.go create mode 100644 pkg/emulators/storage/gcsemu/http_wrappers.go create mode 100644 pkg/emulators/storage/gcsemu/memstore.go create mode 100644 pkg/emulators/storage/gcsemu/memstore_test.go create mode 100644 pkg/emulators/storage/gcsemu/meta.go create mode 100644 pkg/emulators/storage/gcsemu/multipart.go create mode 100644 pkg/emulators/storage/gcsemu/parse.go create mode 100644 pkg/emulators/storage/gcsemu/range.go create mode 100644 pkg/emulators/storage/gcsemu/range_test.go create mode 100644 pkg/emulators/storage/gcsemu/raw_http_test.go create mode 100644 pkg/emulators/storage/gcsemu/remote_test.go create mode 100644 pkg/emulators/storage/gcsemu/server.go create mode 100644 pkg/emulators/storage/gcsemu/store.go create mode 100644 pkg/emulators/storage/gcsemu/util.go create mode 100644 pkg/emulators/storage/gcsemu/walk.go create mode 100644 pkg/emulators/storage/gcsutil/counted_lock.go create mode 100644 pkg/emulators/storage/gcsutil/doc.go create mode 100644 pkg/emulators/storage/gcsutil/gcspagetoken.go create mode 100644 pkg/emulators/storage/gcsutil/gcspagetoken.pb.go create mode 100644 pkg/emulators/storage/gcsutil/gcspagetoken.proto create mode 100644 pkg/emulators/storage/gcsutil/gcspagetoken_test.go create mode 100644 pkg/emulators/storage/gcsutil/transient_lock_map.go create mode 100644 pkg/emulators/storage/gcsutil/transient_lock_map_test.go diff --git a/cli/daemon/objects/objects.go b/cli/daemon/objects/objects.go index 88549b15ce..ae9bbdd16c 100644 --- a/cli/daemon/objects/objects.go +++ b/cli/daemon/objects/objects.go @@ -7,8 +7,8 @@ import ( "net" "net/http" + "encr.dev/pkg/emulators/storage/gcsemu" "github.com/cockroachdb/errors" - "github.com/fullstorydev/emulators/storage/gcsemu" "github.com/rs/zerolog/log" "go4.org/syncutil" diff --git a/go.mod b/go.mod index facc77e876..a58506b1aa 100644 --- a/go.mod +++ b/go.mod @@ -3,12 +3,14 @@ module encr.dev go 1.22.2 require ( + cloud.google.com/go/storage v1.43.0 cuelang.org/go v0.4.3 encore.dev v1.1.0 github.com/agnivade/levenshtein v1.1.1 github.com/alecthomas/chroma v0.10.0 github.com/alicebob/miniredis/v2 v2.23.0 github.com/bep/debounce v1.2.1 + github.com/bluele/gcache v0.0.2 github.com/briandowns/spinner v1.19.0 github.com/cenkalti/backoff/v4 v4.2.1 github.com/charmbracelet/bubbles v0.16.1 @@ -23,11 +25,11 @@ require ( github.com/fmstephe/unsafeutil v1.0.0 github.com/frankban/quicktest v1.14.5 github.com/fsnotify/fsnotify v1.6.0 - github.com/fullstorydev/emulators/storage v0.0.0-20241015152032-cb8c4efd6784 github.com/getkin/kin-openapi v0.115.0 github.com/gofrs/uuid v4.4.0+incompatible github.com/golang-migrate/migrate/v4 v4.15.2 github.com/golang/protobuf v1.5.4 + github.com/google/btree v1.1.3 github.com/google/go-cmp v0.6.0 github.com/google/go-containerregistry v0.20.2 github.com/google/renameio/v2 v2.0.0 @@ -74,9 +76,11 @@ require ( golang.org/x/term v0.25.0 golang.org/x/text v0.19.0 golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d + google.golang.org/api v0.200.0 google.golang.org/genproto/googleapis/rpc v0.0.0-20241007155032-5fefd90f89a9 google.golang.org/grpc v1.67.1 google.golang.org/protobuf v1.35.1 + gotest.tools/v3 v3.5.1 sigs.k8s.io/yaml v1.3.0 ) @@ -86,7 +90,6 @@ require ( cloud.google.com/go/auth/oauth2adapt v0.2.4 // indirect cloud.google.com/go/compute/metadata v0.5.2 // indirect cloud.google.com/go/iam v1.2.1 // indirect - cloud.google.com/go/storage v1.43.0 // indirect github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161 // indirect github.com/Microsoft/go-winio v0.6.1 // indirect github.com/alicebob/gopher-json v0.0.0-20200520072559-a9ecdc9d1d3a // indirect @@ -95,7 +98,6 @@ require ( github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect github.com/benbjohnson/clock v1.3.5 // indirect github.com/blang/semver v3.5.1+incompatible // indirect - github.com/bluele/gcache v0.0.2 // indirect github.com/bmizerany/perks v0.0.0-20141205001514-d9a9656a3a4b // indirect github.com/cespare/xxhash/v2 v2.3.0 // indirect github.com/cockroachdb/apd/v2 v2.0.2 // indirect @@ -127,7 +129,6 @@ require ( github.com/golang/glog v1.2.2 // indirect github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect github.com/golang/snappy v0.0.4 // indirect - github.com/google/btree v1.1.3 // indirect github.com/google/cel-go v0.18.2 // indirect github.com/google/s2a-go v0.1.8 // indirect github.com/google/uuid v1.6.0 // indirect @@ -193,7 +194,6 @@ require ( go.uber.org/multierr v1.11.0 // indirect go.uber.org/zap v1.26.0 // indirect golang.org/x/time v0.7.0 // indirect - google.golang.org/api v0.200.0 // indirect google.golang.org/genproto v0.0.0-20241007155032-5fefd90f89a9 // indirect google.golang.org/genproto/googleapis/api v0.0.0-20240930140551-af27646dc61f // indirect gopkg.in/natefinch/lumberjack.v2 v2.2.1 // indirect diff --git a/go.sum b/go.sum index 9f61607cf6..d79b49ffa4 100644 --- a/go.sum +++ b/go.sum @@ -510,8 +510,6 @@ github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4 github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw= github.com/fsouza/fake-gcs-server v1.17.0/go.mod h1:D1rTE4YCyHFNa99oyJJ5HyclvN/0uQR+pM/VdlL83bw= github.com/fullsailor/pkcs7 v0.0.0-20190404230743-d7302db945fa/go.mod h1:KnogPXtdwXqoenmZCw6S+25EAm2MkxbG0deNDu4cbSA= -github.com/fullstorydev/emulators/storage v0.0.0-20241015152032-cb8c4efd6784 h1:4dnqzguS8chqXw3ZqcPAD/4KdPx5RbohzGsKOYOFw1w= -github.com/fullstorydev/emulators/storage v0.0.0-20241015152032-cb8c4efd6784/go.mod h1:HNCVyL+WaX0AiVFKXzRAT3uIHTFo/Tr0K1lJvNV+qU0= github.com/gabriel-vasile/mimetype v1.3.1/go.mod h1:fA8fi6KUiG7MgQQ+mEWotXoEOvmxRtOJlERCzSmRvr8= github.com/gabriel-vasile/mimetype v1.4.0/go.mod h1:fA8fi6KUiG7MgQQ+mEWotXoEOvmxRtOJlERCzSmRvr8= github.com/garyburd/redigo v0.0.0-20150301180006-535138d7bcd7/go.mod h1:NR3MbYisc3/PwhQ00EMzDiPmrwpPxAn5GI05/YaO1SY= @@ -2138,7 +2136,6 @@ gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gorm.io/driver/postgres v1.0.8/go.mod h1:4eOzrI1MUfm6ObJU/UcmbXyiHSs8jSwH95G5P5dxcAg= gorm.io/gorm v1.20.12/go.mod h1:0HFTzE/SqkGTzK6TlDPPQbAYCluiVvhzoA1+aVyzenw= gorm.io/gorm v1.21.4/go.mod h1:0HFTzE/SqkGTzK6TlDPPQbAYCluiVvhzoA1+aVyzenw= -gotest.tools v2.2.0+incompatible h1:VsBPFP1AI068pPrMxtb/S8Zkgf9xEmTLJjfM+P5UIEo= gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw= gotest.tools/v3 v3.0.2/go.mod h1:3SzNCllyD9/Y+b5r9JIKQ474KzkZyqLqEfYqMsX94Bk= gotest.tools/v3 v3.0.3/go.mod h1:Z7Lb0S5l+klDB31fvDQX8ss/FlKDxtlFlw3Oa8Ymbl8= diff --git a/pkg/emulators/storage/LICENSE b/pkg/emulators/storage/LICENSE new file mode 100644 index 0000000000..25706a4763 --- /dev/null +++ b/pkg/emulators/storage/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2021 Engineering at Fullstory + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/pkg/emulators/storage/gcsemu/batch.go b/pkg/emulators/storage/gcsemu/batch.go new file mode 100644 index 0000000000..56077f5fe2 --- /dev/null +++ b/pkg/emulators/storage/gcsemu/batch.go @@ -0,0 +1,107 @@ +package gcsemu + +import ( + "bufio" + "bytes" + "fmt" + "io" + "mime/multipart" + "net/http" + "net/http/httptest" + "net/textproto" +) + +// BatchHandler handles emulated GCS http requests for "storage.googleapis.com/batch/storage/v1". +func (g *GcsEmu) BatchHandler(w http.ResponseWriter, r *http.Request) { + // First parse the entire incoming message. + reader, err := r.MultipartReader() + if err != nil { + g.gapiError(w, httpStatusCodeOf(err), err.Error()) + return + } + + var reqs []*http.Request + var contentIds []string + for i := 0; true; i++ { + part, err := reader.NextPart() + if err == io.EOF { + break // done + } else if err != nil { + g.gapiError(w, http.StatusBadRequest, err.Error()) + return + } + + if ct := part.Header.Get("Content-Type"); ct != "application/http" { + g.gapiError(w, http.StatusBadRequest, fmt.Sprintf("Content-Type: want=application/http, got=%s", ct)) + return + } + + contentId := part.Header.Get("Content-ID") + + content, err := io.ReadAll(part) + _ = part.Close() + if err != nil { + g.gapiError(w, http.StatusBadRequest, fmt.Sprintf("part=%d, Content-ID=%s: read error %v", i, contentId, err)) + return + } + + newReader := bufio.NewReader(bytes.NewReader(content)) + req, err := http.ReadRequest(newReader) + if err != nil { + g.gapiError(w, http.StatusBadRequest, fmt.Sprintf("part=%d, Content-ID=%s: unable to parse request %v", i, contentId, err)) + return + } + // Any remaining bytes are the body. + rem, _ := io.ReadAll(newReader) + if len(rem) > 0 { + req.GetBody = func() (io.ReadCloser, error) { + return io.NopCloser(bytes.NewReader(rem)), nil + } + req.Body, _ = req.GetBody() + } + if cte := part.Header.Get("Content-Transfer-Encoding"); cte != "" { + req.Header.Set("Transfer-Encoding", cte) + } + // encoded requests don't include a host, so patch it up from the incoming request + req.Host = r.Host + reqs = append(reqs, req) + contentIds = append(contentIds, contentId) + } + + // At this point, we can respond with a 200. + mw := multipart.NewWriter(w) + w.Header().Set("Content-Type", "multipart/mixed; boundary="+mw.Boundary()) + w.WriteHeader(http.StatusOK) + + // run each request + for i := range reqs { + req, contentId := reqs[i], contentIds[i] + + rw := httptest.NewRecorder() + g.Handler(rw, req) + rsp := rw.Result() + rsp.ContentLength = int64(rw.Body.Len()) + + partHeaders := textproto.MIMEHeader{} + partHeaders.Set("Content-Type", "application/http") + if contentId != "" { + if contentId[0] == '<' { + contentId = "" + } + return fmt.Sprintf("http error %d: %s", err.code, err.cause) +} diff --git a/pkg/emulators/storage/gcsemu/filestore.go b/pkg/emulators/storage/gcsemu/filestore.go new file mode 100644 index 0000000000..6ccb112469 --- /dev/null +++ b/pkg/emulators/storage/gcsemu/filestore.go @@ -0,0 +1,263 @@ +package gcsemu + +import ( + "bytes" + "context" + "encoding/json" + "fmt" + "os" + "path/filepath" + "strings" + "time" + + cloudstorage "cloud.google.com/go/storage" + "google.golang.org/api/storage/v1" +) + +const ( + metaExtention = ".emumeta" +) + +type filestore struct { + gcsDir string +} + +var _ Store = (*filestore)(nil) + +// NewFileStore returns a new Store that writes to the given directory. +func NewFileStore(gcsDir string) *filestore { + return &filestore{gcsDir: gcsDir} +} + +type composeObj struct { + filename string + conds cloudstorage.Conditions +} + +func (fs *filestore) CreateBucket(bucket string) error { + bucketDir := filepath.Join(fs.gcsDir, bucket) + return os.MkdirAll(bucketDir, 0777) +} + +func (fs *filestore) GetBucketMeta(baseUrl HttpBaseUrl, bucket string) (*storage.Bucket, error) { + f := fs.filename(bucket, "") + fInfo, err := os.Stat(f) + if err != nil { + if os.IsNotExist(err) { + return nil, nil + } + return nil, fmt.Errorf("stating %s: %w", f, err) + } + + obj := BucketMeta(baseUrl, bucket) + obj.Updated = fInfo.ModTime().UTC().Format(time.RFC3339Nano) + return obj, nil +} + +func (fs *filestore) Get(baseUrl HttpBaseUrl, bucket string, filename string) (*storage.Object, []byte, error) { + obj, err := fs.GetMeta(baseUrl, bucket, filename) + if err != nil { + return nil, nil, err + } + if obj == nil { + return nil, nil, nil + } + + f := fs.filename(bucket, filename) + contents, err := os.ReadFile(f) + if err != nil { + return nil, nil, fmt.Errorf("reading %s: %w", f, err) + } + return obj, contents, nil +} + +func (fs *filestore) GetMeta(baseUrl HttpBaseUrl, bucket string, filename string) (*storage.Object, error) { + f := fs.filename(bucket, filename) + fInfo, err := os.Stat(f) + if err != nil { + if os.IsNotExist(err) { + return nil, nil + } + return nil, fmt.Errorf("stating %s: %w", f, err) + } + + return fs.ReadMeta(baseUrl, bucket, filename, fInfo) +} + +func (fs *filestore) Add(bucket string, filename string, contents []byte, meta *storage.Object) error { + f := fs.filename(bucket, filename) + if err := os.MkdirAll(filepath.Dir(f), 0777); err != nil { + return fmt.Errorf("could not create dirs for: %s: %w", f, err) + } + + if err := os.WriteFile(f, contents, 0666); err != nil { + return fmt.Errorf("could not write: %s: %w", f, err) + } + + // Force a new modification time, since this is what Generation is based on. + now := time.Now().UTC() + _ = os.Chtimes(f, now, now) + + InitScrubbedMeta(meta, filename) + meta.Metageneration = 1 + meta.Generation = now.UnixNano() + if meta.TimeCreated == "" { + meta.TimeCreated = now.UTC().Format(time.RFC3339Nano) + } + meta.Id = fmt.Sprintf("%s/%s/%d", bucket, filename, meta.Generation) + meta.Etag = fmt.Sprintf("%d", meta.Generation) + + fMeta := metaFilename(f) + if err := os.WriteFile(fMeta, mustJson(meta), 0666); err != nil { + return fmt.Errorf("could not write metadata file: %s: %w", fMeta, err) + } + + return nil +} + +func (fs *filestore) UpdateMeta(bucket string, filename string, meta *storage.Object, metagen int64) error { + InitScrubbedMeta(meta, filename) + meta.Metageneration = metagen + + fMeta := metaFilename(fs.filename(bucket, filename)) + if err := os.WriteFile(fMeta, mustJson(meta), 0666); err != nil { + return fmt.Errorf("could not write metadata file: %s: %w", fMeta, err) + } + + return nil +} + +func (fs *filestore) Copy(srcBucket string, srcFile string, dstBucket string, dstFile string) (bool, error) { + // Make sure it's there + meta, err := fs.GetMeta(dontNeedUrls, srcBucket, srcFile) + if err != nil { + return false, err + } + // Handle object-not-found + if meta == nil { + return false, nil + } + + // Copy with metadata + f1 := fs.filename(srcBucket, srcFile) + contents, err := os.ReadFile(f1) + if err != nil { + return false, err + } + meta.TimeCreated = "" // reset creation time on the dest file + err = fs.Add(dstBucket, dstFile, contents, meta) + if err != nil { + return false, err + } + + return true, nil +} + +func (fs *filestore) Delete(bucket string, filename string) error { + f := fs.filename(bucket, filename) + + err := func() error { + // Check if the bucket exists + if _, err := os.Stat(f); os.IsNotExist(err) { + return os.ErrNotExist + } + + // Remove the bucket + if filename == "" { + return os.RemoveAll(f) + } + + // Remove just the file and the associated metadata file + if err := os.Remove(f); err != nil { + return err + } + err := os.Remove(metaFilename(f)) + if os.IsNotExist(err) { + // Legacy files do not have an accompanying metadata file. + return nil + } + return err + }() + if err != nil { + if os.IsNotExist(err) { + return err + } + return fmt.Errorf("could not delete %s: %w", f, err) + } + + // Try to delete empty directories + for fp := filepath.Dir(f); len(fp) > len(fs.filename(bucket, "")); fp = filepath.Dir(fp) { + files, err := os.ReadDir(fp) + if err != nil || len(files) > 0 { + // Quit trying to delete the directory + break + } + if err := os.Remove(fp); err != nil { + // If removing fails, quit trying + break + } + } + return nil +} + +func (fs *filestore) ReadMeta(baseUrl HttpBaseUrl, bucket string, filename string, fInfo os.FileInfo) (*storage.Object, error) { + if fInfo.IsDir() { + return nil, nil + } + + f := fs.filename(bucket, filename) + obj := &storage.Object{} + fMeta := metaFilename(f) + buf, err := os.ReadFile(fMeta) + if err != nil { + if !os.IsNotExist(err) { + return nil, fmt.Errorf("could not read metadata file %s: %w", fMeta, err) + } + } + + if len(buf) != 0 { + if err := json.NewDecoder(bytes.NewReader(buf)).Decode(obj); err != nil { + return nil, fmt.Errorf("could not parse file attributes %q for %s: %w", buf, f, err) + } + } + + InitMetaWithUrls(baseUrl, obj, bucket, filename, uint64(fInfo.Size())) + // obj.Generation = fInfo.ModTime().UnixNano() // use the mod time as the generation number + obj.Updated = fInfo.ModTime().UTC().Format(time.RFC3339Nano) + return obj, nil +} + +func (fs *filestore) filename(bucket string, filename string) string { + if filename == "" { + return filepath.Join(fs.gcsDir, bucket) + } + return filepath.Join(fs.gcsDir, bucket, filename) +} + +func metaFilename(filename string) string { + return filename + metaExtention +} + +func (fs *filestore) Walk(ctx context.Context, bucket string, cb func(ctx context.Context, filename string, fInfo os.FileInfo) error) error { + root := filepath.Join(fs.gcsDir, bucket) + return filepath.Walk(root, func(path string, fInfo os.FileInfo, err error) error { + if strings.HasSuffix(path, metaExtention) { + // Ignore metadata files + return nil + } + + filename := strings.TrimPrefix(path, root) + filename = strings.TrimPrefix(filename, "/") + if err != nil { + if os.IsNotExist(err) { + return err + } + return fmt.Errorf("walk error at %s: %w", filename, err) + } + + if err := cb(ctx, filename, fInfo); err != nil { + return err + } + return nil + }) +} diff --git a/pkg/emulators/storage/gcsemu/filestore_test.go b/pkg/emulators/storage/gcsemu/filestore_test.go new file mode 100644 index 0000000000..49b76b3e32 --- /dev/null +++ b/pkg/emulators/storage/gcsemu/filestore_test.go @@ -0,0 +1,67 @@ +package gcsemu + +import ( + "context" + "fmt" + "net/http" + "net/http/httptest" + "os" + "path/filepath" + "testing" + "time" + + "gotest.tools/v3/assert" +) + +func TestFileStore(t *testing.T) { + // Setup an on-disk emulator. + gcsDir := filepath.Join(os.TempDir(), fmt.Sprintf("gcsemu-test-%d", time.Now().Unix())) + gcsEmu := NewGcsEmu(Options{ + Store: NewFileStore(gcsDir), + Verbose: true, + Log: func(err error, fmt string, args ...interface{}) { + t.Helper() + if err != nil { + fmt = "ERROR: " + fmt + ": %s" + args = append(args, err) + } + t.Logf(fmt, args...) + }, + }) + mux := http.NewServeMux() + gcsEmu.Register(mux) + svr := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + t.Logf("about to method=%s host=%s u=%s", r.Method, r.Host, r.URL) + mux.ServeHTTP(w, r) + })) + t.Cleanup(svr.Close) + + gcsClient, err := NewTestClientWithHost(context.Background(), svr.URL) + assert.NilError(t, err) + t.Cleanup(func() { + _ = gcsClient.Close() + }) + + bh := BucketHandle{ + Name: "file-bucket", + BucketHandle: gcsClient.Bucket("file-bucket"), + } + initBucket(t, bh) + attrs, err := bh.Attrs(context.Background()) + assert.NilError(t, err) + assert.Equal(t, bh.Name, attrs.Name) + + t.Parallel() + for _, tc := range testCases { + tc := tc + t.Run(tc.name, func(t *testing.T) { + t.Parallel() + tc.f(t, bh) + }) + } + + t.Run("RawHttp", func(t *testing.T) { + t.Parallel() + testRawHttp(t, bh, http.DefaultClient, svr.URL) + }) +} diff --git a/pkg/emulators/storage/gcsemu/gcsemu.go b/pkg/emulators/storage/gcsemu/gcsemu.go new file mode 100644 index 0000000000..54971cd0af --- /dev/null +++ b/pkg/emulators/storage/gcsemu/gcsemu.go @@ -0,0 +1,830 @@ +// Package gcsemu implements a Google Cloud Storage emulator for development. +package gcsemu + +import ( + "bytes" + "compress/gzip" + "context" + "crypto/md5" + "encoding/base64" + "encoding/json" + "fmt" + "io" + "net/http" + "net/url" + "os" + "strconv" + "strings" + "sync/atomic" + + cloudstorage "cloud.google.com/go/storage" + "encr.dev/pkg/emulators/storage/gcsutil" + "github.com/bluele/gcache" + "google.golang.org/api/storage/v1" +) + +const maybeNotImplementedErrorMsg = "This may be a valid request, but we haven't implemented it in gcsemu yet." + +// Options configure the emulator. +type Options struct { + // A storage layer to use; if nil, defaults to in-mem storage. + Store Store + + // If true, log verbosely. + Verbose bool + + // Optional log function. `err` will be `nil` for informational/debug messages. + Log func(err error, fmt string, args ...interface{}) +} + +// GcsEmu is a Google Cloud Storage emulator for development. +type GcsEmu struct { + // The directory which contains gcs emulation. + store Store + locks *gcsutil.TransientLockMap + + uploadIds gcache.Cache + idCounter int32 + + verbose bool + log func(err error, fmt string, args ...interface{}) +} + +// NewGcsEmu creates a new Google Cloud Storage emulator. +func NewGcsEmu(opts Options) *GcsEmu { + if opts.Store == nil { + opts.Store = NewMemStore() + } + if opts.Log == nil { + opts.Log = func(_ error, _ string, _ ...interface{}) {} + } + return &GcsEmu{ + store: opts.Store, + locks: gcsutil.NewTransientLockMap(), + uploadIds: gcache.New(1024).LRU().Build(), + verbose: opts.Verbose, + log: opts.Log, + } +} + +func lockName(bucket string, filename string) string { + return bucket + "/" + filename +} + +// Register the emulator's HTTP handlers on the given mux. +func (g *GcsEmu) Register(mux *http.ServeMux) { + mux.HandleFunc("/", DrainRequestHandler(GzipRequestHandler(g.Handler))) + mux.HandleFunc("/batch/storage/v1", DrainRequestHandler(GzipRequestHandler(g.BatchHandler))) +} + +// Handler handles emulated GCS http requests for "storage.googleapis.com". +func (g *GcsEmu) Handler(w http.ResponseWriter, r *http.Request) { + baseUrl := dontNeedUrls + { + host := requestHost(r) + if host != "" { + // Prepend the proto. + if r.TLS != nil || r.Header.Get("X-Forwarded-Proto") == "https" { + baseUrl = HttpBaseUrl("https://" + host + "/") + } else { + baseUrl = HttpBaseUrl("http://" + host + "/") + } + } + } + + ctx := r.Context() + p, ok := ParseGcsUrl(r.URL) + if !ok { + g.gapiError(w, http.StatusBadRequest, "unrecognized request") + return + } + object := p.Object + bucket := p.Bucket + + if err := r.ParseForm(); err != nil { + g.gapiError(w, http.StatusBadRequest, fmt.Sprintf("failed to parse form: %s", err)) + return + } + + conds, err := parseConds(r.Form) + if err != nil { + g.gapiError(w, http.StatusBadRequest, err.Error()) + return + } + + if g.verbose { + if object == "" { + g.log(nil, "%s request for bucket %q", r.Method, bucket) + } else { + g.log(nil, "%s request for bucket %q, object %q", r.Method, bucket, object) + } + } + + switch r.Method { + case "DELETE": + g.handleGcsDelete(ctx, w, bucket, object, conds) + case "GET": + if object == "" { + if strings.HasSuffix(r.URL.Path, "/o") { + g.handleGcsListBucket(ctx, baseUrl, w, r.URL.Query(), bucket) + } else { + g.handleGcsMetadataRequest(baseUrl, w, bucket, object) + } + } else { + alt := r.URL.Query().Get("alt") + if alt == "media" || (p.IsPublic && alt == "") { + g.handleGcsMediaRequest(baseUrl, w, r.Header.Get("Accept-Encoding"), bucket, object) + } else if alt == "json" || (!p.IsPublic && alt == "") { + g.handleGcsMetadataRequest(baseUrl, w, bucket, object) + } else { + // should never happen? + g.gapiError(w, http.StatusBadRequest, fmt.Sprintf("unsupported value for alt param to GET: %q\n%s", alt, maybeNotImplementedErrorMsg)) + } + } + case "PATCH": + alt := r.URL.Query().Get("alt") + if alt == "json" || r.Header.Get("Content-Type") == "application/json" { + g.handleGcsUpdateMetadataRequest(ctx, baseUrl, w, r, bucket, object, conds) + } else { + // should never happen? + g.gapiError(w, http.StatusBadRequest, fmt.Sprintf("unsupported value for alt param to PATCH: %q\n%s", alt, maybeNotImplementedErrorMsg)) + } + case "POST": + if bucket == "" { + g.handleGcsNewBucket(ctx, w, r, conds) + } else if object == "" { + g.handleGcsNewObject(ctx, baseUrl, w, r, bucket, conds) + } else if strings.Contains(object, "/compose") { + // TODO: enforce other conditions outside of generation + g.handleGcsCompose(ctx, baseUrl, w, r, bucket, object, conds) + } else if strings.Contains(object, "/rewriteTo/") { + g.handleGcsCopy(ctx, baseUrl, w, bucket, object) + } else if r.Form.Get("upload_id") != "" { + g.handleGcsNewObjectResume(ctx, baseUrl, w, r, r.Form.Get("upload_id")) + } else { + // unsupported method, or maybe should never happen + g.gapiError(w, http.StatusBadRequest, fmt.Sprintf("unsupported POST request: %v\n%s", r.URL, maybeNotImplementedErrorMsg)) + } + case "PUT": + if r.Form.Get("upload_id") != "" { + g.handleGcsNewObjectResume(ctx, baseUrl, w, r, r.Form.Get("upload_id")) + } else { + // unsupported method, or maybe should never happen + g.gapiError(w, http.StatusBadRequest, fmt.Sprintf("unsupported PUT request: %v\n%s", r.URL, maybeNotImplementedErrorMsg)) + } + default: + g.gapiError(w, http.StatusMethodNotAllowed, "") + } +} + +func (g *GcsEmu) handleGcsCompose(ctx context.Context, baseUrl HttpBaseUrl, w http.ResponseWriter, r *http.Request, bucket, object string, conds cloudstorage.Conditions) { + var req storage.ComposeRequest + if err := json.NewDecoder(r.Body).Decode(&req); err != nil { + g.gapiError(w, http.StatusBadRequest, "bad compose request") + return + } + // Get the composed object name from the path + parts := strings.Split(object, "/compose") + if len(parts) != 2 { + g.gapiError(w, http.StatusBadRequest, "bad compose request") + return + } + dst := composeObj{ + filename: parts[0], + conds: conds, + } + + srcs := make([]composeObj, len(req.SourceObjects)) + for i, sObj := range req.SourceObjects { + var generationMatch int64 + if sObj.ObjectPreconditions != nil { + generationMatch = sObj.ObjectPreconditions.IfGenerationMatch + } + srcs[i] = composeObj{ + filename: sObj.Name, + conds: cloudstorage.Conditions{ + GenerationMatch: generationMatch, + }, + } + } + var obj *storage.Object + if err := g.locks.Run(ctx, lockName(bucket, dst.filename), func(_ context.Context) error { + var err error + obj, err = g.finishCompose(baseUrl, bucket, dst, srcs, req.Destination) + return err + }); err != nil { + g.gapiError(w, httpStatusCodeOf(err), fmt.Sprintf("failed to compose objects: %s", err)) + return + } + g.jsonRespond(w, &obj) +} + +func (g *GcsEmu) handleGcsListBucket(ctx context.Context, baseUrl HttpBaseUrl, w http.ResponseWriter, params url.Values, bucket string) { + delimiter := params.Get("delimiter") + prefix := params.Get("prefix") + pageToken := params.Get("pageToken") + + var cursor string + if pageToken != "" { + lastFilename, err := gcsutil.DecodePageToken(pageToken) + if err != nil { + g.gapiError(w, http.StatusBadRequest, fmt.Sprintf("invalid pageToken parameter (failed to decode) %s: %s", pageToken, err)) + return + } + cursor = lastFilename + } + + maxResults := 1000 + maxResultsStr := params.Get("maxResults") + if maxResultsStr != "" { + var err error + maxResults, err = strconv.Atoi(maxResultsStr) + if err != nil || maxResults < 1 { + g.gapiError(w, http.StatusBadRequest, fmt.Sprintf("invalid maxResults parameter: %s", maxResultsStr)) + return + } + } + + g.makeBucketListResults(ctx, baseUrl, w, delimiter, cursor, prefix, bucket, maxResults) +} + +func (g *GcsEmu) handleGcsDelete(ctx context.Context, w http.ResponseWriter, bucket string, filename string, conds cloudstorage.Conditions) { + err := g.locks.Run(ctx, lockName(bucket, filename), func(ctx context.Context) error { + // Find the existing file / meta. + obj, err := g.store.GetMeta(dontNeedUrls, bucket, filename) + if err != nil { + return fmt.Errorf("failed to check existence of %s/%s: %w", bucket, filename, err) + } + + if err := validateConds(obj, conds); err != nil { + return err + } + + if err := g.store.Delete(bucket, filename); err != nil { + if os.IsNotExist(err) { + return fmtErrorfCode(http.StatusNotFound, "%s/%s not found", bucket, filename) + } + return fmt.Errorf("failed to delete %s/%s: %w", bucket, filename, err) + } + + return nil + }) + if err != nil { + g.gapiError(w, httpStatusCodeOf(err), err.Error()) + return + } + + w.WriteHeader(http.StatusNoContent) +} + +func (g *GcsEmu) handleGcsMediaRequest(baseUrl HttpBaseUrl, w http.ResponseWriter, acceptEncoding, bucket, filename string) { + obj, contents, err := g.store.Get(baseUrl, bucket, filename) + if err != nil { + g.gapiError(w, http.StatusInternalServerError, fmt.Sprintf("failed to check existence of %s/%s: %s", bucket, filename, err)) + return + } + if obj == nil { + g.gapiError(w, http.StatusNotFound, fmt.Sprintf("%s/%s not found", bucket, filename)) + return + } + + w.Header().Set("Content-Type", obj.ContentType) + w.Header().Set("X-Goog-Generation", strconv.FormatInt(obj.Generation, 10)) + w.Header().Set("X-Goog-Metageneration", strconv.FormatInt(obj.Metageneration, 10)) + w.Header().Set("Access-Control-Allow-Origin", "*") + w.Header().Set("Access-Control-Expose-Headers", "Content-Type, Content-Length, Content-Encoding, Date, X-Goog-Generation, X-Goog-Metageneration") + w.Header().Set("Content-Disposition", obj.ContentDisposition) + + if obj.ContentEncoding == "gzip" { + if strings.Contains(acceptEncoding, "gzip") { + w.Header().Set("Content-Encoding", "gzip") + } else { + // Uncompress on behalf of the client. + buf := bytes.NewBuffer(contents) + gzipReader, err := gzip.NewReader(buf) + if err != nil { + g.gapiError(w, http.StatusInternalServerError, fmt.Sprintf("failed to gunzip from %s/%s: %s", bucket, filename, err)) + } + if _, err := io.Copy(w, gzipReader); err != nil { + g.gapiError(w, http.StatusInternalServerError, fmt.Sprintf("failed to copy+gunzip from %s/%s: %s", bucket, filename, err)) + } + if err := gzipReader.Close(); err != nil { + g.gapiError(w, http.StatusInternalServerError, fmt.Sprintf("failed to copy+gunzip from %s/%s: %s", bucket, filename, err)) + } + return + } + } + + // Just write the contents + w.Header().Set("Content-Length", strconv.Itoa(len(contents))) + if _, err := w.Write(contents); err != nil { + g.gapiError(w, http.StatusInternalServerError, fmt.Sprintf("failed to copy from %s/%s: %s", bucket, filename, err)) + } +} + +func (g *GcsEmu) handleGcsMetadataRequest(baseUrl HttpBaseUrl, w http.ResponseWriter, bucket string, filename string) { + var obj interface{} + var err error + if filename == "" { + var b *storage.Bucket + b, err = g.store.GetBucketMeta(baseUrl, bucket) + if b != nil { + obj = b + } + } else { + var o *storage.Object + o, err = g.store.GetMeta(baseUrl, bucket, filename) + if o != nil { + obj = o + } + } + + if err != nil { + g.gapiError(w, http.StatusInternalServerError, fmt.Sprintf("failed to get meta for %s/%s: %s", bucket, filename, err)) + return + } + if obj == nil { + g.gapiError(w, http.StatusNotFound, fmt.Sprintf("%s/%s not found", bucket, filename)) + return + } + g.jsonRespond(w, obj) +} + +func (g *GcsEmu) handleGcsUpdateMetadataRequest(ctx context.Context, baseUrl HttpBaseUrl, w http.ResponseWriter, r *http.Request, bucket, filename string, conds cloudstorage.Conditions) { + var obj *storage.Object + err := g.locks.Run(ctx, lockName(bucket, filename), func(ctx context.Context) error { + // Find the existing file / meta. + var err error + obj, err = g.store.GetMeta(baseUrl, bucket, filename) + if err != nil { + return fmt.Errorf("failed to check existence of %s/%s: %w", bucket, filename, err) + } + + if obj == nil { + return nil + } + + if err := validateConds(obj, conds); err != nil { + return err + } + + // Update via json decode. + metagen := obj.Metageneration + err = json.NewDecoder(r.Body).Decode(&obj) + if err != nil { + return fmtErrorfCode(http.StatusBadRequest, "failed to parse request: %w", err) + } + + if err := g.store.UpdateMeta(bucket, filename, obj, metagen+1); err != nil { + return fmt.Errorf("failed to update attrs of %s/%s: %w", bucket, filename, err) + } + + return nil + }) + + if err != nil { + g.gapiError(w, httpStatusCodeOf(err), err.Error()) + return + } + if obj == nil { + g.gapiError(w, http.StatusNotFound, fmt.Sprintf("%s/%s not found", bucket, filename)) + return + } + + // Respond with the updated metadata. + obj, err = g.store.GetMeta(baseUrl, bucket, filename) + if err != nil { + g.gapiError(w, http.StatusInternalServerError, fmt.Sprintf("failed to get meta for %s/%s: %s", bucket, filename, err)) + return + } + g.jsonRespond(w, obj) +} + +func (g *GcsEmu) handleGcsCopy(ctx context.Context, baseUrl HttpBaseUrl, w http.ResponseWriter, b1 string, objectPaths string) { + // TODO(dk): this operation supports conditionals and metadata rewriting, but the emulator implementation currently does not. + // See https://cloud.google.com/storage/docs/json_api/v1/objects/rewrite + parts := strings.Split(objectPaths, "/rewriteTo/b/") + // Copy is implemented using the Rewrite API, with object strings of format /o/sourceObject/rewriteTo/b/destinationBucket/o/destinationObject + if len(parts) != 2 { + g.gapiError(w, http.StatusBadRequest, fmt.Sprintf("Bad rewrite request format: %s", objectPaths)) + return + } + f1 := parts[0] + destParts := strings.Split(parts[1], "/o/") + if len(parts) != 2 { + g.gapiError(w, http.StatusBadRequest, fmt.Sprintf("Bad rewrite request, expected object/file split: %s", parts[1])) + return + } + b2 := destParts[0] + f2 := destParts[1] + + // Must lock the destination object. + var obj *storage.Object + err := g.locks.Run(ctx, lockName(b2, f2), func(ctx context.Context) error { + if ok, err := g.store.Copy(b1, f1, b2, f2); err != nil { + return err + } else if !ok { + return nil // file missing + } else { + obj, err = g.store.GetMeta(baseUrl, b2, f2) + return err + } + }) + if err != nil { + g.gapiError(w, httpStatusCodeOf(err), fmt.Sprintf("failed to copy: %s", err)) + return + } + if obj == nil { + g.gapiError(w, http.StatusNotFound, fmt.Sprintf("%s not found", b1+"/"+f1)) + return + } + + rr := storage.RewriteResponse{ + Kind: "storage#rewriteResponse", + TotalBytesRewritten: int64(obj.Size), + ObjectSize: int64(obj.Size), + Done: true, + RewriteToken: "-not-implemented-", + Resource: obj, + } + + g.jsonRespond(w, &rr) +} + +type uploadData struct { + Object storage.Object + Conds cloudstorage.Conditions + data []byte +} + +func (g *GcsEmu) handleGcsNewBucket(ctx context.Context, w http.ResponseWriter, r *http.Request, _ cloudstorage.Conditions) { + var bucket storage.Bucket + if err := json.NewDecoder(r.Body).Decode(&bucket); err != nil { + g.gapiError(w, http.StatusBadRequest, "failed to parse body as json") + return + } + bucketName := bucket.Name + + err := g.locks.Run(ctx, lockName(bucketName, ""), func(ctx context.Context) error { + if err := g.store.CreateBucket(bucketName); err != nil { + return fmt.Errorf("could not create bucket %s: %w", bucketName, err) + } + return nil + }) + + if err != nil { + g.gapiError(w, httpStatusCodeOf(err), err.Error()) + return + } + + g.jsonRespond(w, bucket) +} + +func (g *GcsEmu) handleGcsNewObject(ctx context.Context, baseUrl HttpBaseUrl, w http.ResponseWriter, r *http.Request, bucket string, conds cloudstorage.Conditions) { + switch r.Form.Get("uploadType") { + case "media": + // simple upload + name := r.Form.Get("name") + if name == "" { + g.gapiError(w, http.StatusBadRequest, "missing object name") + return + } + + contents, err := io.ReadAll(r.Body) + if err != nil { + g.gapiError(w, http.StatusBadRequest, "failed to read body") + return + } + + obj := &storage.Object{ + Bucket: bucket, + ContentType: r.Header.Get("Content-Type"), + Name: name, + Size: uint64(len(contents)), + } + + meta, err := g.finishUpload(ctx, baseUrl, obj, contents, bucket, conds) + if err != nil { + g.gapiError(w, httpStatusCodeOf(err), err.Error()) + return + } + + w.Header().Set("x-goog-generation", strconv.FormatInt(meta.Generation, 10)) + w.Header().Set("X-Goog-Metageneration", strconv.FormatInt(meta.Metageneration, 10)) + g.jsonRespond(w, meta) + return + case "resumable": + var obj storage.Object + if err := json.NewDecoder(r.Body).Decode(&obj); err != nil { + g.gapiError(w, http.StatusBadRequest, "failed to parse body as json") + return + } + obj.Bucket = bucket + + nextId := atomic.AddInt32(&g.idCounter, 1) + id := strconv.Itoa(int(nextId)) + _ = g.uploadIds.Set(id, &uploadData{ + Object: obj, + Conds: conds, + }) + + w.Header().Set("Location", ObjectUrl(baseUrl, bucket, obj.Name)+"?upload_id="+id) + w.Header().Set("Content-Type", obj.ContentType) + w.WriteHeader(http.StatusCreated) + return + case "multipart": + obj, contents, err := readMultipartInsert(r) + if err != nil { + g.gapiError(w, http.StatusBadRequest, fmt.Sprintf("failed to parse request: %s", err)) + return + } + + meta, err := g.finishUpload(ctx, baseUrl, obj, contents, bucket, conds) + if err != nil { + g.gapiError(w, httpStatusCodeOf(err), err.Error()) + return + } + + w.Header().Set("x-goog-generation", strconv.FormatInt(meta.Generation, 10)) + w.Header().Set("X-Goog-Metageneration", strconv.FormatInt(meta.Metageneration, 10)) + g.jsonRespond(w, meta) + return + default: + // TODO + g.gapiError(w, http.StatusNotImplemented, "not yet implemented") + return + + } +} + +func (g *GcsEmu) handleGcsNewObjectResume(ctx context.Context, baseUrl HttpBaseUrl, w http.ResponseWriter, r *http.Request, id string) { + found, err := g.uploadIds.GetIFPresent(id) + if err != nil { + g.gapiError(w, http.StatusInternalServerError, fmt.Sprintf("unexpected error: %s", err)) + return + } + if found == nil { + g.gapiError(w, http.StatusNotFound, "no such id") + return + } + + u := found.(*uploadData) + + contents, err := io.ReadAll(r.Body) + if err != nil { + g.gapiError(w, http.StatusBadRequest, fmt.Sprintf("failed to ready body: %s", err)) + return + } + + contentRange := r.Header.Get("Content-Range") + if contentRange == "" { + g.gapiError(w, http.StatusBadRequest, "expected Content-Range") + return + } + + // Parse the content range + byteRange := parseByteRange(contentRange) + if byteRange == nil { + g.gapiError(w, http.StatusBadRequest, "malformed Content-Range header") + return + } + + if byteRange.lo == -1 && len(contents) != 0 || byteRange.lo != -1 && len(contents) != int(byteRange.hi+1-byteRange.lo) { + g.gapiError(w, http.StatusBadRequest, fmt.Sprintf("Content-Range does not match content size: range=%v, len=%v", contentRange, len(contents))) + return + } + + if len(u.data) < int(byteRange.lo) { + g.gapiError(w, http.StatusBadRequest, "missing content") + return + } + + // Apply the content to our stored data. + if byteRange.lo != -1 { + u.data = u.data[:byteRange.lo] // truncate a previous write if we've seen this range before + } + u.data = append(u.data, contents...) + + // Are we done? + if byteRange.sz < 0 || len(u.data) < int(byteRange.sz) { + // Not finished; save the contents and tell the client to resume. + w.Header().Set("Range", fmt.Sprintf("bytes=0-%d", len(u.data)-1)) + w.Header().Set("Content-Type", u.Object.ContentType) + if r.Header.Get("X-Guploader-No-308") == "yes" { + w.Header().Set("X-Http-Status-Code-Override", "308") + w.WriteHeader(http.StatusOK) + } else { + w.WriteHeader(http.StatusPermanentRedirect) + } + return + } + + // Done + meta, err := g.finishUpload(ctx, baseUrl, &u.Object, u.data, u.Object.Bucket, u.Conds) + if err != nil { + g.gapiError(w, httpStatusCodeOf(err), err.Error()) + return + } + + g.uploadIds.Remove(id) + w.Header().Set("x-goog-generation", strconv.FormatInt(meta.Generation, 10)) + w.Header().Set("X-Goog-Metageneration", strconv.FormatInt(meta.Metageneration, 10)) + g.jsonRespond(w, meta) +} + +func (g *GcsEmu) finishUpload(ctx context.Context, baseUrl HttpBaseUrl, obj *storage.Object, contents []byte, bucket string, conds cloudstorage.Conditions) (*storage.Object, error) { + filename := obj.Name + bHash := md5.Sum(contents) + contentHash := bHash[:] + md5Hash := base64.StdEncoding.EncodeToString(contentHash) + if obj.Md5Hash != "" { + h, err := base64.StdEncoding.DecodeString(obj.Md5Hash) + if err != nil { + return nil, fmtErrorfCode(http.StatusBadRequest, "not a valid md5 hash: %w", err) + } + if !bytes.Equal(contentHash, h) { + return nil, fmtErrorfCode(http.StatusBadRequest, "md5 hash %s != expected %s", obj.Md5Hash, md5Hash) + } + } + obj.Md5Hash = md5Hash + obj.Etag = strconv.Quote(md5Hash) + + err := g.locks.Run(ctx, lockName(bucket, filename), func(ctx context.Context) error { + // Find the existing file / meta. + existing, err := g.store.GetMeta(baseUrl, bucket, filename) + if err != nil { + return fmt.Errorf("failed to check existence of %s/%s: %w", bucket, filename, err) + } + + if err := validateConds(existing, conds); err != nil { + return err + } + + if existing != nil { + obj.TimeCreated = existing.TimeCreated + } + + if err := g.store.Add(bucket, filename, contents, obj); err != nil { + return fmt.Errorf("failed to create %s/%s: %w", bucket, filename, err) + } + return nil + }) + + if err != nil { + return nil, err + } + + // respond with object metadata + meta, err := g.store.GetMeta(baseUrl, bucket, filename) + if err != nil { + return nil, fmt.Errorf("failed to get meta for %s/%s: %w", bucket, filename, err) + } + + meta.Id = fmt.Sprintf("%s/%s/%d", bucket, filename, meta.Generation) + return meta, nil +} + +// Returns true if item is strictly greater than anything that begins with prefix +func greaterThanPrefix(item string, prefix string) bool { + if len(item) < len(prefix) { + return item > prefix + } + return item[:len(prefix)] > prefix +} + +// Returns true if item is strictly less than anything that begins with prefix +func lessThanPrefix(item string, prefix string) bool { + if len(item) < len(prefix) { + return item < prefix[:len(item)] + } + return item < prefix +} + +var ( + emptyConds = cloudstorage.Conditions{} + doesNotExistConds = cloudstorage.Conditions{DoesNotExist: true} +) + +func validateConds(obj *storage.Object, cond cloudstorage.Conditions) error { + if obj == nil { + // The only way a nil object can succeed is if the conds are exactly equal to empty or doesNotExist + if cond == emptyConds || cond == doesNotExistConds { + return nil + } + return fmtErrorfCode(http.StatusPreconditionFailed, "precondition failed") + } + + // obj != nil from here on + + if cond.DoesNotExist { + return fmtErrorfCode(http.StatusPreconditionFailed, "precondition failed") + } + + if cond.GenerationMatch != 0 && obj.Generation != cond.GenerationMatch { + return fmtErrorfCode(http.StatusPreconditionFailed, "precondition failed") + } + + if cond.GenerationNotMatch != 0 && obj.Generation == cond.GenerationNotMatch { + // not-match failures use a different code + return fmtErrorfCode(http.StatusNotModified, "precondition failed") + } + + if cond.MetagenerationMatch != 0 && obj.Metageneration != cond.MetagenerationMatch { + return fmtErrorfCode(http.StatusPreconditionFailed, "precondition failed") + } + + if cond.MetagenerationNotMatch != 0 && obj.Metageneration == cond.MetagenerationNotMatch { + // not-match failures use a different code + return fmtErrorfCode(http.StatusNotModified, "precondition failed") + } + + return nil +} + +func parseConds(vals url.Values) (cloudstorage.Conditions, error) { + var ret cloudstorage.Conditions + for i, e := range []struct { + paramName string + ref *int64 + }{ + {"ifGenerationMatch", &ret.GenerationMatch}, + {"ifGenerationNotMatch", &ret.GenerationNotMatch}, + {"ifMetagenerationMatch", &ret.MetagenerationMatch}, + {"ifMetagenerationNotMatch", &ret.MetagenerationNotMatch}, + } { + v := vals.Get(e.paramName) + if v == "" { + continue + } + val, err := strconv.ParseInt(v, 10, 64) + if err != nil { + return ret, fmt.Errorf("failed to parse %s=%s: %w", e.paramName, v, err) + } + *e.ref = val + if i == 0 { + // Special case + ret.DoesNotExist = val == 0 + } + } + + return ret, nil +} + +const ( + gcsMaxComposeSources = 32 +) + +func (g *GcsEmu) finishCompose(baseUrl HttpBaseUrl, bucket string, dst composeObj, srcs []composeObj, meta *storage.Object) (*storage.Object, error) { + if len(srcs) > gcsMaxComposeSources { + return nil, fmtErrorfCode(http.StatusBadRequest, "too many sources") + } + + // TODO: consider moving this to disk to handle very large compose operations + var data []byte + metas := make([]*storage.Object, len(srcs)) + for i, src := range srcs { + meta, contents, err := g.store.Get(baseUrl, bucket, src.filename) + if err != nil { + return nil, fmt.Errorf("failed to get object %s: %w", src.filename, err) + } + if meta == nil { + return nil, fmtErrorfCode(http.StatusNotFound, "no such source object %s", src.filename) + } + if err := validateConds(meta, src.conds); err != nil { + return nil, err + } + data = append(data, contents...) + metas[i] = meta + } + + for _, m := range metas { + meta.ComponentCount += m.ComponentCount + } + // composite objects do not have an MD5 hash (https://cloud.google.com/storage/docs/composite-objects) + meta.Md5Hash = "" + + dstMeta, err := g.store.GetMeta(baseUrl, bucket, dst.filename) + if err != nil { + return nil, fmt.Errorf("failed to get object %s: %w", dst.filename, err) + } + if err := validateConds(dstMeta, dst.conds); err != nil { + return nil, err + } + if dstMeta != nil { + meta.TimeCreated = dstMeta.TimeCreated + } + if err := g.store.Add(bucket, dst.filename, data, meta); err != nil { + return nil, fmt.Errorf("failed to add new file: %w", err) + } + return g.store.GetMeta(baseUrl, bucket, dst.filename) +} + +// InitBucket creates the given bucket directly. +func (g *GcsEmu) InitBucket(bucketName string) error { + return g.locks.Run(context.Background(), lockName(bucketName, ""), func(ctx context.Context) error { + if err := g.store.CreateBucket(bucketName); err != nil { + return fmt.Errorf("could not create bucket: %s: %w", bucketName, err) + } + return nil + }) +} diff --git a/pkg/emulators/storage/gcsemu/gcsemu_test.go b/pkg/emulators/storage/gcsemu/gcsemu_test.go new file mode 100644 index 0000000000..d201de3f92 --- /dev/null +++ b/pkg/emulators/storage/gcsemu/gcsemu_test.go @@ -0,0 +1,593 @@ +package gcsemu + +import ( + "context" + "crypto/md5" + "fmt" + "io" + "net/http" + "strings" + "testing" + "time" + + "cloud.google.com/go/storage" + "google.golang.org/api/googleapi" + "google.golang.org/api/iterator" + "gotest.tools/v3/assert" +) + +const ( + invalidBucketName = "fullstory-non-existant-bucket" +) + +var ( + testCases = []struct { + name string + f func(t *testing.T, bh BucketHandle) + }{ + {"Basics", testBasics}, + {"MultipleFiles", testMultipleFiles}, + {"HugeFile", testHugeFile}, + {"HugeFile_MultipleOfChunkSize", testHugeFileMultipleOfChunkSize}, + {"HugeFileWithConditional", testHugeFileWithConditional}, + {"ConditionalUpdates", testConditionalUpdates}, + {"GenNotMatchDoesntExist", testGenNotMatchDoesntExist}, + {"CopyBasics", testCopyBasics}, + {"Compose", testCompose}, + {"CopyMetadata", testCopyMetadata}, + {"CopyConditionals", testCopyConditionals}, + } +) + +const ( + v1 = `This file is for gcsemu_intg_test.go, please ignore (v1)` + v2 = `This file is for gcsemu_intg_test.go, please ignore (this is version 2)` + source1 = `This is source file number 1` + source2 = `This is source file number 2` +) + +type BucketHandle struct { + Name string + *storage.BucketHandle +} + +func initBucket(t *testing.T, bh BucketHandle) { + ctx := context.Background() + + _ = bh.Delete(ctx) + err := bh.Create(ctx, "dev", &storage.BucketAttrs{}) + assert.NilError(t, err, "failed") + + attrs, err := bh.Attrs(ctx) + assert.NilError(t, err, "failed") + assert.Equal(t, bh.Name, attrs.Name, "wrong") +} + +func testBasics(t *testing.T, bh BucketHandle) { + const name = "gscemu-test/1.txt" + ctx := context.Background() + oh := bh.Object(name) + + // Forcibly delete the object at the start, make sure it doesn't exist. + err := oh.Delete(ctx) + if err != nil { + assert.Equal(t, storage.ErrObjectNotExist, err, "wrong error") + } + + // Should not exist. + _, err = oh.Attrs(ctx) + assert.Equal(t, storage.ErrObjectNotExist, err, "wrong error") + + // Checker funcs + checkAttrs := func(attrs *storage.ObjectAttrs, content string, metagen int64) { + assert.Equal(t, name, attrs.Name, "wrong") + assert.Equal(t, bh.Name, attrs.Bucket, "wrong") + assert.Equal(t, int64(len(content)), attrs.Size, "wrong") + assert.Equal(t, metagen, attrs.Metageneration, "wrong") + checkSum := md5.Sum([]byte(content)) + assert.DeepEqual(t, checkSum[:], attrs.MD5) + } + + checkObject := func(content string, metagen int64) *storage.ObjectAttrs { + attrs, err := oh.Attrs(ctx) + assert.NilError(t, err, "failed") + checkAttrs(attrs, content, metagen) + + r, err := oh.NewReader(ctx) + assert.NilError(t, err, "failed") + data, err := io.ReadAll(r) + assert.NilError(t, err, "failed") + assert.NilError(t, r.Close(), "failed") + assert.Equal(t, content, string(data), "wrong data") + return attrs + } + + // Create the object. + w := oh.NewWriter(ctx) + assert.NilError(t, write(w, v1), "failed") + checkAttrs(w.Attrs(), v1, 1) + + // Read the object. + attrs := checkObject(v1, 1) + assert.Assert(t, attrs.Generation != 0, "expected non-zero") + gen := attrs.Generation + + // Update the object to version 2. Also test MD5 setting. + w = oh.NewWriter(ctx) + checkSum := md5.Sum([]byte(v2)) + w.MD5 = checkSum[:] + assert.NilError(t, write(w, v2), "failed") + checkAttrs(w.Attrs(), v2, 1) + assert.Assert(t, gen != w.Attrs().Generation, "expected different gen") + gen = w.Attrs().Generation + + // Read the object again. + attrs = checkObject(v2, 1) + assert.Equal(t, gen, attrs.Generation, "expected same gen") + + // Update the attrs. + attrs, err = oh.Update(ctx, storage.ObjectAttrsToUpdate{ + ContentType: "text/plain", + }) + assert.NilError(t, err, "failed") + checkAttrs(attrs, v2, 2) + assert.Equal(t, "text/plain", attrs.ContentType, "wrong") + assert.Equal(t, gen, attrs.Generation, "expected same gen") + + // Delete the object. + assert.NilError(t, oh.Delete(ctx), "failed") + + // Should not exist. + _, err = oh.Attrs(ctx) + assert.Equal(t, storage.ErrObjectNotExist, err, "wrong error") + + // Should not be able to update attrs. + _, err = oh.Update(ctx, storage.ObjectAttrsToUpdate{ + ContentType: "text/plain", + }) + assert.Equal(t, storage.ErrObjectNotExist, err, "wrong error") +} + +func testMultipleFiles(t *testing.T, bh BucketHandle) { + dir := "multi-test/" + ctx := context.Background() + + files := []string{"file1", "file2", "file3"} + for _, f := range files { + oh := bh.Object(dir + f) + w := oh.NewWriter(ctx) + assert.NilError(t, write(w, v1), "failed to write file %s", dir+f) + } + + iter := bh.Objects(ctx, &storage.Query{Prefix: dir}) + for _, f := range files { + obj, err := iter.Next() + assert.NilError(t, err, "failed to fetch next object") + assert.Equal(t, dir+f, obj.Name, "wrong filename") + } + + // No more objects should exist + _, err := iter.Next() + assert.Equal(t, iterator.Done, err, "iteration not finished or failed after first bucket object") +} + +// Tests resumable GCS uploads. +func testHugeFile(t *testing.T, bh BucketHandle) { + doHugeFile(t, bh, "gscemu-test/huge.txt", googleapi.DefaultUploadChunkSize+4*1024*1024) +} + +func testHugeFileMultipleOfChunkSize(t *testing.T, bh BucketHandle) { + doHugeFile(t, bh, "gscemu-test/huge2.txt", googleapi.DefaultUploadChunkSize*4) +} + +func doHugeFile(t *testing.T, bh BucketHandle, name string, size int) { + ctx := context.Background() + oh := bh.Object(name) + + // Forcibly delete the object at the start, make sure it doesn't exist. + err := oh.Delete(ctx) + if err != nil { + assert.Equal(t, storage.ErrObjectNotExist, err, "wrong error") + } + + // Should not exist. + _, err = oh.Attrs(ctx) + assert.Equal(t, storage.ErrObjectNotExist, err, "wrong error") + + // Create the object. + w := oh.NewWriter(ctx) + hash, err := writeHugeObject(t, w, size) + assert.NilError(t, err, "failed") + + attrs, err := oh.Attrs(ctx) + assert.NilError(t, err, "failed") + assert.Equal(t, size, int(attrs.Size), "wrong") + assert.DeepEqual(t, hash, attrs.MD5) +} + +func writeHugeObject(t *testing.T, w *storage.Writer, sz int) ([]byte, error) { + data := []byte(`0123456789ABCDEF`) + hash := md5.New() + for i := 0; i < sz/len(data); i++ { + n, err := w.Write(data) + _, _ = hash.Write(data) + assert.NilError(t, err, "failed") + assert.Equal(t, n, len(data), "short write") + } + return hash.Sum(nil), w.Close() +} + +// Tests resumable GCS uploads. +func testHugeFileWithConditional(t *testing.T, bh BucketHandle) { + const name = "gscemu-test/huge2.txt" + const size = googleapi.DefaultUploadChunkSize*2 + 1024 + + ctx := context.Background() + oh := bh.Object(name) + + // Forcibly delete the object at the start, make sure it doesn't exist. + err := oh.Delete(ctx) + if err != nil { + assert.Equal(t, storage.ErrObjectNotExist, err, "wrong error") + } + + // Should not exist. + _, err = oh.Attrs(ctx) + assert.Equal(t, storage.ErrObjectNotExist, err, "wrong error") + + // Create the object. + w := oh.If(storage.Conditions{DoesNotExist: true}).NewWriter(ctx) + hash, err := writeHugeObject(t, w, size) + assert.NilError(t, err, "failed") + + attrs, err := oh.Attrs(ctx) + assert.NilError(t, err, "failed") + assert.Equal(t, size, int(attrs.Size), "wrong") + assert.DeepEqual(t, hash, attrs.MD5) + + // Should fail this time. + w = oh.If(storage.Conditions{DoesNotExist: true}).NewWriter(ctx) + _, err = writeHugeObject(t, w, size) + assert.Equal(t, http.StatusPreconditionFailed, httpStatusCodeOf(err), "wrong error %T: %s", err, err) +} + +func testConditionalUpdates(t *testing.T, bh BucketHandle) { + const name = "gscemu-test/2.txt" + ctx := context.Background() + oh := bh.Object(name) + + // Forcibly delete the object at the start, make sure it doesn't exist. + err := oh.Delete(ctx) + if err != nil { + assert.Equal(t, storage.ErrObjectNotExist, err, "wrong error") + } + + // Ensure write fails + w := oh.If(storage.Conditions{GenerationMatch: 1}).NewWriter(ctx) + err = write(w, "bogus") + assert.Equal(t, http.StatusPreconditionFailed, httpStatusCodeOf(err), "wrong error %T: %s", err, err) + + // Now actually write it. + w = oh.If(storage.Conditions{DoesNotExist: true}).NewWriter(ctx) + assert.NilError(t, write(w, v1), "failed") + attrs := w.Attrs() + t.Logf("attrs.Generation=%d attrs.Metageneration=%d", attrs.Generation, attrs.Metageneration) + + expectFailConds := func(expectCode int, conds storage.Conditions) { + // Ensure attr update fails. + _, err = oh.If(conds).Update(ctx, storage.ObjectAttrsToUpdate{ContentType: "text/plain"}) + assert.Equal(t, expectCode, httpStatusCodeOf(err), "wrong error %T: %s", err, err) + + // Ensure write fails + w := oh.If(conds).NewWriter(ctx) + err = write(w, "bogus") + assert.Equal(t, expectCode, httpStatusCodeOf(err), "wrong error %T: %s", err, err) + + // Ensure delete fails + err = oh.If(conds).Delete(ctx) + assert.Equal(t, expectCode, httpStatusCodeOf(err), "wrong error %T: %s", err, err) + } + + for i, conds := range []storage.Conditions{ + { + DoesNotExist: true, + }, + { + GenerationMatch: attrs.Generation + 1, + }, + { + MetagenerationMatch: attrs.Metageneration + 1, + }, + { + GenerationMatch: attrs.Generation, + MetagenerationMatch: attrs.Metageneration + 1, + }, + { + GenerationMatch: attrs.Generation + 1, + MetagenerationMatch: attrs.Metageneration, + }, + { + GenerationNotMatch: attrs.Generation, + }, + { + MetagenerationNotMatch: attrs.Metageneration, + }, + { + GenerationNotMatch: attrs.Generation, + MetagenerationMatch: attrs.Metageneration, + }, + { + GenerationMatch: attrs.Generation, + MetagenerationNotMatch: attrs.Metageneration, + }, + } { + t.Logf("case %d", i) + expectCode := http.StatusPreconditionFailed + if i >= 5 { + // For some reason, "not match" cases return 304 rather than 412. + expectCode = http.StatusNotModified + } + expectFailConds(expectCode, conds) + } + + // Actually update the attrs. + attrs, err = oh.If(storage.Conditions{ + GenerationMatch: attrs.Generation, + MetagenerationMatch: attrs.Metageneration, + }).Update(ctx, storage.ObjectAttrsToUpdate{ + ContentType: "text/plain", + }) + assert.NilError(t, err, "failed") + + // Actually update the content. + w = oh.If(storage.Conditions{ + GenerationMatch: attrs.Generation, + MetagenerationMatch: attrs.Metageneration, + }).NewWriter(ctx) + assert.NilError(t, write(w, v2), "failed") + attrs = w.Attrs() + + // Actually delete. + err = oh.If(storage.Conditions{ + GenerationMatch: attrs.Generation, + MetagenerationMatch: attrs.Metageneration, + }).Delete(ctx) + assert.NilError(t, err, "failed") + + // Should not exist. + _, err = oh.Attrs(ctx) + assert.Equal(t, storage.ErrObjectNotExist, err, "wrong error") +} + +func testGenNotMatchDoesntExist(t *testing.T, bh BucketHandle) { + // How does generation not match interact with a non-existent file? + + const name = "gscemu-test-gen-not-match.txt" + ctx := context.Background() + oh := bh.Object(name) + + // Forcibly delete the object at the start, make sure it doesn't exist. + err := oh.Delete(ctx) + if err != nil { + assert.Equal(t, storage.ErrObjectNotExist, err, "wrong error") + } + + // Write should fail on non-existent object, even though the generation doesn't match. + w := oh.If(storage.Conditions{GenerationNotMatch: 1}).NewWriter(ctx) + err = write(w, "bogus") + assert.Equal(t, http.StatusPreconditionFailed, httpStatusCodeOf(err), "wrong error %T: %s", err, err) +} + +func testCopyBasics(t *testing.T, bh BucketHandle) { + ctx := context.Background() + + file1 := "file-1" + file2 := "file-1-again" + + src := bh.Object(file1) + dest := bh.Object(file2) + + // Forcibly delete the object at the start, make sure it doesn't exist. + _ = src.Delete(ctx) + _ = dest.Delete(ctx) + + // Should not exist. + _, err := src.Attrs(ctx) + assert.Equal(t, storage.ErrObjectNotExist, err, "wrong error") + + // Create the object. + w := src.NewWriter(ctx) + n, err := io.Copy(w, strings.NewReader(v1)) + assert.NilError(t, err, "failed") + assert.Equal(t, n, int64(len(v1)), "wrong length") + assert.NilError(t, w.Close(), "failed") + + // Wait a ms to ensure different timestamps. + time.Sleep(time.Millisecond) + + // Copy the object + destAttrs, err := dest.CopierFrom(src).Run(ctx) + assert.NilError(t, err, "failed to copy") + + // Read the object. + r, err := dest.NewReader(ctx) + assert.NilError(t, err, "failed") + data, err := io.ReadAll(r) + assert.NilError(t, err, "failed") + assert.NilError(t, r.Close(), "failed") + assert.Equal(t, string(data), v1, "wrong data") + + // Check the metadata reread correct + reDestAttrs, err := dest.Attrs(ctx) + assert.NilError(t, err, "failed") + assert.DeepEqual(t, destAttrs, reDestAttrs) + + // Check the metadata was copied and makes sense. + srcAttrs, err := src.Attrs(ctx) + assert.NilError(t, err, "failed") + expectAttrs := *srcAttrs + + // Some things should be different + assert.Assert(t, srcAttrs.Name != destAttrs.Name, "should not equal: %s", destAttrs.Name) + expectAttrs.Name = destAttrs.Name + + assert.Assert(t, srcAttrs.MediaLink != destAttrs.MediaLink, "should not equal: %s", destAttrs.MediaLink) + expectAttrs.MediaLink = destAttrs.MediaLink + + assert.Assert(t, srcAttrs.Generation != destAttrs.Generation, "should not equal: %d", destAttrs.Generation) + expectAttrs.Generation = destAttrs.Generation + + assert.Assert(t, srcAttrs.Created != destAttrs.Created, "should not equal: %s", destAttrs.Created) + expectAttrs.Created = destAttrs.Created + + assert.Assert(t, srcAttrs.Updated != destAttrs.Updated, "should not equal: %s", destAttrs.Updated) + expectAttrs.Updated = destAttrs.Updated + + expectAttrs.Etag = destAttrs.Etag + + // Rest should be same + assert.DeepEqual(t, expectAttrs, *destAttrs) + + // Delete the object. + assert.NilError(t, src.Delete(ctx), "failed") + assert.NilError(t, dest.Delete(ctx), "failed") + + // Copy an object that doesn't exist + _, err = dest.CopierFrom(src).Run(ctx) + assert.Equal(t, http.StatusNotFound, httpStatusCodeOf(err), "wrong error %T: %s", err, err) +} + +func testCompose(t *testing.T, bh BucketHandle) { + ctx := context.Background() + + srcFiles := []string{source1, source2} + srcGens := []int64{0, 0} + manualCompose := "" + dstName := "gcs-test-data/dest.txt" + dstNameSecondary := "gcs-test-data/dest-secondary.txt" + + srcs := make([]*storage.ObjectHandle, len(srcFiles)) + for i, src := range srcFiles { + name := fmt.Sprintf("gcs-test-sources/src-%d.txt", i) + srcs[i] = bh.Object(name) + // Forcibly delete the object at the start, make sure it doesn't exist. + _ = srcs[i].Delete(ctx) + + // Should not exist. + _, err := srcs[i].Attrs(ctx) + assert.Equal(t, storage.ErrObjectNotExist, err, "wrong error") + + // Create the object. + w := srcs[i].NewWriter(ctx) + w.ContentType = "text/csv" + n, err := io.Copy(w, strings.NewReader(src)) + assert.NilError(t, err, "failed") + assert.Equal(t, n, int64(len(src)), "wrong length") + assert.NilError(t, w.Close(), "failed") + srcGens[i] = w.Attrs().Generation + + manualCompose += src + } + + dest := bh.Object(dstName) + + destSecondary := bh.Object(dstNameSecondary) + err := destSecondary.Delete(ctx) // this one needs to be deleted to start + assert.Assert(t, err == nil || err == storage.ErrObjectNotExist, "failed to delete secondary") + + composer := dest.ComposerFrom(srcs...) + composer.ContentType = "text/plain" + attrs, err := composer.Run(ctx) + assert.NilError(t, err, "failed to run compose") + + assert.Equal(t, dest.BucketName(), attrs.Bucket, "bucket doesn't match") + assert.Equal(t, dest.ObjectName(), attrs.Name, "object name doesn't match") + assert.Equal(t, "text/plain", attrs.ContentType, "content type doesn't match") + r, err := dest.NewReader(ctx) + assert.NilError(t, err, "failed to create reader for composed file") + data, err := io.ReadAll(r) + assert.NilError(t, err, "failed to read from composed file") + assert.NilError(t, r.Close(), "failed to close composed file reader") + assert.Equal(t, manualCompose, string(data), "content doesn't match") + + // Issue the same request with incorrect generation on a source. + composer = dest.If(storage.Conditions{GenerationMatch: attrs.Generation}).ComposerFrom( + srcs[0].If(storage.Conditions{GenerationMatch: srcGens[0]}), // correct + srcs[1].If(storage.Conditions{GenerationMatch: srcGens[1] + 1})) // incorrect + composer.ContentType = "text/plain" + _, err = composer.Run(ctx) + assert.ErrorContains(t, err, "googleapi: Error 412") + assert.Equal(t, http.StatusPreconditionFailed, httpStatusCodeOf(err), "expected precondition failed") + + // Issue the same request with incorrect generation on the destination. + composer = dest.If(storage.Conditions{DoesNotExist: true}).ComposerFrom( + srcs[0].If(storage.Conditions{GenerationMatch: srcGens[0]}), + srcs[1].If(storage.Conditions{GenerationMatch: srcGens[1]})) + composer.ContentType = "text/plain" + _, err = composer.Run(ctx) + assert.ErrorContains(t, err, "googleapi: Error 412") + assert.Equal(t, http.StatusPreconditionFailed, httpStatusCodeOf(err), "expected precondition failed") + + // Issue the a request does not exist destination. + composer = destSecondary.If(storage.Conditions{DoesNotExist: true}).ComposerFrom( + srcs[0].If(storage.Conditions{GenerationMatch: srcGens[0]}), + srcs[1].If(storage.Conditions{GenerationMatch: srcGens[1]})) + composer.ContentType = "text/plain" + _, err = composer.Run(ctx) + assert.NilError(t, err, "failed to run compose") + // The resulting data should be correct (like in the original test). + r, err = destSecondary.NewReader(ctx) + assert.NilError(t, err, "failed to create reader for composed file") + data, err = io.ReadAll(r) + assert.NilError(t, err, "failed to read from composed file") + assert.NilError(t, r.Close(), "failed to close composed file reader") + assert.Equal(t, manualCompose, string(data), "content doesn't match") + + // Use the new destination as the source for another compose. This is how we append + // Additionally, use generation conditions for all of the source objects. + composer = dest.ComposerFrom( + dest.If(storage.Conditions{GenerationMatch: attrs.Generation}), + srcs[0].If(storage.Conditions{GenerationMatch: srcGens[0]})) + newAttrs, err := composer.Run(ctx) + assert.NilError(t, err, "failed to run compose") + assert.Equal(t, "", newAttrs.ContentType, "content type doesn't match") + + r, err = dest.NewReader(ctx) + assert.NilError(t, err, "failed to create reader for composed file") + data, err = io.ReadAll(r) + assert.NilError(t, err, "failed to read from composed file") + assert.NilError(t, r.Close(), "failed to close composed file reader") + assert.Equal(t, manualCompose+source1, string(data), "content doesn't match") + + // Make sure we get a 404 if the source doesn't exist + dneObj := bh.Object("dneObject") + _ = dneObj.Delete(ctx) + // Should not exist. + _, err = dneObj.Attrs(ctx) + assert.Equal(t, storage.ErrObjectNotExist, err, "wrong error") + + composer = dest.ComposerFrom(dneObj) + _, err = composer.Run(ctx) + assert.Equal(t, http.StatusNotFound, httpStatusCodeOf(err), "wrong error returned") +} + +func testCopyMetadata(t *testing.T, bh BucketHandle) { + // TODO(dk): Metadata-rewriting on copy is not currently implemented. + t.Skip() +} + +func testCopyConditionals(t *testing.T, bh BucketHandle) { + // TODO(dk): Conditional support for copy is not currently implemented. + t.Skip() +} + +func write(w *storage.Writer, content string) error { + n, err := io.Copy(w, strings.NewReader(content)) + if err != nil { + return err + } + if n != int64(len(content)) { + panic("not all content sent") + } + return w.Close() +} diff --git a/pkg/emulators/storage/gcsemu/http_wrappers.go b/pkg/emulators/storage/gcsemu/http_wrappers.go new file mode 100644 index 0000000000..778d8bb2ec --- /dev/null +++ b/pkg/emulators/storage/gcsemu/http_wrappers.go @@ -0,0 +1,35 @@ +package gcsemu + +import ( + "compress/gzip" + "io" + "net/http" +) + +// DrainRequestHandler wraps the given handler to drain the incoming request body on exit. +func DrainRequestHandler(h http.HandlerFunc) http.HandlerFunc { + return func(w http.ResponseWriter, r *http.Request) { + defer func() { + // Always drain and close the request body to properly free up the connection. + // See https://groups.google.com/forum/#!topic/golang-nuts/pP3zyUlbT00 + _, _ = io.Copy(io.Discard, r.Body) + _ = r.Body.Close() + }() + h(w, r) + } +} + +// GzipRequestHandler wraps the given handler to automatically decompress gzipped content. +func GzipRequestHandler(h http.HandlerFunc) http.HandlerFunc { + return func(w http.ResponseWriter, r *http.Request) { + if r.Header.Get("Content-Encoding") == "gzip" { + gzr, err := gzip.NewReader(r.Body) + if err != nil { + http.Error(w, err.Error(), http.StatusBadRequest) + return + } + r.Body = gzr + } + h(w, r) + } +} diff --git a/pkg/emulators/storage/gcsemu/memstore.go b/pkg/emulators/storage/gcsemu/memstore.go new file mode 100644 index 0000000000..8356ce0da6 --- /dev/null +++ b/pkg/emulators/storage/gcsemu/memstore.go @@ -0,0 +1,215 @@ +package gcsemu + +import ( + "context" + "os" + "sync" + "time" + "fmt" + + "github.com/google/btree" + "google.golang.org/api/storage/v1" +) + +type memstore struct { + mu sync.RWMutex + buckets map[string]*memBucket +} + +var _ Store = (*memstore)(nil) + +// NewMemStore returns a Store that operates purely in memory. +func NewMemStore() *memstore { + return &memstore{buckets: map[string]*memBucket{}} +} + +type memBucket struct { + created time.Time + + // mutex required (despite lock map in gcsemu), because btree mutations are not structurally safe + mu sync.RWMutex + files *btree.BTree +} + +func (ms *memstore) getBucket(bucket string) *memBucket { + ms.mu.RLock() + defer ms.mu.RUnlock() + return ms.buckets[bucket] +} + +type memFile struct { + meta storage.Object + data []byte +} + +func (mf *memFile) Less(than btree.Item) bool { + // TODO(dragonsinth): is a simple lexical sort ok for Walk? + return mf.meta.Name < than.(*memFile).meta.Name +} + +var _ btree.Item = (*memFile)(nil) + +func (ms *memstore) CreateBucket(bucket string) error { + ms.mu.Lock() + defer ms.mu.Unlock() + if ms.buckets[bucket] == nil { + ms.buckets[bucket] = &memBucket{ + created: time.Now(), + files: btree.New(16), + } + } + return nil +} + +func (ms *memstore) GetBucketMeta(baseUrl HttpBaseUrl, bucket string) (*storage.Bucket, error) { + if b := ms.getBucket(bucket); b != nil { + obj := BucketMeta(baseUrl, bucket) + obj.Updated = b.created.UTC().Format(time.RFC3339Nano) + return obj, nil + } + return nil, nil +} + +func (ms *memstore) Get(baseUrl HttpBaseUrl, bucket string, filename string) (*storage.Object, []byte, error) { + f := ms.find(bucket, filename) + if f != nil { + return &f.meta, f.data, nil + } + return nil, nil, nil +} + +func (ms *memstore) GetMeta(baseUrl HttpBaseUrl, bucket string, filename string) (*storage.Object, error) { + f := ms.find(bucket, filename) + if f != nil { + meta := f.meta + InitMetaWithUrls(baseUrl, &meta, bucket, filename, uint64(len(f.data))) + return &meta, nil + } + return nil, nil +} + +func (ms *memstore) Add(bucket string, filename string, contents []byte, meta *storage.Object) error { + _ = ms.CreateBucket(bucket) + + InitScrubbedMeta(meta, filename) + meta.Metageneration = 1 + + // Cannot be overridden by caller + now := time.Now().UTC() + meta.Updated = now.UTC().Format(time.RFC3339Nano) + meta.Generation = now.UnixNano() + if meta.TimeCreated == "" { + meta.TimeCreated = meta.Updated + } + meta.Id = fmt.Sprintf("%s/%s/%d", bucket, filename, meta.Generation) + meta.Etag = fmt.Sprintf("%d", meta.Generation) + + b := ms.getBucket(bucket) + b.mu.Lock() + defer b.mu.Unlock() + b.files.ReplaceOrInsert(&memFile{ + meta: *meta, + data: contents, + }) + return nil +} + +func (ms *memstore) UpdateMeta(bucket string, filename string, meta *storage.Object, metagen int64) error { + f := ms.find(bucket, filename) + if f == nil { + return os.ErrNotExist + } + + InitScrubbedMeta(meta, filename) + meta.Metageneration = metagen + + b := ms.getBucket(bucket) + b.mu.Lock() + defer b.mu.Unlock() + b.files.ReplaceOrInsert(&memFile{ + meta: *meta, + data: f.data, + }) + return nil +} + +func (ms *memstore) Copy(srcBucket string, srcFile string, dstBucket string, dstFile string) (bool, error) { + src := ms.find(srcBucket, srcFile) + if src == nil { + return false, nil + } + + // Copy with metadata + meta := src.meta + meta.TimeCreated = "" // reset creation time on the dest file + err := ms.Add(dstBucket, dstFile, src.data, &meta) + if err != nil { + return false, err + } + + return true, nil +} + +func (ms *memstore) Delete(bucket string, filename string) error { + if filename == "" { + // Remove the bucket + ms.mu.Lock() + defer ms.mu.Unlock() + if _, ok := ms.buckets[bucket]; !ok { + return os.ErrNotExist + } + + delete(ms.buckets, bucket) + } else if b := ms.getBucket(bucket); b != nil { + // Remove just the file + b.mu.Lock() + defer b.mu.Unlock() + if b.files.Delete(ms.key(filename)) == nil { + // case file does not exist + return os.ErrNotExist + } + } else { + return os.ErrNotExist + } + + return nil +} + +func (ms *memstore) ReadMeta(baseUrl HttpBaseUrl, bucket string, filename string, _ os.FileInfo) (*storage.Object, error) { + return ms.GetMeta(baseUrl, bucket, filename) +} + +func (ms *memstore) Walk(ctx context.Context, bucket string, cb func(ctx context.Context, filename string, fInfo os.FileInfo) error) error { + if b := ms.getBucket(bucket); b != nil { + var err error + b.mu.RLock() + defer b.mu.RUnlock() + b.files.Ascend(func(i btree.Item) bool { + mf := i.(*memFile) + err = cb(ctx, mf.meta.Name, nil) + return err == nil + }) + return nil + } + return os.ErrNotExist +} + +func (ms *memstore) key(filename string) btree.Item { + return &memFile{ + meta: storage.Object{ + Name: filename, + }, + } +} + +func (ms *memstore) find(bucket string, filename string) *memFile { + if b := ms.getBucket(bucket); b != nil { + b.mu.Lock() + defer b.mu.Unlock() + f := b.files.Get(ms.key(filename)) + if f != nil { + return f.(*memFile) + } + } + return nil +} diff --git a/pkg/emulators/storage/gcsemu/memstore_test.go b/pkg/emulators/storage/gcsemu/memstore_test.go new file mode 100644 index 0000000000..150a5cafd8 --- /dev/null +++ b/pkg/emulators/storage/gcsemu/memstore_test.go @@ -0,0 +1,61 @@ +package gcsemu + +import ( + "context" + "net/http" + "net/http/httptest" + "testing" + + "gotest.tools/v3/assert" +) + +func TestMemStore(t *testing.T) { + // Setup an in-memory emulator. + gcsEmu := NewGcsEmu(Options{ + Verbose: true, + Log: func(err error, fmt string, args ...interface{}) { + t.Helper() + if err != nil { + fmt = "ERROR: " + fmt + ": %s" + args = append(args, err) + } + t.Logf(fmt, args...) + }, + }) + mux := http.NewServeMux() + gcsEmu.Register(mux) + svr := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + t.Logf("about to method=%s host=%s u=%s", r.Method, r.Host, r.URL) + mux.ServeHTTP(w, r) + })) + t.Cleanup(svr.Close) + + gcsClient, err := NewTestClientWithHost(context.Background(), svr.URL) + assert.NilError(t, err) + t.Cleanup(func() { + _ = gcsClient.Close() + }) + + bh := BucketHandle{ + Name: "mem-bucket", + BucketHandle: gcsClient.Bucket("mem-bucket"), + } + initBucket(t, bh) + attrs, err := bh.Attrs(context.Background()) + assert.NilError(t, err) + assert.Equal(t, bh.Name, attrs.Name) + + t.Parallel() + for _, tc := range testCases { + tc := tc + t.Run(tc.name, func(t *testing.T) { + t.Parallel() + tc.f(t, bh) + }) + } + + t.Run("RawHttp", func(t *testing.T) { + t.Parallel() + testRawHttp(t, bh, http.DefaultClient, svr.URL) + }) +} diff --git a/pkg/emulators/storage/gcsemu/meta.go b/pkg/emulators/storage/gcsemu/meta.go new file mode 100644 index 0000000000..dd5fdae2fa --- /dev/null +++ b/pkg/emulators/storage/gcsemu/meta.go @@ -0,0 +1,84 @@ +package gcsemu + +import ( + "fmt" + "mime" + "strings" + + "google.golang.org/api/storage/v1" +) + +// BucketMeta returns a default bucket metadata for the given name and base url. +func BucketMeta(baseUrl HttpBaseUrl, bucket string) *storage.Bucket { + return &storage.Bucket{ + Kind: "storage#bucket", + Name: bucket, + SelfLink: BucketUrl(baseUrl, bucket), + StorageClass: "STANDARD", + } +} + +// InitScrubbedMeta "bakes" metadata with intrinsic values and removes fields that are intrinsic / computed. +func InitScrubbedMeta(meta *storage.Object, filename string) { + parts := strings.Split(filename, ".") + ext := parts[len(parts)-1] + + if meta.ContentType == "" { + meta.ContentType = mime.TypeByExtension(ext) + } + meta.Name = filename + ScrubMeta(meta) +} + +// InitMetaWithUrls "bakes" metadata with intrinsic values, including computed links. +func InitMetaWithUrls(baseUrl HttpBaseUrl, meta *storage.Object, bucket string, filename string, size uint64) { + parts := strings.Split(filename, ".") + ext := parts[len(parts)-1] + + meta.Bucket = bucket + if meta.ContentType == "" { + meta.ContentType = mime.TypeByExtension(ext) + } + meta.Kind = "storage#object" + meta.MediaLink = ObjectUrl(baseUrl, bucket, filename) + "?alt=media" + meta.Name = filename + meta.SelfLink = ObjectUrl(baseUrl, bucket, filename) + meta.Size = size + meta.StorageClass = "STANDARD" +} + +// ScrubMeta removes fields that are intrinsic / computed for minimal storage. +func ScrubMeta(meta *storage.Object) { + meta.Bucket = "" + meta.Kind = "" + meta.MediaLink = "" + meta.SelfLink = "" + meta.Size = 0 + meta.StorageClass = "" +} + +// BucketUrl returns the URL for a bucket. +func BucketUrl(baseUrl HttpBaseUrl, bucket string) string { + return fmt.Sprintf("%sstorage/v1/b/%s", normalizeBaseUrl(baseUrl), bucket) +} + +// ObjectUrl returns the URL for a file. +func ObjectUrl(baseUrl HttpBaseUrl, bucket string, filepath string) string { + return fmt.Sprintf("%sstorage/v1/b/%s/o/%s", normalizeBaseUrl(baseUrl), bucket, filepath) +} + +// HttpBaseUrl represents the emulator base URL, including trailing slash; e.g. https://www.googleapis.com/ +type HttpBaseUrl string + +// when the caller doesn't really care about the object meta URLs +const dontNeedUrls = HttpBaseUrl("") + +func normalizeBaseUrl(baseUrl HttpBaseUrl) HttpBaseUrl { + if baseUrl == dontNeedUrls || baseUrl == "https://storage.googleapis.com/" { + return "https://www.googleapis.com/" + } else if baseUrl == "http://storage.googleapis.com/" { + return "http://www.googleapis.com/" + } else { + return baseUrl + } +} diff --git a/pkg/emulators/storage/gcsemu/multipart.go b/pkg/emulators/storage/gcsemu/multipart.go new file mode 100644 index 0000000000..c0362e87b7 --- /dev/null +++ b/pkg/emulators/storage/gcsemu/multipart.go @@ -0,0 +1,65 @@ +package gcsemu + +import ( + "encoding/json" + "fmt" + "io" + "mime" + "mime/multipart" + "net/http" + + "google.golang.org/api/storage/v1" +) + +func readMultipartInsert(r *http.Request) (*storage.Object, []byte, error) { + v := r.Header.Get("Content-Type") + if v == "" { + return nil, nil, fmt.Errorf("failed to parse Content-Type header: %q", v) + } + d, params, err := mime.ParseMediaType(v) + if err != nil || d != "multipart/related" { + return nil, nil, fmt.Errorf("failed to parse Content-Type header: %q", v) + } + boundary, ok := params["boundary"] + if !ok { + return nil, nil, fmt.Errorf("Content-Type header is missing boundary: %q", v) + } + + reader := multipart.NewReader(r.Body, boundary) + + readPart := func() ([]byte, error) { + part, err := reader.NextPart() + if err != nil { + return nil, fmt.Errorf("failed to get multipart: %w", err) + } + + b, err := io.ReadAll(part) + if err != nil { + return nil, fmt.Errorf("failed to get read multipart: %w", err) + } + + return b, nil + } + + // read the first part to get the storage.Object (in json) + b, err := readPart() + if err != nil { + return nil, nil, fmt.Errorf("failed to read first part of body: %w", err) + } + + var obj storage.Object + err = json.Unmarshal(b, &obj) + if err != nil { + return nil, nil, fmt.Errorf("failed to parse body as json: %w", err) + } + + // read the next part to get the file contents + contents, err := readPart() + if err != nil { + return nil, nil, fmt.Errorf("failed to read second part of body: %w", err) + } + + obj.Size = uint64(len(contents)) + + return &obj, contents, nil +} diff --git a/pkg/emulators/storage/gcsemu/parse.go b/pkg/emulators/storage/gcsemu/parse.go new file mode 100644 index 0000000000..6baacff2d3 --- /dev/null +++ b/pkg/emulators/storage/gcsemu/parse.go @@ -0,0 +1,65 @@ +package gcsemu + +import ( + "net/url" + "regexp" +) + +const ( + // example: "/storage/v1/b/my-bucket/o/2013-tax-returns.pdf" (for a file) or "/storage/v1/b/my-bucket/o" (for a bucket) + gcsObjectPathPattern = "/storage/v1/b/([^\\/]+)/o(?:/(.+))?" + // example: "//b/my-bucket/o/2013-tax-returns.pdf" (for a file) or "/b/my-bucket/o" (for a bucket) + gcsObjectPathPattern2 = "/b/([^\\/]+)/o(?:/(.+))?" + // example: "/storage/v1/b/my-bucket + gcsBucketPathPattern = "/storage/v1/b(?:/([^\\/]+))?" + // example: "/my-bucket/2013-tax-returns.pdf" (for a file) + gcsStoragePathPattern = "/([^\\/]+)/(.+)" +) + +var ( + gcsObjectPathRegex = regexp.MustCompile(gcsObjectPathPattern) + gcsObjectPathRegex2 = regexp.MustCompile(gcsObjectPathPattern2) + gcsBucketPathRegex = regexp.MustCompile(gcsBucketPathPattern) + gcsStoragePathRegex = regexp.MustCompile(gcsStoragePathPattern) +) + +// GcsParams represent a parsed GCS url. +type GcsParams struct { + Bucket string + Object string + IsPublic bool +} + +// ParseGcsUrl parses a GCS url. +func ParseGcsUrl(u *url.URL) (*GcsParams, bool) { + if g, ok := parseGcsUrl(gcsObjectPathRegex, u); ok { + return g, true + } + if g, ok := parseGcsUrl(gcsBucketPathRegex, u); ok { + return g, true + } + if g, ok := parseGcsUrl(gcsObjectPathRegex2, u); ok { + return g, true + } + if g, ok := parseGcsUrl(gcsStoragePathRegex, u); ok { + g.IsPublic = true + return g, true + } + return nil, false +} + +func parseGcsUrl(re *regexp.Regexp, u *url.URL) (*GcsParams, bool) { + submatches := re.FindStringSubmatch(u.Path) + if submatches == nil { + return nil, false + } + + g := &GcsParams{} + if len(submatches) > 1 { + g.Bucket = submatches[1] + } + if len(submatches) > 2 { + g.Object = submatches[2] + } + return g, true +} diff --git a/pkg/emulators/storage/gcsemu/range.go b/pkg/emulators/storage/gcsemu/range.go new file mode 100644 index 0000000000..1c7a832a15 --- /dev/null +++ b/pkg/emulators/storage/gcsemu/range.go @@ -0,0 +1,52 @@ +package gcsemu + +import ( + "strconv" + "strings" +) + +type byteRange struct { + lo, hi, sz int64 +} + +func parseByteRange(in string) *byteRange { + var err error + if !strings.HasPrefix(in, "bytes ") { + return nil + } + in = strings.TrimPrefix(in, "bytes ") + parts := strings.Split(in, "/") + if len(parts) != 2 { + return nil + } + + ret := byteRange{ + lo: -1, + hi: -1, + sz: -1, + } + + if parts[0] != "*" { + parts := strings.Split(parts[0], "-") + if len(parts) != 2 { + return nil + } + ret.lo, err = strconv.ParseInt(parts[0], 10, 64) + if err != nil { + return nil + } + ret.hi, err = strconv.ParseInt(parts[1], 10, 64) + if err != nil { + return nil + } + } + + if parts[1] != "*" { + ret.sz, err = strconv.ParseInt(parts[1], 10, 64) + if err != nil { + return nil + } + } + + return &ret +} diff --git a/pkg/emulators/storage/gcsemu/range_test.go b/pkg/emulators/storage/gcsemu/range_test.go new file mode 100644 index 0000000000..6cac438b34 --- /dev/null +++ b/pkg/emulators/storage/gcsemu/range_test.go @@ -0,0 +1,23 @@ +package gcsemu + +import ( + "testing" + + "gotest.tools/v3/assert" +) + +func TestParseByteRange(t *testing.T) { + tcs := []struct { + in string + expect byteRange + }{ + {in: "bytes 0-8388607/*", expect: byteRange{lo: 0, hi: 8388607, sz: -1}}, + {in: "bytes 8388608-10485759/10485760", expect: byteRange{lo: 8388608, hi: 10485759, sz: 10485760}}, + {in: "bytes */10485760", expect: byteRange{lo: -1, hi: -1, sz: 10485760}}, + } + + for _, tc := range tcs { + t.Logf("test case: %s", tc.in) + assert.Equal(t, tc.expect, *parseByteRange(tc.in)) + } +} diff --git a/pkg/emulators/storage/gcsemu/raw_http_test.go b/pkg/emulators/storage/gcsemu/raw_http_test.go new file mode 100644 index 0000000000..20ba7b30e2 --- /dev/null +++ b/pkg/emulators/storage/gcsemu/raw_http_test.go @@ -0,0 +1,299 @@ +package gcsemu + +import ( + "bufio" + "bytes" + "context" + "encoding/json" + "fmt" + api "google.golang.org/api/storage/v1" + "gotest.tools/v3/assert" + "io" + "mime" + "mime/multipart" + "net/http" + "net/http/httputil" + "net/textproto" + "strings" + "testing" +) + +func testRawHttp(t *testing.T, bh BucketHandle, httpClient *http.Client, url string) { + const name = "gscemu-test3.txt" + const name2 = "gscemu-test4.txt" + const delName = "gscemu-test-deletion.txt" // used for successful deletion + const delName2 = "gscemu-test-deletion-2.txt" // used for not found deletion + + expectMetaGen := int64(1) + tcs := []struct { + name string + makeRequest func(*testing.T) *http.Request + checkResponse func(*testing.T, *http.Response) + }{ + { + name: "rawGetObject", + makeRequest: func(t *testing.T) *http.Request { + u := fmt.Sprintf("%s/download/storage/v1/b/%s/o/%s?alt=media", url, bh.Name, name) + t.Log(u) + req, err := http.NewRequest("GET", u, nil) + assert.NilError(t, err) + return req + }, + checkResponse: func(t *testing.T, rsp *http.Response) { + body, err := io.ReadAll(rsp.Body) + assert.NilError(t, err) + assert.Equal(t, http.StatusOK, rsp.StatusCode) + assert.Equal(t, v1, string(body)) + }, + }, + { + name: "rawGetMeta", + makeRequest: func(t *testing.T) *http.Request { + u := fmt.Sprintf("%s/storage/v1/b/%s/o/%s", url, bh.Name, name) + t.Log(u) + req, err := http.NewRequest("GET", u, nil) + assert.NilError(t, err) + return req + }, + checkResponse: func(t *testing.T, rsp *http.Response) { + body, err := io.ReadAll(rsp.Body) + assert.NilError(t, err) + assert.Equal(t, http.StatusOK, rsp.StatusCode) + + var attrs api.Object + err = json.NewDecoder(bytes.NewReader(body)).Decode(&attrs) + assert.NilError(t, err) + assert.Equal(t, name, attrs.Name) + assert.Equal(t, bh.Name, attrs.Bucket) + assert.Equal(t, uint64(len(v1)), attrs.Size) + assert.Equal(t, expectMetaGen, attrs.Metageneration) + }, + }, + { + name: "rawPatchMeta", + makeRequest: func(t *testing.T) *http.Request { + u := fmt.Sprintf("%s/storage/v1/b/%s/o/%s", url, bh.Name, name) + t.Log(u) + req, err := http.NewRequest("PATCH", u, strings.NewReader(`{"metadata": {"type": "tabby"}}`)) + assert.NilError(t, err) + req.Header.Set("Content-Type", "application/json") + return req + }, + checkResponse: func(t *testing.T, rsp *http.Response) { + body, err := io.ReadAll(rsp.Body) + assert.NilError(t, err) + assert.Equal(t, http.StatusOK, rsp.StatusCode) + + expectMetaGen++ + + var attrs api.Object + err = json.NewDecoder(bytes.NewReader(body)).Decode(&attrs) + assert.NilError(t, err) + assert.Equal(t, name, attrs.Name) + assert.Equal(t, bh.Name, attrs.Bucket) + assert.Equal(t, uint64(len(v1)), attrs.Size) + assert.Equal(t, expectMetaGen, attrs.Metageneration) + assert.Equal(t, "tabby", attrs.Metadata["type"]) + }, + }, + { + name: "rawDeleteObject-Success", + makeRequest: func(t *testing.T) *http.Request { + u := fmt.Sprintf("%s/storage/v1/b/%s/o/%s", url, bh.Name, delName) + t.Log(u) + req, err := http.NewRequest("DELETE", u, nil) + assert.NilError(t, err) + req.Header.Set("Content-Type", "text/plain") + return req + }, + checkResponse: func(t *testing.T, rsp *http.Response) { + assert.Equal(t, http.StatusNoContent, rsp.StatusCode) + }, + }, + { + name: "rawDeleteObject-ObjectNotFound", + makeRequest: func(t *testing.T) *http.Request { + u := fmt.Sprintf("%s/storage/v1/b/%s/o/%s", url, bh.Name, delName2) + t.Log(u) + req, err := http.NewRequest("DELETE", u, nil) + assert.NilError(t, err) + req.Header.Set("Content-Type", "text/plain") + return req + }, + checkResponse: func(t *testing.T, rsp *http.Response) { + assert.Equal(t, http.StatusNotFound, rsp.StatusCode) + }, + }, + { + name: "rawDeleteObject-BucketNotFound", + makeRequest: func(t *testing.T) *http.Request { + u := fmt.Sprintf("%s/storage/v1/b/%s/o/%s", url, invalidBucketName, delName) + t.Log(u) + req, err := http.NewRequest("DELETE", u, nil) + assert.NilError(t, err) + req.Header.Set("Content-Type", "text/plain") + return req + }, + checkResponse: func(t *testing.T, rsp *http.Response) { + assert.Equal(t, http.StatusNotFound, rsp.StatusCode) + }, + }, + { + name: "rawDeleteBucket-BucketNotFound", + makeRequest: func(t *testing.T) *http.Request { + u := fmt.Sprintf("%s/storage/v1/b/%s", url, invalidBucketName) + t.Log(u) + req, err := http.NewRequest("DELETE", u, nil) + assert.NilError(t, err) + req.Header.Set("Content-Type", "text/plain") + return req + }, + checkResponse: func(t *testing.T, rsp *http.Response) { + assert.Equal(t, http.StatusNotFound, rsp.StatusCode) + }, + }, + { + name: "rawUpload", + makeRequest: func(t *testing.T) *http.Request { + u := fmt.Sprintf("%s/upload/storage/v1/b/%s/o?uploadType=media&name=%s", url, bh.Name, name2) + t.Log(u) + req, err := http.NewRequest("POST", u, strings.NewReader(v2)) + assert.NilError(t, err) + req.Header.Set("Content-Type", "text/plain") + return req + }, + checkResponse: func(t *testing.T, rsp *http.Response) { + body, err := io.ReadAll(rsp.Body) + assert.NilError(t, err) + assert.Equal(t, http.StatusOK, rsp.StatusCode) + + var attrs api.Object + err = json.NewDecoder(bytes.NewReader(body)).Decode(&attrs) + assert.NilError(t, err) + assert.Equal(t, name2, attrs.Name) + assert.Equal(t, bh.Name, attrs.Bucket) + assert.Equal(t, uint64(len(v2)), attrs.Size) + assert.Equal(t, int64(1), attrs.Metageneration) + }, + }, + { + name: "publicUrl", + makeRequest: func(t *testing.T) *http.Request { + u := fmt.Sprintf("%s/%s/%s?alt=media", url, bh.Name, name) + t.Log(u) + req, err := http.NewRequest("GET", u, nil) + assert.NilError(t, err) + return req + }, + checkResponse: func(t *testing.T, rsp *http.Response) { + body, err := io.ReadAll(rsp.Body) + assert.NilError(t, err) + assert.Equal(t, http.StatusOK, rsp.StatusCode) + assert.Equal(t, v1, string(body)) + }, + }, + } + + ctx := context.Background() + oh := bh.Object(name) + + // Create the object 1. + w := oh.NewWriter(ctx) + assert.NilError(t, write(w, v1)) + + // Make sure object 2 is not there. + _ = bh.Object(name2).Delete(ctx) + + // batch setup + // Create the object for successful deletion. + w = bh.Object(delName).NewWriter(ctx) + assert.NilError(t, write(w, v1)) + // Make sure object for not found deletion is not there. + _ = bh.Object(delName2).Delete(ctx) + + // Run each test individually. + for _, tc := range tcs { + tc := tc + t.Run(tc.name, func(t *testing.T) { + req := tc.makeRequest(t) + rsp, err := httpClient.Do(req) + assert.NilError(t, err) + body, err := httputil.DumpResponse(rsp, true) + assert.NilError(t, err) + t.Log(string(body)) + tc.checkResponse(t, rsp) + }) + } + + // batch setup again for batch deletion step + // Create the object for successful deletion. + w = bh.Object(delName).NewWriter(ctx) + assert.NilError(t, write(w, v1)) + // Make sure object for not found deletion is not there. + _ = bh.Object(delName2).Delete(ctx) + + // Batch requests don't support upload and download, only metadata stuff. + t.Run("batch", func(t *testing.T) { + var buf bytes.Buffer + w := multipart.NewWriter(&buf) + + // Only use the [second, fifth] requests. + batchTcs := tcs[1:6] + for i, tc := range batchTcs { + req := tc.makeRequest(t) + req.Host = "" + req.URL.Host = "" + + p, _ := w.CreatePart(textproto.MIMEHeader{ + "Content-Type": []string{"application/http"}, + "Content-Transfer-Encoding": []string{"binary"}, + "Content-ID": []string{fmt.Sprintf("", i)}, + }) + buf, err := httputil.DumpRequest(req, true) + assert.NilError(t, err) + _, _ = p.Write(buf) + } + _ = w.Close() + + // Compile the request + req, err := http.NewRequest("POST", fmt.Sprintf("%s/batch/storage/v1", url), &buf) + assert.NilError(t, err) + req.Header.Set("Content-Type", "multipart/mixed; boundary="+w.Boundary()) + + body, err := httputil.DumpRequest(req, true) + assert.NilError(t, err) + t.Log(string(body)) + + rsp, err := httpClient.Do(req) + assert.NilError(t, err) + assert.Equal(t, http.StatusOK, rsp.StatusCode) + + body, err = httputil.DumpResponse(rsp, true) + assert.NilError(t, err) + t.Log(string(body)) + + // decode the multipart response + v := rsp.Header.Get("Content-type") + assert.Check(t, v != "") + d, params, err := mime.ParseMediaType(v) + assert.NilError(t, err) + assert.Equal(t, "multipart/mixed", d) + boundary, ok := params["boundary"] + assert.Check(t, ok) + + r := multipart.NewReader(rsp.Body, boundary) + for i, tc := range batchTcs { + part, err := r.NextPart() + assert.NilError(t, err) + assert.Equal(t, "application/http", part.Header.Get("Content-Type")) + assert.Equal(t, fmt.Sprintf("", i), part.Header.Get("Content-ID")) + b, err := io.ReadAll(part) + assert.NilError(t, err) + + // Decode the buffer into an http.Response + rsp, err := http.ReadResponse(bufio.NewReader(bytes.NewReader(b)), nil) + assert.NilError(t, err) + tc.checkResponse(t, rsp) + } + }) +} diff --git a/pkg/emulators/storage/gcsemu/remote_test.go b/pkg/emulators/storage/gcsemu/remote_test.go new file mode 100644 index 0000000000..55dc4d5387 --- /dev/null +++ b/pkg/emulators/storage/gcsemu/remote_test.go @@ -0,0 +1,50 @@ +package gcsemu + +import ( + "context" + "os" + "testing" + + "cloud.google.com/go/storage" + "golang.org/x/oauth2/google" + "gotest.tools/v3/assert" +) + +func TestRealStore(t *testing.T) { + bucket := os.Getenv("BUCKET_ID") + if bucket == "" { + t.Skip("BUCKET_ID must be set to run this") + } + + ctx := context.Background() + gcsClient, err := storage.NewClient(ctx) + assert.NilError(t, err) + t.Cleanup(func() { + _ = gcsClient.Close() + }) + + bh := BucketHandle{ + Name: bucket, + BucketHandle: gcsClient.Bucket(bucket), + } + + // Instead of `initBucket`, just check that it exists. + _, err = bh.Attrs(ctx) + assert.NilError(t, err) + + t.Parallel() + for _, tc := range testCases { + tc := tc + t.Run(tc.name, func(t *testing.T) { + t.Parallel() + tc.f(t, bh) + }) + } + + httpClient, err := google.DefaultClient(context.Background()) + assert.NilError(t, err) + t.Run("RawHttp", func(t *testing.T) { + t.Parallel() + testRawHttp(t, bh, httpClient, "https://storage.googleapis.com") + }) +} diff --git a/pkg/emulators/storage/gcsemu/server.go b/pkg/emulators/storage/gcsemu/server.go new file mode 100644 index 0000000000..0d407b8030 --- /dev/null +++ b/pkg/emulators/storage/gcsemu/server.go @@ -0,0 +1,44 @@ +package gcsemu + +import ( + "fmt" + "net" + "net/http" + "net/http/httptest" + "strings" +) + +// Server is an in-memory Cloud Storage emulator; it is unauthenticated, and only a rough approximation. +type Server struct { + Addr string + *httptest.Server + *GcsEmu +} + +// NewServer creates a new Server with the given options. +// The Server will be listening for HTTP connections, without TLS, +// on the provided address. The resolved address is named by the Addr field. +// An address with a port of 0 will bind to an open port on the system. +// +// For running a full in-process setup (e.g. unit tests), initialize +// os.Setenv("GCS_EMULATOR_HOST", srv.Addr) so that subsequent calls to NewClient() +// will return an in-process targeted storage client. +func NewServer(laddr string, opts Options) (*Server, error) { + gcsEmu := NewGcsEmu(opts) + mux := http.NewServeMux() + gcsEmu.Register(mux) + + srv := httptest.NewUnstartedServer(mux) + l, err := net.Listen("tcp", laddr) + if err != nil { + return nil, fmt.Errorf("failed to listen on addr %s: %w", laddr, err) + } + srv.Listener = l + srv.Start() + + return &Server{ + Addr: strings.TrimPrefix(srv.URL, "http://"), + Server: srv, + GcsEmu: gcsEmu, + }, nil +} diff --git a/pkg/emulators/storage/gcsemu/store.go b/pkg/emulators/storage/gcsemu/store.go new file mode 100644 index 0000000000..92ea335c6c --- /dev/null +++ b/pkg/emulators/storage/gcsemu/store.go @@ -0,0 +1,41 @@ +package gcsemu + +import ( + "context" + "os" + + "google.golang.org/api/storage/v1" +) + +// Store is an interface to either on-disk or in-mem storage +type Store interface { + // CreateBucket creates a bucket; no error if the bucket already exists. + CreateBucket(bucket string) error + + // Get returns a bucket's metadata. + GetBucketMeta(baseUrl HttpBaseUrl, bucket string) (*storage.Bucket, error) + + // Get returns a file's contents and metadata. + Get(url HttpBaseUrl, bucket string, filename string) (*storage.Object, []byte, error) + + // GetMeta returns a file's metadata. + GetMeta(url HttpBaseUrl, bucket string, filename string) (*storage.Object, error) + + // Add creates the specified file. + Add(bucket string, filename string, contents []byte, meta *storage.Object) error + + // UpdateMeta updates the given file's metadata. + UpdateMeta(bucket string, filename string, meta *storage.Object, metagen int64) error + + // Copy copies the file + Copy(srcBucket string, srcFile string, dstBucket string, dstFile string) (bool, error) + + // Delete deletes the file. + Delete(bucket string, filename string) error + + // ReadMeta reads the GCS metadata for a file, when you already have file info. + ReadMeta(url HttpBaseUrl, bucket string, filename string, fInfo os.FileInfo) (*storage.Object, error) + + // Walks the given bucket. + Walk(ctx context.Context, bucket string, cb func(ctx context.Context, filename string, fInfo os.FileInfo) error) error +} diff --git a/pkg/emulators/storage/gcsemu/util.go b/pkg/emulators/storage/gcsemu/util.go new file mode 100644 index 0000000000..0d9a197eb5 --- /dev/null +++ b/pkg/emulators/storage/gcsemu/util.go @@ -0,0 +1,141 @@ +package gcsemu + +import ( + "encoding/json" + "errors" + "net/http" + "regexp" + "strings" + + "google.golang.org/api/googleapi" +) + +// jsonRespond json-encodes rsp and writes it to w. If an error occurs, then it is logged and a 500 error is written to w. +func (g *GcsEmu) jsonRespond(w http.ResponseWriter, rsp interface{}) { + // do NOT write a http status since OK will be the default and this allows the caller to use their own if they want + w.Header().Set("Content-Type", "application/json; charset=utf-8") + + encoder := json.NewEncoder(w) + if err := encoder.Encode(rsp); err != nil { + g.log(err, "failed to send response") + http.Error(w, "Internal Server Error", http.StatusInternalServerError) + } +} + +type gapiErrorPartial struct { + // Code is the HTTP response status code and will always be populated. + Code int `json:"code"` + + // Message is the server response message and is only populated when + // explicitly referenced by the JSON server response. + Message string `json:"message"` + + Errors []googleapi.ErrorItem `json:"errors,omitempty"` +} + +// gapiError responds to the client with a GAPI error +func (g *GcsEmu) gapiError(w http.ResponseWriter, code int, message string) { + if code == 0 { + code = http.StatusInternalServerError + } + if code != http.StatusNotFound { + g.log(errors.New(message), "responding with HTTP %d", code) + } + if message == "" { + message = http.StatusText(code) + } + + // format copied from errorReply struct in google.golang.org/api/googleapi + rsp := struct { + Error gapiErrorPartial `json:"error"` + }{ + Error: gapiErrorPartial{ + Code: code, + Message: message, + }, + } + + w.Header().Set("Content-Type", "application/json; charset=utf-8") + w.WriteHeader(code) + enc := json.NewEncoder(w) + enc.SetIndent("", " ") + _ = enc.Encode(&rsp) +} + +// mustJson serializes the given value to json, panicking on failure +func mustJson(val interface{}) []byte { + if val == nil { + return []byte("null") + } + + b, err := json.MarshalIndent(val, "", " ") + if err != nil { + panic(err) + } + return b +} + +// requestHost returns the host from an http.Request, respecting proxy headers. Works locally with devproxy +// and gulp proxies as well as in AppEngine (both real GAE and the dev_appserver). +func requestHost(req *http.Request) string { + // proxies like gulp are supposed to accumulate original host, next-step-host, etc in order from + // client-most to server-most in X-ForwardedHost; return the first entry from that if any are listed + if proxyHost := req.Header.Get("X-Forwarded-Host"); proxyHost != "" { + // Use the first (closest to client) host + splits := strings.SplitN(proxyHost, ",", 2) + return splits[0] + } + + // Forwarded is the standardized version of X-Forwarded-Host. + f := parseForwardedHeader(req.Header.Get("Forwarded")) + if len(f.Host) > 0 && len(f.Host[0]) > 0 { + return f.Host[0] + } + + // Clients that generate HTTP/2 requests should use the :authority header instead + // of Host. See http://httpwg.org/specs/rfc7540.html#rfc.section.8.1.2.3 + host := req.Header.Get("Authority") + if len(host) > 0 { + return host + } + + // Fall back to the host line. + return req.Host +} + +// forwarded represents the values of a Forwarded HTTP header. +// +// For more details, see the RFC: https://tools.ietf.org/html/rfc7239 and +// MDN: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Forwarded. +type forwarded struct { + By []string + For []string + Host []string + Proto []string +} + +var ( + forwardedHostRx = regexp.MustCompile(`(?i)host=(.*?)(?:[,;\s]|$)`) +) + +func removeDoubleQuotes(s string) string { + return strings.TrimSuffix(strings.TrimPrefix(s, `"`), `"`) +} + +// Note: this currently only supports the forwarded.Host field. +func parseForwardedHeader(s string) forwarded { + var f forwarded + + if s == "" { + return f + } + + matches := forwardedHostRx.FindAllStringSubmatch(s, -1) + for _, m := range matches { + if len(m) > 0 { + f.Host = append(f.Host, removeDoubleQuotes(m[1])) + } + } + + return f +} diff --git a/pkg/emulators/storage/gcsemu/walk.go b/pkg/emulators/storage/gcsemu/walk.go new file mode 100644 index 0000000000..cafc2627db --- /dev/null +++ b/pkg/emulators/storage/gcsemu/walk.go @@ -0,0 +1,139 @@ +package gcsemu + +import ( + "context" + "errors" + "fmt" + "net/http" + "os" + "path/filepath" + "strings" + + "encr.dev/pkg/emulators/storage/gcsutil" + "google.golang.org/api/storage/v1" +) + +// Iterate over the file system to serve a GCS list-bucket request. +func (g *GcsEmu) makeBucketListResults(ctx context.Context, baseUrl HttpBaseUrl, w http.ResponseWriter, delimiter string, cursor string, prefix string, bucket string, maxResults int) { + var errAbort = errors.New("sentinel error to abort walk") + + type item struct { + filename string + fInfo os.FileInfo + } + var found []item + var prefixes []string + seenPrefixes := make(map[string]bool) + + dbgWalk := func(fmt string, args ...interface{}) { + if g.verbose { + g.log(nil, fmt, args...) + } + } + + moreResults := false + count := 0 + err := g.store.Walk(ctx, bucket, func(ctx context.Context, filename string, fInfo os.FileInfo) error { + dbgWalk("walk: %s", filename) + + // If we're beyond the prefix, we're completely done. + if greaterThanPrefix(filename, prefix) { + dbgWalk("%q > prefix=%q aborting", filename, prefix) + return errAbort + } + + // In the filesystem implementation, skip any directories strictly less than the cursor or prefix. + if fInfo != nil && fInfo.IsDir() { + if lessThanPrefix(filename, cursor) { + dbgWalk("%q < cursor=%q skip dir", filename, cursor) + return filepath.SkipDir + } + if lessThanPrefix(filename, prefix) { + dbgWalk("%q < prefix=%q skip dir", filename, prefix) + return filepath.SkipDir + } + return nil // keep going + } + + // If the file is <= cursor, or < prefix, skip. + if filename <= cursor { + dbgWalk("%q <= cursor=%q skipping", filename, cursor) + return nil + } + if !strings.HasPrefix(filename, prefix) { + dbgWalk("%q < prefix=%q skipping", filename, prefix) + return nil + } + + if count >= maxResults { + moreResults = true + return errAbort + } + count++ + + if delimiter != "" { + // See if the filename (beyond the prefix) contains delimiter, if it does, don't record the item, + // instead record the prefix (including the delimiter). + withoutPrefix := strings.TrimPrefix(filename, prefix) + delimiterPos := strings.Index(withoutPrefix, delimiter) + if delimiterPos >= 0 { + // Got a hit, reconstruct the item's prefix, including the trailing delimiter + itemPrefix := filename[:len(prefix)+delimiterPos+len(delimiter)] + if !seenPrefixes[itemPrefix] { + seenPrefixes[itemPrefix] = true + prefixes = append(prefixes, itemPrefix) + } + return nil + } + } + + found = append(found, item{ + filename: filename, + fInfo: fInfo, + }) + return nil + }) + // Sentinel error is not an error + if err == errAbort { + err = nil + } + if err != nil { + if len(found) == 0 { + if os.IsNotExist(err) { + g.gapiError(w, http.StatusNotFound, fmt.Sprintf("%s not found", bucket)) + } else { + g.gapiError(w, http.StatusInternalServerError, "failed to iterate: "+err.Error()) + } + return + } + // return our partial results + the cursor so that the client can retry from this point + g.log(nil, "failed to iterate") + } + + // Resolve the found items. + var items []*storage.Object + for _, item := range found { + if obj, err := g.store.ReadMeta(baseUrl, bucket, item.filename, item.fInfo); err != nil { + // return our partial results + the cursor so that the client can retry from this point + g.log(nil, "failed to resolve: %s", item.filename) + break + } else { + items = append(items, obj) + } + } + + var nextPageToken = "" + if moreResults && len(items) > 0 { + lastItemName := items[len(items)-1].Name + nextPageToken = gcsutil.EncodePageToken(lastItemName) + } + + rsp := storage.Objects{ + Kind: "storage#objects", + NextPageToken: nextPageToken, + Items: items, + Prefixes: prefixes, + } + + g.jsonRespond(w, &rsp) +} diff --git a/pkg/emulators/storage/gcsutil/counted_lock.go b/pkg/emulators/storage/gcsutil/counted_lock.go new file mode 100644 index 0000000000..724aed7136 --- /dev/null +++ b/pkg/emulators/storage/gcsutil/counted_lock.go @@ -0,0 +1,51 @@ +package gcsutil + +import ( + "context" +) + +type countedLock struct { + // a 1 element channel; empty == unlocked, full == locked + // push an element into the channel to lock, remove the element to unlock + ch chan struct{} + + // should only be accessed while the outer _map_ lock is held (not this key lock) + refcount int64 +} + +func newCountedLock() *countedLock { + return &countedLock{ + ch: make(chan struct{}, 1), + refcount: 0, + } +} + +func (m *countedLock) Lock(ctx context.Context) bool { + // If the context is already cancelled don't even try to lock. + if ctx.Err() != nil { + return false + } + select { + case m.ch <- struct{}{}: + return true + case <-ctx.Done(): + return false + } +} + +func (m *countedLock) Unlock() { + select { + case <-m.ch: + return + default: + panic("BUG: lock not held") + } +} + +func (m *countedLock) Run(ctx context.Context, f func(ctx context.Context) error) error { + if !m.Lock(ctx) { + return ctx.Err() + } + defer m.Unlock() + return f(ctx) +} diff --git a/pkg/emulators/storage/gcsutil/doc.go b/pkg/emulators/storage/gcsutil/doc.go new file mode 100644 index 0000000000..b38272099f --- /dev/null +++ b/pkg/emulators/storage/gcsutil/doc.go @@ -0,0 +1,3 @@ +// Package gcsutil contains some generic utilities to support gcsemu. +// TODO(dragonsinth): consider open sourcing these separately, or finding open source replacements. +package gcsutil diff --git a/pkg/emulators/storage/gcsutil/gcspagetoken.go b/pkg/emulators/storage/gcsutil/gcspagetoken.go new file mode 100644 index 0000000000..79c6669e17 --- /dev/null +++ b/pkg/emulators/storage/gcsutil/gcspagetoken.go @@ -0,0 +1,37 @@ +package gcsutil + +//go:generate protoc --go_out=. --go_opt=paths=source_relative gcspagetoken.proto + +import ( + "encoding/base64" + "fmt" + + "google.golang.org/protobuf/proto" +) + +// EncodePageToken returns a synthetic page token to find files greater than the given string. +// If this is part of a prefix query, the token should fall within the prefixed range. +// BRITTLE: relies on a reverse-engineered internal GCS token format, which may be subject to change. +func EncodePageToken(greaterThan string) string { + bytes, err := proto.Marshal(&GcsPageToken{ + LastFile: greaterThan, + }) + if err != nil { + panic("could not encode gcsPageToken:" + err.Error()) + } + return base64.StdEncoding.EncodeToString(bytes) +} + +// DecodePageToken decodes a GCS pageToken to the name of the last file returned. +func DecodePageToken(pageToken string) (string, error) { + bytes, err := base64.StdEncoding.DecodeString(pageToken) + if err != nil { + return "", fmt.Errorf("could not base64 decode pageToken %s: %w", pageToken, err) + } + var message GcsPageToken + if err := proto.Unmarshal(bytes, &message); err != nil { + return "", fmt.Errorf("could not unmarshal proto: %w", err) + } + + return message.LastFile, nil +} diff --git a/pkg/emulators/storage/gcsutil/gcspagetoken.pb.go b/pkg/emulators/storage/gcsutil/gcspagetoken.pb.go new file mode 100644 index 0000000000..52d42b48dd --- /dev/null +++ b/pkg/emulators/storage/gcsutil/gcspagetoken.pb.go @@ -0,0 +1,146 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.31.0 +// protoc v4.23.4 +// source: gcspagetoken.proto + +package gcsutil + +import ( + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + reflect "reflect" + sync "sync" +) + +const ( + // Verify that this generated code is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) + // Verify that runtime/protoimpl is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) +) + +type GcsPageToken struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // The full name of the last result file, when returned from the server. + // When sent as a cursor, interpreted as "return files greater than this value". + LastFile string `protobuf:"bytes,1,opt,name=LastFile,proto3" json:"LastFile,omitempty"` +} + +func (x *GcsPageToken) Reset() { + *x = GcsPageToken{} + if protoimpl.UnsafeEnabled { + mi := &file_gcspagetoken_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *GcsPageToken) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GcsPageToken) ProtoMessage() {} + +func (x *GcsPageToken) ProtoReflect() protoreflect.Message { + mi := &file_gcspagetoken_proto_msgTypes[0] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use GcsPageToken.ProtoReflect.Descriptor instead. +func (*GcsPageToken) Descriptor() ([]byte, []int) { + return file_gcspagetoken_proto_rawDescGZIP(), []int{0} +} + +func (x *GcsPageToken) GetLastFile() string { + if x != nil { + return x.LastFile + } + return "" +} + +var File_gcspagetoken_proto protoreflect.FileDescriptor + +var file_gcspagetoken_proto_rawDesc = []byte{ + 0x0a, 0x12, 0x67, 0x63, 0x73, 0x70, 0x61, 0x67, 0x65, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x2e, 0x70, + 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x07, 0x67, 0x63, 0x73, 0x75, 0x74, 0x69, 0x6c, 0x22, 0x2a, 0x0a, + 0x0c, 0x47, 0x63, 0x73, 0x50, 0x61, 0x67, 0x65, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x12, 0x1a, 0x0a, + 0x08, 0x4c, 0x61, 0x73, 0x74, 0x46, 0x69, 0x6c, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x08, 0x4c, 0x61, 0x73, 0x74, 0x46, 0x69, 0x6c, 0x65, 0x42, 0x28, 0x5a, 0x26, 0x65, 0x6e, 0x63, + 0x72, 0x2e, 0x64, 0x65, 0x76, 0x2f, 0x70, 0x6b, 0x67, 0x2f, 0x65, 0x6d, 0x75, 0x6c, 0x61, 0x74, + 0x6f, 0x72, 0x73, 0x2f, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x2f, 0x67, 0x63, 0x73, 0x75, + 0x74, 0x69, 0x6c, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, +} + +var ( + file_gcspagetoken_proto_rawDescOnce sync.Once + file_gcspagetoken_proto_rawDescData = file_gcspagetoken_proto_rawDesc +) + +func file_gcspagetoken_proto_rawDescGZIP() []byte { + file_gcspagetoken_proto_rawDescOnce.Do(func() { + file_gcspagetoken_proto_rawDescData = protoimpl.X.CompressGZIP(file_gcspagetoken_proto_rawDescData) + }) + return file_gcspagetoken_proto_rawDescData +} + +var file_gcspagetoken_proto_msgTypes = make([]protoimpl.MessageInfo, 1) +var file_gcspagetoken_proto_goTypes = []interface{}{ + (*GcsPageToken)(nil), // 0: gcsutil.GcsPageToken +} +var file_gcspagetoken_proto_depIdxs = []int32{ + 0, // [0:0] is the sub-list for method output_type + 0, // [0:0] is the sub-list for method input_type + 0, // [0:0] is the sub-list for extension type_name + 0, // [0:0] is the sub-list for extension extendee + 0, // [0:0] is the sub-list for field type_name +} + +func init() { file_gcspagetoken_proto_init() } +func file_gcspagetoken_proto_init() { + if File_gcspagetoken_proto != nil { + return + } + if !protoimpl.UnsafeEnabled { + file_gcspagetoken_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*GcsPageToken); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + } + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: file_gcspagetoken_proto_rawDesc, + NumEnums: 0, + NumMessages: 1, + NumExtensions: 0, + NumServices: 0, + }, + GoTypes: file_gcspagetoken_proto_goTypes, + DependencyIndexes: file_gcspagetoken_proto_depIdxs, + MessageInfos: file_gcspagetoken_proto_msgTypes, + }.Build() + File_gcspagetoken_proto = out.File + file_gcspagetoken_proto_rawDesc = nil + file_gcspagetoken_proto_goTypes = nil + file_gcspagetoken_proto_depIdxs = nil +} diff --git a/pkg/emulators/storage/gcsutil/gcspagetoken.proto b/pkg/emulators/storage/gcsutil/gcspagetoken.proto new file mode 100644 index 0000000000..ada481ed35 --- /dev/null +++ b/pkg/emulators/storage/gcsutil/gcspagetoken.proto @@ -0,0 +1,11 @@ +syntax = "proto3"; + +package gcsutil; + +option go_package = "encr.dev/pkg/emulators/storage/gcsutil"; + +message GcsPageToken{ + // The full name of the last result file, when returned from the server. + // When sent as a cursor, interpreted as "return files greater than this value". + string LastFile = 1; +} diff --git a/pkg/emulators/storage/gcsutil/gcspagetoken_test.go b/pkg/emulators/storage/gcsutil/gcspagetoken_test.go new file mode 100644 index 0000000000..17d2cd7bd2 --- /dev/null +++ b/pkg/emulators/storage/gcsutil/gcspagetoken_test.go @@ -0,0 +1,103 @@ +package gcsutil + +import ( + "testing" + + "gotest.tools/v3/assert" +) + +// TODO(dragonsinth): it would be nice to have an integration test that hits real GCS with known data stored. + +// TestGcsTokenGen tests that we produce expected GCS tokens that match real data we collected. +func TestGcsTokenGen(t *testing.T) { + tcs := []struct { + lastFile string + cursor string + }{ + { + lastFile: "containers/images/4dcc5142000d12f1a0f67c1e95df4035ca0ebba70117cc04101e53422d391d61/json", + cursor: "Cldjb250YWluZXJzL2ltYWdlcy80ZGNjNTE0MjAwMGQxMmYxYTBmNjdjMWU5NWRmNDAzNWNhMGViYmE3MDExN2NjMDQxMDFlNTM0MjJkMzkxZDYxL2pzb24=", + }, + { + lastFile: "containers/images/sha256:0e89fc4aeb48f92acff2dddaf610b2ceea5d76a93a44d4c20b31e69d1ed68c10", + cursor: "Clljb250YWluZXJzL2ltYWdlcy9zaGEyNTY6MGU4OWZjNGFlYjQ4ZjkyYWNmZjJkZGRhZjYxMGIyY2VlYTVkNzZhOTNhNDRkNGMyMGIzMWU2OWQxZWQ2OGMxMA==", + }, + { + lastFile: "containers/images/sha256:2072bc4567e1f13081af323d046f39453f010471701fa11fc50b786b60512e99", + cursor: "Clljb250YWluZXJzL2ltYWdlcy9zaGEyNTY6MjA3MmJjNDU2N2UxZjEzMDgxYWYzMjNkMDQ2ZjM5NDUzZjAxMDQ3MTcwMWZhMTFmYzUwYjc4NmI2MDUxMmU5OQ==", + }, + { + lastFile: "containers/images/sha256:43ecade58b2ddff87a696fada3970491de28c8ea1dca09c988b447b5c5a56412", + cursor: "Clljb250YWluZXJzL2ltYWdlcy9zaGEyNTY6NDNlY2FkZTU4YjJkZGZmODdhNjk2ZmFkYTM5NzA0OTFkZTI4YzhlYTFkY2EwOWM5ODhiNDQ3YjVjNWE1NjQxMg==", + }, + { + lastFile: "containers/images/sha256:57076bf87737c2f448e75324ceba121b3b90372ab913f604f928a40da4ebddc7", + cursor: "Clljb250YWluZXJzL2ltYWdlcy9zaGEyNTY6NTcwNzZiZjg3NzM3YzJmNDQ4ZTc1MzI0Y2ViYTEyMWIzYjkwMzcyYWI5MTNmNjA0ZjkyOGE0MGRhNGViZGRjNw==", + }, + { + lastFile: "containers/images/sha256:6f73a9b0052f169d8296382660a31050004b691f3e6252008545a3dcb7371a49", + cursor: "Clljb250YWluZXJzL2ltYWdlcy9zaGEyNTY6NmY3M2E5YjAwNTJmMTY5ZDgyOTYzODI2NjBhMzEwNTAwMDRiNjkxZjNlNjI1MjAwODU0NWEzZGNiNzM3MWE0OQ==", + }, + { + lastFile: "containers/images/sha256:765b6a129bd04f06c876f3d5b5a346e71a72fae522b230215f67375ccb659a11", + cursor: "Clljb250YWluZXJzL2ltYWdlcy9zaGEyNTY6NzY1YjZhMTI5YmQwNGYwNmM4NzZmM2Q1YjVhMzQ2ZTcxYTcyZmFlNTIyYjIzMDIxNWY2NzM3NWNjYjY1OWExMQ==", + }, + { + lastFile: "containers/images/sha256:973d921f391393e65d20a6e990e8ad5aa1129681ab8c54bf59c9192b809594c4", + cursor: "Clljb250YWluZXJzL2ltYWdlcy9zaGEyNTY6OTczZDkyMWYzOTEzOTNlNjVkMjBhNmU5OTBlOGFkNWFhMTEyOTY4MWFiOGM1NGJmNTljOTE5MmI4MDk1OTRjNA==", + }, + { + lastFile: "containers/images/sha256:a9d5802ef798d88c0f4f9dc0094249db5e26d8a8a18ba4c2194aab4a44983d2f", + cursor: "Clljb250YWluZXJzL2ltYWdlcy9zaGEyNTY6YTlkNTgwMmVmNzk4ZDg4YzBmNGY5ZGMwMDk0MjQ5ZGI1ZTI2ZDhhOGExOGJhNGMyMTk0YWFiNGE0NDk4M2QyZg==", + }, + { + lastFile: "containers/images/sha256:b5714cf3ed3a6b5f1fbc736ef0d5673c5637ccb14d53a23b8728dd828f21a22d", + cursor: "Clljb250YWluZXJzL2ltYWdlcy9zaGEyNTY6YjU3MTRjZjNlZDNhNmI1ZjFmYmM3MzZlZjBkNTY3M2M1NjM3Y2NiMTRkNTNhMjNiODcyOGRkODI4ZjIxYTIyZA==", + }, + { + lastFile: "containers/images/sha256:bfdef622d405cb35c466aa29ea7411fd594e7985127bec4e8080572d7ef45cfd", + cursor: "Clljb250YWluZXJzL2ltYWdlcy9zaGEyNTY6YmZkZWY2MjJkNDA1Y2IzNWM0NjZhYTI5ZWE3NDExZmQ1OTRlNzk4NTEyN2JlYzRlODA4MDU3MmQ3ZWY0NWNmZA==", + }, + { + lastFile: "containers/images/sha256:da543d0747020a528b8eee057912f6bd07e28f9c006606da28c82c70dac962e2", + cursor: "Clljb250YWluZXJzL2ltYWdlcy9zaGEyNTY6ZGE1NDNkMDc0NzAyMGE1MjhiOGVlZTA1NzkxMmY2YmQwN2UyOGY5YzAwNjYwNmRhMjhjODJjNzBkYWM5NjJlMg==", + }, + { + lastFile: "containers/images/sha256:ee73b32b0f0a9dafabd9445a3380094f984858e79c85eafde56c2e037c039c6a", + cursor: "Clljb250YWluZXJzL2ltYWdlcy9zaGEyNTY6ZWU3M2IzMmIwZjBhOWRhZmFiZDk0NDVhMzM4MDA5NGY5ODQ4NThlNzljODVlYWZkZTU2YzJlMDM3YzAzOWM2YQ==", + }, + { + lastFile: "containers/repositories/library/cpu-test/tag_v1", + cursor: "Ci9jb250YWluZXJzL3JlcG9zaXRvcmllcy9saWJyYXJ5L2NwdS10ZXN0L3RhZ192MQ==", + }, + { + lastFile: "containers/repositories/library/dns-test/manifest_sha256:9759da4d052f154ba5d0ea32bf0404f442082e3dbad3f3cf6dc6529c6575aec2", + cursor: "Cnljb250YWluZXJzL3JlcG9zaXRvcmllcy9saWJyYXJ5L2Rucy10ZXN0L21hbmlmZXN0X3NoYTI1Njo5NzU5ZGE0ZDA1MmYxNTRiYTVkMGVhMzJiZjA0MDRmNDQyMDgyZTNkYmFkM2YzY2Y2ZGM2NTI5YzY1NzVhZWMy", + }, + { + lastFile: "containers/repositories/library/dns-test/manifest_sha256:ee49d4935c33260d84499e38dbd5ec3f426c9a2a7e100a901267c712874e4c1d", + cursor: "Cnljb250YWluZXJzL3JlcG9zaXRvcmllcy9saWJyYXJ5L2Rucy10ZXN0L21hbmlmZXN0X3NoYTI1NjplZTQ5ZDQ5MzVjMzMyNjBkODQ0OTllMzhkYmQ1ZWMzZjQyNmM5YTJhN2UxMDBhOTAxMjY3YzcxMjg3NGU0YzFk", + }, + { + lastFile: "containers/repositories/library/dns-test/tag_v2", + cursor: "Ci9jb250YWluZXJzL3JlcG9zaXRvcmllcy9saWJyYXJ5L2Rucy10ZXN0L3RhZ192Mg==", + }, + { + lastFile: "containers/repositories/library/kapi-test/manifest_sha256:ca6d9c13fba12363760e6d2495811081b1d2e6fcbf974e551605b65cb5b0a94e", + cursor: "Cnpjb250YWluZXJzL3JlcG9zaXRvcmllcy9saWJyYXJ5L2thcGktdGVzdC9tYW5pZmVzdF9zaGEyNTY6Y2E2ZDljMTNmYmExMjM2Mzc2MGU2ZDI0OTU4MTEwODFiMWQyZTZmY2JmOTc0ZTU1MTYwNWI2NWNiNWIwYTk0ZQ==", + }, + { + lastFile: "containers/repositories/library/memclient-test/manifest_sha256:7b7979b351c9a019062446c1f033b2f8491868cf943758f006ae219eca231e01", + cursor: "Cn9jb250YWluZXJzL3JlcG9zaXRvcmllcy9saWJyYXJ5L21lbWNsaWVudC10ZXN0L21hbmlmZXN0X3NoYTI1Njo3Yjc5NzliMzUxYzlhMDE5MDYyNDQ2YzFmMDMzYjJmODQ5MTg2OGNmOTQzNzU4ZjAwNmFlMjE5ZWNhMjMxZTAx", + }, + } + + for i, tc := range tcs { + actualCursor := EncodePageToken(tc.lastFile) + assert.Equal(t, tc.cursor, actualCursor, "case %d", i) + + lastFile, err := DecodePageToken(actualCursor) + assert.NilError(t, err, "case %i: failed to decode", i) + assert.Equal(t, tc.lastFile, lastFile, "case %d", i) + } +} diff --git a/pkg/emulators/storage/gcsutil/transient_lock_map.go b/pkg/emulators/storage/gcsutil/transient_lock_map.go new file mode 100644 index 0000000000..1c2377d6db --- /dev/null +++ b/pkg/emulators/storage/gcsutil/transient_lock_map.go @@ -0,0 +1,94 @@ +package gcsutil + +import ( + "context" + "fmt" + "sync" +) + +// TransientLockMap is a map of mutexes that is safe for concurrent access. It does not bother to save mutexes after +// they have been unlocked, and thus this data structure is best for situations where the space of keys is very large. +// If the space of keys is small then it may be inefficient to constantly recreate mutexes whenever they are needed. +type TransientLockMap struct { + mu sync.Mutex // the mutex that locks the map + locks map[string]*countedLock // all locks that are currently held +} + +// NewTransientLockMap returns a new TransientLockMap. +func NewTransientLockMap() *TransientLockMap { + return &TransientLockMap{ + locks: make(map[string]*countedLock), + } +} + +// Lock acquires the lock for the specified key and returns true, unless the context finishes before the lock could be +// acquired, in which case false is returned. +func (l *TransientLockMap) Lock(ctx context.Context, key string) bool { + lock := func() *countedLock { + // If there is high lock contention, we could use a readonly lock to check if the lock is already in the map (and + // thus no map writes are necessary), but this is complicated enough as it is so we skip that optimization for now. + l.mu.Lock() + defer l.mu.Unlock() + + // Check if there is already a lock for this key. + lock, ok := l.locks[key] + if !ok { + // no lock yet, so make one and add it to the map + lock = newCountedLock() + l.locks[key] = lock + } + + // Order is very important here. First we have to increment the refcount while we still have the map locked; this + // will prevent anyone else from evicting this lock after we unlock the map but before we lock the key. Second we + // have to unlock the map _before_ we start trying to lock the key (because locking the key could take a long time + // and we don't want to keep the map locked that whole time). + lock.refcount++ // incremented while holding _map_ lock + return lock + }() + + if !lock.Lock(ctx) { + l.returnLockObj(key, lock) + return false + } + return true +} + +// Unlock unlocks the lock for the specified key. Panics if the lock is not currently held. +func (l *TransientLockMap) Unlock(key string) { + lock := func() *countedLock { + l.mu.Lock() + defer l.mu.Unlock() + + lock, ok := l.locks[key] + if !ok { + panic(fmt.Sprintf("lock not held for key %s", key)) + } + return lock + }() + + lock.Unlock() + l.returnLockObj(key, lock) +} + +// Run runs the given callback while holding the lock, unless the context finishes before the lock could be +// acquired, in which case the context error is returned. +func (l *TransientLockMap) Run(ctx context.Context, key string, f func(ctx context.Context) error) error { + if !l.Lock(ctx, key) { + return ctx.Err() + } + defer l.Unlock(key) + return f(ctx) +} + +func (l *TransientLockMap) returnLockObj(key string, lock *countedLock) { + l.mu.Lock() + defer l.mu.Unlock() + + lock.refcount-- + if lock.refcount < 0 { + panic(fmt.Sprintf("BUG: somehow the lock.refcount for %q dropped to %d", key, lock.refcount)) + } + if lock.refcount == 0 { + delete(l.locks, key) + } +} diff --git a/pkg/emulators/storage/gcsutil/transient_lock_map_test.go b/pkg/emulators/storage/gcsutil/transient_lock_map_test.go new file mode 100644 index 0000000000..ae53d7c38a --- /dev/null +++ b/pkg/emulators/storage/gcsutil/transient_lock_map_test.go @@ -0,0 +1,293 @@ +package gcsutil + +import ( + "context" + "sync" + "sync/atomic" + "testing" + "time" + + "gotest.tools/v3/assert" +) + +func (l *TransientLockMap) len() int { + l.mu.Lock() + defer l.mu.Unlock() + return len(l.locks) +} + +func TestTransientLockMapBasics(t *testing.T) { + m := NewTransientLockMap() + upstream := make(chan int, 1) + downstream := make(chan int, 1) + + waitFor := func(c <-chan int, expected int) { + val := <-c + assert.Equal(t, expected, val, "got an unexpected value from a channel") + } + + readSignalNow := func(c <-chan int, expected int, msg string) { + select { + case val := <-c: + assert.Equal(t, expected, val, "got an unexpected value from a channel") + default: + t.Fatal(msg) + } + } + + // wrt upstream and downstream, this goroutine is "above" the main thread, so it writes to downstream and reads from + // upstream + go func() { + lockWithin(t, m, "foo", 10*time.Millisecond) + downstream <- 1 + waitFor(upstream, 2) + downstream <- 3 + m.Unlock("foo") + }() + + // wait until the other goroutine has locked "foo" + waitFor(downstream, 1) + + // prove that we can lock other keys without a problem + lockWithin(t, m, "bar", 10*time.Millisecond) + lockWithin(t, m, "baz", 10*time.Millisecond) + + assert.Equal(t, 3, m.len(), "wrong number of internal locks") + + // start trying to lock "foo" which will block until we signal the other goroutine to unlock it + time.AfterFunc(20*time.Millisecond, func() { upstream <- 2 }) + lockWithin(t, m, "foo", 200*time.Millisecond) + + // there better be a 3 already queued up in the downstream, otherwise we locked too fast + readSignalNow(downstream, 3, "locked \"foo\" before the other goroutine unlocked it...") + + assert.Equal(t, 3, m.len(), "wrong number of internal locks") + + // we can unlock out of order, and locks go away as we unlock them + m.Unlock("bar") + assert.Equal(t, 2, m.len(), "wrong number of internal locks") + + m.Unlock("baz") + assert.Equal(t, 1, m.len(), "wrong number of internal locks") + + m.Unlock("foo") + assert.Equal(t, 0, m.len(), "wrong number of internal locks") +} + +func TestTransientLockMapBadUnlock(t *testing.T) { + // call a function, and fail the test if the function doesn't panic + index := 0 + shouldPanic := func(f func()) { + index++ + defer func() { + if recovered := recover(); recovered != nil { + // ok, all is well + } else { + // we were supposed to panic but didn't - fail the test! + t.Fatalf("test #%d did not panic as expected...", index) + } + }() + f() + } + + // unlocking a key that has never been referenced + m := NewTransientLockMap() + shouldPanic(func() { + m.Unlock("foo") + }) + assert.Equal(t, 0, m.len(), "wrong number of internal locks") + + // double-unlocking a key in the same goroutine + m = NewTransientLockMap() + shouldPanic(func() { + assertLock(t, m, "foo") + m.Unlock("foo") + m.Unlock("foo") + }) + assert.Equal(t, 0, m.len(), "wrong number of internal locks") + + // double-unlocking a key across 2 goroutines + m = NewTransientLockMap() + shouldPanic(func() { + signal := make(chan struct{}) + + go func() { + assertLock(t, m, "foo") + m.Unlock("foo") + close(signal) + }() + + <-signal + m.Unlock("foo") + }) + assert.Equal(t, 0, m.len(), "wrong number of internal locks") +} + +func TestTransientLockMapSequence(t *testing.T) { + m := NewTransientLockMap() + + // signals + partnerAboutToLock := make(chan struct{}) + partnerGotLock := make(chan struct{}) + + assertLock(t, m, "foo") + go func() { + close(partnerAboutToLock) + assertLock(t, m, "foo") + close(partnerGotLock) + time.Sleep(125 * time.Millisecond) + m.Unlock("foo") + }() + + <-partnerAboutToLock + time.Sleep(100 * time.Millisecond) // give our partner time to actually call Lock() + m.Unlock("foo") + + <-partnerGotLock + start := time.Now() + assertLock(t, m, "foo") + + // ensure that the prior Lock() call actually blocked and waited for a while, as intended + if d := time.Since(start); d < 100*time.Millisecond { + t.Fatalf("Lock acquired too fast (%s)", d) + } + m.Unlock("foo") + assert.Equal(t, 0, m.len(), "wrong number of internal locks") +} + +func TestTransientLockMapContention(t *testing.T) { + m := NewTransientLockMap() + + var wg sync.WaitGroup + + assertLock(t, m, "foo") + assertLock(t, m, "bar") + + for i := 0; i < 100; i++ { + wg.Add(1) + go func() { + defer wg.Done() + assertLock(t, m, "foo") + m.Unlock("foo") + assertLock(t, m, "bar") + m.Unlock("bar") + }() + } + + time.Sleep(30 * time.Millisecond) + m.Unlock("bar") + m.Unlock("foo") + + wg.Wait() + + assert.Equal(t, 0, m.len(), "wrong number of internal locks") +} + +func TestTransientLockMapTimeout(t *testing.T) { + m := NewTransientLockMap() + + assertLock(t, m, "foo") + + ctx, cancel := context.WithTimeout(context.Background(), 50*time.Millisecond) + defer cancel() + + res := m.Lock(ctx, "foo") + assert.Equal(t, false, res, "lock foo (2)") + + m.Unlock("foo") + lockWithin(t, m, "foo", 10*time.Millisecond) // should be able to lock near instantly + + ctx2, cancel := context.WithCancel(context.Background()) + done := make(chan struct{}) + go func() { + res := m.Lock(ctx2, "foo") + assert.Equal(t, false, res, "lock foo (4)") + close(done) + }() + + time.Sleep(25 * time.Millisecond) + + // goroutine should not be done yet (i.e. Lock() should still be blocked) + select { + case <-done: + t.Fatal("done should not be closed yet!") + default: + } + + cancel() + + select { + case <-time.After(25 * time.Millisecond): + t.Fatal("timeout waiting for done channel to close") + case <-done: + // ok + } + + // finally, verify that we can unlock and lock still + m.Unlock("foo") + lockWithin(t, m, "foo", 10*time.Millisecond) // should be able to lock near instantly + m.Unlock("foo") + + assert.Equal(t, 0, m.len(), "wrong number of internal locks") + + // We should not be able to lock with an already-cancelled context. + for i := 0; i < 10; i++ { + assert.Assert(t, !m.Lock(ctx2, "foo"), "lock foo (final) expected canceled") + } +} + +func TestTransientLockMapRun(t *testing.T) { + m := NewTransientLockMap() + + start := make(chan struct{}) + var wgSucc sync.WaitGroup + var wgFail sync.WaitGroup + var nFail int32 + var nSucc int32 + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + for i := 0; i < 10; i++ { + if i == 0 { + wgSucc.Add(1) + } else { + wgFail.Add(1) + } + go func() { + <-start + err := m.Run(ctx, "foo", func(ctx context.Context) error { + // Whoever got the lock should cancel everyone else. + cancel() + wgFail.Wait() + return nil + }) + if err == nil { + defer wgSucc.Done() + atomic.AddInt32(&nSucc, 1) + } else { + defer wgFail.Done() + if err == context.Canceled { + atomic.AddInt32(&nFail, 1) + } else { + t.Errorf("expected canceled, got %T: %s", err, err) + } + } + }() + } + + close(start) + wgSucc.Wait() + + assert.Equal(t, int32(1), nSucc, "wrong # success") + assert.Equal(t, int32(9), nFail, "wrong # failures") +} + +func assertLock(t *testing.T, m *TransientLockMap, key string) { + assert.Assert(t, m.Lock(context.Background(), key), "should have locked") +} + +// grabs a lock, panicking if this takes longer than expected +func lockWithin(t *testing.T, m *TransientLockMap, key string, timeout time.Duration) { + ctx, cancel := context.WithTimeout(context.Background(), timeout) + defer cancel() + assert.Assert(t, m.Lock(ctx, key), "should have locked") +} From 44029ab4e1b15fa9c54e99f485e292e9b936443a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Eriksson?= Date: Wed, 30 Oct 2024 16:56:59 +0100 Subject: [PATCH 14/37] Add options to most apis --- proto/encore/runtime/v1/infra.pb.go | 156 ++++++++++-------- runtimes/core/src/objects/gcs/bucket.rs | 110 +++++++++--- runtimes/core/src/objects/mod.rs | 81 +++++---- runtimes/core/src/objects/noop/mod.rs | 20 ++- runtimes/core/src/objects/s3/bucket.rs | 62 +++++-- .../js/encore.dev/storage/objects/bucket.ts | 50 +++++- runtimes/js/src/objects.rs | 125 ++++++++++---- 7 files changed, 426 insertions(+), 178 deletions(-) diff --git a/proto/encore/runtime/v1/infra.pb.go b/proto/encore/runtime/v1/infra.pb.go index 3ed6fdfab3..89c26d907d 100644 --- a/proto/encore/runtime/v1/infra.pb.go +++ b/proto/encore/runtime/v1/infra.pb.go @@ -1631,6 +1631,11 @@ type Bucket struct { EncoreName string `protobuf:"bytes,2,opt,name=encore_name,json=encoreName,proto3" json:"encore_name,omitempty"` // The cloud name of the bucket. CloudName string `protobuf:"bytes,3,opt,name=cloud_name,json=cloudName,proto3" json:"cloud_name,omitempty"` + // Optional key prefix to prepend to all bucket keys. + // + // Note: make sure it ends with a slash ("/") if you want + // to group objects within a certain folder. + KeyPrefix *string `protobuf:"bytes,4,opt,name=key_prefix,json=keyPrefix,proto3,oneof" json:"key_prefix,omitempty"` } func (x *Bucket) Reset() { @@ -1686,6 +1691,13 @@ func (x *Bucket) GetCloudName() string { return "" } +func (x *Bucket) GetKeyPrefix() string { + if x != nil && x.KeyPrefix != nil { + return *x.KeyPrefix + } + return "" +} + type Gateway struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -2941,76 +2953,79 @@ var file_encore_runtime_v1_infra_proto_rawDesc = []byte{ 0x47, 0x43, 0x53, 0x12, 0x1f, 0x0a, 0x08, 0x65, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x48, 0x00, 0x52, 0x08, 0x65, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x88, 0x01, 0x01, 0x42, 0x0b, 0x0a, 0x09, 0x5f, 0x65, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, - 0x74, 0x42, 0x0a, 0x0a, 0x08, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x22, 0x5a, 0x0a, - 0x06, 0x42, 0x75, 0x63, 0x6b, 0x65, 0x74, 0x12, 0x10, 0x0a, 0x03, 0x72, 0x69, 0x64, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x72, 0x69, 0x64, 0x12, 0x1f, 0x0a, 0x0b, 0x65, 0x6e, 0x63, - 0x6f, 0x72, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, - 0x65, 0x6e, 0x63, 0x6f, 0x72, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x1d, 0x0a, 0x0a, 0x63, 0x6c, - 0x6f, 0x75, 0x64, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, - 0x63, 0x6c, 0x6f, 0x75, 0x64, 0x4e, 0x61, 0x6d, 0x65, 0x22, 0xb9, 0x06, 0x0a, 0x07, 0x47, 0x61, - 0x74, 0x65, 0x77, 0x61, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x72, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x03, 0x72, 0x69, 0x64, 0x12, 0x1f, 0x0a, 0x0b, 0x65, 0x6e, 0x63, 0x6f, 0x72, - 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x65, 0x6e, - 0x63, 0x6f, 0x72, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x19, 0x0a, 0x08, 0x62, 0x61, 0x73, 0x65, - 0x5f, 0x75, 0x72, 0x6c, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x62, 0x61, 0x73, 0x65, - 0x55, 0x72, 0x6c, 0x12, 0x1c, 0x0a, 0x09, 0x68, 0x6f, 0x73, 0x74, 0x6e, 0x61, 0x6d, 0x65, 0x73, - 0x18, 0x04, 0x20, 0x03, 0x28, 0x09, 0x52, 0x09, 0x68, 0x6f, 0x73, 0x74, 0x6e, 0x61, 0x6d, 0x65, - 0x73, 0x12, 0x33, 0x0a, 0x04, 0x63, 0x6f, 0x72, 0x73, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, - 0x1f, 0x2e, 0x65, 0x6e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, - 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x2e, 0x43, 0x4f, 0x52, 0x53, - 0x52, 0x04, 0x63, 0x6f, 0x72, 0x73, 0x1a, 0xcd, 0x04, 0x0a, 0x04, 0x43, 0x4f, 0x52, 0x53, 0x12, - 0x14, 0x0a, 0x05, 0x64, 0x65, 0x62, 0x75, 0x67, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x05, - 0x64, 0x65, 0x62, 0x75, 0x67, 0x12, 0x2f, 0x0a, 0x13, 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, - 0x5f, 0x63, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x73, 0x18, 0x02, 0x20, 0x01, - 0x28, 0x08, 0x52, 0x12, 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x43, 0x72, 0x65, 0x64, 0x65, - 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x73, 0x12, 0x58, 0x0a, 0x0f, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x65, - 0x64, 0x5f, 0x6f, 0x72, 0x69, 0x67, 0x69, 0x6e, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, - 0x2d, 0x2e, 0x65, 0x6e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, - 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x2e, 0x43, 0x4f, 0x52, 0x53, - 0x41, 0x6c, 0x6c, 0x6f, 0x77, 0x65, 0x64, 0x4f, 0x72, 0x69, 0x67, 0x69, 0x6e, 0x73, 0x48, 0x00, - 0x52, 0x0e, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x65, 0x64, 0x4f, 0x72, 0x69, 0x67, 0x69, 0x6e, 0x73, - 0x12, 0x59, 0x0a, 0x29, 0x75, 0x6e, 0x73, 0x61, 0x66, 0x65, 0x5f, 0x61, 0x6c, 0x6c, 0x6f, 0x77, - 0x5f, 0x61, 0x6c, 0x6c, 0x5f, 0x6f, 0x72, 0x69, 0x67, 0x69, 0x6e, 0x73, 0x5f, 0x77, 0x69, 0x74, - 0x68, 0x5f, 0x63, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x73, 0x18, 0x04, 0x20, - 0x01, 0x28, 0x08, 0x48, 0x00, 0x52, 0x24, 0x75, 0x6e, 0x73, 0x61, 0x66, 0x65, 0x41, 0x6c, 0x6c, - 0x6f, 0x77, 0x41, 0x6c, 0x6c, 0x4f, 0x72, 0x69, 0x67, 0x69, 0x6e, 0x73, 0x57, 0x69, 0x74, 0x68, - 0x43, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x73, 0x12, 0x7c, 0x0a, 0x23, 0x61, - 0x6c, 0x6c, 0x6f, 0x77, 0x65, 0x64, 0x5f, 0x6f, 0x72, 0x69, 0x67, 0x69, 0x6e, 0x73, 0x5f, 0x77, - 0x69, 0x74, 0x68, 0x6f, 0x75, 0x74, 0x5f, 0x63, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, - 0x6c, 0x73, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2d, 0x2e, 0x65, 0x6e, 0x63, 0x6f, 0x72, - 0x65, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x61, 0x74, - 0x65, 0x77, 0x61, 0x79, 0x2e, 0x43, 0x4f, 0x52, 0x53, 0x41, 0x6c, 0x6c, 0x6f, 0x77, 0x65, 0x64, - 0x4f, 0x72, 0x69, 0x67, 0x69, 0x6e, 0x73, 0x52, 0x20, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x65, 0x64, - 0x4f, 0x72, 0x69, 0x67, 0x69, 0x6e, 0x73, 0x57, 0x69, 0x74, 0x68, 0x6f, 0x75, 0x74, 0x43, 0x72, - 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x73, 0x12, 0x32, 0x0a, 0x15, 0x65, 0x78, 0x74, - 0x72, 0x61, 0x5f, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x65, 0x64, 0x5f, 0x68, 0x65, 0x61, 0x64, 0x65, - 0x72, 0x73, 0x18, 0x06, 0x20, 0x03, 0x28, 0x09, 0x52, 0x13, 0x65, 0x78, 0x74, 0x72, 0x61, 0x41, - 0x6c, 0x6c, 0x6f, 0x77, 0x65, 0x64, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x73, 0x12, 0x32, 0x0a, - 0x15, 0x65, 0x78, 0x74, 0x72, 0x61, 0x5f, 0x65, 0x78, 0x70, 0x6f, 0x73, 0x65, 0x64, 0x5f, 0x68, - 0x65, 0x61, 0x64, 0x65, 0x72, 0x73, 0x18, 0x07, 0x20, 0x03, 0x28, 0x09, 0x52, 0x13, 0x65, 0x78, - 0x74, 0x72, 0x61, 0x45, 0x78, 0x70, 0x6f, 0x73, 0x65, 0x64, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, - 0x73, 0x12, 0x3f, 0x0a, 0x1c, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x5f, 0x70, 0x72, 0x69, 0x76, 0x61, - 0x74, 0x65, 0x5f, 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x5f, 0x61, 0x63, 0x63, 0x65, 0x73, - 0x73, 0x18, 0x08, 0x20, 0x01, 0x28, 0x08, 0x52, 0x19, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x50, 0x72, - 0x69, 0x76, 0x61, 0x74, 0x65, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x41, 0x63, 0x63, 0x65, - 0x73, 0x73, 0x42, 0x22, 0x0a, 0x20, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x65, 0x64, 0x5f, 0x6f, 0x72, - 0x69, 0x67, 0x69, 0x6e, 0x73, 0x5f, 0x77, 0x69, 0x74, 0x68, 0x5f, 0x63, 0x72, 0x65, 0x64, 0x65, - 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x73, 0x1a, 0x3d, 0x0a, 0x12, 0x43, 0x4f, 0x52, 0x53, 0x41, 0x6c, - 0x6c, 0x6f, 0x77, 0x65, 0x64, 0x4f, 0x72, 0x69, 0x67, 0x69, 0x6e, 0x73, 0x12, 0x27, 0x0a, 0x0f, - 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x65, 0x64, 0x5f, 0x6f, 0x72, 0x69, 0x67, 0x69, 0x6e, 0x73, 0x18, - 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0e, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x65, 0x64, 0x4f, 0x72, - 0x69, 0x67, 0x69, 0x6e, 0x73, 0x2a, 0x7d, 0x0a, 0x0a, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x4b, - 0x69, 0x6e, 0x64, 0x12, 0x1b, 0x0a, 0x17, 0x53, 0x45, 0x52, 0x56, 0x45, 0x52, 0x5f, 0x4b, 0x49, - 0x4e, 0x44, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, - 0x12, 0x17, 0x0a, 0x13, 0x53, 0x45, 0x52, 0x56, 0x45, 0x52, 0x5f, 0x4b, 0x49, 0x4e, 0x44, 0x5f, - 0x50, 0x52, 0x49, 0x4d, 0x41, 0x52, 0x59, 0x10, 0x01, 0x12, 0x1b, 0x0a, 0x17, 0x53, 0x45, 0x52, - 0x56, 0x45, 0x52, 0x5f, 0x4b, 0x49, 0x4e, 0x44, 0x5f, 0x48, 0x4f, 0x54, 0x5f, 0x53, 0x54, 0x41, - 0x4e, 0x44, 0x42, 0x59, 0x10, 0x02, 0x12, 0x1c, 0x0a, 0x18, 0x53, 0x45, 0x52, 0x56, 0x45, 0x52, - 0x5f, 0x4b, 0x49, 0x4e, 0x44, 0x5f, 0x52, 0x45, 0x41, 0x44, 0x5f, 0x52, 0x45, 0x50, 0x4c, 0x49, - 0x43, 0x41, 0x10, 0x03, 0x42, 0x2c, 0x5a, 0x2a, 0x65, 0x6e, 0x63, 0x72, 0x2e, 0x64, 0x65, 0x76, - 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x65, 0x6e, 0x63, 0x6f, 0x72, 0x65, 0x2f, 0x72, 0x75, - 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2f, 0x76, 0x31, 0x3b, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, - 0x76, 0x31, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x74, 0x42, 0x0a, 0x0a, 0x08, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x22, 0x8d, 0x01, + 0x0a, 0x06, 0x42, 0x75, 0x63, 0x6b, 0x65, 0x74, 0x12, 0x10, 0x0a, 0x03, 0x72, 0x69, 0x64, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x72, 0x69, 0x64, 0x12, 0x1f, 0x0a, 0x0b, 0x65, 0x6e, + 0x63, 0x6f, 0x72, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x0a, 0x65, 0x6e, 0x63, 0x6f, 0x72, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x1d, 0x0a, 0x0a, 0x63, + 0x6c, 0x6f, 0x75, 0x64, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x09, 0x63, 0x6c, 0x6f, 0x75, 0x64, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x22, 0x0a, 0x0a, 0x6b, 0x65, + 0x79, 0x5f, 0x70, 0x72, 0x65, 0x66, 0x69, 0x78, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x48, 0x00, + 0x52, 0x09, 0x6b, 0x65, 0x79, 0x50, 0x72, 0x65, 0x66, 0x69, 0x78, 0x88, 0x01, 0x01, 0x42, 0x0d, + 0x0a, 0x0b, 0x5f, 0x6b, 0x65, 0x79, 0x5f, 0x70, 0x72, 0x65, 0x66, 0x69, 0x78, 0x22, 0xb9, 0x06, + 0x0a, 0x07, 0x47, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x72, 0x69, 0x64, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x72, 0x69, 0x64, 0x12, 0x1f, 0x0a, 0x0b, 0x65, + 0x6e, 0x63, 0x6f, 0x72, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x0a, 0x65, 0x6e, 0x63, 0x6f, 0x72, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x19, 0x0a, 0x08, + 0x62, 0x61, 0x73, 0x65, 0x5f, 0x75, 0x72, 0x6c, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, + 0x62, 0x61, 0x73, 0x65, 0x55, 0x72, 0x6c, 0x12, 0x1c, 0x0a, 0x09, 0x68, 0x6f, 0x73, 0x74, 0x6e, + 0x61, 0x6d, 0x65, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x09, 0x52, 0x09, 0x68, 0x6f, 0x73, 0x74, + 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x12, 0x33, 0x0a, 0x04, 0x63, 0x6f, 0x72, 0x73, 0x18, 0x05, 0x20, + 0x01, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x65, 0x6e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x72, 0x75, 0x6e, + 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x2e, + 0x43, 0x4f, 0x52, 0x53, 0x52, 0x04, 0x63, 0x6f, 0x72, 0x73, 0x1a, 0xcd, 0x04, 0x0a, 0x04, 0x43, + 0x4f, 0x52, 0x53, 0x12, 0x14, 0x0a, 0x05, 0x64, 0x65, 0x62, 0x75, 0x67, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x08, 0x52, 0x05, 0x64, 0x65, 0x62, 0x75, 0x67, 0x12, 0x2f, 0x0a, 0x13, 0x64, 0x69, 0x73, + 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x63, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x73, + 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x12, 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x43, + 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x73, 0x12, 0x58, 0x0a, 0x0f, 0x61, 0x6c, + 0x6c, 0x6f, 0x77, 0x65, 0x64, 0x5f, 0x6f, 0x72, 0x69, 0x67, 0x69, 0x6e, 0x73, 0x18, 0x03, 0x20, + 0x01, 0x28, 0x0b, 0x32, 0x2d, 0x2e, 0x65, 0x6e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x72, 0x75, 0x6e, + 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x2e, + 0x43, 0x4f, 0x52, 0x53, 0x41, 0x6c, 0x6c, 0x6f, 0x77, 0x65, 0x64, 0x4f, 0x72, 0x69, 0x67, 0x69, + 0x6e, 0x73, 0x48, 0x00, 0x52, 0x0e, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x65, 0x64, 0x4f, 0x72, 0x69, + 0x67, 0x69, 0x6e, 0x73, 0x12, 0x59, 0x0a, 0x29, 0x75, 0x6e, 0x73, 0x61, 0x66, 0x65, 0x5f, 0x61, + 0x6c, 0x6c, 0x6f, 0x77, 0x5f, 0x61, 0x6c, 0x6c, 0x5f, 0x6f, 0x72, 0x69, 0x67, 0x69, 0x6e, 0x73, + 0x5f, 0x77, 0x69, 0x74, 0x68, 0x5f, 0x63, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, + 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x48, 0x00, 0x52, 0x24, 0x75, 0x6e, 0x73, 0x61, 0x66, + 0x65, 0x41, 0x6c, 0x6c, 0x6f, 0x77, 0x41, 0x6c, 0x6c, 0x4f, 0x72, 0x69, 0x67, 0x69, 0x6e, 0x73, + 0x57, 0x69, 0x74, 0x68, 0x43, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x73, 0x12, + 0x7c, 0x0a, 0x23, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x65, 0x64, 0x5f, 0x6f, 0x72, 0x69, 0x67, 0x69, + 0x6e, 0x73, 0x5f, 0x77, 0x69, 0x74, 0x68, 0x6f, 0x75, 0x74, 0x5f, 0x63, 0x72, 0x65, 0x64, 0x65, + 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x73, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2d, 0x2e, 0x65, + 0x6e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, + 0x2e, 0x47, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x2e, 0x43, 0x4f, 0x52, 0x53, 0x41, 0x6c, 0x6c, + 0x6f, 0x77, 0x65, 0x64, 0x4f, 0x72, 0x69, 0x67, 0x69, 0x6e, 0x73, 0x52, 0x20, 0x61, 0x6c, 0x6c, + 0x6f, 0x77, 0x65, 0x64, 0x4f, 0x72, 0x69, 0x67, 0x69, 0x6e, 0x73, 0x57, 0x69, 0x74, 0x68, 0x6f, + 0x75, 0x74, 0x43, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x73, 0x12, 0x32, 0x0a, + 0x15, 0x65, 0x78, 0x74, 0x72, 0x61, 0x5f, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x65, 0x64, 0x5f, 0x68, + 0x65, 0x61, 0x64, 0x65, 0x72, 0x73, 0x18, 0x06, 0x20, 0x03, 0x28, 0x09, 0x52, 0x13, 0x65, 0x78, + 0x74, 0x72, 0x61, 0x41, 0x6c, 0x6c, 0x6f, 0x77, 0x65, 0x64, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, + 0x73, 0x12, 0x32, 0x0a, 0x15, 0x65, 0x78, 0x74, 0x72, 0x61, 0x5f, 0x65, 0x78, 0x70, 0x6f, 0x73, + 0x65, 0x64, 0x5f, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x73, 0x18, 0x07, 0x20, 0x03, 0x28, 0x09, + 0x52, 0x13, 0x65, 0x78, 0x74, 0x72, 0x61, 0x45, 0x78, 0x70, 0x6f, 0x73, 0x65, 0x64, 0x48, 0x65, + 0x61, 0x64, 0x65, 0x72, 0x73, 0x12, 0x3f, 0x0a, 0x1c, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x5f, 0x70, + 0x72, 0x69, 0x76, 0x61, 0x74, 0x65, 0x5f, 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x5f, 0x61, + 0x63, 0x63, 0x65, 0x73, 0x73, 0x18, 0x08, 0x20, 0x01, 0x28, 0x08, 0x52, 0x19, 0x61, 0x6c, 0x6c, + 0x6f, 0x77, 0x50, 0x72, 0x69, 0x76, 0x61, 0x74, 0x65, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, + 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, 0x42, 0x22, 0x0a, 0x20, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x65, + 0x64, 0x5f, 0x6f, 0x72, 0x69, 0x67, 0x69, 0x6e, 0x73, 0x5f, 0x77, 0x69, 0x74, 0x68, 0x5f, 0x63, + 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x73, 0x1a, 0x3d, 0x0a, 0x12, 0x43, 0x4f, + 0x52, 0x53, 0x41, 0x6c, 0x6c, 0x6f, 0x77, 0x65, 0x64, 0x4f, 0x72, 0x69, 0x67, 0x69, 0x6e, 0x73, + 0x12, 0x27, 0x0a, 0x0f, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x65, 0x64, 0x5f, 0x6f, 0x72, 0x69, 0x67, + 0x69, 0x6e, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0e, 0x61, 0x6c, 0x6c, 0x6f, 0x77, + 0x65, 0x64, 0x4f, 0x72, 0x69, 0x67, 0x69, 0x6e, 0x73, 0x2a, 0x7d, 0x0a, 0x0a, 0x53, 0x65, 0x72, + 0x76, 0x65, 0x72, 0x4b, 0x69, 0x6e, 0x64, 0x12, 0x1b, 0x0a, 0x17, 0x53, 0x45, 0x52, 0x56, 0x45, + 0x52, 0x5f, 0x4b, 0x49, 0x4e, 0x44, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, + 0x45, 0x44, 0x10, 0x00, 0x12, 0x17, 0x0a, 0x13, 0x53, 0x45, 0x52, 0x56, 0x45, 0x52, 0x5f, 0x4b, + 0x49, 0x4e, 0x44, 0x5f, 0x50, 0x52, 0x49, 0x4d, 0x41, 0x52, 0x59, 0x10, 0x01, 0x12, 0x1b, 0x0a, + 0x17, 0x53, 0x45, 0x52, 0x56, 0x45, 0x52, 0x5f, 0x4b, 0x49, 0x4e, 0x44, 0x5f, 0x48, 0x4f, 0x54, + 0x5f, 0x53, 0x54, 0x41, 0x4e, 0x44, 0x42, 0x59, 0x10, 0x02, 0x12, 0x1c, 0x0a, 0x18, 0x53, 0x45, + 0x52, 0x56, 0x45, 0x52, 0x5f, 0x4b, 0x49, 0x4e, 0x44, 0x5f, 0x52, 0x45, 0x41, 0x44, 0x5f, 0x52, + 0x45, 0x50, 0x4c, 0x49, 0x43, 0x41, 0x10, 0x03, 0x42, 0x2c, 0x5a, 0x2a, 0x65, 0x6e, 0x63, 0x72, + 0x2e, 0x64, 0x65, 0x76, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x65, 0x6e, 0x63, 0x6f, 0x72, + 0x65, 0x2f, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2f, 0x76, 0x31, 0x3b, 0x72, 0x75, 0x6e, + 0x74, 0x69, 0x6d, 0x65, 0x76, 0x31, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( @@ -3559,6 +3574,7 @@ func file_encore_runtime_v1_infra_proto_init() { (*BucketCluster_S3_)(nil), (*BucketCluster_Gcs)(nil), } + file_encore_runtime_v1_infra_proto_msgTypes[18].OneofWrappers = []interface{}{} file_encore_runtime_v1_infra_proto_msgTypes[29].OneofWrappers = []interface{}{} file_encore_runtime_v1_infra_proto_msgTypes[30].OneofWrappers = []interface{}{} file_encore_runtime_v1_infra_proto_msgTypes[31].OneofWrappers = []interface{}{} diff --git a/runtimes/core/src/objects/gcs/bucket.rs b/runtimes/core/src/objects/gcs/bucket.rs index 0353681f38..0c599489fa 100644 --- a/runtimes/core/src/objects/gcs/bucket.rs +++ b/runtimes/core/src/objects/gcs/bucket.rs @@ -10,7 +10,10 @@ use std::sync::Arc; use tokio::io::AsyncRead; use crate::encore::runtime::v1 as pb; -use crate::objects::{DownloadError, DownloadStream, Error, ListEntry, ObjectAttrs, UploadOptions}; +use crate::objects::{ + AttrsOptions, DeleteOptions, DownloadOptions, DownloadStream, Error, ListEntry, ListOptions, + ObjectAttrs, UploadOptions, +}; use crate::{objects, CloudName}; use google_cloud_storage as gcs; @@ -64,27 +67,47 @@ impl objects::BucketImpl for Bucket { fn list( self: Arc, + options: ListOptions, ) -> Pin> + Send + 'static>> { Box::pin(async move { match self.client.get().await { Ok(client) => { let client = client.clone(); + + let mut total_seen = 0; + const DEFAULT_MAX_RESULTS: u64 = 1000; let s: objects::ListStream = Box::new(try_stream! { + let max_results = if let Some(limit) = options.limit { + limit.min(DEFAULT_MAX_RESULTS) as i32 + } else { + DEFAULT_MAX_RESULTS as i32 + }; + let mut req = gcs::http::objects::list::ListObjectsRequest { bucket: self.name.to_string(), + max_results: Some(max_results), ..Default::default() }; + // Filter by key prefix, if provided. if let Some(key_prefix) = &self.key_prefix { req.prefix = Some(key_prefix.clone()); } + 'PageLoop: loop { let resp = client.list_objects(&req).await.map_err(|e| Error::Other(e.into()))?; if let Some(items) = resp.items { for obj in items { + total_seen += 1; + if let Some(limit) = options.limit { + if total_seen > limit { + break 'PageLoop; + } + } + let entry = ListEntry { name: self.strip_prefix(Cow::Owned(obj.name)).into_owned(), size: obj.size as u64, @@ -95,6 +118,17 @@ impl objects::BucketImpl for Bucket { } req.page_token = resp.next_page_token; + + // Are we close to being done? If so, adjust the max_results + // to avoid over-fetching. + if let Some(limit) = options.limit { + let remaining = (limit - total_seen).max(0); + if remaining == 0 { + break 'PageLoop; + } + req.max_results = Some(remaining.min(DEFAULT_MAX_RESULTS) as i32); + } + if req.page_token.is_none() { break; } @@ -120,18 +154,25 @@ struct Object { } impl objects::ObjectImpl for Object { - fn attrs(self: Arc) -> Pin> + Send>> { + fn attrs( + self: Arc, + options: AttrsOptions, + ) -> Pin> + Send>> { Box::pin(async move { match self.bkt.client.get().await { Ok(client) => { use gcs::http::{error::ErrorResponse, Error as GCSError}; - let req = &gcs::http::objects::get::GetObjectRequest { + let mut req = gcs::http::objects::get::GetObjectRequest { bucket: self.bkt.name.to_string(), object: self.bkt.obj_name(Cow::Borrowed(&self.name)).into_owned(), ..Default::default() }; - match client.get_object(req).await { + if let Some(version) = options.version { + req.generation = Some(parse_version(version)?); + } + + match client.get_object(&req).await { Ok(obj) => Ok(ObjectAttrs { name: obj.name, version: obj.generation.to_string(), @@ -159,18 +200,25 @@ impl objects::ObjectImpl for Object { }) } - fn exists(self: Arc) -> Pin> + Send>> { + fn exists( + self: Arc, + version: Option, + ) -> Pin> + Send>> { Box::pin(async move { match self.bkt.client.get().await { Ok(client) => { use gcs::http::{error::ErrorResponse, Error}; - let req = &gcs::http::objects::get::GetObjectRequest { + let mut req = gcs::http::objects::get::GetObjectRequest { bucket: self.bkt.name.to_string(), object: self.bkt.obj_name(Cow::Borrowed(&self.name)).into_owned(), ..Default::default() }; - match client.get_object(req).await { + if let Some(version) = version { + req.generation = Some(parse_version(version)?); + } + + match client.get_object(&req).await { Ok(_obj) => Ok(true), Err(Error::Response(ErrorResponse { code: 404, .. })) => Ok(false), Err(Error::HttpClient(err)) @@ -223,27 +271,34 @@ impl objects::ObjectImpl for Object { fn download( self: Arc, - ) -> Pin> + Send>> { - fn convert_err(err: gcs::http::Error) -> DownloadError { + options: DownloadOptions, + ) -> Pin> + Send>> { + fn convert_err(err: gcs::http::Error) -> Error { use gcs::http::error::ErrorResponse; - use gcs::http::Error; match err { - Error::Response(ErrorResponse { code: 404, .. }) => DownloadError::NotFound, - Error::HttpClient(err) if err.status().map(|s| s.as_u16()) == Some(404) => { - DownloadError::NotFound + gcs::http::Error::Response(ErrorResponse { code: 404, .. }) => Error::NotFound, + gcs::http::Error::HttpClient(err) + if err.status().map(|s| s.as_u16()) == Some(404) => + { + Error::NotFound } - err => DownloadError::Other(err.into()), + err => Error::Other(err.into()), } } Box::pin(async move { match self.bkt.client.get().await { Ok(client) => { - let req = GetObjectRequest { + let mut req = GetObjectRequest { bucket: self.bkt.name.to_string(), object: self.bkt.obj_name(Cow::Borrowed(&self.name)).into_owned(), ..Default::default() }; + + if let Some(version) = options.version { + req.generation = Some(parse_version(version)?); + } + let resp = client .download_streamed_object(&req, &Range::default()) .await; @@ -252,7 +307,7 @@ impl objects::ObjectImpl for Object { let stream: DownloadStream = Box::pin(stream.map_err(convert_err)); Ok(stream) } - Err(err) => Err(DownloadError::Internal(anyhow::anyhow!( + Err(err) => Err(Error::Internal(anyhow::anyhow!( "unable to resolve client: {}", err ))), @@ -260,18 +315,25 @@ impl objects::ObjectImpl for Object { }) } - fn delete(self: Arc) -> Pin> + Send>> { + fn delete( + self: Arc, + options: DeleteOptions, + ) -> Pin> + Send>> { Box::pin(async move { match self.bkt.client.get().await { Ok(client) => { use gcs::http::{error::ErrorResponse, Error as GCSError}; - let req = &gcs::http::objects::delete::DeleteObjectRequest { + let mut req = gcs::http::objects::delete::DeleteObjectRequest { bucket: self.bkt.name.to_string(), object: self.bkt.obj_name(Cow::Borrowed(&self.name)).into_owned(), ..Default::default() }; - match client.delete_object(req).await { + if let Some(version) = options.version { + req.generation = Some(parse_version(version)?); + } + + match client.delete_object(&req).await { Ok(_) => Ok(()), Err(GCSError::Response(ErrorResponse { code: 404, .. })) => Ok(()), Err(GCSError::HttpClient(err)) @@ -302,3 +364,13 @@ fn apply_upload_opts(opts: UploadOptions, req: &mut UploadObjectRequest, media: } } } + +fn parse_version(version: String) -> Result { + version.parse().map_err(|err| { + Error::Other(anyhow::anyhow!( + "invalid version number {}: {}", + version, + err + )) + }) +} diff --git a/runtimes/core/src/objects/mod.rs b/runtimes/core/src/objects/mod.rs index 0af1464630..3444f659e4 100644 --- a/runtimes/core/src/objects/mod.rs +++ b/runtimes/core/src/objects/mod.rs @@ -14,7 +14,6 @@ mod gcs; mod manager; mod noop; mod s3; -mod s3old; trait ClusterImpl: Debug + Send + Sync { fn bucket(self: Arc, cfg: &pb::Bucket) -> Arc; @@ -25,13 +24,17 @@ trait BucketImpl: Debug + Send + Sync { fn list( self: Arc, + options: ListOptions, ) -> Pin> + Send + 'static>>; } pub type ListStream = Box> + Send>; trait ObjectImpl: Debug + Send + Sync { - fn exists(self: Arc) -> Pin> + Send>>; + fn exists( + self: Arc, + version: Option, + ) -> Pin> + Send>>; fn upload( self: Arc, @@ -41,11 +44,18 @@ trait ObjectImpl: Debug + Send + Sync { fn download( self: Arc, - ) -> Pin> + Send>>; + options: DownloadOptions, + ) -> Pin> + Send>>; - fn attrs(self: Arc) -> Pin> + Send>>; + fn attrs( + self: Arc, + options: AttrsOptions, + ) -> Pin> + Send>>; - fn delete(self: Arc) -> Pin> + Send>>; + fn delete( + self: Arc, + options: DeleteOptions, + ) -> Pin> + Send>>; } #[derive(Debug)] @@ -62,8 +72,8 @@ impl Bucket { } } - pub async fn list(&self) -> Result { - self.imp.clone().list().await + pub async fn list(&self, options: ListOptions) -> Result { + self.imp.clone().list(options).await } } @@ -74,8 +84,8 @@ pub struct Object { } impl Object { - pub async fn exists(&self) -> anyhow::Result { - self.imp.clone().exists().await + pub async fn exists(&self, version: Option) -> anyhow::Result { + self.imp.clone().exists(version).await } pub fn upload( @@ -88,14 +98,16 @@ impl Object { pub fn download_stream( &self, - ) -> impl Future> + Send + 'static { - self.imp.clone().download() + options: DownloadOptions, + ) -> impl Future> + Send + 'static { + self.imp.clone().download(options) } pub fn download_all( &self, - ) -> impl Future, DownloadError>> + Send + 'static { - let stream = self.imp.clone().download(); + options: DownloadOptions, + ) -> impl Future, Error>> + Send + 'static { + let stream = self.imp.clone().download(options); async move { let mut bytes = Vec::new(); let mut stream = stream.await?; @@ -107,12 +119,12 @@ impl Object { } } - pub async fn attrs(&self) -> Result { - self.imp.clone().attrs().await + pub async fn attrs(&self, options: AttrsOptions) -> Result { + self.imp.clone().attrs(options).await } - pub async fn delete(&self) -> Result<(), Error> { - self.imp.clone().delete().await + pub async fn delete(&self, options: DeleteOptions) -> Result<(), Error> { + self.imp.clone().delete(options).await } } @@ -128,19 +140,7 @@ pub enum Error { Other(anyhow::Error), } -#[derive(thiserror::Error, Debug)] -pub enum DownloadError { - #[error("object not found")] - NotFound, - - #[error("internal error: {0:?}")] - Internal(anyhow::Error), - - #[error("{0:?}")] - Other(anyhow::Error), -} - -pub type DownloadStream = Pin> + Send>>; +pub type DownloadStream = Pin> + Send>>; pub struct ObjectAttrs { pub name: String, @@ -166,3 +166,24 @@ pub struct UploadOptions { pub struct UploadPreconditions { pub not_exists: Option, } + +#[derive(Debug, Default)] +pub struct DownloadOptions { + pub version: Option, +} + +#[derive(Debug, Default)] +pub struct AttrsOptions { + pub version: Option, +} + +#[derive(Debug, Default)] +pub struct DeleteOptions { + pub version: Option, +} + +#[derive(Debug, Default)] +pub struct ListOptions { + pub prefix: Option, + pub limit: Option, +} diff --git a/runtimes/core/src/objects/noop/mod.rs b/runtimes/core/src/objects/noop/mod.rs index 5bc297390e..dd4d73a171 100644 --- a/runtimes/core/src/objects/noop/mod.rs +++ b/runtimes/core/src/objects/noop/mod.rs @@ -8,6 +8,8 @@ use tokio::io::AsyncRead; use crate::encore::runtime::v1 as pb; use crate::objects; +use super::{AttrsOptions, DeleteOptions, DownloadOptions, ListOptions}; + #[derive(Debug)] pub struct Cluster; @@ -30,6 +32,7 @@ impl objects::BucketImpl for Bucket { fn list( self: Arc, + _options: ListOptions, ) -> Pin> + Send + 'static>> { Box::pin(async move { @@ -43,13 +46,17 @@ impl objects::BucketImpl for Bucket { impl objects::ObjectImpl for Object { fn attrs( self: Arc, + _options: AttrsOptions, ) -> Pin> + Send>> { Box::pin(future::ready(Err(objects::Error::Internal( anyhow::anyhow!("noop bucket does not support attrs"), )))) } - fn exists(self: Arc) -> Pin> + Send>> { + fn exists( + self: Arc, + _version: Option, + ) -> Pin> + Send>> { Box::pin(future::ready(Err(anyhow::anyhow!( "noop bucket does not support exists" )))) @@ -67,16 +74,19 @@ impl objects::ObjectImpl for Object { fn download( self: Arc, - ) -> Pin> + Send>> - { + _options: DownloadOptions, + ) -> Pin> + Send>> { Box::pin(async move { - Err(objects::DownloadError::Internal(anyhow::anyhow!( + Err(objects::Error::Internal(anyhow::anyhow!( "noop bucket does not support download" ))) }) } - fn delete(self: Arc) -> Pin> + Send>> { + fn delete( + self: Arc, + _options: DeleteOptions, + ) -> Pin> + Send>> { Box::pin(future::ready(Err(objects::Error::Internal( anyhow::anyhow!("noop bucket does not support delete"), )))) diff --git a/runtimes/core/src/objects/s3/bucket.rs b/runtimes/core/src/objects/s3/bucket.rs index bb72eacded..0bbc586c38 100644 --- a/runtimes/core/src/objects/s3/bucket.rs +++ b/runtimes/core/src/objects/s3/bucket.rs @@ -14,7 +14,9 @@ use std::task::Poll; use tokio::io::{AsyncRead, AsyncReadExt}; use crate::encore::runtime::v1 as pb; -use crate::objects::{self, Error, ListEntry, ObjectAttrs}; +use crate::objects::{ + self, AttrsOptions, DeleteOptions, DownloadOptions, Error, ListEntry, ListOptions, ObjectAttrs, +}; use super::LazyS3Client; @@ -71,22 +73,41 @@ impl objects::BucketImpl for Bucket { fn list( self: Arc, + options: ListOptions, ) -> Pin> + Send + 'static>> { Box::pin(async move { + let mut total_seen = 0; let client = self.client.get().await.clone(); let s: objects::ListStream = Box::new(try_stream! { let mut req = client.list_objects_v2() - .bucket(self.name.clone()) - .max_keys(1000); + .bucket(self.name.clone()); if let Some(key_prefix) = self.key_prefix.clone() { req = req.prefix(key_prefix); } - let mut stream = req.into_paginator().send(); + let page_size = if let Some(limit) = options.limit { + limit.min(1000) as i32 + } else { + 1000 + }; + + let mut stream = req.into_paginator() + .page_size(page_size) + .send(); + + 'PageLoop: while let Some(resp) = stream.try_next().await.map_err(|e| Error::Other(e.into()))? { for obj in resp.contents.unwrap_or_default() { + total_seen += 1; + if let Some(limit) = options.limit { + if total_seen > limit { + // We've reached the limit, stop the stream. + break 'PageLoop; + } + } + let entry = ListEntry { name: self.strip_prefix(Cow::Owned(obj.key.unwrap_or_default())).into_owned(), size: obj.size.unwrap_or_default() as u64, @@ -109,7 +130,10 @@ struct Object { } impl objects::ObjectImpl for Object { - fn attrs(self: Arc) -> Pin> + Send>> { + fn attrs( + self: Arc, + options: AttrsOptions, + ) -> Pin> + Send>> { Box::pin(async move { let client = self.bkt.client.get().await.clone(); let cloud_name = self.bkt.obj_name(Cow::Borrowed(&self.cloud_name)); @@ -117,6 +141,7 @@ impl objects::ObjectImpl for Object { .head_object() .bucket(self.bkt.name.clone()) .key(cloud_name) + .set_version_id(options.version) .send() .await; @@ -136,7 +161,10 @@ impl objects::ObjectImpl for Object { }) } - fn exists(self: Arc) -> Pin> + Send>> { + fn exists( + self: Arc, + version: Option, + ) -> Pin> + Send>> { Box::pin(async move { let client = self.bkt.client.get().await.clone(); let cloud_name = self.bkt.obj_name(Cow::Borrowed(&self.cloud_name)); @@ -144,6 +172,7 @@ impl objects::ObjectImpl for Object { .head_object() .bucket(&self.bkt.name) .key(cloud_name) + .set_version_id(version) .send() .await; match res { @@ -240,8 +269,8 @@ impl objects::ObjectImpl for Object { fn download( self: Arc, - ) -> Pin> + Send>> - { + options: DownloadOptions, + ) -> Pin> + Send>> { Box::pin(async move { let client = self.bkt.client.get().await.clone(); let cloud_name = self.bkt.obj_name(Cow::Borrowed(&self.cloud_name)); @@ -249,6 +278,7 @@ impl objects::ObjectImpl for Object { .get_object() .bucket(&self.bkt.name) .key(cloud_name.into_owned()) + .set_version_id(options.version) .send() .await; @@ -256,21 +286,24 @@ impl objects::ObjectImpl for Object { Ok(mut resp) => { let result = stream! { while let Some(chunk) = resp.body.next().await { - yield chunk.map_err(|e| objects::DownloadError::Other(e.into())); + yield chunk.map_err(|e| objects::Error::Other(e.into())); } }; let result: objects::DownloadStream = Box::pin(result); Ok(result) } Err(SdkError::ServiceError(err)) if err.err().is_no_such_key() => { - Err(objects::DownloadError::NotFound) + Err(objects::Error::NotFound) } - Err(err) => Err(objects::DownloadError::Other(err.into())), + Err(err) => Err(objects::Error::Other(err.into())), } }) } - fn delete(self: Arc) -> Pin> + Send>> { + fn delete( + self: Arc, + options: DeleteOptions, + ) -> Pin> + Send>> { Box::pin(async move { let client = self.bkt.client.get().await.clone(); let cloud_name = self.bkt.obj_name(Cow::Borrowed(&self.cloud_name)); @@ -278,6 +311,7 @@ impl objects::ObjectImpl for Object { .delete_object() .bucket(&self.bkt.name) .key(cloud_name.into_owned()) + .set_version_id(options.version) .send() .await; match res { @@ -377,7 +411,7 @@ struct ObjectStream { } impl Stream for ObjectStream { - type Item = Result; + type Item = Result; fn poll_next( self: Pin<&mut Self>, @@ -386,7 +420,7 @@ impl Stream for ObjectStream { let stream = Pin::new(&mut self.get_mut().inner); match stream.poll_next(cx) { Poll::Ready(Some(Err(err))) => { - Poll::Ready(Some(Err(objects::DownloadError::Other(err.into())))) + Poll::Ready(Some(Err(objects::Error::Other(err.into())))) } Poll::Ready(Some(Ok(data))) => Poll::Ready(Some(Ok(data))), Poll::Ready(None) => Poll::Ready(None), diff --git a/runtimes/js/encore.dev/storage/objects/bucket.ts b/runtimes/js/encore.dev/storage/objects/bucket.ts index 6bede1c050..bd392a6703 100644 --- a/runtimes/js/encore.dev/storage/objects/bucket.ts +++ b/runtimes/js/encore.dev/storage/objects/bucket.ts @@ -50,9 +50,9 @@ export class Bucket { * Returns the object's attributes. * Throws an error if the object does not exist. */ - async attrs(name: string): Promise { + async attrs(name: string, options?: AttrsOptions): Promise { const impl = this.impl.object(name); - return impl.attrs(); + return impl.attrs(options); } /** @@ -66,22 +66,60 @@ export class Bucket { /** * Downloads an object from the bucket and returns its contents. */ - async download(name: string): Promise { + async download(name: string, options?: DownloadOptions): Promise { const impl = this.impl.object(name); - return impl.downloadAll(); + return impl.downloadAll(options); } /** * Removes an object from the bucket. * Throws an error on network failure. */ - async remove(name: string): Promise { + async remove(name: string, options?: DeleteOptions): Promise { const impl = this.impl.object(name); - return impl.delete(); + return impl.delete(options); } } export interface ListOptions { + /** + * Only include objects with this prefix in the listing. + * If unset, all objects are included. + */ + prefix?: string; + + /** Maximum number of objects to return. Defaults to no limit. */ + limit?: number; +} + +export interface AttrsOptions { + /** + * The object version to retrieve attributes for. + * Defaults to the lastest version if unset. + * + * If bucket versioning is not enabled, this option is ignored. + */ + version?: string; +} + +export interface DeleteOptions { + /** + * The object version to delete. + * Defaults to the lastest version if unset. + * + * If bucket versioning is not enabled, this option is ignored. + */ + version?: string; +} + +export interface DownloadOptions { + /** + * The object version to download. + * Defaults to the lastest version if unset. + * + * If bucket versioning is not enabled, this option is ignored. + */ + version?: string; } export interface ObjectAttrs { diff --git a/runtimes/js/src/objects.rs b/runtimes/js/src/objects.rs index 2c305ea3fe..e9729d1a45 100644 --- a/runtimes/js/src/objects.rs +++ b/runtimes/js/src/objects.rs @@ -1,23 +1,18 @@ use std::pin::Pin; +use encore_runtime_core::objects as core; use napi::bindgen_prelude::Buffer; use napi::{Env, JsBuffer, JsObject}; use napi_derive::napi; -use encore_runtime_core::objects::{ - Bucket as CoreBucket, DownloadError, ListEntry as CoreListEntry, ListStream, - Object as CoreObject, ObjectAttrs as CoreAttrs, UploadOptions as CoreUploadOptions, - UploadPreconditions as CoreUploadPreconditions, -}; - #[napi] pub struct Bucket { - bkt: CoreBucket, + bkt: core::Bucket, } #[napi] impl Bucket { - pub(crate) fn new(bkt: CoreBucket) -> Self { + pub(crate) fn new(bkt: core::Bucket) -> Self { Self { bkt } } @@ -27,9 +22,10 @@ impl Bucket { } #[napi] - pub async fn list(&self) -> napi::Result { + pub async fn list(&self, options: Option) -> napi::Result { + let options = options.unwrap_or_default().into(); self.bkt - .list() + .list(options) .await .map_err(map_objects_err) .map(ListIterator::new) @@ -38,27 +34,28 @@ impl Bucket { #[napi] pub struct BucketObject { - obj: CoreObject, + obj: core::Object, } #[napi] impl BucketObject { - pub(crate) fn new(obj: CoreObject) -> Self { + pub(crate) fn new(obj: core::Object) -> Self { Self { obj } } #[napi] - pub async fn attrs(&self) -> napi::Result { + pub async fn attrs(&self, options: Option) -> napi::Result { + let options = options.unwrap_or_default().into(); self.obj - .attrs() + .attrs(options) .await .map(ObjectAttrs::from) .map_err(map_objects_err) } #[napi] - pub async fn exists(&self) -> napi::Result { - self.obj.exists().await.map_err(napi::Error::from) + pub async fn exists(&self, version: Option) -> napi::Result { + self.obj.exists(version).await.map_err(napi::Error::from) } #[napi(ts_return_type = "Promise")] @@ -87,14 +84,20 @@ impl BucketObject { } #[napi] - pub async fn download_all(&self) -> napi::Result { - let buf = self.obj.download_all().await.map_err(map_download_err)?; + pub async fn download_all(&self, options: Option) -> napi::Result { + let options = options.unwrap_or_default().into(); + let buf = self + .obj + .download_all(options) + .await + .map_err(map_objects_err)?; Ok(buf.into()) } - #[napi] - pub async fn delete(&self) -> napi::Result<()> { - self.obj.delete().await.map_err(map_objects_err) + #[napi(ts_return_type = "Promise")] + pub async fn delete(&self, options: Option) -> napi::Result<()> { + let options = options.unwrap_or_default().into(); + self.obj.delete(options).await.map_err(map_objects_err) } } @@ -107,8 +110,8 @@ pub struct ObjectAttrs { pub etag: String, } -impl From for ObjectAttrs { - fn from(value: CoreAttrs) -> Self { +impl From for ObjectAttrs { + fn from(value: core::ObjectAttrs) -> Self { Self { name: value.name, version: value.version, @@ -126,8 +129,8 @@ pub struct ListEntry { pub etag: String, } -impl From for ListEntry { - fn from(value: CoreListEntry) -> Self { +impl From for ListEntry { + fn from(value: core::ListEntry) -> Self { Self { name: value.name, size: value.size as i64, @@ -149,7 +152,7 @@ pub struct UploadPreconditions { pub not_exists: Option, } -impl From for CoreUploadOptions { +impl From for core::UploadOptions { fn from(value: UploadOptions) -> Self { Self { content_type: value.content_type, @@ -158,7 +161,7 @@ impl From for CoreUploadOptions { } } -impl From for CoreUploadPreconditions { +impl From for core::UploadPreconditions { fn from(value: UploadPreconditions) -> Self { Self { not_exists: value.not_exists, @@ -166,22 +169,18 @@ impl From for CoreUploadPreconditions { } } -fn map_objects_err(err: encore_runtime_core::objects::Error) -> napi::Error { - napi::Error::new(napi::Status::GenericFailure, err) -} - -fn map_download_err(err: DownloadError) -> napi::Error { +fn map_objects_err(err: core::Error) -> napi::Error { napi::Error::new(napi::Status::GenericFailure, err) } #[napi] pub struct ListIterator { - stream: tokio::sync::Mutex>, + stream: tokio::sync::Mutex>, } #[napi] impl ListIterator { - fn new(stream: ListStream) -> Self { + fn new(stream: core::ListStream) -> Self { Self { stream: tokio::sync::Mutex::new(stream.into()), } @@ -200,3 +199,61 @@ impl ListIterator { Ok(row.map(ListEntry::from)) } } + +#[napi(object)] +#[derive(Debug, Default)] +pub struct AttrsOptions { + pub version: Option, +} + +#[napi(object)] +#[derive(Debug, Default)] +pub struct DeleteOptions { + pub version: Option, +} + +#[napi(object)] +#[derive(Debug, Default)] +pub struct DownloadOptions { + pub version: Option, +} + +#[napi(object)] +#[derive(Debug, Default)] +pub struct ListOptions { + pub prefix: Option, + pub limit: Option, +} + +impl From for core::DownloadOptions { + fn from(value: DownloadOptions) -> Self { + Self { + version: value.version, + } + } +} + +impl From for core::DeleteOptions { + fn from(value: DeleteOptions) -> Self { + Self { + version: value.version, + } + } +} + +impl From for core::AttrsOptions { + fn from(value: AttrsOptions) -> Self { + Self { + version: value.version, + } + } +} + +impl From for core::ListOptions { + fn from(value: ListOptions) -> Self { + Self { + prefix: value.prefix, + limit: value.limit.map(|v| v as u64), + } + } +} From 1663b008011079d0efbe9954bd0162358cd858a0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Eriksson?= Date: Thu, 31 Oct 2024 12:46:49 +0100 Subject: [PATCH 15/37] runtimes/core: better error handling, api --- runtimes/core/src/objects/gcs/bucket.rs | 98 ++++++----- runtimes/core/src/objects/mod.rs | 13 +- runtimes/core/src/objects/noop/mod.rs | 12 +- runtimes/core/src/objects/s3/bucket.rs | 163 ++++++++++++++---- .../js/encore.dev/storage/objects/bucket.ts | 7 +- runtimes/js/src/objects.rs | 15 +- 6 files changed, 205 insertions(+), 103 deletions(-) diff --git a/runtimes/core/src/objects/gcs/bucket.rs b/runtimes/core/src/objects/gcs/bucket.rs index 0c599489fa..5b2529fca3 100644 --- a/runtimes/core/src/objects/gcs/bucket.rs +++ b/runtimes/core/src/objects/gcs/bucket.rs @@ -161,7 +161,6 @@ impl objects::ObjectImpl for Object { Box::pin(async move { match self.bkt.client.get().await { Ok(client) => { - use gcs::http::{error::ErrorResponse, Error as GCSError}; let mut req = gcs::http::objects::get::GetObjectRequest { bucket: self.bkt.name.to_string(), object: self.bkt.obj_name(Cow::Borrowed(&self.name)).into_owned(), @@ -172,25 +171,14 @@ impl objects::ObjectImpl for Object { req.generation = Some(parse_version(version)?); } - match client.get_object(&req).await { - Ok(obj) => Ok(ObjectAttrs { - name: obj.name, - version: obj.generation.to_string(), - size: obj.size as u64, - content_type: obj.content_type, - etag: obj.etag, - }), - Err(GCSError::Response(ErrorResponse { code: 404, .. })) => { - Err(Error::NotFound) - } - Err(GCSError::HttpClient(err)) - if err.status().is_some_and(|v| v.as_u16() == 404) => - { - Err(Error::NotFound) - } - - Err(err) => Err(Error::Other(err.into())), - } + let obj = client.get_object(&req).await.map_err(map_err)?; + Ok(ObjectAttrs { + name: obj.name, + version: Some(obj.generation.to_string()), + size: obj.size as u64, + content_type: obj.content_type, + etag: obj.etag, + }) } Err(err) => Err(Error::Internal(anyhow::anyhow!( "unable to resolve client: {}", @@ -203,11 +191,10 @@ impl objects::ObjectImpl for Object { fn exists( self: Arc, version: Option, - ) -> Pin> + Send>> { + ) -> Pin> + Send>> { Box::pin(async move { match self.bkt.client.get().await { Ok(client) => { - use gcs::http::{error::ErrorResponse, Error}; let mut req = gcs::http::objects::get::GetObjectRequest { bucket: self.bkt.name.to_string(), object: self.bkt.obj_name(Cow::Borrowed(&self.name)).into_owned(), @@ -218,19 +205,16 @@ impl objects::ObjectImpl for Object { req.generation = Some(parse_version(version)?); } - match client.get_object(&req).await { + match client.get_object(&req).await.map_err(map_err) { Ok(_obj) => Ok(true), - Err(Error::Response(ErrorResponse { code: 404, .. })) => Ok(false), - Err(Error::HttpClient(err)) - if err.status().is_some_and(|v| v.as_u16() == 404) => - { - Ok(false) - } - - Err(err) => Err(err.into()), + Err(Error::NotFound) => Ok(false), + Err(err) => Err(err), } } - Err(err) => Err(anyhow::anyhow!("unable to resolve client: {}", err)), + Err(err) => Err(Error::Internal(anyhow::anyhow!( + "unable to resolve client: {}", + err + ))), } }) } @@ -239,7 +223,7 @@ impl objects::ObjectImpl for Object { self: Arc, data: Box, opts: objects::UploadOptions, - ) -> Pin> + Send>> { + ) -> Pin> + Send>> { Box::pin(async move { match self.bkt.client.get().await { Ok(client) => { @@ -260,11 +244,20 @@ impl objects::ObjectImpl for Object { .upload_streamed_object(&req, stream, &upload_type) .await { - Ok(_obj) => Ok(()), - Err(err) => Err(err.into()), + Ok(obj) => Ok(ObjectAttrs { + name: obj.name, + version: Some(obj.generation.to_string()), + size: obj.size as u64, + content_type: obj.content_type, + etag: obj.etag, + }), + Err(err) => Err(map_err(err)), } } - Err(err) => Err(anyhow::anyhow!("unable to resolve client: {}", err)), + Err(err) => Err(Error::Internal(anyhow::anyhow!( + "unable to resolve client: {}", + err + ))), } }) } @@ -322,7 +315,6 @@ impl objects::ObjectImpl for Object { Box::pin(async move { match self.bkt.client.get().await { Ok(client) => { - use gcs::http::{error::ErrorResponse, Error as GCSError}; let mut req = gcs::http::objects::delete::DeleteObjectRequest { bucket: self.bkt.name.to_string(), object: self.bkt.obj_name(Cow::Borrowed(&self.name)).into_owned(), @@ -333,16 +325,10 @@ impl objects::ObjectImpl for Object { req.generation = Some(parse_version(version)?); } - match client.delete_object(&req).await { + match client.delete_object(&req).await.map_err(map_err) { Ok(_) => Ok(()), - Err(GCSError::Response(ErrorResponse { code: 404, .. })) => Ok(()), - Err(GCSError::HttpClient(err)) - if err.status().is_some_and(|v| v.as_u16() == 404) => - { - Ok(()) - } - - Err(err) => Err(Error::Other(err.into())), + Err(Error::NotFound) => Ok(()), + Err(err) => Err(err), } } Err(err) => Err(Error::Internal(anyhow::anyhow!( @@ -374,3 +360,23 @@ fn parse_version(version: String) -> Result { )) }) } + +fn map_err(err: gcs::http::Error) -> Error { + use gcs::http::error::ErrorResponse; + match err { + gcs::http::Error::Response(ErrorResponse { code, .. }) => match code { + 404 => Error::NotFound, + 412 => Error::PreconditionFailed, + _ => Error::Other(err.into()), + }, + gcs::http::Error::HttpClient(err) => { + let status = err.status().map(|s| s.as_u16()); + match status { + Some(404) => Error::NotFound, + Some(412) => Error::PreconditionFailed, + _ => Error::Other(err.into()), + } + } + err => Error::Other(err.into()), + } +} diff --git a/runtimes/core/src/objects/mod.rs b/runtimes/core/src/objects/mod.rs index 3444f659e4..6db14076a2 100644 --- a/runtimes/core/src/objects/mod.rs +++ b/runtimes/core/src/objects/mod.rs @@ -34,13 +34,13 @@ trait ObjectImpl: Debug + Send + Sync { fn exists( self: Arc, version: Option, - ) -> Pin> + Send>>; + ) -> Pin> + Send>>; fn upload( self: Arc, data: Box, options: UploadOptions, - ) -> Pin> + Send>>; + ) -> Pin> + Send>>; fn download( self: Arc, @@ -84,7 +84,7 @@ pub struct Object { } impl Object { - pub async fn exists(&self, version: Option) -> anyhow::Result { + pub async fn exists(&self, version: Option) -> Result { self.imp.clone().exists(version).await } @@ -92,7 +92,7 @@ impl Object { &self, data: Box, options: UploadOptions, - ) -> impl Future> + Send + 'static { + ) -> impl Future> + Send + 'static { self.imp.clone().upload(data, options) } @@ -133,6 +133,9 @@ pub enum Error { #[error("object not found")] NotFound, + #[error("precondition failed")] + PreconditionFailed, + #[error("internal error: {0:?}")] Internal(anyhow::Error), @@ -144,7 +147,7 @@ pub type DownloadStream = Pin> + Send pub struct ObjectAttrs { pub name: String, - pub version: String, + pub version: Option, pub size: u64, pub content_type: Option, pub etag: String, diff --git a/runtimes/core/src/objects/noop/mod.rs b/runtimes/core/src/objects/noop/mod.rs index dd4d73a171..af830cc613 100644 --- a/runtimes/core/src/objects/noop/mod.rs +++ b/runtimes/core/src/objects/noop/mod.rs @@ -56,9 +56,9 @@ impl objects::ObjectImpl for Object { fn exists( self: Arc, _version: Option, - ) -> Pin> + Send>> { - Box::pin(future::ready(Err(anyhow::anyhow!( - "noop bucket does not support exists" + ) -> Pin> + Send>> { + Box::pin(future::ready(Err(objects::Error::Internal( + anyhow::anyhow!("noop bucket does not support exists"), )))) } @@ -66,10 +66,10 @@ impl objects::ObjectImpl for Object { self: Arc, _data: Box, _options: objects::UploadOptions, - ) -> Pin> + Send>> { - Box::pin(future::ready(Err(anyhow::anyhow!( + ) -> Pin> + Send>> { + Box::pin(future::ready(Err(objects::Error::Other(anyhow::anyhow!( "noop bucket does not support upload" - )))) + ))))) } fn download( diff --git a/runtimes/core/src/objects/s3/bucket.rs b/runtimes/core/src/objects/s3/bucket.rs index 0bbc586c38..567c4839e2 100644 --- a/runtimes/core/src/objects/s3/bucket.rs +++ b/runtimes/core/src/objects/s3/bucket.rs @@ -1,4 +1,3 @@ -use anyhow::Context; use async_stream::{stream, try_stream}; use aws_sdk_s3 as s3; use aws_sdk_s3::error::SdkError; @@ -148,10 +147,10 @@ impl objects::ObjectImpl for Object { match res { Ok(obj) => Ok(ObjectAttrs { name: self.cloud_name.clone(), - version: obj.version_id.unwrap_or_default(), + version: obj.version_id, size: obj.content_length.unwrap_or_default() as u64, content_type: obj.content_type, - etag: obj.e_tag.unwrap_or_default(), + etag: parse_etag(obj.e_tag), }), Err(SdkError::ServiceError(err)) if err.err().is_not_found() => { Err(Error::NotFound) @@ -164,7 +163,7 @@ impl objects::ObjectImpl for Object { fn exists( self: Arc, version: Option, - ) -> Pin> + Send>> { + ) -> Pin> + Send>> { Box::pin(async move { let client = self.bkt.client.get().await.clone(); let cloud_name = self.bkt.obj_name(Cow::Borrowed(&self.cloud_name)); @@ -178,7 +177,7 @@ impl objects::ObjectImpl for Object { match res { Ok(_) => Ok(true), Err(SdkError::ServiceError(err)) if err.err().is_not_found() => Ok(false), - Err(err) => Err(err.into()), + Err(err) => Err(Error::Other(err.into())), } }) } @@ -187,13 +186,13 @@ impl objects::ObjectImpl for Object { self: Arc, mut data: Box, options: objects::UploadOptions, - ) -> Pin> + Send>> { + ) -> Pin> + Send>> { Box::pin(async move { let client = self.bkt.client.get().await.clone(); let cloud_name = self.bkt.obj_name(Cow::Borrowed(&self.cloud_name)); - let first_chunk = read_chunk_async(&mut data) - .await - .context("unable to read from data source")?; + let first_chunk = read_chunk_async(&mut data).await.map_err(|e| { + Error::Other(anyhow::anyhow!("uable to read from data source: {}", e)) + })?; match first_chunk { Chunk::Complete(chunk) => { @@ -209,7 +208,7 @@ impl objects::ObjectImpl for Object { .key(cloud_name) .content_length(total_size as i64) .content_md5(content_md5) - .set_content_type(options.content_type) + .set_content_type(options.content_type.clone()) .body(ByteStream::from(chunk)); if let Some(precond) = options.preconditions { @@ -218,8 +217,14 @@ impl objects::ObjectImpl for Object { } } - let _ = req.send().await?; - Ok(()) + let resp = req.send().await.map_err(map_upload_err)?; + Ok(ObjectAttrs { + name: self.cloud_name.clone(), + version: resp.version_id, + size: total_size as u64, + content_type: options.content_type, + etag: resp.e_tag.unwrap_or_default(), + }) } Chunk::Part(chunk) => { @@ -228,14 +233,21 @@ impl objects::ObjectImpl for Object { .create_multipart_upload() .bucket(&self.bkt.name) .key(cloud_name.to_string()) - .set_content_type(options.content_type) + .set_content_type(options.content_type.clone()) .send() .await - .context("unable to begin streaming upload")?; - - let upload_id = upload - .upload_id - .context("missing upload_id in streaming upload")?; + .map_err(|err| { + Error::Other(anyhow::anyhow!( + "unable to begin streaming upload: {}", + err + )) + })?; + + let Some(upload_id) = upload.upload_id else { + return Err(Error::Other(anyhow::anyhow!( + "missing upload_id in streaming_upload" + ))); + }; let res = upload_multipart_chunks( &client, @@ -244,24 +256,40 @@ impl objects::ObjectImpl for Object { &upload_id, &self.bkt.name, &cloud_name, + &options, ) .await; - match res { - Ok(()) => Ok(()), - Err(err) => { - let fut = client - .abort_multipart_upload() - .bucket(&self.bkt.name) - .key(cloud_name) - .upload_id(upload_id) - .send(); - tokio::spawn(async move { - let _ = fut.await; - }); - return Err(err); - } + if let UploadMultipartResult::CompleteSuccess { total_size, output } = res { + return Ok(ObjectAttrs { + name: self.cloud_name.clone(), + version: output.version_id, + size: total_size, + content_type: options.content_type, + etag: parse_etag(output.e_tag), + }); } + + // Abort the upload. + let fut = client + .abort_multipart_upload() + .bucket(&self.bkt.name) + .key(cloud_name) + .upload_id(upload_id) + .send(); + tokio::spawn(async move { + let _ = fut.await; + }); + + Err(match res { + UploadMultipartResult::CompleteSuccess { .. } => unreachable!(), + UploadMultipartResult::UploadError(err) => Error::Other(err.into()), + UploadMultipartResult::CompleteError(err) => map_upload_err(err), + UploadMultipartResult::ReadContents(err) => Error::Other(anyhow::anyhow!( + "unable to read from data source: {}", + err + )), + }) } } }) @@ -359,6 +387,18 @@ async fn read_chunk_async(reader: &mut R) -> std: Ok(Chunk::Part(buf)) } +enum UploadMultipartResult { + CompleteSuccess { + total_size: u64, + output: s3::operation::complete_multipart_upload::CompleteMultipartUploadOutput, + }, + CompleteError( + s3::error::SdkError, + ), + UploadError(s3::error::SdkError), + ReadContents(std::io::Error), +} + async fn upload_multipart_chunks( client: &s3::Client, reader: &mut R, @@ -366,13 +406,16 @@ async fn upload_multipart_chunks( upload_id: &str, bucket: &str, key: &str, -) -> anyhow::Result<()> { + options: &objects::UploadOptions, +) -> UploadMultipartResult { let mut handles = Vec::new(); let mut part_number = 0; + let mut total_size = 0; let mut upload_part = |chunk: Bytes| { part_number += 1; let content_md5 = base64::engine::general_purpose::STANDARD.encode(md5::compute(&chunk).as_ref()); + total_size += chunk.len() as u64; let handle = client .upload_part() .bucket(bucket) @@ -388,7 +431,10 @@ async fn upload_multipart_chunks( upload_part(first_chunk); loop { - let bytes = read_chunk_async(reader).await?.into_bytes(); + let bytes = match read_chunk_async(reader).await { + Ok(chunk) => chunk.into_bytes(), + Err(err) => return UploadMultipartResult::ReadContents(err), + }; if bytes.is_empty() { break; } @@ -400,10 +446,28 @@ async fn upload_multipart_chunks( // Check for errors. for res in responses { - let _ = res?; + if let Err(err) = res { + return UploadMultipartResult::UploadError(err); + } } - Ok(()) + let mut req = client + .complete_multipart_upload() + .bucket(bucket) + .key(key) + .upload_id(upload_id); + + if let Some(precond) = &options.preconditions { + if precond.not_exists == Some(true) { + req = req.if_none_match("*"); + } + } + + let resp = req.send().await; + match resp { + Ok(output) => UploadMultipartResult::CompleteSuccess { total_size, output }, + Err(err) => UploadMultipartResult::CompleteError(err), + } } struct ObjectStream { @@ -428,3 +492,30 @@ impl Stream for ObjectStream { } } } + +fn parse_etag(s: Option) -> String { + match s { + Some(s) => { + if s.starts_with('"') && s.ends_with('"') { + s[1..s.len() - 1].to_string() + } else { + s + } + } + None => "".to_string(), + } +} + +fn map_upload_err(err: s3::error::SdkError) -> objects::Error +where + E: std::fmt::Debug, +{ + if err + .raw_response() + .is_some_and(|r| r.status().as_u16() == 412) + { + Error::PreconditionFailed + } else { + Error::Other(anyhow::anyhow!("failed to upload: {:?}", err)) + } +} diff --git a/runtimes/js/encore.dev/storage/objects/bucket.ts b/runtimes/js/encore.dev/storage/objects/bucket.ts index bd392a6703..f3b8235e45 100644 --- a/runtimes/js/encore.dev/storage/objects/bucket.ts +++ b/runtimes/js/encore.dev/storage/objects/bucket.ts @@ -58,7 +58,7 @@ export class Bucket { /** * Uploads an object to the bucket. */ - async upload(name: string, data: Buffer, options?: UploadOptions): Promise { + async upload(name: string, data: Buffer, options?: UploadOptions): Promise { const impl = this.impl.object(name); return impl.upload(data, options); } @@ -77,7 +77,7 @@ export class Bucket { */ async remove(name: string, options?: DeleteOptions): Promise { const impl = this.impl.object(name); - return impl.delete(options); + const _ = await impl.delete(options); } } @@ -125,7 +125,8 @@ export interface DownloadOptions { export interface ObjectAttrs { name: string; size: number; - version: string; + /** The version of the object, if bucket versioning is enabled. */ + version?: string; etag: string; contentType?: string; } diff --git a/runtimes/js/src/objects.rs b/runtimes/js/src/objects.rs index e9729d1a45..e23a99353e 100644 --- a/runtimes/js/src/objects.rs +++ b/runtimes/js/src/objects.rs @@ -55,10 +55,10 @@ impl BucketObject { #[napi] pub async fn exists(&self, version: Option) -> napi::Result { - self.obj.exists(version).await.map_err(napi::Error::from) + self.obj.exists(version).await.map_err(map_objects_err) } - #[napi(ts_return_type = "Promise")] + #[napi(ts_return_type = "Promise")] pub fn upload( &self, env: Env, @@ -79,7 +79,7 @@ impl BucketObject { env.execute_tokio_future(fut, move |&mut _env, result| { // TODO: Decrement the ref count on the data buffer. - result.map_err(napi::Error::from) + result.map(ObjectAttrs::from).map_err(map_objects_err) }) } @@ -94,17 +94,18 @@ impl BucketObject { Ok(buf.into()) } - #[napi(ts_return_type = "Promise")] - pub async fn delete(&self, options: Option) -> napi::Result<()> { + #[napi] + pub async fn delete(&self, options: Option) -> napi::Result { let options = options.unwrap_or_default().into(); - self.obj.delete(options).await.map_err(map_objects_err) + self.obj.delete(options).await.map_err(map_objects_err)?; + Ok(true) } } #[napi] pub struct ObjectAttrs { pub name: String, - pub version: String, + pub version: Option, pub size: i64, pub content_type: Option, pub etag: String, From b0318e33fa3b8a0c50423d07194dcb5f636674e2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Eriksson?= Date: Thu, 31 Oct 2024 16:58:57 +0100 Subject: [PATCH 16/37] Add object storage tracing --- pkg/traceparser/parser.go | 122 + proto/encore/engine/trace2/trace2.pb.go | 2584 ++++++++++++----- proto/encore/engine/trace2/trace2.proto | 81 + runtimes/core/src/names.rs | 37 + runtimes/core/src/objects/gcs/bucket.rs | 57 +- runtimes/core/src/objects/manager.rs | 2 +- runtimes/core/src/objects/mod.rs | 298 +- runtimes/core/src/objects/noop/mod.rs | 46 +- runtimes/core/src/objects/s3/bucket.rs | 68 +- runtimes/core/src/trace/protocol.rs | 361 ++- .../go/appruntime/exported/trace2/events.go | 79 +- .../js/encore.dev/storage/objects/bucket.ts | 37 +- runtimes/js/src/objects.rs | 103 +- .../src/parser/resources/infra/objects.rs | 8 +- 14 files changed, 3094 insertions(+), 789 deletions(-) diff --git a/pkg/traceparser/parser.go b/pkg/traceparser/parser.go index 2c7763590e..ecf9419fc6 100644 --- a/pkg/traceparser/parser.go +++ b/pkg/traceparser/parser.go @@ -233,6 +233,27 @@ func (tp *traceParser) spanEvent(eventType trace2.EventType) *tracepb2.SpanEvent ev.Data = &tracepb2.SpanEvent_CacheCallEnd{CacheCallEnd: tp.cacheCallEnd()} case trace2.BodyStream: ev.Data = &tracepb2.SpanEvent_BodyStream{BodyStream: tp.bodyStream()} + case trace2.BucketObjectUploadStart: + ev.Data = &tracepb2.SpanEvent_BucketObjectUploadStart{BucketObjectUploadStart: tp.bucketObjectUploadStart()} + case trace2.BucketObjectUploadEnd: + ev.Data = &tracepb2.SpanEvent_BucketObjectUploadEnd{BucketObjectUploadEnd: tp.bucketObjectUploadEnd()} + case trace2.BucketObjectDownloadStart: + ev.Data = &tracepb2.SpanEvent_BucketObjectDownloadStart{BucketObjectDownloadStart: tp.bucketObjectDownloadStart()} + case trace2.BucketObjectDownloadEnd: + ev.Data = &tracepb2.SpanEvent_BucketObjectDownloadEnd{BucketObjectDownloadEnd: tp.bucketObjectDownloadEnd()} + case trace2.BucketObjectGetAttrsStart: + ev.Data = &tracepb2.SpanEvent_BucketObjectGetAttrsStart{BucketObjectGetAttrsStart: tp.bucketObjectGetAttrsStart()} + case trace2.BucketObjectGetAttrsEnd: + ev.Data = &tracepb2.SpanEvent_BucketObjectGetAttrsEnd{BucketObjectGetAttrsEnd: tp.bucketObjectGetAttrsEnd()} + case trace2.BucketListObjectsStart: + ev.Data = &tracepb2.SpanEvent_BucketListObjectsStart{BucketListObjectsStart: tp.bucketListObjectsStart()} + case trace2.BucketListObjectsEnd: + ev.Data = &tracepb2.SpanEvent_BucketListObjectsEnd{BucketListObjectsEnd: tp.bucketListObjectsEnd()} + case trace2.BucketDeleteObjectsStart: + ev.Data = &tracepb2.SpanEvent_BucketDeleteObjectsStart{BucketDeleteObjectsStart: tp.bucketDeleteObjectsStart()} + case trace2.BucketDeleteObjectsEnd: + ev.Data = &tracepb2.SpanEvent_BucketDeleteObjectsEnd{BucketDeleteObjectsEnd: tp.bucketDeleteObjectsEnd()} + default: tp.bailout(fmt.Errorf("unknown event %v", eventType)) } @@ -559,6 +580,107 @@ func (tp *traceParser) cacheCallEnd() *tracepb2.CacheCallEnd { } } +func (tp *traceParser) bucketObjectUploadStart() *tracepb2.BucketObjectUploadStart { + return &tracepb2.BucketObjectUploadStart{ + Bucket: tp.String(), + Object: tp.String(), + Attrs: tp.bucketObjectAttrs(), + Stack: tp.stack(), + } +} + +func (tp *traceParser) bucketObjectAttrs() *tracepb2.BucketObjectAttributes { + return &tracepb2.BucketObjectAttributes{ + Size: ptrOrNil(tp.Uint64()), + Version: ptrOrNil(tp.String()), + Etag: ptrOrNil(tp.String()), + ContentType: ptrOrNil(tp.String()), + } +} + +func (tp *traceParser) bucketObjectUploadEnd() *tracepb2.BucketObjectUploadEnd { + return &tracepb2.BucketObjectUploadEnd{ + Size: ptrOrNil(tp.Uint64()), + Err: tp.errWithStack(), + } +} + +func (tp *traceParser) bucketObjectDownloadStart() *tracepb2.BucketObjectDownloadStart { + return &tracepb2.BucketObjectDownloadStart{ + Bucket: tp.String(), + Object: tp.String(), + Version: ptrOrNil(tp.String()), + Stack: tp.stack(), + } +} + +func (tp *traceParser) bucketObjectDownloadEnd() *tracepb2.BucketObjectDownloadEnd { + return &tracepb2.BucketObjectDownloadEnd{ + Size: ptrOrNil(tp.Uint64()), + Err: tp.errWithStack(), + } +} + +func (tp *traceParser) bucketDeleteObjectsStart() *tracepb2.BucketDeleteObjectsStart { + ev := &tracepb2.BucketDeleteObjectsStart{ + Bucket: tp.String(), + Stack: tp.stack(), + } + + num := tp.UVarint() + for i := 0; i < int(num); i++ { + ev.Entries = append(ev.Entries, &tracepb2.BucketDeleteObjectEntry{ + Object: tp.String(), + Version: ptrOrNil(tp.String()), + }) + } + + return ev +} + +func (tp *traceParser) bucketDeleteObjectsEnd() *tracepb2.BucketDeleteObjectsEnd { + return &tracepb2.BucketDeleteObjectsEnd{ + Err: tp.errWithStack(), + } +} + +func (tp *traceParser) bucketListObjectsStart() *tracepb2.BucketListObjectsStart { + return &tracepb2.BucketListObjectsStart{ + Bucket: tp.String(), + Prefix: ptrOrNil(tp.String()), + Stack: tp.stack(), + } +} + +func (tp *traceParser) bucketListObjectsEnd() *tracepb2.BucketListObjectsEnd { + return &tracepb2.BucketListObjectsEnd{ + Err: tp.errWithStack(), + Observed: tp.UVarint(), + HasMore: tp.Bool(), + } +} + +func (tp *traceParser) bucketObjectGetAttrsStart() *tracepb2.BucketObjectGetAttrsStart { + return &tracepb2.BucketObjectGetAttrsStart{ + Bucket: tp.String(), + Object: tp.String(), + Version: ptrOrNil(tp.String()), + Stack: tp.stack(), + } +} + +func (tp *traceParser) bucketObjectGetAttrsEnd() *tracepb2.BucketObjectGetAttrsEnd { + ev := &tracepb2.BucketObjectGetAttrsEnd{ + Err: tp.errWithStack(), + } + + if ev.Err == nil { + ev.Attrs = tp.bucketObjectAttrs() + } + + return ev +} + func (tp *traceParser) bodyStream() *tracepb2.BodyStream { flags := tp.Byte() data := tp.ByteString() diff --git a/proto/encore/engine/trace2/trace2.pb.go b/proto/encore/engine/trace2/trace2.pb.go index 3ce477a607..a4d72c671a 100644 --- a/proto/encore/engine/trace2/trace2.pb.go +++ b/proto/encore/engine/trace2/trace2.pb.go @@ -318,7 +318,7 @@ func (x LogMessage_Level) Number() protoreflect.EnumNumber { // Deprecated: Use LogMessage_Level.Descriptor instead. func (LogMessage_Level) EnumDescriptor() ([]byte, []int) { - return file_encore_engine_trace2_trace2_proto_rawDescGZIP(), []int{48, 0} + return file_encore_engine_trace2_trace2_proto_rawDescGZIP(), []int{60, 0} } // SpanSummary summarizes a span for display purposes. @@ -1700,6 +1700,16 @@ type SpanEvent struct { // *SpanEvent_CacheCallEnd // *SpanEvent_ServiceInitStart // *SpanEvent_ServiceInitEnd + // *SpanEvent_BucketObjectUploadStart + // *SpanEvent_BucketObjectUploadEnd + // *SpanEvent_BucketObjectDownloadStart + // *SpanEvent_BucketObjectDownloadEnd + // *SpanEvent_BucketObjectGetAttrsStart + // *SpanEvent_BucketObjectGetAttrsEnd + // *SpanEvent_BucketListObjectsStart + // *SpanEvent_BucketListObjectsEnd + // *SpanEvent_BucketDeleteObjectsStart + // *SpanEvent_BucketDeleteObjectsEnd Data isSpanEvent_Data `protobuf_oneof:"data"` } @@ -1875,6 +1885,76 @@ func (x *SpanEvent) GetServiceInitEnd() *ServiceInitEnd { return nil } +func (x *SpanEvent) GetBucketObjectUploadStart() *BucketObjectUploadStart { + if x, ok := x.GetData().(*SpanEvent_BucketObjectUploadStart); ok { + return x.BucketObjectUploadStart + } + return nil +} + +func (x *SpanEvent) GetBucketObjectUploadEnd() *BucketObjectUploadEnd { + if x, ok := x.GetData().(*SpanEvent_BucketObjectUploadEnd); ok { + return x.BucketObjectUploadEnd + } + return nil +} + +func (x *SpanEvent) GetBucketObjectDownloadStart() *BucketObjectDownloadStart { + if x, ok := x.GetData().(*SpanEvent_BucketObjectDownloadStart); ok { + return x.BucketObjectDownloadStart + } + return nil +} + +func (x *SpanEvent) GetBucketObjectDownloadEnd() *BucketObjectDownloadEnd { + if x, ok := x.GetData().(*SpanEvent_BucketObjectDownloadEnd); ok { + return x.BucketObjectDownloadEnd + } + return nil +} + +func (x *SpanEvent) GetBucketObjectGetAttrsStart() *BucketObjectGetAttrsStart { + if x, ok := x.GetData().(*SpanEvent_BucketObjectGetAttrsStart); ok { + return x.BucketObjectGetAttrsStart + } + return nil +} + +func (x *SpanEvent) GetBucketObjectGetAttrsEnd() *BucketObjectGetAttrsEnd { + if x, ok := x.GetData().(*SpanEvent_BucketObjectGetAttrsEnd); ok { + return x.BucketObjectGetAttrsEnd + } + return nil +} + +func (x *SpanEvent) GetBucketListObjectsStart() *BucketListObjectsStart { + if x, ok := x.GetData().(*SpanEvent_BucketListObjectsStart); ok { + return x.BucketListObjectsStart + } + return nil +} + +func (x *SpanEvent) GetBucketListObjectsEnd() *BucketListObjectsEnd { + if x, ok := x.GetData().(*SpanEvent_BucketListObjectsEnd); ok { + return x.BucketListObjectsEnd + } + return nil +} + +func (x *SpanEvent) GetBucketDeleteObjectsStart() *BucketDeleteObjectsStart { + if x, ok := x.GetData().(*SpanEvent_BucketDeleteObjectsStart); ok { + return x.BucketDeleteObjectsStart + } + return nil +} + +func (x *SpanEvent) GetBucketDeleteObjectsEnd() *BucketDeleteObjectsEnd { + if x, ok := x.GetData().(*SpanEvent_BucketDeleteObjectsEnd); ok { + return x.BucketDeleteObjectsEnd + } + return nil +} + type isSpanEvent_Data interface { isSpanEvent_Data() } @@ -1943,6 +2023,46 @@ type SpanEvent_ServiceInitEnd struct { ServiceInitEnd *ServiceInitEnd `protobuf:"bytes,25,opt,name=service_init_end,json=serviceInitEnd,proto3,oneof"` } +type SpanEvent_BucketObjectUploadStart struct { + BucketObjectUploadStart *BucketObjectUploadStart `protobuf:"bytes,26,opt,name=bucket_object_upload_start,json=bucketObjectUploadStart,proto3,oneof"` +} + +type SpanEvent_BucketObjectUploadEnd struct { + BucketObjectUploadEnd *BucketObjectUploadEnd `protobuf:"bytes,27,opt,name=bucket_object_upload_end,json=bucketObjectUploadEnd,proto3,oneof"` +} + +type SpanEvent_BucketObjectDownloadStart struct { + BucketObjectDownloadStart *BucketObjectDownloadStart `protobuf:"bytes,28,opt,name=bucket_object_download_start,json=bucketObjectDownloadStart,proto3,oneof"` +} + +type SpanEvent_BucketObjectDownloadEnd struct { + BucketObjectDownloadEnd *BucketObjectDownloadEnd `protobuf:"bytes,29,opt,name=bucket_object_download_end,json=bucketObjectDownloadEnd,proto3,oneof"` +} + +type SpanEvent_BucketObjectGetAttrsStart struct { + BucketObjectGetAttrsStart *BucketObjectGetAttrsStart `protobuf:"bytes,30,opt,name=bucket_object_get_attrs_start,json=bucketObjectGetAttrsStart,proto3,oneof"` +} + +type SpanEvent_BucketObjectGetAttrsEnd struct { + BucketObjectGetAttrsEnd *BucketObjectGetAttrsEnd `protobuf:"bytes,31,opt,name=bucket_object_get_attrs_end,json=bucketObjectGetAttrsEnd,proto3,oneof"` +} + +type SpanEvent_BucketListObjectsStart struct { + BucketListObjectsStart *BucketListObjectsStart `protobuf:"bytes,32,opt,name=bucket_list_objects_start,json=bucketListObjectsStart,proto3,oneof"` +} + +type SpanEvent_BucketListObjectsEnd struct { + BucketListObjectsEnd *BucketListObjectsEnd `protobuf:"bytes,33,opt,name=bucket_list_objects_end,json=bucketListObjectsEnd,proto3,oneof"` +} + +type SpanEvent_BucketDeleteObjectsStart struct { + BucketDeleteObjectsStart *BucketDeleteObjectsStart `protobuf:"bytes,34,opt,name=bucket_delete_objects_start,json=bucketDeleteObjectsStart,proto3,oneof"` +} + +type SpanEvent_BucketDeleteObjectsEnd struct { + BucketDeleteObjectsEnd *BucketDeleteObjectsEnd `protobuf:"bytes,35,opt,name=bucket_delete_objects_end,json=bucketDeleteObjectsEnd,proto3,oneof"` +} + func (*SpanEvent_LogMessage) isSpanEvent_Data() {} func (*SpanEvent_BodyStream) isSpanEvent_Data() {} @@ -1975,6 +2095,26 @@ func (*SpanEvent_ServiceInitStart) isSpanEvent_Data() {} func (*SpanEvent_ServiceInitEnd) isSpanEvent_Data() {} +func (*SpanEvent_BucketObjectUploadStart) isSpanEvent_Data() {} + +func (*SpanEvent_BucketObjectUploadEnd) isSpanEvent_Data() {} + +func (*SpanEvent_BucketObjectDownloadStart) isSpanEvent_Data() {} + +func (*SpanEvent_BucketObjectDownloadEnd) isSpanEvent_Data() {} + +func (*SpanEvent_BucketObjectGetAttrsStart) isSpanEvent_Data() {} + +func (*SpanEvent_BucketObjectGetAttrsEnd) isSpanEvent_Data() {} + +func (*SpanEvent_BucketListObjectsStart) isSpanEvent_Data() {} + +func (*SpanEvent_BucketListObjectsEnd) isSpanEvent_Data() {} + +func (*SpanEvent_BucketDeleteObjectsStart) isSpanEvent_Data() {} + +func (*SpanEvent_BucketDeleteObjectsEnd) isSpanEvent_Data() {} + type RPCCallStart struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -2398,8 +2538,724 @@ func (x *PubsubPublishStart) String() string { func (*PubsubPublishStart) ProtoMessage() {} -func (x *PubsubPublishStart) ProtoReflect() protoreflect.Message { - mi := &file_encore_engine_trace2_trace2_proto_msgTypes[23] +func (x *PubsubPublishStart) ProtoReflect() protoreflect.Message { + mi := &file_encore_engine_trace2_trace2_proto_msgTypes[23] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use PubsubPublishStart.ProtoReflect.Descriptor instead. +func (*PubsubPublishStart) Descriptor() ([]byte, []int) { + return file_encore_engine_trace2_trace2_proto_rawDescGZIP(), []int{23} +} + +func (x *PubsubPublishStart) GetTopic() string { + if x != nil { + return x.Topic + } + return "" +} + +func (x *PubsubPublishStart) GetMessage() []byte { + if x != nil { + return x.Message + } + return nil +} + +func (x *PubsubPublishStart) GetStack() *StackTrace { + if x != nil { + return x.Stack + } + return nil +} + +type PubsubPublishEnd struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + MessageId *string `protobuf:"bytes,1,opt,name=message_id,json=messageId,proto3,oneof" json:"message_id,omitempty"` + Err *Error `protobuf:"bytes,2,opt,name=err,proto3,oneof" json:"err,omitempty"` +} + +func (x *PubsubPublishEnd) Reset() { + *x = PubsubPublishEnd{} + if protoimpl.UnsafeEnabled { + mi := &file_encore_engine_trace2_trace2_proto_msgTypes[24] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *PubsubPublishEnd) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*PubsubPublishEnd) ProtoMessage() {} + +func (x *PubsubPublishEnd) ProtoReflect() protoreflect.Message { + mi := &file_encore_engine_trace2_trace2_proto_msgTypes[24] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use PubsubPublishEnd.ProtoReflect.Descriptor instead. +func (*PubsubPublishEnd) Descriptor() ([]byte, []int) { + return file_encore_engine_trace2_trace2_proto_rawDescGZIP(), []int{24} +} + +func (x *PubsubPublishEnd) GetMessageId() string { + if x != nil && x.MessageId != nil { + return *x.MessageId + } + return "" +} + +func (x *PubsubPublishEnd) GetErr() *Error { + if x != nil { + return x.Err + } + return nil +} + +type ServiceInitStart struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Service string `protobuf:"bytes,1,opt,name=service,proto3" json:"service,omitempty"` +} + +func (x *ServiceInitStart) Reset() { + *x = ServiceInitStart{} + if protoimpl.UnsafeEnabled { + mi := &file_encore_engine_trace2_trace2_proto_msgTypes[25] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ServiceInitStart) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ServiceInitStart) ProtoMessage() {} + +func (x *ServiceInitStart) ProtoReflect() protoreflect.Message { + mi := &file_encore_engine_trace2_trace2_proto_msgTypes[25] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ServiceInitStart.ProtoReflect.Descriptor instead. +func (*ServiceInitStart) Descriptor() ([]byte, []int) { + return file_encore_engine_trace2_trace2_proto_rawDescGZIP(), []int{25} +} + +func (x *ServiceInitStart) GetService() string { + if x != nil { + return x.Service + } + return "" +} + +type ServiceInitEnd struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Err *Error `protobuf:"bytes,1,opt,name=err,proto3,oneof" json:"err,omitempty"` +} + +func (x *ServiceInitEnd) Reset() { + *x = ServiceInitEnd{} + if protoimpl.UnsafeEnabled { + mi := &file_encore_engine_trace2_trace2_proto_msgTypes[26] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ServiceInitEnd) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ServiceInitEnd) ProtoMessage() {} + +func (x *ServiceInitEnd) ProtoReflect() protoreflect.Message { + mi := &file_encore_engine_trace2_trace2_proto_msgTypes[26] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ServiceInitEnd.ProtoReflect.Descriptor instead. +func (*ServiceInitEnd) Descriptor() ([]byte, []int) { + return file_encore_engine_trace2_trace2_proto_rawDescGZIP(), []int{26} +} + +func (x *ServiceInitEnd) GetErr() *Error { + if x != nil { + return x.Err + } + return nil +} + +type CacheCallStart struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Operation string `protobuf:"bytes,1,opt,name=operation,proto3" json:"operation,omitempty"` + Keys []string `protobuf:"bytes,2,rep,name=keys,proto3" json:"keys,omitempty"` + Write bool `protobuf:"varint,3,opt,name=write,proto3" json:"write,omitempty"` + Stack *StackTrace `protobuf:"bytes,4,opt,name=stack,proto3" json:"stack,omitempty"` // TODO include more info (like inputs) +} + +func (x *CacheCallStart) Reset() { + *x = CacheCallStart{} + if protoimpl.UnsafeEnabled { + mi := &file_encore_engine_trace2_trace2_proto_msgTypes[27] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *CacheCallStart) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*CacheCallStart) ProtoMessage() {} + +func (x *CacheCallStart) ProtoReflect() protoreflect.Message { + mi := &file_encore_engine_trace2_trace2_proto_msgTypes[27] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use CacheCallStart.ProtoReflect.Descriptor instead. +func (*CacheCallStart) Descriptor() ([]byte, []int) { + return file_encore_engine_trace2_trace2_proto_rawDescGZIP(), []int{27} +} + +func (x *CacheCallStart) GetOperation() string { + if x != nil { + return x.Operation + } + return "" +} + +func (x *CacheCallStart) GetKeys() []string { + if x != nil { + return x.Keys + } + return nil +} + +func (x *CacheCallStart) GetWrite() bool { + if x != nil { + return x.Write + } + return false +} + +func (x *CacheCallStart) GetStack() *StackTrace { + if x != nil { + return x.Stack + } + return nil +} + +type CacheCallEnd struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Result CacheCallEnd_Result `protobuf:"varint,1,opt,name=result,proto3,enum=encore.engine.trace2.CacheCallEnd_Result" json:"result,omitempty"` + Err *Error `protobuf:"bytes,2,opt,name=err,proto3,oneof" json:"err,omitempty"` // TODO include more info (like outputs) +} + +func (x *CacheCallEnd) Reset() { + *x = CacheCallEnd{} + if protoimpl.UnsafeEnabled { + mi := &file_encore_engine_trace2_trace2_proto_msgTypes[28] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *CacheCallEnd) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*CacheCallEnd) ProtoMessage() {} + +func (x *CacheCallEnd) ProtoReflect() protoreflect.Message { + mi := &file_encore_engine_trace2_trace2_proto_msgTypes[28] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use CacheCallEnd.ProtoReflect.Descriptor instead. +func (*CacheCallEnd) Descriptor() ([]byte, []int) { + return file_encore_engine_trace2_trace2_proto_rawDescGZIP(), []int{28} +} + +func (x *CacheCallEnd) GetResult() CacheCallEnd_Result { + if x != nil { + return x.Result + } + return CacheCallEnd_UNKNOWN +} + +func (x *CacheCallEnd) GetErr() *Error { + if x != nil { + return x.Err + } + return nil +} + +type BucketObjectUploadStart struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Bucket string `protobuf:"bytes,1,opt,name=bucket,proto3" json:"bucket,omitempty"` + Object string `protobuf:"bytes,2,opt,name=object,proto3" json:"object,omitempty"` + Attrs *BucketObjectAttributes `protobuf:"bytes,3,opt,name=attrs,proto3" json:"attrs,omitempty"` + Stack *StackTrace `protobuf:"bytes,4,opt,name=stack,proto3" json:"stack,omitempty"` +} + +func (x *BucketObjectUploadStart) Reset() { + *x = BucketObjectUploadStart{} + if protoimpl.UnsafeEnabled { + mi := &file_encore_engine_trace2_trace2_proto_msgTypes[29] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *BucketObjectUploadStart) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*BucketObjectUploadStart) ProtoMessage() {} + +func (x *BucketObjectUploadStart) ProtoReflect() protoreflect.Message { + mi := &file_encore_engine_trace2_trace2_proto_msgTypes[29] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use BucketObjectUploadStart.ProtoReflect.Descriptor instead. +func (*BucketObjectUploadStart) Descriptor() ([]byte, []int) { + return file_encore_engine_trace2_trace2_proto_rawDescGZIP(), []int{29} +} + +func (x *BucketObjectUploadStart) GetBucket() string { + if x != nil { + return x.Bucket + } + return "" +} + +func (x *BucketObjectUploadStart) GetObject() string { + if x != nil { + return x.Object + } + return "" +} + +func (x *BucketObjectUploadStart) GetAttrs() *BucketObjectAttributes { + if x != nil { + return x.Attrs + } + return nil +} + +func (x *BucketObjectUploadStart) GetStack() *StackTrace { + if x != nil { + return x.Stack + } + return nil +} + +type BucketObjectUploadEnd struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Err *Error `protobuf:"bytes,1,opt,name=err,proto3,oneof" json:"err,omitempty"` + Size *uint64 `protobuf:"varint,2,opt,name=size,proto3,oneof" json:"size,omitempty"` +} + +func (x *BucketObjectUploadEnd) Reset() { + *x = BucketObjectUploadEnd{} + if protoimpl.UnsafeEnabled { + mi := &file_encore_engine_trace2_trace2_proto_msgTypes[30] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *BucketObjectUploadEnd) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*BucketObjectUploadEnd) ProtoMessage() {} + +func (x *BucketObjectUploadEnd) ProtoReflect() protoreflect.Message { + mi := &file_encore_engine_trace2_trace2_proto_msgTypes[30] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use BucketObjectUploadEnd.ProtoReflect.Descriptor instead. +func (*BucketObjectUploadEnd) Descriptor() ([]byte, []int) { + return file_encore_engine_trace2_trace2_proto_rawDescGZIP(), []int{30} +} + +func (x *BucketObjectUploadEnd) GetErr() *Error { + if x != nil { + return x.Err + } + return nil +} + +func (x *BucketObjectUploadEnd) GetSize() uint64 { + if x != nil && x.Size != nil { + return *x.Size + } + return 0 +} + +type BucketObjectDownloadStart struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Bucket string `protobuf:"bytes,1,opt,name=bucket,proto3" json:"bucket,omitempty"` + Object string `protobuf:"bytes,2,opt,name=object,proto3" json:"object,omitempty"` + Version *string `protobuf:"bytes,3,opt,name=version,proto3,oneof" json:"version,omitempty"` + Stack *StackTrace `protobuf:"bytes,4,opt,name=stack,proto3" json:"stack,omitempty"` +} + +func (x *BucketObjectDownloadStart) Reset() { + *x = BucketObjectDownloadStart{} + if protoimpl.UnsafeEnabled { + mi := &file_encore_engine_trace2_trace2_proto_msgTypes[31] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *BucketObjectDownloadStart) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*BucketObjectDownloadStart) ProtoMessage() {} + +func (x *BucketObjectDownloadStart) ProtoReflect() protoreflect.Message { + mi := &file_encore_engine_trace2_trace2_proto_msgTypes[31] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use BucketObjectDownloadStart.ProtoReflect.Descriptor instead. +func (*BucketObjectDownloadStart) Descriptor() ([]byte, []int) { + return file_encore_engine_trace2_trace2_proto_rawDescGZIP(), []int{31} +} + +func (x *BucketObjectDownloadStart) GetBucket() string { + if x != nil { + return x.Bucket + } + return "" +} + +func (x *BucketObjectDownloadStart) GetObject() string { + if x != nil { + return x.Object + } + return "" +} + +func (x *BucketObjectDownloadStart) GetVersion() string { + if x != nil && x.Version != nil { + return *x.Version + } + return "" +} + +func (x *BucketObjectDownloadStart) GetStack() *StackTrace { + if x != nil { + return x.Stack + } + return nil +} + +type BucketObjectDownloadEnd struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Err *Error `protobuf:"bytes,1,opt,name=err,proto3,oneof" json:"err,omitempty"` + Size *uint64 `protobuf:"varint,2,opt,name=size,proto3,oneof" json:"size,omitempty"` +} + +func (x *BucketObjectDownloadEnd) Reset() { + *x = BucketObjectDownloadEnd{} + if protoimpl.UnsafeEnabled { + mi := &file_encore_engine_trace2_trace2_proto_msgTypes[32] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *BucketObjectDownloadEnd) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*BucketObjectDownloadEnd) ProtoMessage() {} + +func (x *BucketObjectDownloadEnd) ProtoReflect() protoreflect.Message { + mi := &file_encore_engine_trace2_trace2_proto_msgTypes[32] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use BucketObjectDownloadEnd.ProtoReflect.Descriptor instead. +func (*BucketObjectDownloadEnd) Descriptor() ([]byte, []int) { + return file_encore_engine_trace2_trace2_proto_rawDescGZIP(), []int{32} +} + +func (x *BucketObjectDownloadEnd) GetErr() *Error { + if x != nil { + return x.Err + } + return nil +} + +func (x *BucketObjectDownloadEnd) GetSize() uint64 { + if x != nil && x.Size != nil { + return *x.Size + } + return 0 +} + +type BucketObjectGetAttrsStart struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Bucket string `protobuf:"bytes,1,opt,name=bucket,proto3" json:"bucket,omitempty"` + Object string `protobuf:"bytes,2,opt,name=object,proto3" json:"object,omitempty"` + Version *string `protobuf:"bytes,3,opt,name=version,proto3,oneof" json:"version,omitempty"` + Stack *StackTrace `protobuf:"bytes,4,opt,name=stack,proto3" json:"stack,omitempty"` +} + +func (x *BucketObjectGetAttrsStart) Reset() { + *x = BucketObjectGetAttrsStart{} + if protoimpl.UnsafeEnabled { + mi := &file_encore_engine_trace2_trace2_proto_msgTypes[33] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *BucketObjectGetAttrsStart) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*BucketObjectGetAttrsStart) ProtoMessage() {} + +func (x *BucketObjectGetAttrsStart) ProtoReflect() protoreflect.Message { + mi := &file_encore_engine_trace2_trace2_proto_msgTypes[33] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use BucketObjectGetAttrsStart.ProtoReflect.Descriptor instead. +func (*BucketObjectGetAttrsStart) Descriptor() ([]byte, []int) { + return file_encore_engine_trace2_trace2_proto_rawDescGZIP(), []int{33} +} + +func (x *BucketObjectGetAttrsStart) GetBucket() string { + if x != nil { + return x.Bucket + } + return "" +} + +func (x *BucketObjectGetAttrsStart) GetObject() string { + if x != nil { + return x.Object + } + return "" +} + +func (x *BucketObjectGetAttrsStart) GetVersion() string { + if x != nil && x.Version != nil { + return *x.Version + } + return "" +} + +func (x *BucketObjectGetAttrsStart) GetStack() *StackTrace { + if x != nil { + return x.Stack + } + return nil +} + +type BucketObjectGetAttrsEnd struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Err *Error `protobuf:"bytes,1,opt,name=err,proto3,oneof" json:"err,omitempty"` + Attrs *BucketObjectAttributes `protobuf:"bytes,2,opt,name=attrs,proto3,oneof" json:"attrs,omitempty"` +} + +func (x *BucketObjectGetAttrsEnd) Reset() { + *x = BucketObjectGetAttrsEnd{} + if protoimpl.UnsafeEnabled { + mi := &file_encore_engine_trace2_trace2_proto_msgTypes[34] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *BucketObjectGetAttrsEnd) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*BucketObjectGetAttrsEnd) ProtoMessage() {} + +func (x *BucketObjectGetAttrsEnd) ProtoReflect() protoreflect.Message { + mi := &file_encore_engine_trace2_trace2_proto_msgTypes[34] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use BucketObjectGetAttrsEnd.ProtoReflect.Descriptor instead. +func (*BucketObjectGetAttrsEnd) Descriptor() ([]byte, []int) { + return file_encore_engine_trace2_trace2_proto_rawDescGZIP(), []int{34} +} + +func (x *BucketObjectGetAttrsEnd) GetErr() *Error { + if x != nil { + return x.Err + } + return nil +} + +func (x *BucketObjectGetAttrsEnd) GetAttrs() *BucketObjectAttributes { + if x != nil { + return x.Attrs + } + return nil +} + +type BucketListObjectsStart struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Bucket string `protobuf:"bytes,1,opt,name=bucket,proto3" json:"bucket,omitempty"` + Prefix *string `protobuf:"bytes,2,opt,name=prefix,proto3,oneof" json:"prefix,omitempty"` + Stack *StackTrace `protobuf:"bytes,3,opt,name=stack,proto3" json:"stack,omitempty"` +} + +func (x *BucketListObjectsStart) Reset() { + *x = BucketListObjectsStart{} + if protoimpl.UnsafeEnabled { + mi := &file_encore_engine_trace2_trace2_proto_msgTypes[35] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *BucketListObjectsStart) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*BucketListObjectsStart) ProtoMessage() {} + +func (x *BucketListObjectsStart) ProtoReflect() protoreflect.Message { + mi := &file_encore_engine_trace2_trace2_proto_msgTypes[35] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2410,58 +3266,59 @@ func (x *PubsubPublishStart) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use PubsubPublishStart.ProtoReflect.Descriptor instead. -func (*PubsubPublishStart) Descriptor() ([]byte, []int) { - return file_encore_engine_trace2_trace2_proto_rawDescGZIP(), []int{23} +// Deprecated: Use BucketListObjectsStart.ProtoReflect.Descriptor instead. +func (*BucketListObjectsStart) Descriptor() ([]byte, []int) { + return file_encore_engine_trace2_trace2_proto_rawDescGZIP(), []int{35} } -func (x *PubsubPublishStart) GetTopic() string { +func (x *BucketListObjectsStart) GetBucket() string { if x != nil { - return x.Topic + return x.Bucket } return "" } -func (x *PubsubPublishStart) GetMessage() []byte { - if x != nil { - return x.Message +func (x *BucketListObjectsStart) GetPrefix() string { + if x != nil && x.Prefix != nil { + return *x.Prefix } - return nil + return "" } -func (x *PubsubPublishStart) GetStack() *StackTrace { +func (x *BucketListObjectsStart) GetStack() *StackTrace { if x != nil { return x.Stack } return nil } -type PubsubPublishEnd struct { +type BucketListObjectsEnd struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - MessageId *string `protobuf:"bytes,1,opt,name=message_id,json=messageId,proto3,oneof" json:"message_id,omitempty"` - Err *Error `protobuf:"bytes,2,opt,name=err,proto3,oneof" json:"err,omitempty"` + Err *Error `protobuf:"bytes,1,opt,name=err,proto3,oneof" json:"err,omitempty"` + Observed uint64 `protobuf:"varint,2,opt,name=observed,proto3" json:"observed,omitempty"` + HasMore bool `protobuf:"varint,3,opt,name=has_more,json=hasMore,proto3" json:"has_more,omitempty"` } -func (x *PubsubPublishEnd) Reset() { - *x = PubsubPublishEnd{} +func (x *BucketListObjectsEnd) Reset() { + *x = BucketListObjectsEnd{} if protoimpl.UnsafeEnabled { - mi := &file_encore_engine_trace2_trace2_proto_msgTypes[24] + mi := &file_encore_engine_trace2_trace2_proto_msgTypes[36] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } -func (x *PubsubPublishEnd) String() string { +func (x *BucketListObjectsEnd) String() string { return protoimpl.X.MessageStringOf(x) } -func (*PubsubPublishEnd) ProtoMessage() {} +func (*BucketListObjectsEnd) ProtoMessage() {} -func (x *PubsubPublishEnd) ProtoReflect() protoreflect.Message { - mi := &file_encore_engine_trace2_trace2_proto_msgTypes[24] +func (x *BucketListObjectsEnd) ProtoReflect() protoreflect.Message { + mi := &file_encore_engine_trace2_trace2_proto_msgTypes[36] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2472,50 +3329,59 @@ func (x *PubsubPublishEnd) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use PubsubPublishEnd.ProtoReflect.Descriptor instead. -func (*PubsubPublishEnd) Descriptor() ([]byte, []int) { - return file_encore_engine_trace2_trace2_proto_rawDescGZIP(), []int{24} +// Deprecated: Use BucketListObjectsEnd.ProtoReflect.Descriptor instead. +func (*BucketListObjectsEnd) Descriptor() ([]byte, []int) { + return file_encore_engine_trace2_trace2_proto_rawDescGZIP(), []int{36} } -func (x *PubsubPublishEnd) GetMessageId() string { - if x != nil && x.MessageId != nil { - return *x.MessageId +func (x *BucketListObjectsEnd) GetErr() *Error { + if x != nil { + return x.Err } - return "" + return nil } -func (x *PubsubPublishEnd) GetErr() *Error { +func (x *BucketListObjectsEnd) GetObserved() uint64 { if x != nil { - return x.Err + return x.Observed } - return nil + return 0 } -type ServiceInitStart struct { +func (x *BucketListObjectsEnd) GetHasMore() bool { + if x != nil { + return x.HasMore + } + return false +} + +type BucketDeleteObjectsStart struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - Service string `protobuf:"bytes,1,opt,name=service,proto3" json:"service,omitempty"` + Bucket string `protobuf:"bytes,1,opt,name=bucket,proto3" json:"bucket,omitempty"` + Stack *StackTrace `protobuf:"bytes,2,opt,name=stack,proto3" json:"stack,omitempty"` + Entries []*BucketDeleteObjectEntry `protobuf:"bytes,3,rep,name=entries,proto3" json:"entries,omitempty"` } -func (x *ServiceInitStart) Reset() { - *x = ServiceInitStart{} +func (x *BucketDeleteObjectsStart) Reset() { + *x = BucketDeleteObjectsStart{} if protoimpl.UnsafeEnabled { - mi := &file_encore_engine_trace2_trace2_proto_msgTypes[25] + mi := &file_encore_engine_trace2_trace2_proto_msgTypes[37] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } -func (x *ServiceInitStart) String() string { +func (x *BucketDeleteObjectsStart) String() string { return protoimpl.X.MessageStringOf(x) } -func (*ServiceInitStart) ProtoMessage() {} +func (*BucketDeleteObjectsStart) ProtoMessage() {} -func (x *ServiceInitStart) ProtoReflect() protoreflect.Message { - mi := &file_encore_engine_trace2_trace2_proto_msgTypes[25] +func (x *BucketDeleteObjectsStart) ProtoReflect() protoreflect.Message { + mi := &file_encore_engine_trace2_trace2_proto_msgTypes[37] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2526,43 +3392,58 @@ func (x *ServiceInitStart) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use ServiceInitStart.ProtoReflect.Descriptor instead. -func (*ServiceInitStart) Descriptor() ([]byte, []int) { - return file_encore_engine_trace2_trace2_proto_rawDescGZIP(), []int{25} +// Deprecated: Use BucketDeleteObjectsStart.ProtoReflect.Descriptor instead. +func (*BucketDeleteObjectsStart) Descriptor() ([]byte, []int) { + return file_encore_engine_trace2_trace2_proto_rawDescGZIP(), []int{37} } -func (x *ServiceInitStart) GetService() string { +func (x *BucketDeleteObjectsStart) GetBucket() string { if x != nil { - return x.Service + return x.Bucket } return "" } -type ServiceInitEnd struct { +func (x *BucketDeleteObjectsStart) GetStack() *StackTrace { + if x != nil { + return x.Stack + } + return nil +} + +func (x *BucketDeleteObjectsStart) GetEntries() []*BucketDeleteObjectEntry { + if x != nil { + return x.Entries + } + return nil +} + +type BucketDeleteObjectEntry struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - Err *Error `protobuf:"bytes,1,opt,name=err,proto3,oneof" json:"err,omitempty"` + Object string `protobuf:"bytes,1,opt,name=object,proto3" json:"object,omitempty"` + Version *string `protobuf:"bytes,2,opt,name=version,proto3,oneof" json:"version,omitempty"` } -func (x *ServiceInitEnd) Reset() { - *x = ServiceInitEnd{} +func (x *BucketDeleteObjectEntry) Reset() { + *x = BucketDeleteObjectEntry{} if protoimpl.UnsafeEnabled { - mi := &file_encore_engine_trace2_trace2_proto_msgTypes[26] + mi := &file_encore_engine_trace2_trace2_proto_msgTypes[38] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } -func (x *ServiceInitEnd) String() string { +func (x *BucketDeleteObjectEntry) String() string { return protoimpl.X.MessageStringOf(x) } -func (*ServiceInitEnd) ProtoMessage() {} +func (*BucketDeleteObjectEntry) ProtoMessage() {} -func (x *ServiceInitEnd) ProtoReflect() protoreflect.Message { - mi := &file_encore_engine_trace2_trace2_proto_msgTypes[26] +func (x *BucketDeleteObjectEntry) ProtoReflect() protoreflect.Message { + mi := &file_encore_engine_trace2_trace2_proto_msgTypes[38] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2573,46 +3454,50 @@ func (x *ServiceInitEnd) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use ServiceInitEnd.ProtoReflect.Descriptor instead. -func (*ServiceInitEnd) Descriptor() ([]byte, []int) { - return file_encore_engine_trace2_trace2_proto_rawDescGZIP(), []int{26} +// Deprecated: Use BucketDeleteObjectEntry.ProtoReflect.Descriptor instead. +func (*BucketDeleteObjectEntry) Descriptor() ([]byte, []int) { + return file_encore_engine_trace2_trace2_proto_rawDescGZIP(), []int{38} } -func (x *ServiceInitEnd) GetErr() *Error { +func (x *BucketDeleteObjectEntry) GetObject() string { if x != nil { - return x.Err + return x.Object } - return nil + return "" } -type CacheCallStart struct { +func (x *BucketDeleteObjectEntry) GetVersion() string { + if x != nil && x.Version != nil { + return *x.Version + } + return "" +} + +type BucketDeleteObjectsEnd struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - Operation string `protobuf:"bytes,1,opt,name=operation,proto3" json:"operation,omitempty"` - Keys []string `protobuf:"bytes,2,rep,name=keys,proto3" json:"keys,omitempty"` - Write bool `protobuf:"varint,3,opt,name=write,proto3" json:"write,omitempty"` - Stack *StackTrace `protobuf:"bytes,4,opt,name=stack,proto3" json:"stack,omitempty"` // TODO include more info (like inputs) + Err *Error `protobuf:"bytes,1,opt,name=err,proto3,oneof" json:"err,omitempty"` } -func (x *CacheCallStart) Reset() { - *x = CacheCallStart{} +func (x *BucketDeleteObjectsEnd) Reset() { + *x = BucketDeleteObjectsEnd{} if protoimpl.UnsafeEnabled { - mi := &file_encore_engine_trace2_trace2_proto_msgTypes[27] + mi := &file_encore_engine_trace2_trace2_proto_msgTypes[39] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } -func (x *CacheCallStart) String() string { +func (x *BucketDeleteObjectsEnd) String() string { return protoimpl.X.MessageStringOf(x) } -func (*CacheCallStart) ProtoMessage() {} +func (*BucketDeleteObjectsEnd) ProtoMessage() {} -func (x *CacheCallStart) ProtoReflect() protoreflect.Message { - mi := &file_encore_engine_trace2_trace2_proto_msgTypes[27] +func (x *BucketDeleteObjectsEnd) ProtoReflect() protoreflect.Message { + mi := &file_encore_engine_trace2_trace2_proto_msgTypes[39] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2623,65 +3508,46 @@ func (x *CacheCallStart) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use CacheCallStart.ProtoReflect.Descriptor instead. -func (*CacheCallStart) Descriptor() ([]byte, []int) { - return file_encore_engine_trace2_trace2_proto_rawDescGZIP(), []int{27} -} - -func (x *CacheCallStart) GetOperation() string { - if x != nil { - return x.Operation - } - return "" -} - -func (x *CacheCallStart) GetKeys() []string { - if x != nil { - return x.Keys - } - return nil -} - -func (x *CacheCallStart) GetWrite() bool { - if x != nil { - return x.Write - } - return false +// Deprecated: Use BucketDeleteObjectsEnd.ProtoReflect.Descriptor instead. +func (*BucketDeleteObjectsEnd) Descriptor() ([]byte, []int) { + return file_encore_engine_trace2_trace2_proto_rawDescGZIP(), []int{39} } -func (x *CacheCallStart) GetStack() *StackTrace { +func (x *BucketDeleteObjectsEnd) GetErr() *Error { if x != nil { - return x.Stack + return x.Err } return nil } -type CacheCallEnd struct { +type BucketObjectAttributes struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - Result CacheCallEnd_Result `protobuf:"varint,1,opt,name=result,proto3,enum=encore.engine.trace2.CacheCallEnd_Result" json:"result,omitempty"` - Err *Error `protobuf:"bytes,2,opt,name=err,proto3,oneof" json:"err,omitempty"` // TODO include more info (like outputs) + Size *uint64 `protobuf:"varint,1,opt,name=size,proto3,oneof" json:"size,omitempty"` + Version *string `protobuf:"bytes,2,opt,name=version,proto3,oneof" json:"version,omitempty"` + Etag *string `protobuf:"bytes,3,opt,name=etag,proto3,oneof" json:"etag,omitempty"` + ContentType *string `protobuf:"bytes,4,opt,name=content_type,json=contentType,proto3,oneof" json:"content_type,omitempty"` } -func (x *CacheCallEnd) Reset() { - *x = CacheCallEnd{} +func (x *BucketObjectAttributes) Reset() { + *x = BucketObjectAttributes{} if protoimpl.UnsafeEnabled { - mi := &file_encore_engine_trace2_trace2_proto_msgTypes[28] + mi := &file_encore_engine_trace2_trace2_proto_msgTypes[40] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } -func (x *CacheCallEnd) String() string { +func (x *BucketObjectAttributes) String() string { return protoimpl.X.MessageStringOf(x) } -func (*CacheCallEnd) ProtoMessage() {} +func (*BucketObjectAttributes) ProtoMessage() {} -func (x *CacheCallEnd) ProtoReflect() protoreflect.Message { - mi := &file_encore_engine_trace2_trace2_proto_msgTypes[28] +func (x *BucketObjectAttributes) ProtoReflect() protoreflect.Message { + mi := &file_encore_engine_trace2_trace2_proto_msgTypes[40] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2692,23 +3558,37 @@ func (x *CacheCallEnd) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use CacheCallEnd.ProtoReflect.Descriptor instead. -func (*CacheCallEnd) Descriptor() ([]byte, []int) { - return file_encore_engine_trace2_trace2_proto_rawDescGZIP(), []int{28} +// Deprecated: Use BucketObjectAttributes.ProtoReflect.Descriptor instead. +func (*BucketObjectAttributes) Descriptor() ([]byte, []int) { + return file_encore_engine_trace2_trace2_proto_rawDescGZIP(), []int{40} } -func (x *CacheCallEnd) GetResult() CacheCallEnd_Result { - if x != nil { - return x.Result +func (x *BucketObjectAttributes) GetSize() uint64 { + if x != nil && x.Size != nil { + return *x.Size } - return CacheCallEnd_UNKNOWN + return 0 } -func (x *CacheCallEnd) GetErr() *Error { - if x != nil { - return x.Err +func (x *BucketObjectAttributes) GetVersion() string { + if x != nil && x.Version != nil { + return *x.Version } - return nil + return "" +} + +func (x *BucketObjectAttributes) GetEtag() string { + if x != nil && x.Etag != nil { + return *x.Etag + } + return "" +} + +func (x *BucketObjectAttributes) GetContentType() string { + if x != nil && x.ContentType != nil { + return *x.ContentType + } + return "" } type BodyStream struct { @@ -2724,7 +3604,7 @@ type BodyStream struct { func (x *BodyStream) Reset() { *x = BodyStream{} if protoimpl.UnsafeEnabled { - mi := &file_encore_engine_trace2_trace2_proto_msgTypes[29] + mi := &file_encore_engine_trace2_trace2_proto_msgTypes[41] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2737,7 +3617,7 @@ func (x *BodyStream) String() string { func (*BodyStream) ProtoMessage() {} func (x *BodyStream) ProtoReflect() protoreflect.Message { - mi := &file_encore_engine_trace2_trace2_proto_msgTypes[29] + mi := &file_encore_engine_trace2_trace2_proto_msgTypes[41] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2750,7 +3630,7 @@ func (x *BodyStream) ProtoReflect() protoreflect.Message { // Deprecated: Use BodyStream.ProtoReflect.Descriptor instead. func (*BodyStream) Descriptor() ([]byte, []int) { - return file_encore_engine_trace2_trace2_proto_rawDescGZIP(), []int{29} + return file_encore_engine_trace2_trace2_proto_rawDescGZIP(), []int{41} } func (x *BodyStream) GetIsResponse() bool { @@ -2791,7 +3671,7 @@ type HTTPCallStart struct { func (x *HTTPCallStart) Reset() { *x = HTTPCallStart{} if protoimpl.UnsafeEnabled { - mi := &file_encore_engine_trace2_trace2_proto_msgTypes[30] + mi := &file_encore_engine_trace2_trace2_proto_msgTypes[42] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2804,7 +3684,7 @@ func (x *HTTPCallStart) String() string { func (*HTTPCallStart) ProtoMessage() {} func (x *HTTPCallStart) ProtoReflect() protoreflect.Message { - mi := &file_encore_engine_trace2_trace2_proto_msgTypes[30] + mi := &file_encore_engine_trace2_trace2_proto_msgTypes[42] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2817,7 +3697,7 @@ func (x *HTTPCallStart) ProtoReflect() protoreflect.Message { // Deprecated: Use HTTPCallStart.ProtoReflect.Descriptor instead. func (*HTTPCallStart) Descriptor() ([]byte, []int) { - return file_encore_engine_trace2_trace2_proto_rawDescGZIP(), []int{30} + return file_encore_engine_trace2_trace2_proto_rawDescGZIP(), []int{42} } func (x *HTTPCallStart) GetCorrelationParentSpanId() uint64 { @@ -2871,7 +3751,7 @@ type HTTPCallEnd struct { func (x *HTTPCallEnd) Reset() { *x = HTTPCallEnd{} if protoimpl.UnsafeEnabled { - mi := &file_encore_engine_trace2_trace2_proto_msgTypes[31] + mi := &file_encore_engine_trace2_trace2_proto_msgTypes[43] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2884,7 +3764,7 @@ func (x *HTTPCallEnd) String() string { func (*HTTPCallEnd) ProtoMessage() {} func (x *HTTPCallEnd) ProtoReflect() protoreflect.Message { - mi := &file_encore_engine_trace2_trace2_proto_msgTypes[31] + mi := &file_encore_engine_trace2_trace2_proto_msgTypes[43] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2897,7 +3777,7 @@ func (x *HTTPCallEnd) ProtoReflect() protoreflect.Message { // Deprecated: Use HTTPCallEnd.ProtoReflect.Descriptor instead. func (*HTTPCallEnd) Descriptor() ([]byte, []int) { - return file_encore_engine_trace2_trace2_proto_rawDescGZIP(), []int{31} + return file_encore_engine_trace2_trace2_proto_rawDescGZIP(), []int{43} } func (x *HTTPCallEnd) GetStatusCode() uint32 { @@ -2949,7 +3829,7 @@ type HTTPTraceEvent struct { func (x *HTTPTraceEvent) Reset() { *x = HTTPTraceEvent{} if protoimpl.UnsafeEnabled { - mi := &file_encore_engine_trace2_trace2_proto_msgTypes[32] + mi := &file_encore_engine_trace2_trace2_proto_msgTypes[44] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2962,7 +3842,7 @@ func (x *HTTPTraceEvent) String() string { func (*HTTPTraceEvent) ProtoMessage() {} func (x *HTTPTraceEvent) ProtoReflect() protoreflect.Message { - mi := &file_encore_engine_trace2_trace2_proto_msgTypes[32] + mi := &file_encore_engine_trace2_trace2_proto_msgTypes[44] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2975,7 +3855,7 @@ func (x *HTTPTraceEvent) ProtoReflect() protoreflect.Message { // Deprecated: Use HTTPTraceEvent.ProtoReflect.Descriptor instead. func (*HTTPTraceEvent) Descriptor() ([]byte, []int) { - return file_encore_engine_trace2_trace2_proto_rawDescGZIP(), []int{32} + return file_encore_engine_trace2_trace2_proto_rawDescGZIP(), []int{44} } func (x *HTTPTraceEvent) GetNanotime() int64 { @@ -3189,7 +4069,7 @@ type HTTPGetConn struct { func (x *HTTPGetConn) Reset() { *x = HTTPGetConn{} if protoimpl.UnsafeEnabled { - mi := &file_encore_engine_trace2_trace2_proto_msgTypes[33] + mi := &file_encore_engine_trace2_trace2_proto_msgTypes[45] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -3202,7 +4082,7 @@ func (x *HTTPGetConn) String() string { func (*HTTPGetConn) ProtoMessage() {} func (x *HTTPGetConn) ProtoReflect() protoreflect.Message { - mi := &file_encore_engine_trace2_trace2_proto_msgTypes[33] + mi := &file_encore_engine_trace2_trace2_proto_msgTypes[45] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3215,7 +4095,7 @@ func (x *HTTPGetConn) ProtoReflect() protoreflect.Message { // Deprecated: Use HTTPGetConn.ProtoReflect.Descriptor instead. func (*HTTPGetConn) Descriptor() ([]byte, []int) { - return file_encore_engine_trace2_trace2_proto_rawDescGZIP(), []int{33} + return file_encore_engine_trace2_trace2_proto_rawDescGZIP(), []int{45} } func (x *HTTPGetConn) GetHostPort() string { @@ -3238,7 +4118,7 @@ type HTTPGotConn struct { func (x *HTTPGotConn) Reset() { *x = HTTPGotConn{} if protoimpl.UnsafeEnabled { - mi := &file_encore_engine_trace2_trace2_proto_msgTypes[34] + mi := &file_encore_engine_trace2_trace2_proto_msgTypes[46] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -3251,7 +4131,7 @@ func (x *HTTPGotConn) String() string { func (*HTTPGotConn) ProtoMessage() {} func (x *HTTPGotConn) ProtoReflect() protoreflect.Message { - mi := &file_encore_engine_trace2_trace2_proto_msgTypes[34] + mi := &file_encore_engine_trace2_trace2_proto_msgTypes[46] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3264,7 +4144,7 @@ func (x *HTTPGotConn) ProtoReflect() protoreflect.Message { // Deprecated: Use HTTPGotConn.ProtoReflect.Descriptor instead. func (*HTTPGotConn) Descriptor() ([]byte, []int) { - return file_encore_engine_trace2_trace2_proto_rawDescGZIP(), []int{34} + return file_encore_engine_trace2_trace2_proto_rawDescGZIP(), []int{46} } func (x *HTTPGotConn) GetReused() bool { @@ -3297,7 +4177,7 @@ type HTTPGotFirstResponseByte struct { func (x *HTTPGotFirstResponseByte) Reset() { *x = HTTPGotFirstResponseByte{} if protoimpl.UnsafeEnabled { - mi := &file_encore_engine_trace2_trace2_proto_msgTypes[35] + mi := &file_encore_engine_trace2_trace2_proto_msgTypes[47] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -3310,7 +4190,7 @@ func (x *HTTPGotFirstResponseByte) String() string { func (*HTTPGotFirstResponseByte) ProtoMessage() {} func (x *HTTPGotFirstResponseByte) ProtoReflect() protoreflect.Message { - mi := &file_encore_engine_trace2_trace2_proto_msgTypes[35] + mi := &file_encore_engine_trace2_trace2_proto_msgTypes[47] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3323,7 +4203,7 @@ func (x *HTTPGotFirstResponseByte) ProtoReflect() protoreflect.Message { // Deprecated: Use HTTPGotFirstResponseByte.ProtoReflect.Descriptor instead. func (*HTTPGotFirstResponseByte) Descriptor() ([]byte, []int) { - return file_encore_engine_trace2_trace2_proto_rawDescGZIP(), []int{35} + return file_encore_engine_trace2_trace2_proto_rawDescGZIP(), []int{47} } type HTTPGot1XxResponse struct { @@ -3337,7 +4217,7 @@ type HTTPGot1XxResponse struct { func (x *HTTPGot1XxResponse) Reset() { *x = HTTPGot1XxResponse{} if protoimpl.UnsafeEnabled { - mi := &file_encore_engine_trace2_trace2_proto_msgTypes[36] + mi := &file_encore_engine_trace2_trace2_proto_msgTypes[48] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -3350,7 +4230,7 @@ func (x *HTTPGot1XxResponse) String() string { func (*HTTPGot1XxResponse) ProtoMessage() {} func (x *HTTPGot1XxResponse) ProtoReflect() protoreflect.Message { - mi := &file_encore_engine_trace2_trace2_proto_msgTypes[36] + mi := &file_encore_engine_trace2_trace2_proto_msgTypes[48] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3363,7 +4243,7 @@ func (x *HTTPGot1XxResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use HTTPGot1XxResponse.ProtoReflect.Descriptor instead. func (*HTTPGot1XxResponse) Descriptor() ([]byte, []int) { - return file_encore_engine_trace2_trace2_proto_rawDescGZIP(), []int{36} + return file_encore_engine_trace2_trace2_proto_rawDescGZIP(), []int{48} } func (x *HTTPGot1XxResponse) GetCode() int32 { @@ -3384,7 +4264,7 @@ type HTTPDNSStart struct { func (x *HTTPDNSStart) Reset() { *x = HTTPDNSStart{} if protoimpl.UnsafeEnabled { - mi := &file_encore_engine_trace2_trace2_proto_msgTypes[37] + mi := &file_encore_engine_trace2_trace2_proto_msgTypes[49] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -3397,7 +4277,7 @@ func (x *HTTPDNSStart) String() string { func (*HTTPDNSStart) ProtoMessage() {} func (x *HTTPDNSStart) ProtoReflect() protoreflect.Message { - mi := &file_encore_engine_trace2_trace2_proto_msgTypes[37] + mi := &file_encore_engine_trace2_trace2_proto_msgTypes[49] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3410,7 +4290,7 @@ func (x *HTTPDNSStart) ProtoReflect() protoreflect.Message { // Deprecated: Use HTTPDNSStart.ProtoReflect.Descriptor instead. func (*HTTPDNSStart) Descriptor() ([]byte, []int) { - return file_encore_engine_trace2_trace2_proto_rawDescGZIP(), []int{37} + return file_encore_engine_trace2_trace2_proto_rawDescGZIP(), []int{49} } func (x *HTTPDNSStart) GetHost() string { @@ -3432,7 +4312,7 @@ type HTTPDNSDone struct { func (x *HTTPDNSDone) Reset() { *x = HTTPDNSDone{} if protoimpl.UnsafeEnabled { - mi := &file_encore_engine_trace2_trace2_proto_msgTypes[38] + mi := &file_encore_engine_trace2_trace2_proto_msgTypes[50] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -3445,7 +4325,7 @@ func (x *HTTPDNSDone) String() string { func (*HTTPDNSDone) ProtoMessage() {} func (x *HTTPDNSDone) ProtoReflect() protoreflect.Message { - mi := &file_encore_engine_trace2_trace2_proto_msgTypes[38] + mi := &file_encore_engine_trace2_trace2_proto_msgTypes[50] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3458,7 +4338,7 @@ func (x *HTTPDNSDone) ProtoReflect() protoreflect.Message { // Deprecated: Use HTTPDNSDone.ProtoReflect.Descriptor instead. func (*HTTPDNSDone) Descriptor() ([]byte, []int) { - return file_encore_engine_trace2_trace2_proto_rawDescGZIP(), []int{38} + return file_encore_engine_trace2_trace2_proto_rawDescGZIP(), []int{50} } func (x *HTTPDNSDone) GetErr() []byte { @@ -3486,7 +4366,7 @@ type DNSAddr struct { func (x *DNSAddr) Reset() { *x = DNSAddr{} if protoimpl.UnsafeEnabled { - mi := &file_encore_engine_trace2_trace2_proto_msgTypes[39] + mi := &file_encore_engine_trace2_trace2_proto_msgTypes[51] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -3499,7 +4379,7 @@ func (x *DNSAddr) String() string { func (*DNSAddr) ProtoMessage() {} func (x *DNSAddr) ProtoReflect() protoreflect.Message { - mi := &file_encore_engine_trace2_trace2_proto_msgTypes[39] + mi := &file_encore_engine_trace2_trace2_proto_msgTypes[51] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3512,7 +4392,7 @@ func (x *DNSAddr) ProtoReflect() protoreflect.Message { // Deprecated: Use DNSAddr.ProtoReflect.Descriptor instead. func (*DNSAddr) Descriptor() ([]byte, []int) { - return file_encore_engine_trace2_trace2_proto_rawDescGZIP(), []int{39} + return file_encore_engine_trace2_trace2_proto_rawDescGZIP(), []int{51} } func (x *DNSAddr) GetIp() []byte { @@ -3534,7 +4414,7 @@ type HTTPConnectStart struct { func (x *HTTPConnectStart) Reset() { *x = HTTPConnectStart{} if protoimpl.UnsafeEnabled { - mi := &file_encore_engine_trace2_trace2_proto_msgTypes[40] + mi := &file_encore_engine_trace2_trace2_proto_msgTypes[52] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -3547,7 +4427,7 @@ func (x *HTTPConnectStart) String() string { func (*HTTPConnectStart) ProtoMessage() {} func (x *HTTPConnectStart) ProtoReflect() protoreflect.Message { - mi := &file_encore_engine_trace2_trace2_proto_msgTypes[40] + mi := &file_encore_engine_trace2_trace2_proto_msgTypes[52] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3560,7 +4440,7 @@ func (x *HTTPConnectStart) ProtoReflect() protoreflect.Message { // Deprecated: Use HTTPConnectStart.ProtoReflect.Descriptor instead. func (*HTTPConnectStart) Descriptor() ([]byte, []int) { - return file_encore_engine_trace2_trace2_proto_rawDescGZIP(), []int{40} + return file_encore_engine_trace2_trace2_proto_rawDescGZIP(), []int{52} } func (x *HTTPConnectStart) GetNetwork() string { @@ -3590,7 +4470,7 @@ type HTTPConnectDone struct { func (x *HTTPConnectDone) Reset() { *x = HTTPConnectDone{} if protoimpl.UnsafeEnabled { - mi := &file_encore_engine_trace2_trace2_proto_msgTypes[41] + mi := &file_encore_engine_trace2_trace2_proto_msgTypes[53] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -3603,7 +4483,7 @@ func (x *HTTPConnectDone) String() string { func (*HTTPConnectDone) ProtoMessage() {} func (x *HTTPConnectDone) ProtoReflect() protoreflect.Message { - mi := &file_encore_engine_trace2_trace2_proto_msgTypes[41] + mi := &file_encore_engine_trace2_trace2_proto_msgTypes[53] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3616,7 +4496,7 @@ func (x *HTTPConnectDone) ProtoReflect() protoreflect.Message { // Deprecated: Use HTTPConnectDone.ProtoReflect.Descriptor instead. func (*HTTPConnectDone) Descriptor() ([]byte, []int) { - return file_encore_engine_trace2_trace2_proto_rawDescGZIP(), []int{41} + return file_encore_engine_trace2_trace2_proto_rawDescGZIP(), []int{53} } func (x *HTTPConnectDone) GetNetwork() string { @@ -3649,7 +4529,7 @@ type HTTPTLSHandshakeStart struct { func (x *HTTPTLSHandshakeStart) Reset() { *x = HTTPTLSHandshakeStart{} if protoimpl.UnsafeEnabled { - mi := &file_encore_engine_trace2_trace2_proto_msgTypes[42] + mi := &file_encore_engine_trace2_trace2_proto_msgTypes[54] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -3662,7 +4542,7 @@ func (x *HTTPTLSHandshakeStart) String() string { func (*HTTPTLSHandshakeStart) ProtoMessage() {} func (x *HTTPTLSHandshakeStart) ProtoReflect() protoreflect.Message { - mi := &file_encore_engine_trace2_trace2_proto_msgTypes[42] + mi := &file_encore_engine_trace2_trace2_proto_msgTypes[54] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3675,7 +4555,7 @@ func (x *HTTPTLSHandshakeStart) ProtoReflect() protoreflect.Message { // Deprecated: Use HTTPTLSHandshakeStart.ProtoReflect.Descriptor instead. func (*HTTPTLSHandshakeStart) Descriptor() ([]byte, []int) { - return file_encore_engine_trace2_trace2_proto_rawDescGZIP(), []int{42} + return file_encore_engine_trace2_trace2_proto_rawDescGZIP(), []int{54} } type HTTPTLSHandshakeDone struct { @@ -3693,7 +4573,7 @@ type HTTPTLSHandshakeDone struct { func (x *HTTPTLSHandshakeDone) Reset() { *x = HTTPTLSHandshakeDone{} if protoimpl.UnsafeEnabled { - mi := &file_encore_engine_trace2_trace2_proto_msgTypes[43] + mi := &file_encore_engine_trace2_trace2_proto_msgTypes[55] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -3706,7 +4586,7 @@ func (x *HTTPTLSHandshakeDone) String() string { func (*HTTPTLSHandshakeDone) ProtoMessage() {} func (x *HTTPTLSHandshakeDone) ProtoReflect() protoreflect.Message { - mi := &file_encore_engine_trace2_trace2_proto_msgTypes[43] + mi := &file_encore_engine_trace2_trace2_proto_msgTypes[55] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3719,7 +4599,7 @@ func (x *HTTPTLSHandshakeDone) ProtoReflect() protoreflect.Message { // Deprecated: Use HTTPTLSHandshakeDone.ProtoReflect.Descriptor instead. func (*HTTPTLSHandshakeDone) Descriptor() ([]byte, []int) { - return file_encore_engine_trace2_trace2_proto_rawDescGZIP(), []int{43} + return file_encore_engine_trace2_trace2_proto_rawDescGZIP(), []int{55} } func (x *HTTPTLSHandshakeDone) GetErr() []byte { @@ -3766,7 +4646,7 @@ type HTTPWroteHeaders struct { func (x *HTTPWroteHeaders) Reset() { *x = HTTPWroteHeaders{} if protoimpl.UnsafeEnabled { - mi := &file_encore_engine_trace2_trace2_proto_msgTypes[44] + mi := &file_encore_engine_trace2_trace2_proto_msgTypes[56] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -3779,7 +4659,7 @@ func (x *HTTPWroteHeaders) String() string { func (*HTTPWroteHeaders) ProtoMessage() {} func (x *HTTPWroteHeaders) ProtoReflect() protoreflect.Message { - mi := &file_encore_engine_trace2_trace2_proto_msgTypes[44] + mi := &file_encore_engine_trace2_trace2_proto_msgTypes[56] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3792,7 +4672,7 @@ func (x *HTTPWroteHeaders) ProtoReflect() protoreflect.Message { // Deprecated: Use HTTPWroteHeaders.ProtoReflect.Descriptor instead. func (*HTTPWroteHeaders) Descriptor() ([]byte, []int) { - return file_encore_engine_trace2_trace2_proto_rawDescGZIP(), []int{44} + return file_encore_engine_trace2_trace2_proto_rawDescGZIP(), []int{56} } type HTTPWroteRequest struct { @@ -3806,7 +4686,7 @@ type HTTPWroteRequest struct { func (x *HTTPWroteRequest) Reset() { *x = HTTPWroteRequest{} if protoimpl.UnsafeEnabled { - mi := &file_encore_engine_trace2_trace2_proto_msgTypes[45] + mi := &file_encore_engine_trace2_trace2_proto_msgTypes[57] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -3819,7 +4699,7 @@ func (x *HTTPWroteRequest) String() string { func (*HTTPWroteRequest) ProtoMessage() {} func (x *HTTPWroteRequest) ProtoReflect() protoreflect.Message { - mi := &file_encore_engine_trace2_trace2_proto_msgTypes[45] + mi := &file_encore_engine_trace2_trace2_proto_msgTypes[57] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3832,7 +4712,7 @@ func (x *HTTPWroteRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use HTTPWroteRequest.ProtoReflect.Descriptor instead. func (*HTTPWroteRequest) Descriptor() ([]byte, []int) { - return file_encore_engine_trace2_trace2_proto_rawDescGZIP(), []int{45} + return file_encore_engine_trace2_trace2_proto_rawDescGZIP(), []int{57} } func (x *HTTPWroteRequest) GetErr() []byte { @@ -3851,7 +4731,7 @@ type HTTPWait100Continue struct { func (x *HTTPWait100Continue) Reset() { *x = HTTPWait100Continue{} if protoimpl.UnsafeEnabled { - mi := &file_encore_engine_trace2_trace2_proto_msgTypes[46] + mi := &file_encore_engine_trace2_trace2_proto_msgTypes[58] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -3864,7 +4744,7 @@ func (x *HTTPWait100Continue) String() string { func (*HTTPWait100Continue) ProtoMessage() {} func (x *HTTPWait100Continue) ProtoReflect() protoreflect.Message { - mi := &file_encore_engine_trace2_trace2_proto_msgTypes[46] + mi := &file_encore_engine_trace2_trace2_proto_msgTypes[58] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3877,7 +4757,7 @@ func (x *HTTPWait100Continue) ProtoReflect() protoreflect.Message { // Deprecated: Use HTTPWait100Continue.ProtoReflect.Descriptor instead. func (*HTTPWait100Continue) Descriptor() ([]byte, []int) { - return file_encore_engine_trace2_trace2_proto_rawDescGZIP(), []int{46} + return file_encore_engine_trace2_trace2_proto_rawDescGZIP(), []int{58} } type HTTPClosedBodyData struct { @@ -3891,7 +4771,7 @@ type HTTPClosedBodyData struct { func (x *HTTPClosedBodyData) Reset() { *x = HTTPClosedBodyData{} if protoimpl.UnsafeEnabled { - mi := &file_encore_engine_trace2_trace2_proto_msgTypes[47] + mi := &file_encore_engine_trace2_trace2_proto_msgTypes[59] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -3904,7 +4784,7 @@ func (x *HTTPClosedBodyData) String() string { func (*HTTPClosedBodyData) ProtoMessage() {} func (x *HTTPClosedBodyData) ProtoReflect() protoreflect.Message { - mi := &file_encore_engine_trace2_trace2_proto_msgTypes[47] + mi := &file_encore_engine_trace2_trace2_proto_msgTypes[59] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3917,7 +4797,7 @@ func (x *HTTPClosedBodyData) ProtoReflect() protoreflect.Message { // Deprecated: Use HTTPClosedBodyData.ProtoReflect.Descriptor instead. func (*HTTPClosedBodyData) Descriptor() ([]byte, []int) { - return file_encore_engine_trace2_trace2_proto_rawDescGZIP(), []int{47} + return file_encore_engine_trace2_trace2_proto_rawDescGZIP(), []int{59} } func (x *HTTPClosedBodyData) GetErr() []byte { @@ -3941,7 +4821,7 @@ type LogMessage struct { func (x *LogMessage) Reset() { *x = LogMessage{} if protoimpl.UnsafeEnabled { - mi := &file_encore_engine_trace2_trace2_proto_msgTypes[48] + mi := &file_encore_engine_trace2_trace2_proto_msgTypes[60] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -3954,7 +4834,7 @@ func (x *LogMessage) String() string { func (*LogMessage) ProtoMessage() {} func (x *LogMessage) ProtoReflect() protoreflect.Message { - mi := &file_encore_engine_trace2_trace2_proto_msgTypes[48] + mi := &file_encore_engine_trace2_trace2_proto_msgTypes[60] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3967,7 +4847,7 @@ func (x *LogMessage) ProtoReflect() protoreflect.Message { // Deprecated: Use LogMessage.ProtoReflect.Descriptor instead. func (*LogMessage) Descriptor() ([]byte, []int) { - return file_encore_engine_trace2_trace2_proto_rawDescGZIP(), []int{48} + return file_encore_engine_trace2_trace2_proto_rawDescGZIP(), []int{60} } func (x *LogMessage) GetLevel() LogMessage_Level { @@ -4023,7 +4903,7 @@ type LogField struct { func (x *LogField) Reset() { *x = LogField{} if protoimpl.UnsafeEnabled { - mi := &file_encore_engine_trace2_trace2_proto_msgTypes[49] + mi := &file_encore_engine_trace2_trace2_proto_msgTypes[61] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -4036,7 +4916,7 @@ func (x *LogField) String() string { func (*LogField) ProtoMessage() {} func (x *LogField) ProtoReflect() protoreflect.Message { - mi := &file_encore_engine_trace2_trace2_proto_msgTypes[49] + mi := &file_encore_engine_trace2_trace2_proto_msgTypes[61] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -4049,7 +4929,7 @@ func (x *LogField) ProtoReflect() protoreflect.Message { // Deprecated: Use LogField.ProtoReflect.Descriptor instead. func (*LogField) Descriptor() ([]byte, []int) { - return file_encore_engine_trace2_trace2_proto_rawDescGZIP(), []int{49} + return file_encore_engine_trace2_trace2_proto_rawDescGZIP(), []int{61} } func (x *LogField) GetKey() string { @@ -4225,7 +5105,7 @@ type StackTrace struct { func (x *StackTrace) Reset() { *x = StackTrace{} if protoimpl.UnsafeEnabled { - mi := &file_encore_engine_trace2_trace2_proto_msgTypes[50] + mi := &file_encore_engine_trace2_trace2_proto_msgTypes[62] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -4238,7 +5118,7 @@ func (x *StackTrace) String() string { func (*StackTrace) ProtoMessage() {} func (x *StackTrace) ProtoReflect() protoreflect.Message { - mi := &file_encore_engine_trace2_trace2_proto_msgTypes[50] + mi := &file_encore_engine_trace2_trace2_proto_msgTypes[62] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -4251,7 +5131,7 @@ func (x *StackTrace) ProtoReflect() protoreflect.Message { // Deprecated: Use StackTrace.ProtoReflect.Descriptor instead. func (*StackTrace) Descriptor() ([]byte, []int) { - return file_encore_engine_trace2_trace2_proto_rawDescGZIP(), []int{50} + return file_encore_engine_trace2_trace2_proto_rawDescGZIP(), []int{62} } func (x *StackTrace) GetPcs() []int64 { @@ -4281,7 +5161,7 @@ type StackFrame struct { func (x *StackFrame) Reset() { *x = StackFrame{} if protoimpl.UnsafeEnabled { - mi := &file_encore_engine_trace2_trace2_proto_msgTypes[51] + mi := &file_encore_engine_trace2_trace2_proto_msgTypes[63] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -4294,7 +5174,7 @@ func (x *StackFrame) String() string { func (*StackFrame) ProtoMessage() {} func (x *StackFrame) ProtoReflect() protoreflect.Message { - mi := &file_encore_engine_trace2_trace2_proto_msgTypes[51] + mi := &file_encore_engine_trace2_trace2_proto_msgTypes[63] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -4307,7 +5187,7 @@ func (x *StackFrame) ProtoReflect() protoreflect.Message { // Deprecated: Use StackFrame.ProtoReflect.Descriptor instead. func (*StackFrame) Descriptor() ([]byte, []int) { - return file_encore_engine_trace2_trace2_proto_rawDescGZIP(), []int{51} + return file_encore_engine_trace2_trace2_proto_rawDescGZIP(), []int{63} } func (x *StackFrame) GetFilename() string { @@ -4343,7 +5223,7 @@ type Error struct { func (x *Error) Reset() { *x = Error{} if protoimpl.UnsafeEnabled { - mi := &file_encore_engine_trace2_trace2_proto_msgTypes[52] + mi := &file_encore_engine_trace2_trace2_proto_msgTypes[64] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -4356,7 +5236,7 @@ func (x *Error) String() string { func (*Error) ProtoMessage() {} func (x *Error) ProtoReflect() protoreflect.Message { - mi := &file_encore_engine_trace2_trace2_proto_msgTypes[52] + mi := &file_encore_engine_trace2_trace2_proto_msgTypes[64] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -4369,7 +5249,7 @@ func (x *Error) ProtoReflect() protoreflect.Message { // Deprecated: Use Error.ProtoReflect.Descriptor instead. func (*Error) Descriptor() ([]byte, []int) { - return file_encore_engine_trace2_trace2_proto_rawDescGZIP(), []int{52} + return file_encore_engine_trace2_trace2_proto_rawDescGZIP(), []int{64} } func (x *Error) GetMsg() string { @@ -4681,7 +5561,7 @@ var file_encore_engine_trace2_trace2_proto_rawDesc = []byte{ 0x74, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x66, 0x61, 0x69, 0x6c, 0x65, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x06, 0x66, 0x61, 0x69, 0x6c, 0x65, 0x64, 0x12, 0x18, 0x0a, 0x07, 0x73, 0x6b, 0x69, 0x70, 0x70, 0x65, 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, - 0x73, 0x6b, 0x69, 0x70, 0x70, 0x65, 0x64, 0x22, 0x9b, 0x0b, 0x0a, 0x09, 0x53, 0x70, 0x61, 0x6e, + 0x73, 0x6b, 0x69, 0x70, 0x70, 0x65, 0x64, 0x22, 0xe3, 0x13, 0x0a, 0x09, 0x53, 0x70, 0x61, 0x6e, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x67, 0x6f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x04, 0x67, 0x6f, 0x69, 0x64, 0x12, 0x1c, 0x0a, 0x07, 0x64, 0x65, 0x66, 0x5f, 0x6c, 0x6f, 0x63, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x48, 0x01, 0x52, 0x06, 0x64, 0x65, @@ -4768,346 +5648,527 @@ var file_encore_engine_trace2_trace2_proto_rawDesc = []byte{ 0x32, 0x24, 0x2e, 0x65, 0x6e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x65, 0x6e, 0x67, 0x69, 0x6e, 0x65, 0x2e, 0x74, 0x72, 0x61, 0x63, 0x65, 0x32, 0x2e, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x49, 0x6e, 0x69, 0x74, 0x45, 0x6e, 0x64, 0x48, 0x00, 0x52, 0x0e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, - 0x65, 0x49, 0x6e, 0x69, 0x74, 0x45, 0x6e, 0x64, 0x42, 0x06, 0x0a, 0x04, 0x64, 0x61, 0x74, 0x61, - 0x42, 0x0a, 0x0a, 0x08, 0x5f, 0x64, 0x65, 0x66, 0x5f, 0x6c, 0x6f, 0x63, 0x42, 0x17, 0x0a, 0x15, - 0x5f, 0x63, 0x6f, 0x72, 0x72, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x65, 0x76, 0x65, - 0x6e, 0x74, 0x5f, 0x69, 0x64, 0x22, 0xa8, 0x01, 0x0a, 0x0c, 0x52, 0x50, 0x43, 0x43, 0x61, 0x6c, - 0x6c, 0x53, 0x74, 0x61, 0x72, 0x74, 0x12, 0x2e, 0x0a, 0x13, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, - 0x5f, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x11, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x53, 0x65, 0x72, 0x76, 0x69, - 0x63, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x30, 0x0a, 0x14, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, - 0x5f, 0x65, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x12, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x45, 0x6e, 0x64, 0x70, - 0x6f, 0x69, 0x6e, 0x74, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x36, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x63, - 0x6b, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x20, 0x2e, 0x65, 0x6e, 0x63, 0x6f, 0x72, 0x65, - 0x2e, 0x65, 0x6e, 0x67, 0x69, 0x6e, 0x65, 0x2e, 0x74, 0x72, 0x61, 0x63, 0x65, 0x32, 0x2e, 0x53, - 0x74, 0x61, 0x63, 0x6b, 0x54, 0x72, 0x61, 0x63, 0x65, 0x52, 0x05, 0x73, 0x74, 0x61, 0x63, 0x6b, - 0x22, 0x48, 0x0a, 0x0a, 0x52, 0x50, 0x43, 0x43, 0x61, 0x6c, 0x6c, 0x45, 0x6e, 0x64, 0x12, 0x32, - 0x0a, 0x03, 0x65, 0x72, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x65, 0x6e, - 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x65, 0x6e, 0x67, 0x69, 0x6e, 0x65, 0x2e, 0x74, 0x72, 0x61, 0x63, - 0x65, 0x32, 0x2e, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x48, 0x00, 0x52, 0x03, 0x65, 0x72, 0x72, 0x88, - 0x01, 0x01, 0x42, 0x06, 0x0a, 0x04, 0x5f, 0x65, 0x72, 0x72, 0x22, 0x10, 0x0a, 0x0e, 0x47, 0x6f, - 0x72, 0x6f, 0x75, 0x74, 0x69, 0x6e, 0x65, 0x53, 0x74, 0x61, 0x72, 0x74, 0x22, 0x0e, 0x0a, 0x0c, - 0x47, 0x6f, 0x72, 0x6f, 0x75, 0x74, 0x69, 0x6e, 0x65, 0x45, 0x6e, 0x64, 0x22, 0x4c, 0x0a, 0x12, - 0x44, 0x42, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x74, 0x61, - 0x72, 0x74, 0x12, 0x36, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x63, 0x6b, 0x18, 0x01, 0x20, 0x01, 0x28, - 0x0b, 0x32, 0x20, 0x2e, 0x65, 0x6e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x65, 0x6e, 0x67, 0x69, 0x6e, - 0x65, 0x2e, 0x74, 0x72, 0x61, 0x63, 0x65, 0x32, 0x2e, 0x53, 0x74, 0x61, 0x63, 0x6b, 0x54, 0x72, - 0x61, 0x63, 0x65, 0x52, 0x05, 0x73, 0x74, 0x61, 0x63, 0x6b, 0x22, 0x89, 0x02, 0x0a, 0x10, 0x44, - 0x42, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x45, 0x6e, 0x64, 0x12, - 0x55, 0x0a, 0x0a, 0x63, 0x6f, 0x6d, 0x70, 0x6c, 0x65, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x0e, 0x32, 0x35, 0x2e, 0x65, 0x6e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x65, 0x6e, 0x67, - 0x69, 0x6e, 0x65, 0x2e, 0x74, 0x72, 0x61, 0x63, 0x65, 0x32, 0x2e, 0x44, 0x42, 0x54, 0x72, 0x61, - 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x45, 0x6e, 0x64, 0x2e, 0x43, 0x6f, 0x6d, 0x70, - 0x6c, 0x65, 0x74, 0x69, 0x6f, 0x6e, 0x54, 0x79, 0x70, 0x65, 0x52, 0x0a, 0x63, 0x6f, 0x6d, 0x70, - 0x6c, 0x65, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x36, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x63, 0x6b, 0x18, - 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x20, 0x2e, 0x65, 0x6e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x65, - 0x6e, 0x67, 0x69, 0x6e, 0x65, 0x2e, 0x74, 0x72, 0x61, 0x63, 0x65, 0x32, 0x2e, 0x53, 0x74, 0x61, - 0x63, 0x6b, 0x54, 0x72, 0x61, 0x63, 0x65, 0x52, 0x05, 0x73, 0x74, 0x61, 0x63, 0x6b, 0x12, 0x32, - 0x0a, 0x03, 0x65, 0x72, 0x72, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x65, 0x6e, + 0x65, 0x49, 0x6e, 0x69, 0x74, 0x45, 0x6e, 0x64, 0x12, 0x6c, 0x0a, 0x1a, 0x62, 0x75, 0x63, 0x6b, + 0x65, 0x74, 0x5f, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x5f, 0x75, 0x70, 0x6c, 0x6f, 0x61, 0x64, + 0x5f, 0x73, 0x74, 0x61, 0x72, 0x74, 0x18, 0x1a, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2d, 0x2e, 0x65, + 0x6e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x65, 0x6e, 0x67, 0x69, 0x6e, 0x65, 0x2e, 0x74, 0x72, 0x61, + 0x63, 0x65, 0x32, 0x2e, 0x42, 0x75, 0x63, 0x6b, 0x65, 0x74, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, + 0x55, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x53, 0x74, 0x61, 0x72, 0x74, 0x48, 0x00, 0x52, 0x17, 0x62, + 0x75, 0x63, 0x6b, 0x65, 0x74, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x55, 0x70, 0x6c, 0x6f, 0x61, + 0x64, 0x53, 0x74, 0x61, 0x72, 0x74, 0x12, 0x66, 0x0a, 0x18, 0x62, 0x75, 0x63, 0x6b, 0x65, 0x74, + 0x5f, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x5f, 0x75, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x5f, 0x65, + 0x6e, 0x64, 0x18, 0x1b, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2b, 0x2e, 0x65, 0x6e, 0x63, 0x6f, 0x72, + 0x65, 0x2e, 0x65, 0x6e, 0x67, 0x69, 0x6e, 0x65, 0x2e, 0x74, 0x72, 0x61, 0x63, 0x65, 0x32, 0x2e, + 0x42, 0x75, 0x63, 0x6b, 0x65, 0x74, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x55, 0x70, 0x6c, 0x6f, + 0x61, 0x64, 0x45, 0x6e, 0x64, 0x48, 0x00, 0x52, 0x15, 0x62, 0x75, 0x63, 0x6b, 0x65, 0x74, 0x4f, + 0x62, 0x6a, 0x65, 0x63, 0x74, 0x55, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x45, 0x6e, 0x64, 0x12, 0x72, + 0x0a, 0x1c, 0x62, 0x75, 0x63, 0x6b, 0x65, 0x74, 0x5f, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x5f, + 0x64, 0x6f, 0x77, 0x6e, 0x6c, 0x6f, 0x61, 0x64, 0x5f, 0x73, 0x74, 0x61, 0x72, 0x74, 0x18, 0x1c, + 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2f, 0x2e, 0x65, 0x6e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x65, 0x6e, + 0x67, 0x69, 0x6e, 0x65, 0x2e, 0x74, 0x72, 0x61, 0x63, 0x65, 0x32, 0x2e, 0x42, 0x75, 0x63, 0x6b, + 0x65, 0x74, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x44, 0x6f, 0x77, 0x6e, 0x6c, 0x6f, 0x61, 0x64, + 0x53, 0x74, 0x61, 0x72, 0x74, 0x48, 0x00, 0x52, 0x19, 0x62, 0x75, 0x63, 0x6b, 0x65, 0x74, 0x4f, + 0x62, 0x6a, 0x65, 0x63, 0x74, 0x44, 0x6f, 0x77, 0x6e, 0x6c, 0x6f, 0x61, 0x64, 0x53, 0x74, 0x61, + 0x72, 0x74, 0x12, 0x6c, 0x0a, 0x1a, 0x62, 0x75, 0x63, 0x6b, 0x65, 0x74, 0x5f, 0x6f, 0x62, 0x6a, + 0x65, 0x63, 0x74, 0x5f, 0x64, 0x6f, 0x77, 0x6e, 0x6c, 0x6f, 0x61, 0x64, 0x5f, 0x65, 0x6e, 0x64, + 0x18, 0x1d, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2d, 0x2e, 0x65, 0x6e, 0x63, 0x6f, 0x72, 0x65, 0x2e, + 0x65, 0x6e, 0x67, 0x69, 0x6e, 0x65, 0x2e, 0x74, 0x72, 0x61, 0x63, 0x65, 0x32, 0x2e, 0x42, 0x75, + 0x63, 0x6b, 0x65, 0x74, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x44, 0x6f, 0x77, 0x6e, 0x6c, 0x6f, + 0x61, 0x64, 0x45, 0x6e, 0x64, 0x48, 0x00, 0x52, 0x17, 0x62, 0x75, 0x63, 0x6b, 0x65, 0x74, 0x4f, + 0x62, 0x6a, 0x65, 0x63, 0x74, 0x44, 0x6f, 0x77, 0x6e, 0x6c, 0x6f, 0x61, 0x64, 0x45, 0x6e, 0x64, + 0x12, 0x73, 0x0a, 0x1d, 0x62, 0x75, 0x63, 0x6b, 0x65, 0x74, 0x5f, 0x6f, 0x62, 0x6a, 0x65, 0x63, + 0x74, 0x5f, 0x67, 0x65, 0x74, 0x5f, 0x61, 0x74, 0x74, 0x72, 0x73, 0x5f, 0x73, 0x74, 0x61, 0x72, + 0x74, 0x18, 0x1e, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2f, 0x2e, 0x65, 0x6e, 0x63, 0x6f, 0x72, 0x65, + 0x2e, 0x65, 0x6e, 0x67, 0x69, 0x6e, 0x65, 0x2e, 0x74, 0x72, 0x61, 0x63, 0x65, 0x32, 0x2e, 0x42, + 0x75, 0x63, 0x6b, 0x65, 0x74, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x47, 0x65, 0x74, 0x41, 0x74, + 0x74, 0x72, 0x73, 0x53, 0x74, 0x61, 0x72, 0x74, 0x48, 0x00, 0x52, 0x19, 0x62, 0x75, 0x63, 0x6b, + 0x65, 0x74, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x47, 0x65, 0x74, 0x41, 0x74, 0x74, 0x72, 0x73, + 0x53, 0x74, 0x61, 0x72, 0x74, 0x12, 0x6d, 0x0a, 0x1b, 0x62, 0x75, 0x63, 0x6b, 0x65, 0x74, 0x5f, + 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x5f, 0x67, 0x65, 0x74, 0x5f, 0x61, 0x74, 0x74, 0x72, 0x73, + 0x5f, 0x65, 0x6e, 0x64, 0x18, 0x1f, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2d, 0x2e, 0x65, 0x6e, 0x63, + 0x6f, 0x72, 0x65, 0x2e, 0x65, 0x6e, 0x67, 0x69, 0x6e, 0x65, 0x2e, 0x74, 0x72, 0x61, 0x63, 0x65, + 0x32, 0x2e, 0x42, 0x75, 0x63, 0x6b, 0x65, 0x74, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x47, 0x65, + 0x74, 0x41, 0x74, 0x74, 0x72, 0x73, 0x45, 0x6e, 0x64, 0x48, 0x00, 0x52, 0x17, 0x62, 0x75, 0x63, + 0x6b, 0x65, 0x74, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x47, 0x65, 0x74, 0x41, 0x74, 0x74, 0x72, + 0x73, 0x45, 0x6e, 0x64, 0x12, 0x69, 0x0a, 0x19, 0x62, 0x75, 0x63, 0x6b, 0x65, 0x74, 0x5f, 0x6c, + 0x69, 0x73, 0x74, 0x5f, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x73, 0x5f, 0x73, 0x74, 0x61, 0x72, + 0x74, 0x18, 0x20, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2c, 0x2e, 0x65, 0x6e, 0x63, 0x6f, 0x72, 0x65, + 0x2e, 0x65, 0x6e, 0x67, 0x69, 0x6e, 0x65, 0x2e, 0x74, 0x72, 0x61, 0x63, 0x65, 0x32, 0x2e, 0x42, + 0x75, 0x63, 0x6b, 0x65, 0x74, 0x4c, 0x69, 0x73, 0x74, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x73, + 0x53, 0x74, 0x61, 0x72, 0x74, 0x48, 0x00, 0x52, 0x16, 0x62, 0x75, 0x63, 0x6b, 0x65, 0x74, 0x4c, + 0x69, 0x73, 0x74, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x73, 0x53, 0x74, 0x61, 0x72, 0x74, 0x12, + 0x63, 0x0a, 0x17, 0x62, 0x75, 0x63, 0x6b, 0x65, 0x74, 0x5f, 0x6c, 0x69, 0x73, 0x74, 0x5f, 0x6f, + 0x62, 0x6a, 0x65, 0x63, 0x74, 0x73, 0x5f, 0x65, 0x6e, 0x64, 0x18, 0x21, 0x20, 0x01, 0x28, 0x0b, + 0x32, 0x2a, 0x2e, 0x65, 0x6e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x65, 0x6e, 0x67, 0x69, 0x6e, 0x65, + 0x2e, 0x74, 0x72, 0x61, 0x63, 0x65, 0x32, 0x2e, 0x42, 0x75, 0x63, 0x6b, 0x65, 0x74, 0x4c, 0x69, + 0x73, 0x74, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x73, 0x45, 0x6e, 0x64, 0x48, 0x00, 0x52, 0x14, + 0x62, 0x75, 0x63, 0x6b, 0x65, 0x74, 0x4c, 0x69, 0x73, 0x74, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, + 0x73, 0x45, 0x6e, 0x64, 0x12, 0x6f, 0x0a, 0x1b, 0x62, 0x75, 0x63, 0x6b, 0x65, 0x74, 0x5f, 0x64, + 0x65, 0x6c, 0x65, 0x74, 0x65, 0x5f, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x73, 0x5f, 0x73, 0x74, + 0x61, 0x72, 0x74, 0x18, 0x22, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2e, 0x2e, 0x65, 0x6e, 0x63, 0x6f, + 0x72, 0x65, 0x2e, 0x65, 0x6e, 0x67, 0x69, 0x6e, 0x65, 0x2e, 0x74, 0x72, 0x61, 0x63, 0x65, 0x32, + 0x2e, 0x42, 0x75, 0x63, 0x6b, 0x65, 0x74, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x4f, 0x62, 0x6a, + 0x65, 0x63, 0x74, 0x73, 0x53, 0x74, 0x61, 0x72, 0x74, 0x48, 0x00, 0x52, 0x18, 0x62, 0x75, 0x63, + 0x6b, 0x65, 0x74, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x73, + 0x53, 0x74, 0x61, 0x72, 0x74, 0x12, 0x69, 0x0a, 0x19, 0x62, 0x75, 0x63, 0x6b, 0x65, 0x74, 0x5f, + 0x64, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x5f, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x73, 0x5f, 0x65, + 0x6e, 0x64, 0x18, 0x23, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2c, 0x2e, 0x65, 0x6e, 0x63, 0x6f, 0x72, + 0x65, 0x2e, 0x65, 0x6e, 0x67, 0x69, 0x6e, 0x65, 0x2e, 0x74, 0x72, 0x61, 0x63, 0x65, 0x32, 0x2e, + 0x42, 0x75, 0x63, 0x6b, 0x65, 0x74, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x4f, 0x62, 0x6a, 0x65, + 0x63, 0x74, 0x73, 0x45, 0x6e, 0x64, 0x48, 0x00, 0x52, 0x16, 0x62, 0x75, 0x63, 0x6b, 0x65, 0x74, + 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x73, 0x45, 0x6e, 0x64, + 0x42, 0x06, 0x0a, 0x04, 0x64, 0x61, 0x74, 0x61, 0x42, 0x0a, 0x0a, 0x08, 0x5f, 0x64, 0x65, 0x66, + 0x5f, 0x6c, 0x6f, 0x63, 0x42, 0x17, 0x0a, 0x15, 0x5f, 0x63, 0x6f, 0x72, 0x72, 0x65, 0x6c, 0x61, + 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x5f, 0x69, 0x64, 0x22, 0xa8, 0x01, + 0x0a, 0x0c, 0x52, 0x50, 0x43, 0x43, 0x61, 0x6c, 0x6c, 0x53, 0x74, 0x61, 0x72, 0x74, 0x12, 0x2e, + 0x0a, 0x13, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, + 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x11, 0x74, 0x61, 0x72, + 0x67, 0x65, 0x74, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x30, + 0x0a, 0x14, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x5f, 0x65, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, + 0x74, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x12, 0x74, 0x61, + 0x72, 0x67, 0x65, 0x74, 0x45, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x4e, 0x61, 0x6d, 0x65, + 0x12, 0x36, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x63, 0x6b, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x20, 0x2e, 0x65, 0x6e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x65, 0x6e, 0x67, 0x69, 0x6e, 0x65, 0x2e, + 0x74, 0x72, 0x61, 0x63, 0x65, 0x32, 0x2e, 0x53, 0x74, 0x61, 0x63, 0x6b, 0x54, 0x72, 0x61, 0x63, + 0x65, 0x52, 0x05, 0x73, 0x74, 0x61, 0x63, 0x6b, 0x22, 0x48, 0x0a, 0x0a, 0x52, 0x50, 0x43, 0x43, + 0x61, 0x6c, 0x6c, 0x45, 0x6e, 0x64, 0x12, 0x32, 0x0a, 0x03, 0x65, 0x72, 0x72, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x65, 0x6e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x65, 0x6e, 0x67, + 0x69, 0x6e, 0x65, 0x2e, 0x74, 0x72, 0x61, 0x63, 0x65, 0x32, 0x2e, 0x45, 0x72, 0x72, 0x6f, 0x72, + 0x48, 0x00, 0x52, 0x03, 0x65, 0x72, 0x72, 0x88, 0x01, 0x01, 0x42, 0x06, 0x0a, 0x04, 0x5f, 0x65, + 0x72, 0x72, 0x22, 0x10, 0x0a, 0x0e, 0x47, 0x6f, 0x72, 0x6f, 0x75, 0x74, 0x69, 0x6e, 0x65, 0x53, + 0x74, 0x61, 0x72, 0x74, 0x22, 0x0e, 0x0a, 0x0c, 0x47, 0x6f, 0x72, 0x6f, 0x75, 0x74, 0x69, 0x6e, + 0x65, 0x45, 0x6e, 0x64, 0x22, 0x4c, 0x0a, 0x12, 0x44, 0x42, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x61, + 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x74, 0x61, 0x72, 0x74, 0x12, 0x36, 0x0a, 0x05, 0x73, 0x74, + 0x61, 0x63, 0x6b, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x20, 0x2e, 0x65, 0x6e, 0x63, 0x6f, + 0x72, 0x65, 0x2e, 0x65, 0x6e, 0x67, 0x69, 0x6e, 0x65, 0x2e, 0x74, 0x72, 0x61, 0x63, 0x65, 0x32, + 0x2e, 0x53, 0x74, 0x61, 0x63, 0x6b, 0x54, 0x72, 0x61, 0x63, 0x65, 0x52, 0x05, 0x73, 0x74, 0x61, + 0x63, 0x6b, 0x22, 0x89, 0x02, 0x0a, 0x10, 0x44, 0x42, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, + 0x74, 0x69, 0x6f, 0x6e, 0x45, 0x6e, 0x64, 0x12, 0x55, 0x0a, 0x0a, 0x63, 0x6f, 0x6d, 0x70, 0x6c, + 0x65, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x35, 0x2e, 0x65, 0x6e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x65, 0x6e, 0x67, 0x69, 0x6e, 0x65, 0x2e, 0x74, 0x72, 0x61, 0x63, - 0x65, 0x32, 0x2e, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x48, 0x00, 0x52, 0x03, 0x65, 0x72, 0x72, 0x88, - 0x01, 0x01, 0x22, 0x2a, 0x0a, 0x0e, 0x43, 0x6f, 0x6d, 0x70, 0x6c, 0x65, 0x74, 0x69, 0x6f, 0x6e, - 0x54, 0x79, 0x70, 0x65, 0x12, 0x0c, 0x0a, 0x08, 0x52, 0x4f, 0x4c, 0x4c, 0x42, 0x41, 0x43, 0x4b, - 0x10, 0x00, 0x12, 0x0a, 0x0a, 0x06, 0x43, 0x4f, 0x4d, 0x4d, 0x49, 0x54, 0x10, 0x01, 0x42, 0x06, - 0x0a, 0x04, 0x5f, 0x65, 0x72, 0x72, 0x22, 0x5c, 0x0a, 0x0c, 0x44, 0x42, 0x51, 0x75, 0x65, 0x72, - 0x79, 0x53, 0x74, 0x61, 0x72, 0x74, 0x12, 0x14, 0x0a, 0x05, 0x71, 0x75, 0x65, 0x72, 0x79, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x71, 0x75, 0x65, 0x72, 0x79, 0x12, 0x36, 0x0a, 0x05, - 0x73, 0x74, 0x61, 0x63, 0x6b, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x20, 0x2e, 0x65, 0x6e, + 0x65, 0x32, 0x2e, 0x44, 0x42, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, + 0x45, 0x6e, 0x64, 0x2e, 0x43, 0x6f, 0x6d, 0x70, 0x6c, 0x65, 0x74, 0x69, 0x6f, 0x6e, 0x54, 0x79, + 0x70, 0x65, 0x52, 0x0a, 0x63, 0x6f, 0x6d, 0x70, 0x6c, 0x65, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x36, + 0x0a, 0x05, 0x73, 0x74, 0x61, 0x63, 0x6b, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x20, 0x2e, + 0x65, 0x6e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x65, 0x6e, 0x67, 0x69, 0x6e, 0x65, 0x2e, 0x74, 0x72, + 0x61, 0x63, 0x65, 0x32, 0x2e, 0x53, 0x74, 0x61, 0x63, 0x6b, 0x54, 0x72, 0x61, 0x63, 0x65, 0x52, + 0x05, 0x73, 0x74, 0x61, 0x63, 0x6b, 0x12, 0x32, 0x0a, 0x03, 0x65, 0x72, 0x72, 0x18, 0x03, 0x20, + 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x65, 0x6e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x65, 0x6e, 0x67, + 0x69, 0x6e, 0x65, 0x2e, 0x74, 0x72, 0x61, 0x63, 0x65, 0x32, 0x2e, 0x45, 0x72, 0x72, 0x6f, 0x72, + 0x48, 0x00, 0x52, 0x03, 0x65, 0x72, 0x72, 0x88, 0x01, 0x01, 0x22, 0x2a, 0x0a, 0x0e, 0x43, 0x6f, + 0x6d, 0x70, 0x6c, 0x65, 0x74, 0x69, 0x6f, 0x6e, 0x54, 0x79, 0x70, 0x65, 0x12, 0x0c, 0x0a, 0x08, + 0x52, 0x4f, 0x4c, 0x4c, 0x42, 0x41, 0x43, 0x4b, 0x10, 0x00, 0x12, 0x0a, 0x0a, 0x06, 0x43, 0x4f, + 0x4d, 0x4d, 0x49, 0x54, 0x10, 0x01, 0x42, 0x06, 0x0a, 0x04, 0x5f, 0x65, 0x72, 0x72, 0x22, 0x5c, + 0x0a, 0x0c, 0x44, 0x42, 0x51, 0x75, 0x65, 0x72, 0x79, 0x53, 0x74, 0x61, 0x72, 0x74, 0x12, 0x14, + 0x0a, 0x05, 0x71, 0x75, 0x65, 0x72, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x71, + 0x75, 0x65, 0x72, 0x79, 0x12, 0x36, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x63, 0x6b, 0x18, 0x02, 0x20, + 0x01, 0x28, 0x0b, 0x32, 0x20, 0x2e, 0x65, 0x6e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x65, 0x6e, 0x67, + 0x69, 0x6e, 0x65, 0x2e, 0x74, 0x72, 0x61, 0x63, 0x65, 0x32, 0x2e, 0x53, 0x74, 0x61, 0x63, 0x6b, + 0x54, 0x72, 0x61, 0x63, 0x65, 0x52, 0x05, 0x73, 0x74, 0x61, 0x63, 0x6b, 0x22, 0x48, 0x0a, 0x0a, + 0x44, 0x42, 0x51, 0x75, 0x65, 0x72, 0x79, 0x45, 0x6e, 0x64, 0x12, 0x32, 0x0a, 0x03, 0x65, 0x72, + 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x65, 0x6e, 0x63, 0x6f, 0x72, 0x65, + 0x2e, 0x65, 0x6e, 0x67, 0x69, 0x6e, 0x65, 0x2e, 0x74, 0x72, 0x61, 0x63, 0x65, 0x32, 0x2e, 0x45, + 0x72, 0x72, 0x6f, 0x72, 0x48, 0x00, 0x52, 0x03, 0x65, 0x72, 0x72, 0x88, 0x01, 0x01, 0x42, 0x06, + 0x0a, 0x04, 0x5f, 0x65, 0x72, 0x72, 0x22, 0x7c, 0x0a, 0x12, 0x50, 0x75, 0x62, 0x73, 0x75, 0x62, + 0x50, 0x75, 0x62, 0x6c, 0x69, 0x73, 0x68, 0x53, 0x74, 0x61, 0x72, 0x74, 0x12, 0x14, 0x0a, 0x05, + 0x74, 0x6f, 0x70, 0x69, 0x63, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x74, 0x6f, 0x70, + 0x69, 0x63, 0x12, 0x18, 0x0a, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, 0x02, 0x20, + 0x01, 0x28, 0x0c, 0x52, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x36, 0x0a, 0x05, + 0x73, 0x74, 0x61, 0x63, 0x6b, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x20, 0x2e, 0x65, 0x6e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x65, 0x6e, 0x67, 0x69, 0x6e, 0x65, 0x2e, 0x74, 0x72, 0x61, 0x63, 0x65, 0x32, 0x2e, 0x53, 0x74, 0x61, 0x63, 0x6b, 0x54, 0x72, 0x61, 0x63, 0x65, 0x52, 0x05, 0x73, - 0x74, 0x61, 0x63, 0x6b, 0x22, 0x48, 0x0a, 0x0a, 0x44, 0x42, 0x51, 0x75, 0x65, 0x72, 0x79, 0x45, - 0x6e, 0x64, 0x12, 0x32, 0x0a, 0x03, 0x65, 0x72, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, - 0x1b, 0x2e, 0x65, 0x6e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x65, 0x6e, 0x67, 0x69, 0x6e, 0x65, 0x2e, - 0x74, 0x72, 0x61, 0x63, 0x65, 0x32, 0x2e, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x48, 0x00, 0x52, 0x03, - 0x65, 0x72, 0x72, 0x88, 0x01, 0x01, 0x42, 0x06, 0x0a, 0x04, 0x5f, 0x65, 0x72, 0x72, 0x22, 0x7c, - 0x0a, 0x12, 0x50, 0x75, 0x62, 0x73, 0x75, 0x62, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x73, 0x68, 0x53, - 0x74, 0x61, 0x72, 0x74, 0x12, 0x14, 0x0a, 0x05, 0x74, 0x6f, 0x70, 0x69, 0x63, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x05, 0x74, 0x6f, 0x70, 0x69, 0x63, 0x12, 0x18, 0x0a, 0x07, 0x6d, 0x65, - 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x07, 0x6d, 0x65, 0x73, - 0x73, 0x61, 0x67, 0x65, 0x12, 0x36, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x63, 0x6b, 0x18, 0x03, 0x20, + 0x74, 0x61, 0x63, 0x6b, 0x22, 0x81, 0x01, 0x0a, 0x10, 0x50, 0x75, 0x62, 0x73, 0x75, 0x62, 0x50, + 0x75, 0x62, 0x6c, 0x69, 0x73, 0x68, 0x45, 0x6e, 0x64, 0x12, 0x22, 0x0a, 0x0a, 0x6d, 0x65, 0x73, + 0x73, 0x61, 0x67, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x48, 0x00, 0x52, + 0x09, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x49, 0x64, 0x88, 0x01, 0x01, 0x12, 0x32, 0x0a, + 0x03, 0x65, 0x72, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x65, 0x6e, 0x63, + 0x6f, 0x72, 0x65, 0x2e, 0x65, 0x6e, 0x67, 0x69, 0x6e, 0x65, 0x2e, 0x74, 0x72, 0x61, 0x63, 0x65, + 0x32, 0x2e, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x48, 0x01, 0x52, 0x03, 0x65, 0x72, 0x72, 0x88, 0x01, + 0x01, 0x42, 0x0d, 0x0a, 0x0b, 0x5f, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x5f, 0x69, 0x64, + 0x42, 0x06, 0x0a, 0x04, 0x5f, 0x65, 0x72, 0x72, 0x22, 0x2c, 0x0a, 0x10, 0x53, 0x65, 0x72, 0x76, + 0x69, 0x63, 0x65, 0x49, 0x6e, 0x69, 0x74, 0x53, 0x74, 0x61, 0x72, 0x74, 0x12, 0x18, 0x0a, 0x07, + 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x73, + 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x22, 0x4c, 0x0a, 0x0e, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, + 0x65, 0x49, 0x6e, 0x69, 0x74, 0x45, 0x6e, 0x64, 0x12, 0x32, 0x0a, 0x03, 0x65, 0x72, 0x72, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x65, 0x6e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x65, + 0x6e, 0x67, 0x69, 0x6e, 0x65, 0x2e, 0x74, 0x72, 0x61, 0x63, 0x65, 0x32, 0x2e, 0x45, 0x72, 0x72, + 0x6f, 0x72, 0x48, 0x00, 0x52, 0x03, 0x65, 0x72, 0x72, 0x88, 0x01, 0x01, 0x42, 0x06, 0x0a, 0x04, + 0x5f, 0x65, 0x72, 0x72, 0x22, 0x90, 0x01, 0x0a, 0x0e, 0x43, 0x61, 0x63, 0x68, 0x65, 0x43, 0x61, + 0x6c, 0x6c, 0x53, 0x74, 0x61, 0x72, 0x74, 0x12, 0x1c, 0x0a, 0x09, 0x6f, 0x70, 0x65, 0x72, 0x61, + 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x6f, 0x70, 0x65, 0x72, + 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x12, 0x0a, 0x04, 0x6b, 0x65, 0x79, 0x73, 0x18, 0x02, 0x20, + 0x03, 0x28, 0x09, 0x52, 0x04, 0x6b, 0x65, 0x79, 0x73, 0x12, 0x14, 0x0a, 0x05, 0x77, 0x72, 0x69, + 0x74, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x05, 0x77, 0x72, 0x69, 0x74, 0x65, 0x12, + 0x36, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x63, 0x6b, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x20, + 0x2e, 0x65, 0x6e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x65, 0x6e, 0x67, 0x69, 0x6e, 0x65, 0x2e, 0x74, + 0x72, 0x61, 0x63, 0x65, 0x32, 0x2e, 0x53, 0x74, 0x61, 0x63, 0x6b, 0x54, 0x72, 0x61, 0x63, 0x65, + 0x52, 0x05, 0x73, 0x74, 0x61, 0x63, 0x6b, 0x22, 0xd4, 0x01, 0x0a, 0x0c, 0x43, 0x61, 0x63, 0x68, + 0x65, 0x43, 0x61, 0x6c, 0x6c, 0x45, 0x6e, 0x64, 0x12, 0x41, 0x0a, 0x06, 0x72, 0x65, 0x73, 0x75, + 0x6c, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x29, 0x2e, 0x65, 0x6e, 0x63, 0x6f, 0x72, + 0x65, 0x2e, 0x65, 0x6e, 0x67, 0x69, 0x6e, 0x65, 0x2e, 0x74, 0x72, 0x61, 0x63, 0x65, 0x32, 0x2e, + 0x43, 0x61, 0x63, 0x68, 0x65, 0x43, 0x61, 0x6c, 0x6c, 0x45, 0x6e, 0x64, 0x2e, 0x52, 0x65, 0x73, + 0x75, 0x6c, 0x74, 0x52, 0x06, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x12, 0x32, 0x0a, 0x03, 0x65, + 0x72, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x65, 0x6e, 0x63, 0x6f, 0x72, + 0x65, 0x2e, 0x65, 0x6e, 0x67, 0x69, 0x6e, 0x65, 0x2e, 0x74, 0x72, 0x61, 0x63, 0x65, 0x32, 0x2e, + 0x45, 0x72, 0x72, 0x6f, 0x72, 0x48, 0x00, 0x52, 0x03, 0x65, 0x72, 0x72, 0x88, 0x01, 0x01, 0x22, + 0x45, 0x0a, 0x06, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x12, 0x0b, 0x0a, 0x07, 0x55, 0x4e, 0x4b, + 0x4e, 0x4f, 0x57, 0x4e, 0x10, 0x00, 0x12, 0x06, 0x0a, 0x02, 0x4f, 0x4b, 0x10, 0x01, 0x12, 0x0f, + 0x0a, 0x0b, 0x4e, 0x4f, 0x5f, 0x53, 0x55, 0x43, 0x48, 0x5f, 0x4b, 0x45, 0x59, 0x10, 0x02, 0x12, + 0x0c, 0x0a, 0x08, 0x43, 0x4f, 0x4e, 0x46, 0x4c, 0x49, 0x43, 0x54, 0x10, 0x03, 0x12, 0x07, 0x0a, + 0x03, 0x45, 0x52, 0x52, 0x10, 0x04, 0x42, 0x06, 0x0a, 0x04, 0x5f, 0x65, 0x72, 0x72, 0x22, 0xc5, + 0x01, 0x0a, 0x17, 0x42, 0x75, 0x63, 0x6b, 0x65, 0x74, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x55, + 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x53, 0x74, 0x61, 0x72, 0x74, 0x12, 0x16, 0x0a, 0x06, 0x62, 0x75, + 0x63, 0x6b, 0x65, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x62, 0x75, 0x63, 0x6b, + 0x65, 0x74, 0x12, 0x16, 0x0a, 0x06, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x06, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x12, 0x42, 0x0a, 0x05, 0x61, 0x74, + 0x74, 0x72, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2c, 0x2e, 0x65, 0x6e, 0x63, 0x6f, + 0x72, 0x65, 0x2e, 0x65, 0x6e, 0x67, 0x69, 0x6e, 0x65, 0x2e, 0x74, 0x72, 0x61, 0x63, 0x65, 0x32, + 0x2e, 0x42, 0x75, 0x63, 0x6b, 0x65, 0x74, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x41, 0x74, 0x74, + 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x52, 0x05, 0x61, 0x74, 0x74, 0x72, 0x73, 0x12, 0x36, + 0x0a, 0x05, 0x73, 0x74, 0x61, 0x63, 0x6b, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x20, 0x2e, + 0x65, 0x6e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x65, 0x6e, 0x67, 0x69, 0x6e, 0x65, 0x2e, 0x74, 0x72, + 0x61, 0x63, 0x65, 0x32, 0x2e, 0x53, 0x74, 0x61, 0x63, 0x6b, 0x54, 0x72, 0x61, 0x63, 0x65, 0x52, + 0x05, 0x73, 0x74, 0x61, 0x63, 0x6b, 0x22, 0x75, 0x0a, 0x15, 0x42, 0x75, 0x63, 0x6b, 0x65, 0x74, + 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x55, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x45, 0x6e, 0x64, 0x12, + 0x32, 0x0a, 0x03, 0x65, 0x72, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x65, + 0x6e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x65, 0x6e, 0x67, 0x69, 0x6e, 0x65, 0x2e, 0x74, 0x72, 0x61, + 0x63, 0x65, 0x32, 0x2e, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x48, 0x00, 0x52, 0x03, 0x65, 0x72, 0x72, + 0x88, 0x01, 0x01, 0x12, 0x17, 0x0a, 0x04, 0x73, 0x69, 0x7a, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x04, 0x48, 0x01, 0x52, 0x04, 0x73, 0x69, 0x7a, 0x65, 0x88, 0x01, 0x01, 0x42, 0x06, 0x0a, 0x04, + 0x5f, 0x65, 0x72, 0x72, 0x42, 0x07, 0x0a, 0x05, 0x5f, 0x73, 0x69, 0x7a, 0x65, 0x22, 0xae, 0x01, + 0x0a, 0x19, 0x42, 0x75, 0x63, 0x6b, 0x65, 0x74, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x44, 0x6f, + 0x77, 0x6e, 0x6c, 0x6f, 0x61, 0x64, 0x53, 0x74, 0x61, 0x72, 0x74, 0x12, 0x16, 0x0a, 0x06, 0x62, + 0x75, 0x63, 0x6b, 0x65, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x62, 0x75, 0x63, + 0x6b, 0x65, 0x74, 0x12, 0x16, 0x0a, 0x06, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x18, 0x02, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x06, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x12, 0x1d, 0x0a, 0x07, 0x76, + 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x48, 0x00, 0x52, 0x07, + 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x88, 0x01, 0x01, 0x12, 0x36, 0x0a, 0x05, 0x73, 0x74, + 0x61, 0x63, 0x6b, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x20, 0x2e, 0x65, 0x6e, 0x63, 0x6f, + 0x72, 0x65, 0x2e, 0x65, 0x6e, 0x67, 0x69, 0x6e, 0x65, 0x2e, 0x74, 0x72, 0x61, 0x63, 0x65, 0x32, + 0x2e, 0x53, 0x74, 0x61, 0x63, 0x6b, 0x54, 0x72, 0x61, 0x63, 0x65, 0x52, 0x05, 0x73, 0x74, 0x61, + 0x63, 0x6b, 0x42, 0x0a, 0x0a, 0x08, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x22, 0x77, + 0x0a, 0x17, 0x42, 0x75, 0x63, 0x6b, 0x65, 0x74, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x44, 0x6f, + 0x77, 0x6e, 0x6c, 0x6f, 0x61, 0x64, 0x45, 0x6e, 0x64, 0x12, 0x32, 0x0a, 0x03, 0x65, 0x72, 0x72, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x65, 0x6e, 0x63, 0x6f, 0x72, 0x65, 0x2e, + 0x65, 0x6e, 0x67, 0x69, 0x6e, 0x65, 0x2e, 0x74, 0x72, 0x61, 0x63, 0x65, 0x32, 0x2e, 0x45, 0x72, + 0x72, 0x6f, 0x72, 0x48, 0x00, 0x52, 0x03, 0x65, 0x72, 0x72, 0x88, 0x01, 0x01, 0x12, 0x17, 0x0a, + 0x04, 0x73, 0x69, 0x7a, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x48, 0x01, 0x52, 0x04, 0x73, + 0x69, 0x7a, 0x65, 0x88, 0x01, 0x01, 0x42, 0x06, 0x0a, 0x04, 0x5f, 0x65, 0x72, 0x72, 0x42, 0x07, + 0x0a, 0x05, 0x5f, 0x73, 0x69, 0x7a, 0x65, 0x22, 0xae, 0x01, 0x0a, 0x19, 0x42, 0x75, 0x63, 0x6b, + 0x65, 0x74, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x47, 0x65, 0x74, 0x41, 0x74, 0x74, 0x72, 0x73, + 0x53, 0x74, 0x61, 0x72, 0x74, 0x12, 0x16, 0x0a, 0x06, 0x62, 0x75, 0x63, 0x6b, 0x65, 0x74, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x62, 0x75, 0x63, 0x6b, 0x65, 0x74, 0x12, 0x16, 0x0a, + 0x06, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x6f, + 0x62, 0x6a, 0x65, 0x63, 0x74, 0x12, 0x1d, 0x0a, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, + 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x48, 0x00, 0x52, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, + 0x6e, 0x88, 0x01, 0x01, 0x12, 0x36, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x63, 0x6b, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x20, 0x2e, 0x65, 0x6e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x65, 0x6e, 0x67, 0x69, 0x6e, 0x65, 0x2e, 0x74, 0x72, 0x61, 0x63, 0x65, 0x32, 0x2e, 0x53, 0x74, 0x61, 0x63, 0x6b, - 0x54, 0x72, 0x61, 0x63, 0x65, 0x52, 0x05, 0x73, 0x74, 0x61, 0x63, 0x6b, 0x22, 0x81, 0x01, 0x0a, - 0x10, 0x50, 0x75, 0x62, 0x73, 0x75, 0x62, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x73, 0x68, 0x45, 0x6e, - 0x64, 0x12, 0x22, 0x0a, 0x0a, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x5f, 0x69, 0x64, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x09, 0x48, 0x00, 0x52, 0x09, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, - 0x49, 0x64, 0x88, 0x01, 0x01, 0x12, 0x32, 0x0a, 0x03, 0x65, 0x72, 0x72, 0x18, 0x02, 0x20, 0x01, - 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x65, 0x6e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x65, 0x6e, 0x67, 0x69, - 0x6e, 0x65, 0x2e, 0x74, 0x72, 0x61, 0x63, 0x65, 0x32, 0x2e, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x48, - 0x01, 0x52, 0x03, 0x65, 0x72, 0x72, 0x88, 0x01, 0x01, 0x42, 0x0d, 0x0a, 0x0b, 0x5f, 0x6d, 0x65, - 0x73, 0x73, 0x61, 0x67, 0x65, 0x5f, 0x69, 0x64, 0x42, 0x06, 0x0a, 0x04, 0x5f, 0x65, 0x72, 0x72, - 0x22, 0x2c, 0x0a, 0x10, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x49, 0x6e, 0x69, 0x74, 0x53, - 0x74, 0x61, 0x72, 0x74, 0x12, 0x18, 0x0a, 0x07, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x22, 0x4c, - 0x0a, 0x0e, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x49, 0x6e, 0x69, 0x74, 0x45, 0x6e, 0x64, + 0x54, 0x72, 0x61, 0x63, 0x65, 0x52, 0x05, 0x73, 0x74, 0x61, 0x63, 0x6b, 0x42, 0x0a, 0x0a, 0x08, + 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x22, 0xa8, 0x01, 0x0a, 0x17, 0x42, 0x75, 0x63, + 0x6b, 0x65, 0x74, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x47, 0x65, 0x74, 0x41, 0x74, 0x74, 0x72, + 0x73, 0x45, 0x6e, 0x64, 0x12, 0x32, 0x0a, 0x03, 0x65, 0x72, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x1b, 0x2e, 0x65, 0x6e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x65, 0x6e, 0x67, 0x69, 0x6e, + 0x65, 0x2e, 0x74, 0x72, 0x61, 0x63, 0x65, 0x32, 0x2e, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x48, 0x00, + 0x52, 0x03, 0x65, 0x72, 0x72, 0x88, 0x01, 0x01, 0x12, 0x47, 0x0a, 0x05, 0x61, 0x74, 0x74, 0x72, + 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2c, 0x2e, 0x65, 0x6e, 0x63, 0x6f, 0x72, 0x65, + 0x2e, 0x65, 0x6e, 0x67, 0x69, 0x6e, 0x65, 0x2e, 0x74, 0x72, 0x61, 0x63, 0x65, 0x32, 0x2e, 0x42, + 0x75, 0x63, 0x6b, 0x65, 0x74, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x41, 0x74, 0x74, 0x72, 0x69, + 0x62, 0x75, 0x74, 0x65, 0x73, 0x48, 0x01, 0x52, 0x05, 0x61, 0x74, 0x74, 0x72, 0x73, 0x88, 0x01, + 0x01, 0x42, 0x06, 0x0a, 0x04, 0x5f, 0x65, 0x72, 0x72, 0x42, 0x08, 0x0a, 0x06, 0x5f, 0x61, 0x74, + 0x74, 0x72, 0x73, 0x22, 0x90, 0x01, 0x0a, 0x16, 0x42, 0x75, 0x63, 0x6b, 0x65, 0x74, 0x4c, 0x69, + 0x73, 0x74, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x73, 0x53, 0x74, 0x61, 0x72, 0x74, 0x12, 0x16, + 0x0a, 0x06, 0x62, 0x75, 0x63, 0x6b, 0x65, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, + 0x62, 0x75, 0x63, 0x6b, 0x65, 0x74, 0x12, 0x1b, 0x0a, 0x06, 0x70, 0x72, 0x65, 0x66, 0x69, 0x78, + 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x48, 0x00, 0x52, 0x06, 0x70, 0x72, 0x65, 0x66, 0x69, 0x78, + 0x88, 0x01, 0x01, 0x12, 0x36, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x63, 0x6b, 0x18, 0x03, 0x20, 0x01, + 0x28, 0x0b, 0x32, 0x20, 0x2e, 0x65, 0x6e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x65, 0x6e, 0x67, 0x69, + 0x6e, 0x65, 0x2e, 0x74, 0x72, 0x61, 0x63, 0x65, 0x32, 0x2e, 0x53, 0x74, 0x61, 0x63, 0x6b, 0x54, + 0x72, 0x61, 0x63, 0x65, 0x52, 0x05, 0x73, 0x74, 0x61, 0x63, 0x6b, 0x42, 0x09, 0x0a, 0x07, 0x5f, + 0x70, 0x72, 0x65, 0x66, 0x69, 0x78, 0x22, 0x89, 0x01, 0x0a, 0x14, 0x42, 0x75, 0x63, 0x6b, 0x65, + 0x74, 0x4c, 0x69, 0x73, 0x74, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x73, 0x45, 0x6e, 0x64, 0x12, + 0x32, 0x0a, 0x03, 0x65, 0x72, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x65, + 0x6e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x65, 0x6e, 0x67, 0x69, 0x6e, 0x65, 0x2e, 0x74, 0x72, 0x61, + 0x63, 0x65, 0x32, 0x2e, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x48, 0x00, 0x52, 0x03, 0x65, 0x72, 0x72, + 0x88, 0x01, 0x01, 0x12, 0x1a, 0x0a, 0x08, 0x6f, 0x62, 0x73, 0x65, 0x72, 0x76, 0x65, 0x64, 0x18, + 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, 0x08, 0x6f, 0x62, 0x73, 0x65, 0x72, 0x76, 0x65, 0x64, 0x12, + 0x19, 0x0a, 0x08, 0x68, 0x61, 0x73, 0x5f, 0x6d, 0x6f, 0x72, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, + 0x08, 0x52, 0x07, 0x68, 0x61, 0x73, 0x4d, 0x6f, 0x72, 0x65, 0x42, 0x06, 0x0a, 0x04, 0x5f, 0x65, + 0x72, 0x72, 0x22, 0xb3, 0x01, 0x0a, 0x18, 0x42, 0x75, 0x63, 0x6b, 0x65, 0x74, 0x44, 0x65, 0x6c, + 0x65, 0x74, 0x65, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x73, 0x53, 0x74, 0x61, 0x72, 0x74, 0x12, + 0x16, 0x0a, 0x06, 0x62, 0x75, 0x63, 0x6b, 0x65, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x06, 0x62, 0x75, 0x63, 0x6b, 0x65, 0x74, 0x12, 0x36, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x63, 0x6b, + 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x20, 0x2e, 0x65, 0x6e, 0x63, 0x6f, 0x72, 0x65, 0x2e, + 0x65, 0x6e, 0x67, 0x69, 0x6e, 0x65, 0x2e, 0x74, 0x72, 0x61, 0x63, 0x65, 0x32, 0x2e, 0x53, 0x74, + 0x61, 0x63, 0x6b, 0x54, 0x72, 0x61, 0x63, 0x65, 0x52, 0x05, 0x73, 0x74, 0x61, 0x63, 0x6b, 0x12, + 0x47, 0x0a, 0x07, 0x65, 0x6e, 0x74, 0x72, 0x69, 0x65, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, + 0x32, 0x2d, 0x2e, 0x65, 0x6e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x65, 0x6e, 0x67, 0x69, 0x6e, 0x65, + 0x2e, 0x74, 0x72, 0x61, 0x63, 0x65, 0x32, 0x2e, 0x42, 0x75, 0x63, 0x6b, 0x65, 0x74, 0x44, 0x65, + 0x6c, 0x65, 0x74, 0x65, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, + 0x07, 0x65, 0x6e, 0x74, 0x72, 0x69, 0x65, 0x73, 0x22, 0x5c, 0x0a, 0x17, 0x42, 0x75, 0x63, 0x6b, + 0x65, 0x74, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x45, 0x6e, + 0x74, 0x72, 0x79, 0x12, 0x16, 0x0a, 0x06, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x06, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x12, 0x1d, 0x0a, 0x07, 0x76, + 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x48, 0x00, 0x52, 0x07, + 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x88, 0x01, 0x01, 0x42, 0x0a, 0x0a, 0x08, 0x5f, 0x76, + 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x22, 0x54, 0x0a, 0x16, 0x42, 0x75, 0x63, 0x6b, 0x65, 0x74, + 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x73, 0x45, 0x6e, 0x64, 0x12, 0x32, 0x0a, 0x03, 0x65, 0x72, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x65, 0x6e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x65, 0x6e, 0x67, 0x69, 0x6e, 0x65, 0x2e, 0x74, 0x72, 0x61, 0x63, 0x65, 0x32, 0x2e, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x48, 0x00, 0x52, 0x03, 0x65, 0x72, - 0x72, 0x88, 0x01, 0x01, 0x42, 0x06, 0x0a, 0x04, 0x5f, 0x65, 0x72, 0x72, 0x22, 0x90, 0x01, 0x0a, - 0x0e, 0x43, 0x61, 0x63, 0x68, 0x65, 0x43, 0x61, 0x6c, 0x6c, 0x53, 0x74, 0x61, 0x72, 0x74, 0x12, - 0x1c, 0x0a, 0x09, 0x6f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x09, 0x6f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x12, 0x0a, - 0x04, 0x6b, 0x65, 0x79, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x09, 0x52, 0x04, 0x6b, 0x65, 0x79, - 0x73, 0x12, 0x14, 0x0a, 0x05, 0x77, 0x72, 0x69, 0x74, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, - 0x52, 0x05, 0x77, 0x72, 0x69, 0x74, 0x65, 0x12, 0x36, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x63, 0x6b, - 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x20, 0x2e, 0x65, 0x6e, 0x63, 0x6f, 0x72, 0x65, 0x2e, - 0x65, 0x6e, 0x67, 0x69, 0x6e, 0x65, 0x2e, 0x74, 0x72, 0x61, 0x63, 0x65, 0x32, 0x2e, 0x53, 0x74, - 0x61, 0x63, 0x6b, 0x54, 0x72, 0x61, 0x63, 0x65, 0x52, 0x05, 0x73, 0x74, 0x61, 0x63, 0x6b, 0x22, - 0xd4, 0x01, 0x0a, 0x0c, 0x43, 0x61, 0x63, 0x68, 0x65, 0x43, 0x61, 0x6c, 0x6c, 0x45, 0x6e, 0x64, - 0x12, 0x41, 0x0a, 0x06, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, - 0x32, 0x29, 0x2e, 0x65, 0x6e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x65, 0x6e, 0x67, 0x69, 0x6e, 0x65, - 0x2e, 0x74, 0x72, 0x61, 0x63, 0x65, 0x32, 0x2e, 0x43, 0x61, 0x63, 0x68, 0x65, 0x43, 0x61, 0x6c, - 0x6c, 0x45, 0x6e, 0x64, 0x2e, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x52, 0x06, 0x72, 0x65, 0x73, - 0x75, 0x6c, 0x74, 0x12, 0x32, 0x0a, 0x03, 0x65, 0x72, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, - 0x32, 0x1b, 0x2e, 0x65, 0x6e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x65, 0x6e, 0x67, 0x69, 0x6e, 0x65, - 0x2e, 0x74, 0x72, 0x61, 0x63, 0x65, 0x32, 0x2e, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x48, 0x00, 0x52, - 0x03, 0x65, 0x72, 0x72, 0x88, 0x01, 0x01, 0x22, 0x45, 0x0a, 0x06, 0x52, 0x65, 0x73, 0x75, 0x6c, - 0x74, 0x12, 0x0b, 0x0a, 0x07, 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x10, 0x00, 0x12, 0x06, - 0x0a, 0x02, 0x4f, 0x4b, 0x10, 0x01, 0x12, 0x0f, 0x0a, 0x0b, 0x4e, 0x4f, 0x5f, 0x53, 0x55, 0x43, - 0x48, 0x5f, 0x4b, 0x45, 0x59, 0x10, 0x02, 0x12, 0x0c, 0x0a, 0x08, 0x43, 0x4f, 0x4e, 0x46, 0x4c, - 0x49, 0x43, 0x54, 0x10, 0x03, 0x12, 0x07, 0x0a, 0x03, 0x45, 0x52, 0x52, 0x10, 0x04, 0x42, 0x06, - 0x0a, 0x04, 0x5f, 0x65, 0x72, 0x72, 0x22, 0x61, 0x0a, 0x0a, 0x42, 0x6f, 0x64, 0x79, 0x53, 0x74, - 0x72, 0x65, 0x61, 0x6d, 0x12, 0x1f, 0x0a, 0x0b, 0x69, 0x73, 0x5f, 0x72, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0a, 0x69, 0x73, 0x52, 0x65, 0x73, - 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1e, 0x0a, 0x0a, 0x6f, 0x76, 0x65, 0x72, 0x66, 0x6c, 0x6f, - 0x77, 0x65, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0a, 0x6f, 0x76, 0x65, 0x72, 0x66, - 0x6c, 0x6f, 0x77, 0x65, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x64, 0x61, 0x74, 0x61, 0x18, 0x03, 0x20, - 0x01, 0x28, 0x0c, 0x52, 0x04, 0x64, 0x61, 0x74, 0x61, 0x22, 0xd5, 0x01, 0x0a, 0x0d, 0x48, 0x54, - 0x54, 0x50, 0x43, 0x61, 0x6c, 0x6c, 0x53, 0x74, 0x61, 0x72, 0x74, 0x12, 0x3b, 0x0a, 0x1a, 0x63, - 0x6f, 0x72, 0x72, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x70, 0x61, 0x72, 0x65, 0x6e, - 0x74, 0x5f, 0x73, 0x70, 0x61, 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, - 0x17, 0x63, 0x6f, 0x72, 0x72, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x50, 0x61, 0x72, 0x65, - 0x6e, 0x74, 0x53, 0x70, 0x61, 0x6e, 0x49, 0x64, 0x12, 0x16, 0x0a, 0x06, 0x6d, 0x65, 0x74, 0x68, - 0x6f, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x6d, 0x65, 0x74, 0x68, 0x6f, 0x64, - 0x12, 0x10, 0x0a, 0x03, 0x75, 0x72, 0x6c, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x75, - 0x72, 0x6c, 0x12, 0x36, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x63, 0x6b, 0x18, 0x04, 0x20, 0x01, 0x28, - 0x0b, 0x32, 0x20, 0x2e, 0x65, 0x6e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x65, 0x6e, 0x67, 0x69, 0x6e, - 0x65, 0x2e, 0x74, 0x72, 0x61, 0x63, 0x65, 0x32, 0x2e, 0x53, 0x74, 0x61, 0x63, 0x6b, 0x54, 0x72, - 0x61, 0x63, 0x65, 0x52, 0x05, 0x73, 0x74, 0x61, 0x63, 0x6b, 0x12, 0x25, 0x0a, 0x0e, 0x73, 0x74, - 0x61, 0x72, 0x74, 0x5f, 0x6e, 0x61, 0x6e, 0x6f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x05, 0x20, 0x01, - 0x28, 0x03, 0x52, 0x0d, 0x73, 0x74, 0x61, 0x72, 0x74, 0x4e, 0x61, 0x6e, 0x6f, 0x74, 0x69, 0x6d, - 0x65, 0x22, 0xc8, 0x01, 0x0a, 0x0b, 0x48, 0x54, 0x54, 0x50, 0x43, 0x61, 0x6c, 0x6c, 0x45, 0x6e, - 0x64, 0x12, 0x24, 0x0a, 0x0b, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x5f, 0x63, 0x6f, 0x64, 0x65, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x48, 0x00, 0x52, 0x0a, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, - 0x43, 0x6f, 0x64, 0x65, 0x88, 0x01, 0x01, 0x12, 0x32, 0x0a, 0x03, 0x65, 0x72, 0x72, 0x18, 0x02, - 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x65, 0x6e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x65, 0x6e, - 0x67, 0x69, 0x6e, 0x65, 0x2e, 0x74, 0x72, 0x61, 0x63, 0x65, 0x32, 0x2e, 0x45, 0x72, 0x72, 0x6f, - 0x72, 0x48, 0x01, 0x52, 0x03, 0x65, 0x72, 0x72, 0x88, 0x01, 0x01, 0x12, 0x47, 0x0a, 0x0c, 0x74, - 0x72, 0x61, 0x63, 0x65, 0x5f, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, - 0x0b, 0x32, 0x24, 0x2e, 0x65, 0x6e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x65, 0x6e, 0x67, 0x69, 0x6e, - 0x65, 0x2e, 0x74, 0x72, 0x61, 0x63, 0x65, 0x32, 0x2e, 0x48, 0x54, 0x54, 0x50, 0x54, 0x72, 0x61, - 0x63, 0x65, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x52, 0x0b, 0x74, 0x72, 0x61, 0x63, 0x65, 0x45, 0x76, - 0x65, 0x6e, 0x74, 0x73, 0x42, 0x0e, 0x0a, 0x0c, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x5f, - 0x63, 0x6f, 0x64, 0x65, 0x42, 0x06, 0x0a, 0x04, 0x5f, 0x65, 0x72, 0x72, 0x22, 0x90, 0x09, 0x0a, - 0x0e, 0x48, 0x54, 0x54, 0x50, 0x54, 0x72, 0x61, 0x63, 0x65, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x12, - 0x1a, 0x0a, 0x08, 0x6e, 0x61, 0x6e, 0x6f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, - 0x03, 0x52, 0x08, 0x6e, 0x61, 0x6e, 0x6f, 0x74, 0x69, 0x6d, 0x65, 0x12, 0x3e, 0x0a, 0x08, 0x67, - 0x65, 0x74, 0x5f, 0x63, 0x6f, 0x6e, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x21, 0x2e, + 0x72, 0x88, 0x01, 0x01, 0x42, 0x06, 0x0a, 0x04, 0x5f, 0x65, 0x72, 0x72, 0x22, 0xc0, 0x01, 0x0a, + 0x16, 0x42, 0x75, 0x63, 0x6b, 0x65, 0x74, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x41, 0x74, 0x74, + 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x12, 0x17, 0x0a, 0x04, 0x73, 0x69, 0x7a, 0x65, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x04, 0x48, 0x00, 0x52, 0x04, 0x73, 0x69, 0x7a, 0x65, 0x88, 0x01, 0x01, + 0x12, 0x1d, 0x0a, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x09, 0x48, 0x01, 0x52, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x88, 0x01, 0x01, 0x12, + 0x17, 0x0a, 0x04, 0x65, 0x74, 0x61, 0x67, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x48, 0x02, 0x52, + 0x04, 0x65, 0x74, 0x61, 0x67, 0x88, 0x01, 0x01, 0x12, 0x26, 0x0a, 0x0c, 0x63, 0x6f, 0x6e, 0x74, + 0x65, 0x6e, 0x74, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x48, 0x03, + 0x52, 0x0b, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x54, 0x79, 0x70, 0x65, 0x88, 0x01, 0x01, + 0x42, 0x07, 0x0a, 0x05, 0x5f, 0x73, 0x69, 0x7a, 0x65, 0x42, 0x0a, 0x0a, 0x08, 0x5f, 0x76, 0x65, + 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x42, 0x07, 0x0a, 0x05, 0x5f, 0x65, 0x74, 0x61, 0x67, 0x42, 0x0f, + 0x0a, 0x0d, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x22, + 0x61, 0x0a, 0x0a, 0x42, 0x6f, 0x64, 0x79, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x12, 0x1f, 0x0a, + 0x0b, 0x69, 0x73, 0x5f, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x08, 0x52, 0x0a, 0x69, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1e, + 0x0a, 0x0a, 0x6f, 0x76, 0x65, 0x72, 0x66, 0x6c, 0x6f, 0x77, 0x65, 0x64, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x08, 0x52, 0x0a, 0x6f, 0x76, 0x65, 0x72, 0x66, 0x6c, 0x6f, 0x77, 0x65, 0x64, 0x12, 0x12, + 0x0a, 0x04, 0x64, 0x61, 0x74, 0x61, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x04, 0x64, 0x61, + 0x74, 0x61, 0x22, 0xd5, 0x01, 0x0a, 0x0d, 0x48, 0x54, 0x54, 0x50, 0x43, 0x61, 0x6c, 0x6c, 0x53, + 0x74, 0x61, 0x72, 0x74, 0x12, 0x3b, 0x0a, 0x1a, 0x63, 0x6f, 0x72, 0x72, 0x65, 0x6c, 0x61, 0x74, + 0x69, 0x6f, 0x6e, 0x5f, 0x70, 0x61, 0x72, 0x65, 0x6e, 0x74, 0x5f, 0x73, 0x70, 0x61, 0x6e, 0x5f, + 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x17, 0x63, 0x6f, 0x72, 0x72, 0x65, 0x6c, + 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x50, 0x61, 0x72, 0x65, 0x6e, 0x74, 0x53, 0x70, 0x61, 0x6e, 0x49, + 0x64, 0x12, 0x16, 0x0a, 0x06, 0x6d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x06, 0x6d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x12, 0x10, 0x0a, 0x03, 0x75, 0x72, 0x6c, + 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x75, 0x72, 0x6c, 0x12, 0x36, 0x0a, 0x05, 0x73, + 0x74, 0x61, 0x63, 0x6b, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x20, 0x2e, 0x65, 0x6e, 0x63, + 0x6f, 0x72, 0x65, 0x2e, 0x65, 0x6e, 0x67, 0x69, 0x6e, 0x65, 0x2e, 0x74, 0x72, 0x61, 0x63, 0x65, + 0x32, 0x2e, 0x53, 0x74, 0x61, 0x63, 0x6b, 0x54, 0x72, 0x61, 0x63, 0x65, 0x52, 0x05, 0x73, 0x74, + 0x61, 0x63, 0x6b, 0x12, 0x25, 0x0a, 0x0e, 0x73, 0x74, 0x61, 0x72, 0x74, 0x5f, 0x6e, 0x61, 0x6e, + 0x6f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0d, 0x73, 0x74, 0x61, + 0x72, 0x74, 0x4e, 0x61, 0x6e, 0x6f, 0x74, 0x69, 0x6d, 0x65, 0x22, 0xc8, 0x01, 0x0a, 0x0b, 0x48, + 0x54, 0x54, 0x50, 0x43, 0x61, 0x6c, 0x6c, 0x45, 0x6e, 0x64, 0x12, 0x24, 0x0a, 0x0b, 0x73, 0x74, + 0x61, 0x74, 0x75, 0x73, 0x5f, 0x63, 0x6f, 0x64, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x48, + 0x00, 0x52, 0x0a, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x43, 0x6f, 0x64, 0x65, 0x88, 0x01, 0x01, + 0x12, 0x32, 0x0a, 0x03, 0x65, 0x72, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x65, 0x6e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x65, 0x6e, 0x67, 0x69, 0x6e, 0x65, 0x2e, 0x74, 0x72, - 0x61, 0x63, 0x65, 0x32, 0x2e, 0x48, 0x54, 0x54, 0x50, 0x47, 0x65, 0x74, 0x43, 0x6f, 0x6e, 0x6e, - 0x48, 0x00, 0x52, 0x07, 0x67, 0x65, 0x74, 0x43, 0x6f, 0x6e, 0x6e, 0x12, 0x3e, 0x0a, 0x08, 0x67, - 0x6f, 0x74, 0x5f, 0x63, 0x6f, 0x6e, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x21, 0x2e, + 0x61, 0x63, 0x65, 0x32, 0x2e, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x48, 0x01, 0x52, 0x03, 0x65, 0x72, + 0x72, 0x88, 0x01, 0x01, 0x12, 0x47, 0x0a, 0x0c, 0x74, 0x72, 0x61, 0x63, 0x65, 0x5f, 0x65, 0x76, + 0x65, 0x6e, 0x74, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x24, 0x2e, 0x65, 0x6e, 0x63, + 0x6f, 0x72, 0x65, 0x2e, 0x65, 0x6e, 0x67, 0x69, 0x6e, 0x65, 0x2e, 0x74, 0x72, 0x61, 0x63, 0x65, + 0x32, 0x2e, 0x48, 0x54, 0x54, 0x50, 0x54, 0x72, 0x61, 0x63, 0x65, 0x45, 0x76, 0x65, 0x6e, 0x74, + 0x52, 0x0b, 0x74, 0x72, 0x61, 0x63, 0x65, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x42, 0x0e, 0x0a, + 0x0c, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x5f, 0x63, 0x6f, 0x64, 0x65, 0x42, 0x06, 0x0a, + 0x04, 0x5f, 0x65, 0x72, 0x72, 0x22, 0x90, 0x09, 0x0a, 0x0e, 0x48, 0x54, 0x54, 0x50, 0x54, 0x72, + 0x61, 0x63, 0x65, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x6e, 0x61, 0x6e, 0x6f, + 0x74, 0x69, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x08, 0x6e, 0x61, 0x6e, 0x6f, + 0x74, 0x69, 0x6d, 0x65, 0x12, 0x3e, 0x0a, 0x08, 0x67, 0x65, 0x74, 0x5f, 0x63, 0x6f, 0x6e, 0x6e, + 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x21, 0x2e, 0x65, 0x6e, 0x63, 0x6f, 0x72, 0x65, 0x2e, + 0x65, 0x6e, 0x67, 0x69, 0x6e, 0x65, 0x2e, 0x74, 0x72, 0x61, 0x63, 0x65, 0x32, 0x2e, 0x48, 0x54, + 0x54, 0x50, 0x47, 0x65, 0x74, 0x43, 0x6f, 0x6e, 0x6e, 0x48, 0x00, 0x52, 0x07, 0x67, 0x65, 0x74, + 0x43, 0x6f, 0x6e, 0x6e, 0x12, 0x3e, 0x0a, 0x08, 0x67, 0x6f, 0x74, 0x5f, 0x63, 0x6f, 0x6e, 0x6e, + 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x21, 0x2e, 0x65, 0x6e, 0x63, 0x6f, 0x72, 0x65, 0x2e, + 0x65, 0x6e, 0x67, 0x69, 0x6e, 0x65, 0x2e, 0x74, 0x72, 0x61, 0x63, 0x65, 0x32, 0x2e, 0x48, 0x54, + 0x54, 0x50, 0x47, 0x6f, 0x74, 0x43, 0x6f, 0x6e, 0x6e, 0x48, 0x00, 0x52, 0x07, 0x67, 0x6f, 0x74, + 0x43, 0x6f, 0x6e, 0x6e, 0x12, 0x67, 0x0a, 0x17, 0x67, 0x6f, 0x74, 0x5f, 0x66, 0x69, 0x72, 0x73, + 0x74, 0x5f, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x5f, 0x62, 0x79, 0x74, 0x65, 0x18, + 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2e, 0x2e, 0x65, 0x6e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x65, + 0x6e, 0x67, 0x69, 0x6e, 0x65, 0x2e, 0x74, 0x72, 0x61, 0x63, 0x65, 0x32, 0x2e, 0x48, 0x54, 0x54, + 0x50, 0x47, 0x6f, 0x74, 0x46, 0x69, 0x72, 0x73, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x42, 0x79, 0x74, 0x65, 0x48, 0x00, 0x52, 0x14, 0x67, 0x6f, 0x74, 0x46, 0x69, 0x72, 0x73, + 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x42, 0x79, 0x74, 0x65, 0x12, 0x54, 0x0a, + 0x10, 0x67, 0x6f, 0x74, 0x5f, 0x31, 0x78, 0x78, 0x5f, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x28, 0x2e, 0x65, 0x6e, 0x63, 0x6f, 0x72, 0x65, + 0x2e, 0x65, 0x6e, 0x67, 0x69, 0x6e, 0x65, 0x2e, 0x74, 0x72, 0x61, 0x63, 0x65, 0x32, 0x2e, 0x48, + 0x54, 0x54, 0x50, 0x47, 0x6f, 0x74, 0x31, 0x78, 0x78, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x48, 0x00, 0x52, 0x0e, 0x67, 0x6f, 0x74, 0x31, 0x78, 0x78, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x12, 0x41, 0x0a, 0x09, 0x64, 0x6e, 0x73, 0x5f, 0x73, 0x74, 0x61, 0x72, 0x74, + 0x18, 0x06, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x22, 0x2e, 0x65, 0x6e, 0x63, 0x6f, 0x72, 0x65, 0x2e, + 0x65, 0x6e, 0x67, 0x69, 0x6e, 0x65, 0x2e, 0x74, 0x72, 0x61, 0x63, 0x65, 0x32, 0x2e, 0x48, 0x54, + 0x54, 0x50, 0x44, 0x4e, 0x53, 0x53, 0x74, 0x61, 0x72, 0x74, 0x48, 0x00, 0x52, 0x08, 0x64, 0x6e, + 0x73, 0x53, 0x74, 0x61, 0x72, 0x74, 0x12, 0x3e, 0x0a, 0x08, 0x64, 0x6e, 0x73, 0x5f, 0x64, 0x6f, + 0x6e, 0x65, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x21, 0x2e, 0x65, 0x6e, 0x63, 0x6f, 0x72, + 0x65, 0x2e, 0x65, 0x6e, 0x67, 0x69, 0x6e, 0x65, 0x2e, 0x74, 0x72, 0x61, 0x63, 0x65, 0x32, 0x2e, + 0x48, 0x54, 0x54, 0x50, 0x44, 0x4e, 0x53, 0x44, 0x6f, 0x6e, 0x65, 0x48, 0x00, 0x52, 0x07, 0x64, + 0x6e, 0x73, 0x44, 0x6f, 0x6e, 0x65, 0x12, 0x4d, 0x0a, 0x0d, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, + 0x74, 0x5f, 0x73, 0x74, 0x61, 0x72, 0x74, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x26, 0x2e, 0x65, 0x6e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x65, 0x6e, 0x67, 0x69, 0x6e, 0x65, 0x2e, 0x74, 0x72, - 0x61, 0x63, 0x65, 0x32, 0x2e, 0x48, 0x54, 0x54, 0x50, 0x47, 0x6f, 0x74, 0x43, 0x6f, 0x6e, 0x6e, - 0x48, 0x00, 0x52, 0x07, 0x67, 0x6f, 0x74, 0x43, 0x6f, 0x6e, 0x6e, 0x12, 0x67, 0x0a, 0x17, 0x67, - 0x6f, 0x74, 0x5f, 0x66, 0x69, 0x72, 0x73, 0x74, 0x5f, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, - 0x65, 0x5f, 0x62, 0x79, 0x74, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2e, 0x2e, 0x65, - 0x6e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x65, 0x6e, 0x67, 0x69, 0x6e, 0x65, 0x2e, 0x74, 0x72, 0x61, - 0x63, 0x65, 0x32, 0x2e, 0x48, 0x54, 0x54, 0x50, 0x47, 0x6f, 0x74, 0x46, 0x69, 0x72, 0x73, 0x74, - 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x42, 0x79, 0x74, 0x65, 0x48, 0x00, 0x52, 0x14, - 0x67, 0x6f, 0x74, 0x46, 0x69, 0x72, 0x73, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, - 0x42, 0x79, 0x74, 0x65, 0x12, 0x54, 0x0a, 0x10, 0x67, 0x6f, 0x74, 0x5f, 0x31, 0x78, 0x78, 0x5f, - 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x28, + 0x61, 0x63, 0x65, 0x32, 0x2e, 0x48, 0x54, 0x54, 0x50, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, + 0x53, 0x74, 0x61, 0x72, 0x74, 0x48, 0x00, 0x52, 0x0c, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, + 0x53, 0x74, 0x61, 0x72, 0x74, 0x12, 0x4a, 0x0a, 0x0c, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, + 0x5f, 0x64, 0x6f, 0x6e, 0x65, 0x18, 0x09, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x25, 0x2e, 0x65, 0x6e, + 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x65, 0x6e, 0x67, 0x69, 0x6e, 0x65, 0x2e, 0x74, 0x72, 0x61, 0x63, + 0x65, 0x32, 0x2e, 0x48, 0x54, 0x54, 0x50, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x44, 0x6f, + 0x6e, 0x65, 0x48, 0x00, 0x52, 0x0b, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x44, 0x6f, 0x6e, + 0x65, 0x12, 0x5d, 0x0a, 0x13, 0x74, 0x6c, 0x73, 0x5f, 0x68, 0x61, 0x6e, 0x64, 0x73, 0x68, 0x61, + 0x6b, 0x65, 0x5f, 0x73, 0x74, 0x61, 0x72, 0x74, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2b, 0x2e, 0x65, 0x6e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x65, 0x6e, 0x67, 0x69, 0x6e, 0x65, 0x2e, 0x74, - 0x72, 0x61, 0x63, 0x65, 0x32, 0x2e, 0x48, 0x54, 0x54, 0x50, 0x47, 0x6f, 0x74, 0x31, 0x78, 0x78, - 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x48, 0x00, 0x52, 0x0e, 0x67, 0x6f, 0x74, 0x31, - 0x78, 0x78, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x41, 0x0a, 0x09, 0x64, 0x6e, - 0x73, 0x5f, 0x73, 0x74, 0x61, 0x72, 0x74, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x22, 0x2e, - 0x65, 0x6e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x65, 0x6e, 0x67, 0x69, 0x6e, 0x65, 0x2e, 0x74, 0x72, - 0x61, 0x63, 0x65, 0x32, 0x2e, 0x48, 0x54, 0x54, 0x50, 0x44, 0x4e, 0x53, 0x53, 0x74, 0x61, 0x72, - 0x74, 0x48, 0x00, 0x52, 0x08, 0x64, 0x6e, 0x73, 0x53, 0x74, 0x61, 0x72, 0x74, 0x12, 0x3e, 0x0a, - 0x08, 0x64, 0x6e, 0x73, 0x5f, 0x64, 0x6f, 0x6e, 0x65, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0b, 0x32, - 0x21, 0x2e, 0x65, 0x6e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x65, 0x6e, 0x67, 0x69, 0x6e, 0x65, 0x2e, - 0x74, 0x72, 0x61, 0x63, 0x65, 0x32, 0x2e, 0x48, 0x54, 0x54, 0x50, 0x44, 0x4e, 0x53, 0x44, 0x6f, - 0x6e, 0x65, 0x48, 0x00, 0x52, 0x07, 0x64, 0x6e, 0x73, 0x44, 0x6f, 0x6e, 0x65, 0x12, 0x4d, 0x0a, - 0x0d, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x5f, 0x73, 0x74, 0x61, 0x72, 0x74, 0x18, 0x08, - 0x20, 0x01, 0x28, 0x0b, 0x32, 0x26, 0x2e, 0x65, 0x6e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x65, 0x6e, - 0x67, 0x69, 0x6e, 0x65, 0x2e, 0x74, 0x72, 0x61, 0x63, 0x65, 0x32, 0x2e, 0x48, 0x54, 0x54, 0x50, - 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x53, 0x74, 0x61, 0x72, 0x74, 0x48, 0x00, 0x52, 0x0c, - 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x53, 0x74, 0x61, 0x72, 0x74, 0x12, 0x4a, 0x0a, 0x0c, - 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x5f, 0x64, 0x6f, 0x6e, 0x65, 0x18, 0x09, 0x20, 0x01, - 0x28, 0x0b, 0x32, 0x25, 0x2e, 0x65, 0x6e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x65, 0x6e, 0x67, 0x69, - 0x6e, 0x65, 0x2e, 0x74, 0x72, 0x61, 0x63, 0x65, 0x32, 0x2e, 0x48, 0x54, 0x54, 0x50, 0x43, 0x6f, - 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x44, 0x6f, 0x6e, 0x65, 0x48, 0x00, 0x52, 0x0b, 0x63, 0x6f, 0x6e, - 0x6e, 0x65, 0x63, 0x74, 0x44, 0x6f, 0x6e, 0x65, 0x12, 0x5d, 0x0a, 0x13, 0x74, 0x6c, 0x73, 0x5f, - 0x68, 0x61, 0x6e, 0x64, 0x73, 0x68, 0x61, 0x6b, 0x65, 0x5f, 0x73, 0x74, 0x61, 0x72, 0x74, 0x18, - 0x0a, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2b, 0x2e, 0x65, 0x6e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x65, - 0x6e, 0x67, 0x69, 0x6e, 0x65, 0x2e, 0x74, 0x72, 0x61, 0x63, 0x65, 0x32, 0x2e, 0x48, 0x54, 0x54, - 0x50, 0x54, 0x4c, 0x53, 0x48, 0x61, 0x6e, 0x64, 0x73, 0x68, 0x61, 0x6b, 0x65, 0x53, 0x74, 0x61, - 0x72, 0x74, 0x48, 0x00, 0x52, 0x11, 0x74, 0x6c, 0x73, 0x48, 0x61, 0x6e, 0x64, 0x73, 0x68, 0x61, - 0x6b, 0x65, 0x53, 0x74, 0x61, 0x72, 0x74, 0x12, 0x5a, 0x0a, 0x12, 0x74, 0x6c, 0x73, 0x5f, 0x68, - 0x61, 0x6e, 0x64, 0x73, 0x68, 0x61, 0x6b, 0x65, 0x5f, 0x64, 0x6f, 0x6e, 0x65, 0x18, 0x0b, 0x20, - 0x01, 0x28, 0x0b, 0x32, 0x2a, 0x2e, 0x65, 0x6e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x65, 0x6e, 0x67, - 0x69, 0x6e, 0x65, 0x2e, 0x74, 0x72, 0x61, 0x63, 0x65, 0x32, 0x2e, 0x48, 0x54, 0x54, 0x50, 0x54, - 0x4c, 0x53, 0x48, 0x61, 0x6e, 0x64, 0x73, 0x68, 0x61, 0x6b, 0x65, 0x44, 0x6f, 0x6e, 0x65, 0x48, - 0x00, 0x52, 0x10, 0x74, 0x6c, 0x73, 0x48, 0x61, 0x6e, 0x64, 0x73, 0x68, 0x61, 0x6b, 0x65, 0x44, - 0x6f, 0x6e, 0x65, 0x12, 0x4d, 0x0a, 0x0d, 0x77, 0x72, 0x6f, 0x74, 0x65, 0x5f, 0x68, 0x65, 0x61, - 0x64, 0x65, 0x72, 0x73, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x26, 0x2e, 0x65, 0x6e, 0x63, - 0x6f, 0x72, 0x65, 0x2e, 0x65, 0x6e, 0x67, 0x69, 0x6e, 0x65, 0x2e, 0x74, 0x72, 0x61, 0x63, 0x65, - 0x32, 0x2e, 0x48, 0x54, 0x54, 0x50, 0x57, 0x72, 0x6f, 0x74, 0x65, 0x48, 0x65, 0x61, 0x64, 0x65, - 0x72, 0x73, 0x48, 0x00, 0x52, 0x0c, 0x77, 0x72, 0x6f, 0x74, 0x65, 0x48, 0x65, 0x61, 0x64, 0x65, - 0x72, 0x73, 0x12, 0x4d, 0x0a, 0x0d, 0x77, 0x72, 0x6f, 0x74, 0x65, 0x5f, 0x72, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x18, 0x0d, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x26, 0x2e, 0x65, 0x6e, 0x63, 0x6f, - 0x72, 0x65, 0x2e, 0x65, 0x6e, 0x67, 0x69, 0x6e, 0x65, 0x2e, 0x74, 0x72, 0x61, 0x63, 0x65, 0x32, - 0x2e, 0x48, 0x54, 0x54, 0x50, 0x57, 0x72, 0x6f, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x48, 0x00, 0x52, 0x0c, 0x77, 0x72, 0x6f, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x12, 0x57, 0x0a, 0x11, 0x77, 0x61, 0x69, 0x74, 0x5f, 0x31, 0x30, 0x30, 0x5f, 0x63, 0x6f, - 0x6e, 0x74, 0x69, 0x6e, 0x75, 0x65, 0x18, 0x0e, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x29, 0x2e, 0x65, + 0x72, 0x61, 0x63, 0x65, 0x32, 0x2e, 0x48, 0x54, 0x54, 0x50, 0x54, 0x4c, 0x53, 0x48, 0x61, 0x6e, + 0x64, 0x73, 0x68, 0x61, 0x6b, 0x65, 0x53, 0x74, 0x61, 0x72, 0x74, 0x48, 0x00, 0x52, 0x11, 0x74, + 0x6c, 0x73, 0x48, 0x61, 0x6e, 0x64, 0x73, 0x68, 0x61, 0x6b, 0x65, 0x53, 0x74, 0x61, 0x72, 0x74, + 0x12, 0x5a, 0x0a, 0x12, 0x74, 0x6c, 0x73, 0x5f, 0x68, 0x61, 0x6e, 0x64, 0x73, 0x68, 0x61, 0x6b, + 0x65, 0x5f, 0x64, 0x6f, 0x6e, 0x65, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2a, 0x2e, 0x65, 0x6e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x65, 0x6e, 0x67, 0x69, 0x6e, 0x65, 0x2e, 0x74, 0x72, 0x61, - 0x63, 0x65, 0x32, 0x2e, 0x48, 0x54, 0x54, 0x50, 0x57, 0x61, 0x69, 0x74, 0x31, 0x30, 0x30, 0x43, - 0x6f, 0x6e, 0x74, 0x69, 0x6e, 0x75, 0x65, 0x48, 0x00, 0x52, 0x0f, 0x77, 0x61, 0x69, 0x74, 0x31, - 0x30, 0x30, 0x43, 0x6f, 0x6e, 0x74, 0x69, 0x6e, 0x75, 0x65, 0x12, 0x4b, 0x0a, 0x0b, 0x63, 0x6c, - 0x6f, 0x73, 0x65, 0x64, 0x5f, 0x62, 0x6f, 0x64, 0x79, 0x18, 0x0f, 0x20, 0x01, 0x28, 0x0b, 0x32, - 0x28, 0x2e, 0x65, 0x6e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x65, 0x6e, 0x67, 0x69, 0x6e, 0x65, 0x2e, - 0x74, 0x72, 0x61, 0x63, 0x65, 0x32, 0x2e, 0x48, 0x54, 0x54, 0x50, 0x43, 0x6c, 0x6f, 0x73, 0x65, - 0x64, 0x42, 0x6f, 0x64, 0x79, 0x44, 0x61, 0x74, 0x61, 0x48, 0x00, 0x52, 0x0a, 0x63, 0x6c, 0x6f, - 0x73, 0x65, 0x64, 0x42, 0x6f, 0x64, 0x79, 0x42, 0x06, 0x0a, 0x04, 0x64, 0x61, 0x74, 0x61, 0x22, - 0x2a, 0x0a, 0x0b, 0x48, 0x54, 0x54, 0x50, 0x47, 0x65, 0x74, 0x43, 0x6f, 0x6e, 0x6e, 0x12, 0x1b, - 0x0a, 0x09, 0x68, 0x6f, 0x73, 0x74, 0x5f, 0x70, 0x6f, 0x72, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x08, 0x68, 0x6f, 0x73, 0x74, 0x50, 0x6f, 0x72, 0x74, 0x22, 0x6a, 0x0a, 0x0b, 0x48, - 0x54, 0x54, 0x50, 0x47, 0x6f, 0x74, 0x43, 0x6f, 0x6e, 0x6e, 0x12, 0x16, 0x0a, 0x06, 0x72, 0x65, - 0x75, 0x73, 0x65, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x06, 0x72, 0x65, 0x75, 0x73, - 0x65, 0x64, 0x12, 0x19, 0x0a, 0x08, 0x77, 0x61, 0x73, 0x5f, 0x69, 0x64, 0x6c, 0x65, 0x18, 0x02, - 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x77, 0x61, 0x73, 0x49, 0x64, 0x6c, 0x65, 0x12, 0x28, 0x0a, - 0x10, 0x69, 0x64, 0x6c, 0x65, 0x5f, 0x64, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x6e, - 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0e, 0x69, 0x64, 0x6c, 0x65, 0x44, 0x75, 0x72, - 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x4e, 0x73, 0x22, 0x1a, 0x0a, 0x18, 0x48, 0x54, 0x54, 0x50, 0x47, - 0x6f, 0x74, 0x46, 0x69, 0x72, 0x73, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x42, - 0x79, 0x74, 0x65, 0x22, 0x28, 0x0a, 0x12, 0x48, 0x54, 0x54, 0x50, 0x47, 0x6f, 0x74, 0x31, 0x78, - 0x78, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x63, 0x6f, 0x64, - 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x04, 0x63, 0x6f, 0x64, 0x65, 0x22, 0x22, 0x0a, - 0x0c, 0x48, 0x54, 0x54, 0x50, 0x44, 0x4e, 0x53, 0x53, 0x74, 0x61, 0x72, 0x74, 0x12, 0x12, 0x0a, - 0x04, 0x68, 0x6f, 0x73, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x68, 0x6f, 0x73, - 0x74, 0x22, 0x61, 0x0a, 0x0b, 0x48, 0x54, 0x54, 0x50, 0x44, 0x4e, 0x53, 0x44, 0x6f, 0x6e, 0x65, - 0x12, 0x15, 0x0a, 0x03, 0x65, 0x72, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x48, 0x00, 0x52, - 0x03, 0x65, 0x72, 0x72, 0x88, 0x01, 0x01, 0x12, 0x33, 0x0a, 0x05, 0x61, 0x64, 0x64, 0x72, 0x73, - 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x65, 0x6e, 0x63, 0x6f, 0x72, 0x65, 0x2e, - 0x65, 0x6e, 0x67, 0x69, 0x6e, 0x65, 0x2e, 0x74, 0x72, 0x61, 0x63, 0x65, 0x32, 0x2e, 0x44, 0x4e, - 0x53, 0x41, 0x64, 0x64, 0x72, 0x52, 0x05, 0x61, 0x64, 0x64, 0x72, 0x73, 0x42, 0x06, 0x0a, 0x04, - 0x5f, 0x65, 0x72, 0x72, 0x22, 0x19, 0x0a, 0x07, 0x44, 0x4e, 0x53, 0x41, 0x64, 0x64, 0x72, 0x12, - 0x0e, 0x0a, 0x02, 0x69, 0x70, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x02, 0x69, 0x70, 0x22, - 0x40, 0x0a, 0x10, 0x48, 0x54, 0x54, 0x50, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x53, 0x74, - 0x61, 0x72, 0x74, 0x12, 0x18, 0x0a, 0x07, 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x12, 0x12, 0x0a, - 0x04, 0x61, 0x64, 0x64, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x61, 0x64, 0x64, - 0x72, 0x22, 0x51, 0x0a, 0x0f, 0x48, 0x54, 0x54, 0x50, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, - 0x44, 0x6f, 0x6e, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x12, 0x12, - 0x0a, 0x04, 0x61, 0x64, 0x64, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x61, 0x64, - 0x64, 0x72, 0x12, 0x10, 0x0a, 0x03, 0x65, 0x72, 0x72, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, - 0x03, 0x65, 0x72, 0x72, 0x22, 0x17, 0x0a, 0x15, 0x48, 0x54, 0x54, 0x50, 0x54, 0x4c, 0x53, 0x48, - 0x61, 0x6e, 0x64, 0x73, 0x68, 0x61, 0x6b, 0x65, 0x53, 0x74, 0x61, 0x72, 0x74, 0x22, 0xcb, 0x01, - 0x0a, 0x14, 0x48, 0x54, 0x54, 0x50, 0x54, 0x4c, 0x53, 0x48, 0x61, 0x6e, 0x64, 0x73, 0x68, 0x61, - 0x6b, 0x65, 0x44, 0x6f, 0x6e, 0x65, 0x12, 0x15, 0x0a, 0x03, 0x65, 0x72, 0x72, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x0c, 0x48, 0x00, 0x52, 0x03, 0x65, 0x72, 0x72, 0x88, 0x01, 0x01, 0x12, 0x1f, 0x0a, - 0x0b, 0x74, 0x6c, 0x73, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, - 0x28, 0x0d, 0x52, 0x0a, 0x74, 0x6c, 0x73, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x21, - 0x0a, 0x0c, 0x63, 0x69, 0x70, 0x68, 0x65, 0x72, 0x5f, 0x73, 0x75, 0x69, 0x74, 0x65, 0x18, 0x03, - 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0b, 0x63, 0x69, 0x70, 0x68, 0x65, 0x72, 0x53, 0x75, 0x69, 0x74, - 0x65, 0x12, 0x1f, 0x0a, 0x0b, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x6e, 0x61, 0x6d, 0x65, - 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x4e, 0x61, - 0x6d, 0x65, 0x12, 0x2f, 0x0a, 0x13, 0x6e, 0x65, 0x67, 0x6f, 0x74, 0x69, 0x61, 0x74, 0x65, 0x64, - 0x5f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x12, 0x6e, 0x65, 0x67, 0x6f, 0x74, 0x69, 0x61, 0x74, 0x65, 0x64, 0x50, 0x72, 0x6f, 0x74, 0x6f, - 0x63, 0x6f, 0x6c, 0x42, 0x06, 0x0a, 0x04, 0x5f, 0x65, 0x72, 0x72, 0x22, 0x12, 0x0a, 0x10, 0x48, - 0x54, 0x54, 0x50, 0x57, 0x72, 0x6f, 0x74, 0x65, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x73, 0x22, - 0x31, 0x0a, 0x10, 0x48, 0x54, 0x54, 0x50, 0x57, 0x72, 0x6f, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x12, 0x15, 0x0a, 0x03, 0x65, 0x72, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, - 0x48, 0x00, 0x52, 0x03, 0x65, 0x72, 0x72, 0x88, 0x01, 0x01, 0x42, 0x06, 0x0a, 0x04, 0x5f, 0x65, - 0x72, 0x72, 0x22, 0x15, 0x0a, 0x13, 0x48, 0x54, 0x54, 0x50, 0x57, 0x61, 0x69, 0x74, 0x31, 0x30, - 0x30, 0x43, 0x6f, 0x6e, 0x74, 0x69, 0x6e, 0x75, 0x65, 0x22, 0x33, 0x0a, 0x12, 0x48, 0x54, 0x54, - 0x50, 0x43, 0x6c, 0x6f, 0x73, 0x65, 0x64, 0x42, 0x6f, 0x64, 0x79, 0x44, 0x61, 0x74, 0x61, 0x12, + 0x63, 0x65, 0x32, 0x2e, 0x48, 0x54, 0x54, 0x50, 0x54, 0x4c, 0x53, 0x48, 0x61, 0x6e, 0x64, 0x73, + 0x68, 0x61, 0x6b, 0x65, 0x44, 0x6f, 0x6e, 0x65, 0x48, 0x00, 0x52, 0x10, 0x74, 0x6c, 0x73, 0x48, + 0x61, 0x6e, 0x64, 0x73, 0x68, 0x61, 0x6b, 0x65, 0x44, 0x6f, 0x6e, 0x65, 0x12, 0x4d, 0x0a, 0x0d, + 0x77, 0x72, 0x6f, 0x74, 0x65, 0x5f, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x73, 0x18, 0x0c, 0x20, + 0x01, 0x28, 0x0b, 0x32, 0x26, 0x2e, 0x65, 0x6e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x65, 0x6e, 0x67, + 0x69, 0x6e, 0x65, 0x2e, 0x74, 0x72, 0x61, 0x63, 0x65, 0x32, 0x2e, 0x48, 0x54, 0x54, 0x50, 0x57, + 0x72, 0x6f, 0x74, 0x65, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x73, 0x48, 0x00, 0x52, 0x0c, 0x77, + 0x72, 0x6f, 0x74, 0x65, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x73, 0x12, 0x4d, 0x0a, 0x0d, 0x77, + 0x72, 0x6f, 0x74, 0x65, 0x5f, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x18, 0x0d, 0x20, 0x01, + 0x28, 0x0b, 0x32, 0x26, 0x2e, 0x65, 0x6e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x65, 0x6e, 0x67, 0x69, + 0x6e, 0x65, 0x2e, 0x74, 0x72, 0x61, 0x63, 0x65, 0x32, 0x2e, 0x48, 0x54, 0x54, 0x50, 0x57, 0x72, + 0x6f, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x48, 0x00, 0x52, 0x0c, 0x77, 0x72, + 0x6f, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x57, 0x0a, 0x11, 0x77, 0x61, + 0x69, 0x74, 0x5f, 0x31, 0x30, 0x30, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x69, 0x6e, 0x75, 0x65, 0x18, + 0x0e, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x29, 0x2e, 0x65, 0x6e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x65, + 0x6e, 0x67, 0x69, 0x6e, 0x65, 0x2e, 0x74, 0x72, 0x61, 0x63, 0x65, 0x32, 0x2e, 0x48, 0x54, 0x54, + 0x50, 0x57, 0x61, 0x69, 0x74, 0x31, 0x30, 0x30, 0x43, 0x6f, 0x6e, 0x74, 0x69, 0x6e, 0x75, 0x65, + 0x48, 0x00, 0x52, 0x0f, 0x77, 0x61, 0x69, 0x74, 0x31, 0x30, 0x30, 0x43, 0x6f, 0x6e, 0x74, 0x69, + 0x6e, 0x75, 0x65, 0x12, 0x4b, 0x0a, 0x0b, 0x63, 0x6c, 0x6f, 0x73, 0x65, 0x64, 0x5f, 0x62, 0x6f, + 0x64, 0x79, 0x18, 0x0f, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x28, 0x2e, 0x65, 0x6e, 0x63, 0x6f, 0x72, + 0x65, 0x2e, 0x65, 0x6e, 0x67, 0x69, 0x6e, 0x65, 0x2e, 0x74, 0x72, 0x61, 0x63, 0x65, 0x32, 0x2e, + 0x48, 0x54, 0x54, 0x50, 0x43, 0x6c, 0x6f, 0x73, 0x65, 0x64, 0x42, 0x6f, 0x64, 0x79, 0x44, 0x61, + 0x74, 0x61, 0x48, 0x00, 0x52, 0x0a, 0x63, 0x6c, 0x6f, 0x73, 0x65, 0x64, 0x42, 0x6f, 0x64, 0x79, + 0x42, 0x06, 0x0a, 0x04, 0x64, 0x61, 0x74, 0x61, 0x22, 0x2a, 0x0a, 0x0b, 0x48, 0x54, 0x54, 0x50, + 0x47, 0x65, 0x74, 0x43, 0x6f, 0x6e, 0x6e, 0x12, 0x1b, 0x0a, 0x09, 0x68, 0x6f, 0x73, 0x74, 0x5f, + 0x70, 0x6f, 0x72, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x68, 0x6f, 0x73, 0x74, + 0x50, 0x6f, 0x72, 0x74, 0x22, 0x6a, 0x0a, 0x0b, 0x48, 0x54, 0x54, 0x50, 0x47, 0x6f, 0x74, 0x43, + 0x6f, 0x6e, 0x6e, 0x12, 0x16, 0x0a, 0x06, 0x72, 0x65, 0x75, 0x73, 0x65, 0x64, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x08, 0x52, 0x06, 0x72, 0x65, 0x75, 0x73, 0x65, 0x64, 0x12, 0x19, 0x0a, 0x08, 0x77, + 0x61, 0x73, 0x5f, 0x69, 0x64, 0x6c, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x77, + 0x61, 0x73, 0x49, 0x64, 0x6c, 0x65, 0x12, 0x28, 0x0a, 0x10, 0x69, 0x64, 0x6c, 0x65, 0x5f, 0x64, + 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x6e, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x03, + 0x52, 0x0e, 0x69, 0x64, 0x6c, 0x65, 0x44, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x4e, 0x73, + 0x22, 0x1a, 0x0a, 0x18, 0x48, 0x54, 0x54, 0x50, 0x47, 0x6f, 0x74, 0x46, 0x69, 0x72, 0x73, 0x74, + 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x42, 0x79, 0x74, 0x65, 0x22, 0x28, 0x0a, 0x12, + 0x48, 0x54, 0x54, 0x50, 0x47, 0x6f, 0x74, 0x31, 0x78, 0x78, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, + 0x73, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x63, 0x6f, 0x64, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, + 0x52, 0x04, 0x63, 0x6f, 0x64, 0x65, 0x22, 0x22, 0x0a, 0x0c, 0x48, 0x54, 0x54, 0x50, 0x44, 0x4e, + 0x53, 0x53, 0x74, 0x61, 0x72, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x68, 0x6f, 0x73, 0x74, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x68, 0x6f, 0x73, 0x74, 0x22, 0x61, 0x0a, 0x0b, 0x48, 0x54, + 0x54, 0x50, 0x44, 0x4e, 0x53, 0x44, 0x6f, 0x6e, 0x65, 0x12, 0x15, 0x0a, 0x03, 0x65, 0x72, 0x72, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x48, 0x00, 0x52, 0x03, 0x65, 0x72, 0x72, 0x88, 0x01, 0x01, + 0x12, 0x33, 0x0a, 0x05, 0x61, 0x64, 0x64, 0x72, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, + 0x1d, 0x2e, 0x65, 0x6e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x65, 0x6e, 0x67, 0x69, 0x6e, 0x65, 0x2e, + 0x74, 0x72, 0x61, 0x63, 0x65, 0x32, 0x2e, 0x44, 0x4e, 0x53, 0x41, 0x64, 0x64, 0x72, 0x52, 0x05, + 0x61, 0x64, 0x64, 0x72, 0x73, 0x42, 0x06, 0x0a, 0x04, 0x5f, 0x65, 0x72, 0x72, 0x22, 0x19, 0x0a, + 0x07, 0x44, 0x4e, 0x53, 0x41, 0x64, 0x64, 0x72, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x70, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x0c, 0x52, 0x02, 0x69, 0x70, 0x22, 0x40, 0x0a, 0x10, 0x48, 0x54, 0x54, 0x50, + 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x53, 0x74, 0x61, 0x72, 0x74, 0x12, 0x18, 0x0a, 0x07, + 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x6e, + 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x12, 0x12, 0x0a, 0x04, 0x61, 0x64, 0x64, 0x72, 0x18, 0x02, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x61, 0x64, 0x64, 0x72, 0x22, 0x51, 0x0a, 0x0f, 0x48, 0x54, + 0x54, 0x50, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x44, 0x6f, 0x6e, 0x65, 0x12, 0x18, 0x0a, + 0x07, 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, + 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x12, 0x12, 0x0a, 0x04, 0x61, 0x64, 0x64, 0x72, 0x18, + 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x61, 0x64, 0x64, 0x72, 0x12, 0x10, 0x0a, 0x03, 0x65, + 0x72, 0x72, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x03, 0x65, 0x72, 0x72, 0x22, 0x17, 0x0a, + 0x15, 0x48, 0x54, 0x54, 0x50, 0x54, 0x4c, 0x53, 0x48, 0x61, 0x6e, 0x64, 0x73, 0x68, 0x61, 0x6b, + 0x65, 0x53, 0x74, 0x61, 0x72, 0x74, 0x22, 0xcb, 0x01, 0x0a, 0x14, 0x48, 0x54, 0x54, 0x50, 0x54, + 0x4c, 0x53, 0x48, 0x61, 0x6e, 0x64, 0x73, 0x68, 0x61, 0x6b, 0x65, 0x44, 0x6f, 0x6e, 0x65, 0x12, 0x15, 0x0a, 0x03, 0x65, 0x72, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x48, 0x00, 0x52, 0x03, - 0x65, 0x72, 0x72, 0x88, 0x01, 0x01, 0x42, 0x06, 0x0a, 0x04, 0x5f, 0x65, 0x72, 0x72, 0x22, 0x8a, - 0x02, 0x0a, 0x0a, 0x4c, 0x6f, 0x67, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x3c, 0x0a, - 0x05, 0x6c, 0x65, 0x76, 0x65, 0x6c, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x26, 0x2e, 0x65, - 0x6e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x65, 0x6e, 0x67, 0x69, 0x6e, 0x65, 0x2e, 0x74, 0x72, 0x61, - 0x63, 0x65, 0x32, 0x2e, 0x4c, 0x6f, 0x67, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x2e, 0x4c, - 0x65, 0x76, 0x65, 0x6c, 0x52, 0x05, 0x6c, 0x65, 0x76, 0x65, 0x6c, 0x12, 0x10, 0x0a, 0x03, 0x6d, - 0x73, 0x67, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6d, 0x73, 0x67, 0x12, 0x36, 0x0a, - 0x06, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1e, 0x2e, + 0x65, 0x72, 0x72, 0x88, 0x01, 0x01, 0x12, 0x1f, 0x0a, 0x0b, 0x74, 0x6c, 0x73, 0x5f, 0x76, 0x65, + 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0a, 0x74, 0x6c, 0x73, + 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x21, 0x0a, 0x0c, 0x63, 0x69, 0x70, 0x68, 0x65, + 0x72, 0x5f, 0x73, 0x75, 0x69, 0x74, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0b, 0x63, + 0x69, 0x70, 0x68, 0x65, 0x72, 0x53, 0x75, 0x69, 0x74, 0x65, 0x12, 0x1f, 0x0a, 0x0b, 0x73, 0x65, + 0x72, 0x76, 0x65, 0x72, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x0a, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x2f, 0x0a, 0x13, 0x6e, + 0x65, 0x67, 0x6f, 0x74, 0x69, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, + 0x6f, 0x6c, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x12, 0x6e, 0x65, 0x67, 0x6f, 0x74, 0x69, + 0x61, 0x74, 0x65, 0x64, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x42, 0x06, 0x0a, 0x04, + 0x5f, 0x65, 0x72, 0x72, 0x22, 0x12, 0x0a, 0x10, 0x48, 0x54, 0x54, 0x50, 0x57, 0x72, 0x6f, 0x74, + 0x65, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x73, 0x22, 0x31, 0x0a, 0x10, 0x48, 0x54, 0x54, 0x50, + 0x57, 0x72, 0x6f, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x15, 0x0a, 0x03, + 0x65, 0x72, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x48, 0x00, 0x52, 0x03, 0x65, 0x72, 0x72, + 0x88, 0x01, 0x01, 0x42, 0x06, 0x0a, 0x04, 0x5f, 0x65, 0x72, 0x72, 0x22, 0x15, 0x0a, 0x13, 0x48, + 0x54, 0x54, 0x50, 0x57, 0x61, 0x69, 0x74, 0x31, 0x30, 0x30, 0x43, 0x6f, 0x6e, 0x74, 0x69, 0x6e, + 0x75, 0x65, 0x22, 0x33, 0x0a, 0x12, 0x48, 0x54, 0x54, 0x50, 0x43, 0x6c, 0x6f, 0x73, 0x65, 0x64, + 0x42, 0x6f, 0x64, 0x79, 0x44, 0x61, 0x74, 0x61, 0x12, 0x15, 0x0a, 0x03, 0x65, 0x72, 0x72, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x0c, 0x48, 0x00, 0x52, 0x03, 0x65, 0x72, 0x72, 0x88, 0x01, 0x01, 0x42, + 0x06, 0x0a, 0x04, 0x5f, 0x65, 0x72, 0x72, 0x22, 0x8a, 0x02, 0x0a, 0x0a, 0x4c, 0x6f, 0x67, 0x4d, + 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x3c, 0x0a, 0x05, 0x6c, 0x65, 0x76, 0x65, 0x6c, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x26, 0x2e, 0x65, 0x6e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x65, + 0x6e, 0x67, 0x69, 0x6e, 0x65, 0x2e, 0x74, 0x72, 0x61, 0x63, 0x65, 0x32, 0x2e, 0x4c, 0x6f, 0x67, + 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x2e, 0x4c, 0x65, 0x76, 0x65, 0x6c, 0x52, 0x05, 0x6c, + 0x65, 0x76, 0x65, 0x6c, 0x12, 0x10, 0x0a, 0x03, 0x6d, 0x73, 0x67, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x03, 0x6d, 0x73, 0x67, 0x12, 0x36, 0x0a, 0x06, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x73, + 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1e, 0x2e, 0x65, 0x6e, 0x63, 0x6f, 0x72, 0x65, 0x2e, + 0x65, 0x6e, 0x67, 0x69, 0x6e, 0x65, 0x2e, 0x74, 0x72, 0x61, 0x63, 0x65, 0x32, 0x2e, 0x4c, 0x6f, + 0x67, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x52, 0x06, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x73, 0x12, 0x36, + 0x0a, 0x05, 0x73, 0x74, 0x61, 0x63, 0x6b, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x20, 0x2e, 0x65, 0x6e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x65, 0x6e, 0x67, 0x69, 0x6e, 0x65, 0x2e, 0x74, 0x72, - 0x61, 0x63, 0x65, 0x32, 0x2e, 0x4c, 0x6f, 0x67, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x52, 0x06, 0x66, - 0x69, 0x65, 0x6c, 0x64, 0x73, 0x12, 0x36, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x63, 0x6b, 0x18, 0x04, - 0x20, 0x01, 0x28, 0x0b, 0x32, 0x20, 0x2e, 0x65, 0x6e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x65, 0x6e, - 0x67, 0x69, 0x6e, 0x65, 0x2e, 0x74, 0x72, 0x61, 0x63, 0x65, 0x32, 0x2e, 0x53, 0x74, 0x61, 0x63, - 0x6b, 0x54, 0x72, 0x61, 0x63, 0x65, 0x52, 0x05, 0x73, 0x74, 0x61, 0x63, 0x6b, 0x22, 0x3c, 0x0a, - 0x05, 0x4c, 0x65, 0x76, 0x65, 0x6c, 0x12, 0x09, 0x0a, 0x05, 0x44, 0x45, 0x42, 0x55, 0x47, 0x10, - 0x00, 0x12, 0x08, 0x0a, 0x04, 0x49, 0x4e, 0x46, 0x4f, 0x10, 0x01, 0x12, 0x09, 0x0a, 0x05, 0x45, - 0x52, 0x52, 0x4f, 0x52, 0x10, 0x02, 0x12, 0x08, 0x0a, 0x04, 0x57, 0x41, 0x52, 0x4e, 0x10, 0x03, - 0x12, 0x09, 0x0a, 0x05, 0x54, 0x52, 0x41, 0x43, 0x45, 0x10, 0x04, 0x22, 0xd8, 0x02, 0x0a, 0x08, - 0x4c, 0x6f, 0x67, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x33, 0x0a, 0x05, 0x65, 0x72, - 0x72, 0x6f, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x65, 0x6e, 0x63, 0x6f, - 0x72, 0x65, 0x2e, 0x65, 0x6e, 0x67, 0x69, 0x6e, 0x65, 0x2e, 0x74, 0x72, 0x61, 0x63, 0x65, 0x32, - 0x2e, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x48, 0x00, 0x52, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x12, - 0x12, 0x0a, 0x03, 0x73, 0x74, 0x72, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x48, 0x00, 0x52, 0x03, - 0x73, 0x74, 0x72, 0x12, 0x14, 0x0a, 0x04, 0x62, 0x6f, 0x6f, 0x6c, 0x18, 0x04, 0x20, 0x01, 0x28, - 0x08, 0x48, 0x00, 0x52, 0x04, 0x62, 0x6f, 0x6f, 0x6c, 0x12, 0x30, 0x0a, 0x04, 0x74, 0x69, 0x6d, - 0x65, 0x18, 0x05, 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, 0x48, 0x00, 0x52, 0x04, 0x74, 0x69, 0x6d, 0x65, 0x12, 0x12, 0x0a, 0x03, 0x64, - 0x75, 0x72, 0x18, 0x06, 0x20, 0x01, 0x28, 0x03, 0x48, 0x00, 0x52, 0x03, 0x64, 0x75, 0x72, 0x12, - 0x14, 0x0a, 0x04, 0x75, 0x75, 0x69, 0x64, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0c, 0x48, 0x00, 0x52, - 0x04, 0x75, 0x75, 0x69, 0x64, 0x12, 0x14, 0x0a, 0x04, 0x6a, 0x73, 0x6f, 0x6e, 0x18, 0x08, 0x20, - 0x01, 0x28, 0x0c, 0x48, 0x00, 0x52, 0x04, 0x6a, 0x73, 0x6f, 0x6e, 0x12, 0x12, 0x0a, 0x03, 0x69, - 0x6e, 0x74, 0x18, 0x09, 0x20, 0x01, 0x28, 0x03, 0x48, 0x00, 0x52, 0x03, 0x69, 0x6e, 0x74, 0x12, - 0x14, 0x0a, 0x04, 0x75, 0x69, 0x6e, 0x74, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x04, 0x48, 0x00, 0x52, - 0x04, 0x75, 0x69, 0x6e, 0x74, 0x12, 0x1a, 0x0a, 0x07, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x33, 0x32, - 0x18, 0x0b, 0x20, 0x01, 0x28, 0x02, 0x48, 0x00, 0x52, 0x07, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x33, - 0x32, 0x12, 0x1a, 0x0a, 0x07, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x36, 0x34, 0x18, 0x0c, 0x20, 0x01, - 0x28, 0x01, 0x48, 0x00, 0x52, 0x07, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x36, 0x34, 0x42, 0x07, 0x0a, - 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x22, 0x58, 0x0a, 0x0a, 0x53, 0x74, 0x61, 0x63, 0x6b, 0x54, - 0x72, 0x61, 0x63, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x70, 0x63, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, - 0x03, 0x52, 0x03, 0x70, 0x63, 0x73, 0x12, 0x38, 0x0a, 0x06, 0x66, 0x72, 0x61, 0x6d, 0x65, 0x73, - 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x20, 0x2e, 0x65, 0x6e, 0x63, 0x6f, 0x72, 0x65, 0x2e, - 0x65, 0x6e, 0x67, 0x69, 0x6e, 0x65, 0x2e, 0x74, 0x72, 0x61, 0x63, 0x65, 0x32, 0x2e, 0x53, 0x74, - 0x61, 0x63, 0x6b, 0x46, 0x72, 0x61, 0x6d, 0x65, 0x52, 0x06, 0x66, 0x72, 0x61, 0x6d, 0x65, 0x73, - 0x22, 0x50, 0x0a, 0x0a, 0x53, 0x74, 0x61, 0x63, 0x6b, 0x46, 0x72, 0x61, 0x6d, 0x65, 0x12, 0x1a, - 0x0a, 0x08, 0x66, 0x69, 0x6c, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x08, 0x66, 0x69, 0x6c, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x66, 0x75, - 0x6e, 0x63, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x66, 0x75, 0x6e, 0x63, 0x12, 0x12, - 0x0a, 0x04, 0x6c, 0x69, 0x6e, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x05, 0x52, 0x04, 0x6c, 0x69, - 0x6e, 0x65, 0x22, 0x60, 0x0a, 0x05, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x12, 0x10, 0x0a, 0x03, 0x6d, - 0x73, 0x67, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6d, 0x73, 0x67, 0x12, 0x3b, 0x0a, - 0x05, 0x73, 0x74, 0x61, 0x63, 0x6b, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x20, 0x2e, 0x65, - 0x6e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x65, 0x6e, 0x67, 0x69, 0x6e, 0x65, 0x2e, 0x74, 0x72, 0x61, - 0x63, 0x65, 0x32, 0x2e, 0x53, 0x74, 0x61, 0x63, 0x6b, 0x54, 0x72, 0x61, 0x63, 0x65, 0x48, 0x00, - 0x52, 0x05, 0x73, 0x74, 0x61, 0x63, 0x6b, 0x88, 0x01, 0x01, 0x42, 0x08, 0x0a, 0x06, 0x5f, 0x73, - 0x74, 0x61, 0x63, 0x6b, 0x2a, 0xb1, 0x02, 0x0a, 0x12, 0x48, 0x54, 0x54, 0x50, 0x54, 0x72, 0x61, - 0x63, 0x65, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x43, 0x6f, 0x64, 0x65, 0x12, 0x0b, 0x0a, 0x07, 0x55, - 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x10, 0x00, 0x12, 0x0c, 0x0a, 0x08, 0x47, 0x45, 0x54, 0x5f, - 0x43, 0x4f, 0x4e, 0x4e, 0x10, 0x01, 0x12, 0x0c, 0x0a, 0x08, 0x47, 0x4f, 0x54, 0x5f, 0x43, 0x4f, - 0x4e, 0x4e, 0x10, 0x02, 0x12, 0x1b, 0x0a, 0x17, 0x47, 0x4f, 0x54, 0x5f, 0x46, 0x49, 0x52, 0x53, - 0x54, 0x5f, 0x52, 0x45, 0x53, 0x50, 0x4f, 0x4e, 0x53, 0x45, 0x5f, 0x42, 0x59, 0x54, 0x45, 0x10, - 0x03, 0x12, 0x14, 0x0a, 0x10, 0x47, 0x4f, 0x54, 0x5f, 0x31, 0x58, 0x58, 0x5f, 0x52, 0x45, 0x53, - 0x50, 0x4f, 0x4e, 0x53, 0x45, 0x10, 0x04, 0x12, 0x0d, 0x0a, 0x09, 0x44, 0x4e, 0x53, 0x5f, 0x53, - 0x54, 0x41, 0x52, 0x54, 0x10, 0x05, 0x12, 0x0c, 0x0a, 0x08, 0x44, 0x4e, 0x53, 0x5f, 0x44, 0x4f, - 0x4e, 0x45, 0x10, 0x06, 0x12, 0x11, 0x0a, 0x0d, 0x43, 0x4f, 0x4e, 0x4e, 0x45, 0x43, 0x54, 0x5f, - 0x53, 0x54, 0x41, 0x52, 0x54, 0x10, 0x07, 0x12, 0x10, 0x0a, 0x0c, 0x43, 0x4f, 0x4e, 0x4e, 0x45, - 0x43, 0x54, 0x5f, 0x44, 0x4f, 0x4e, 0x45, 0x10, 0x08, 0x12, 0x17, 0x0a, 0x13, 0x54, 0x4c, 0x53, - 0x5f, 0x48, 0x41, 0x4e, 0x44, 0x53, 0x48, 0x41, 0x4b, 0x45, 0x5f, 0x53, 0x54, 0x41, 0x52, 0x54, - 0x10, 0x09, 0x12, 0x16, 0x0a, 0x12, 0x54, 0x4c, 0x53, 0x5f, 0x48, 0x41, 0x4e, 0x44, 0x53, 0x48, - 0x41, 0x4b, 0x45, 0x5f, 0x44, 0x4f, 0x4e, 0x45, 0x10, 0x0a, 0x12, 0x11, 0x0a, 0x0d, 0x57, 0x52, - 0x4f, 0x54, 0x45, 0x5f, 0x48, 0x45, 0x41, 0x44, 0x45, 0x52, 0x53, 0x10, 0x0b, 0x12, 0x11, 0x0a, - 0x0d, 0x57, 0x52, 0x4f, 0x54, 0x45, 0x5f, 0x52, 0x45, 0x51, 0x55, 0x45, 0x53, 0x54, 0x10, 0x0c, - 0x12, 0x15, 0x0a, 0x11, 0x57, 0x41, 0x49, 0x54, 0x5f, 0x31, 0x30, 0x30, 0x5f, 0x43, 0x4f, 0x4e, - 0x54, 0x49, 0x4e, 0x55, 0x45, 0x10, 0x0d, 0x12, 0x0f, 0x0a, 0x0b, 0x43, 0x4c, 0x4f, 0x53, 0x45, - 0x44, 0x5f, 0x42, 0x4f, 0x44, 0x59, 0x10, 0x0e, 0x42, 0x25, 0x5a, 0x23, 0x65, 0x6e, 0x63, 0x72, - 0x2e, 0x64, 0x65, 0x76, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x65, 0x6e, 0x63, 0x6f, 0x72, - 0x65, 0x2f, 0x65, 0x6e, 0x67, 0x69, 0x6e, 0x65, 0x2f, 0x74, 0x72, 0x61, 0x63, 0x65, 0x32, 0x62, - 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x61, 0x63, 0x65, 0x32, 0x2e, 0x53, 0x74, 0x61, 0x63, 0x6b, 0x54, 0x72, 0x61, 0x63, 0x65, 0x52, + 0x05, 0x73, 0x74, 0x61, 0x63, 0x6b, 0x22, 0x3c, 0x0a, 0x05, 0x4c, 0x65, 0x76, 0x65, 0x6c, 0x12, + 0x09, 0x0a, 0x05, 0x44, 0x45, 0x42, 0x55, 0x47, 0x10, 0x00, 0x12, 0x08, 0x0a, 0x04, 0x49, 0x4e, + 0x46, 0x4f, 0x10, 0x01, 0x12, 0x09, 0x0a, 0x05, 0x45, 0x52, 0x52, 0x4f, 0x52, 0x10, 0x02, 0x12, + 0x08, 0x0a, 0x04, 0x57, 0x41, 0x52, 0x4e, 0x10, 0x03, 0x12, 0x09, 0x0a, 0x05, 0x54, 0x52, 0x41, + 0x43, 0x45, 0x10, 0x04, 0x22, 0xd8, 0x02, 0x0a, 0x08, 0x4c, 0x6f, 0x67, 0x46, 0x69, 0x65, 0x6c, + 0x64, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, + 0x6b, 0x65, 0x79, 0x12, 0x33, 0x0a, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x65, 0x6e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x65, 0x6e, 0x67, 0x69, + 0x6e, 0x65, 0x2e, 0x74, 0x72, 0x61, 0x63, 0x65, 0x32, 0x2e, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x48, + 0x00, 0x52, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x12, 0x12, 0x0a, 0x03, 0x73, 0x74, 0x72, 0x18, + 0x03, 0x20, 0x01, 0x28, 0x09, 0x48, 0x00, 0x52, 0x03, 0x73, 0x74, 0x72, 0x12, 0x14, 0x0a, 0x04, + 0x62, 0x6f, 0x6f, 0x6c, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x48, 0x00, 0x52, 0x04, 0x62, 0x6f, + 0x6f, 0x6c, 0x12, 0x30, 0x0a, 0x04, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x05, 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, 0x48, 0x00, 0x52, 0x04, + 0x74, 0x69, 0x6d, 0x65, 0x12, 0x12, 0x0a, 0x03, 0x64, 0x75, 0x72, 0x18, 0x06, 0x20, 0x01, 0x28, + 0x03, 0x48, 0x00, 0x52, 0x03, 0x64, 0x75, 0x72, 0x12, 0x14, 0x0a, 0x04, 0x75, 0x75, 0x69, 0x64, + 0x18, 0x07, 0x20, 0x01, 0x28, 0x0c, 0x48, 0x00, 0x52, 0x04, 0x75, 0x75, 0x69, 0x64, 0x12, 0x14, + 0x0a, 0x04, 0x6a, 0x73, 0x6f, 0x6e, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0c, 0x48, 0x00, 0x52, 0x04, + 0x6a, 0x73, 0x6f, 0x6e, 0x12, 0x12, 0x0a, 0x03, 0x69, 0x6e, 0x74, 0x18, 0x09, 0x20, 0x01, 0x28, + 0x03, 0x48, 0x00, 0x52, 0x03, 0x69, 0x6e, 0x74, 0x12, 0x14, 0x0a, 0x04, 0x75, 0x69, 0x6e, 0x74, + 0x18, 0x0a, 0x20, 0x01, 0x28, 0x04, 0x48, 0x00, 0x52, 0x04, 0x75, 0x69, 0x6e, 0x74, 0x12, 0x1a, + 0x0a, 0x07, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x33, 0x32, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x02, 0x48, + 0x00, 0x52, 0x07, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x33, 0x32, 0x12, 0x1a, 0x0a, 0x07, 0x66, 0x6c, + 0x6f, 0x61, 0x74, 0x36, 0x34, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x01, 0x48, 0x00, 0x52, 0x07, 0x66, + 0x6c, 0x6f, 0x61, 0x74, 0x36, 0x34, 0x42, 0x07, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x22, + 0x58, 0x0a, 0x0a, 0x53, 0x74, 0x61, 0x63, 0x6b, 0x54, 0x72, 0x61, 0x63, 0x65, 0x12, 0x10, 0x0a, + 0x03, 0x70, 0x63, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x03, 0x52, 0x03, 0x70, 0x63, 0x73, 0x12, + 0x38, 0x0a, 0x06, 0x66, 0x72, 0x61, 0x6d, 0x65, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, + 0x20, 0x2e, 0x65, 0x6e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x65, 0x6e, 0x67, 0x69, 0x6e, 0x65, 0x2e, + 0x74, 0x72, 0x61, 0x63, 0x65, 0x32, 0x2e, 0x53, 0x74, 0x61, 0x63, 0x6b, 0x46, 0x72, 0x61, 0x6d, + 0x65, 0x52, 0x06, 0x66, 0x72, 0x61, 0x6d, 0x65, 0x73, 0x22, 0x50, 0x0a, 0x0a, 0x53, 0x74, 0x61, + 0x63, 0x6b, 0x46, 0x72, 0x61, 0x6d, 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x66, 0x69, 0x6c, 0x65, 0x6e, + 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x66, 0x69, 0x6c, 0x65, 0x6e, + 0x61, 0x6d, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x66, 0x75, 0x6e, 0x63, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x04, 0x66, 0x75, 0x6e, 0x63, 0x12, 0x12, 0x0a, 0x04, 0x6c, 0x69, 0x6e, 0x65, 0x18, + 0x03, 0x20, 0x01, 0x28, 0x05, 0x52, 0x04, 0x6c, 0x69, 0x6e, 0x65, 0x22, 0x60, 0x0a, 0x05, 0x45, + 0x72, 0x72, 0x6f, 0x72, 0x12, 0x10, 0x0a, 0x03, 0x6d, 0x73, 0x67, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x03, 0x6d, 0x73, 0x67, 0x12, 0x3b, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x63, 0x6b, 0x18, + 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x20, 0x2e, 0x65, 0x6e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x65, + 0x6e, 0x67, 0x69, 0x6e, 0x65, 0x2e, 0x74, 0x72, 0x61, 0x63, 0x65, 0x32, 0x2e, 0x53, 0x74, 0x61, + 0x63, 0x6b, 0x54, 0x72, 0x61, 0x63, 0x65, 0x48, 0x00, 0x52, 0x05, 0x73, 0x74, 0x61, 0x63, 0x6b, + 0x88, 0x01, 0x01, 0x42, 0x08, 0x0a, 0x06, 0x5f, 0x73, 0x74, 0x61, 0x63, 0x6b, 0x2a, 0xb1, 0x02, + 0x0a, 0x12, 0x48, 0x54, 0x54, 0x50, 0x54, 0x72, 0x61, 0x63, 0x65, 0x45, 0x76, 0x65, 0x6e, 0x74, + 0x43, 0x6f, 0x64, 0x65, 0x12, 0x0b, 0x0a, 0x07, 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x10, + 0x00, 0x12, 0x0c, 0x0a, 0x08, 0x47, 0x45, 0x54, 0x5f, 0x43, 0x4f, 0x4e, 0x4e, 0x10, 0x01, 0x12, + 0x0c, 0x0a, 0x08, 0x47, 0x4f, 0x54, 0x5f, 0x43, 0x4f, 0x4e, 0x4e, 0x10, 0x02, 0x12, 0x1b, 0x0a, + 0x17, 0x47, 0x4f, 0x54, 0x5f, 0x46, 0x49, 0x52, 0x53, 0x54, 0x5f, 0x52, 0x45, 0x53, 0x50, 0x4f, + 0x4e, 0x53, 0x45, 0x5f, 0x42, 0x59, 0x54, 0x45, 0x10, 0x03, 0x12, 0x14, 0x0a, 0x10, 0x47, 0x4f, + 0x54, 0x5f, 0x31, 0x58, 0x58, 0x5f, 0x52, 0x45, 0x53, 0x50, 0x4f, 0x4e, 0x53, 0x45, 0x10, 0x04, + 0x12, 0x0d, 0x0a, 0x09, 0x44, 0x4e, 0x53, 0x5f, 0x53, 0x54, 0x41, 0x52, 0x54, 0x10, 0x05, 0x12, + 0x0c, 0x0a, 0x08, 0x44, 0x4e, 0x53, 0x5f, 0x44, 0x4f, 0x4e, 0x45, 0x10, 0x06, 0x12, 0x11, 0x0a, + 0x0d, 0x43, 0x4f, 0x4e, 0x4e, 0x45, 0x43, 0x54, 0x5f, 0x53, 0x54, 0x41, 0x52, 0x54, 0x10, 0x07, + 0x12, 0x10, 0x0a, 0x0c, 0x43, 0x4f, 0x4e, 0x4e, 0x45, 0x43, 0x54, 0x5f, 0x44, 0x4f, 0x4e, 0x45, + 0x10, 0x08, 0x12, 0x17, 0x0a, 0x13, 0x54, 0x4c, 0x53, 0x5f, 0x48, 0x41, 0x4e, 0x44, 0x53, 0x48, + 0x41, 0x4b, 0x45, 0x5f, 0x53, 0x54, 0x41, 0x52, 0x54, 0x10, 0x09, 0x12, 0x16, 0x0a, 0x12, 0x54, + 0x4c, 0x53, 0x5f, 0x48, 0x41, 0x4e, 0x44, 0x53, 0x48, 0x41, 0x4b, 0x45, 0x5f, 0x44, 0x4f, 0x4e, + 0x45, 0x10, 0x0a, 0x12, 0x11, 0x0a, 0x0d, 0x57, 0x52, 0x4f, 0x54, 0x45, 0x5f, 0x48, 0x45, 0x41, + 0x44, 0x45, 0x52, 0x53, 0x10, 0x0b, 0x12, 0x11, 0x0a, 0x0d, 0x57, 0x52, 0x4f, 0x54, 0x45, 0x5f, + 0x52, 0x45, 0x51, 0x55, 0x45, 0x53, 0x54, 0x10, 0x0c, 0x12, 0x15, 0x0a, 0x11, 0x57, 0x41, 0x49, + 0x54, 0x5f, 0x31, 0x30, 0x30, 0x5f, 0x43, 0x4f, 0x4e, 0x54, 0x49, 0x4e, 0x55, 0x45, 0x10, 0x0d, + 0x12, 0x0f, 0x0a, 0x0b, 0x43, 0x4c, 0x4f, 0x53, 0x45, 0x44, 0x5f, 0x42, 0x4f, 0x44, 0x59, 0x10, + 0x0e, 0x42, 0x25, 0x5a, 0x23, 0x65, 0x6e, 0x63, 0x72, 0x2e, 0x64, 0x65, 0x76, 0x2f, 0x70, 0x72, + 0x6f, 0x74, 0x6f, 0x2f, 0x65, 0x6e, 0x63, 0x6f, 0x72, 0x65, 0x2f, 0x65, 0x6e, 0x67, 0x69, 0x6e, + 0x65, 0x2f, 0x74, 0x72, 0x61, 0x63, 0x65, 0x32, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( @@ -5123,7 +6184,7 @@ func file_encore_engine_trace2_trace2_proto_rawDescGZIP() []byte { } var file_encore_engine_trace2_trace2_proto_enumTypes = make([]protoimpl.EnumInfo, 5) -var file_encore_engine_trace2_trace2_proto_msgTypes = make([]protoimpl.MessageInfo, 55) +var file_encore_engine_trace2_trace2_proto_msgTypes = make([]protoimpl.MessageInfo, 67) var file_encore_engine_trace2_trace2_proto_goTypes = []interface{}{ (HTTPTraceEventCode)(0), // 0: encore.engine.trace2.HTTPTraceEventCode (SpanSummary_SpanType)(0), // 1: encore.engine.trace2.SpanSummary.SpanType @@ -5159,118 +6220,153 @@ var file_encore_engine_trace2_trace2_proto_goTypes = []interface{}{ (*ServiceInitEnd)(nil), // 31: encore.engine.trace2.ServiceInitEnd (*CacheCallStart)(nil), // 32: encore.engine.trace2.CacheCallStart (*CacheCallEnd)(nil), // 33: encore.engine.trace2.CacheCallEnd - (*BodyStream)(nil), // 34: encore.engine.trace2.BodyStream - (*HTTPCallStart)(nil), // 35: encore.engine.trace2.HTTPCallStart - (*HTTPCallEnd)(nil), // 36: encore.engine.trace2.HTTPCallEnd - (*HTTPTraceEvent)(nil), // 37: encore.engine.trace2.HTTPTraceEvent - (*HTTPGetConn)(nil), // 38: encore.engine.trace2.HTTPGetConn - (*HTTPGotConn)(nil), // 39: encore.engine.trace2.HTTPGotConn - (*HTTPGotFirstResponseByte)(nil), // 40: encore.engine.trace2.HTTPGotFirstResponseByte - (*HTTPGot1XxResponse)(nil), // 41: encore.engine.trace2.HTTPGot1xxResponse - (*HTTPDNSStart)(nil), // 42: encore.engine.trace2.HTTPDNSStart - (*HTTPDNSDone)(nil), // 43: encore.engine.trace2.HTTPDNSDone - (*DNSAddr)(nil), // 44: encore.engine.trace2.DNSAddr - (*HTTPConnectStart)(nil), // 45: encore.engine.trace2.HTTPConnectStart - (*HTTPConnectDone)(nil), // 46: encore.engine.trace2.HTTPConnectDone - (*HTTPTLSHandshakeStart)(nil), // 47: encore.engine.trace2.HTTPTLSHandshakeStart - (*HTTPTLSHandshakeDone)(nil), // 48: encore.engine.trace2.HTTPTLSHandshakeDone - (*HTTPWroteHeaders)(nil), // 49: encore.engine.trace2.HTTPWroteHeaders - (*HTTPWroteRequest)(nil), // 50: encore.engine.trace2.HTTPWroteRequest - (*HTTPWait100Continue)(nil), // 51: encore.engine.trace2.HTTPWait100Continue - (*HTTPClosedBodyData)(nil), // 52: encore.engine.trace2.HTTPClosedBodyData - (*LogMessage)(nil), // 53: encore.engine.trace2.LogMessage - (*LogField)(nil), // 54: encore.engine.trace2.LogField - (*StackTrace)(nil), // 55: encore.engine.trace2.StackTrace - (*StackFrame)(nil), // 56: encore.engine.trace2.StackFrame - (*Error)(nil), // 57: encore.engine.trace2.Error - nil, // 58: encore.engine.trace2.RequestSpanStart.RequestHeadersEntry - nil, // 59: encore.engine.trace2.RequestSpanEnd.ResponseHeadersEntry - (*timestamppb.Timestamp)(nil), // 60: google.protobuf.Timestamp + (*BucketObjectUploadStart)(nil), // 34: encore.engine.trace2.BucketObjectUploadStart + (*BucketObjectUploadEnd)(nil), // 35: encore.engine.trace2.BucketObjectUploadEnd + (*BucketObjectDownloadStart)(nil), // 36: encore.engine.trace2.BucketObjectDownloadStart + (*BucketObjectDownloadEnd)(nil), // 37: encore.engine.trace2.BucketObjectDownloadEnd + (*BucketObjectGetAttrsStart)(nil), // 38: encore.engine.trace2.BucketObjectGetAttrsStart + (*BucketObjectGetAttrsEnd)(nil), // 39: encore.engine.trace2.BucketObjectGetAttrsEnd + (*BucketListObjectsStart)(nil), // 40: encore.engine.trace2.BucketListObjectsStart + (*BucketListObjectsEnd)(nil), // 41: encore.engine.trace2.BucketListObjectsEnd + (*BucketDeleteObjectsStart)(nil), // 42: encore.engine.trace2.BucketDeleteObjectsStart + (*BucketDeleteObjectEntry)(nil), // 43: encore.engine.trace2.BucketDeleteObjectEntry + (*BucketDeleteObjectsEnd)(nil), // 44: encore.engine.trace2.BucketDeleteObjectsEnd + (*BucketObjectAttributes)(nil), // 45: encore.engine.trace2.BucketObjectAttributes + (*BodyStream)(nil), // 46: encore.engine.trace2.BodyStream + (*HTTPCallStart)(nil), // 47: encore.engine.trace2.HTTPCallStart + (*HTTPCallEnd)(nil), // 48: encore.engine.trace2.HTTPCallEnd + (*HTTPTraceEvent)(nil), // 49: encore.engine.trace2.HTTPTraceEvent + (*HTTPGetConn)(nil), // 50: encore.engine.trace2.HTTPGetConn + (*HTTPGotConn)(nil), // 51: encore.engine.trace2.HTTPGotConn + (*HTTPGotFirstResponseByte)(nil), // 52: encore.engine.trace2.HTTPGotFirstResponseByte + (*HTTPGot1XxResponse)(nil), // 53: encore.engine.trace2.HTTPGot1xxResponse + (*HTTPDNSStart)(nil), // 54: encore.engine.trace2.HTTPDNSStart + (*HTTPDNSDone)(nil), // 55: encore.engine.trace2.HTTPDNSDone + (*DNSAddr)(nil), // 56: encore.engine.trace2.DNSAddr + (*HTTPConnectStart)(nil), // 57: encore.engine.trace2.HTTPConnectStart + (*HTTPConnectDone)(nil), // 58: encore.engine.trace2.HTTPConnectDone + (*HTTPTLSHandshakeStart)(nil), // 59: encore.engine.trace2.HTTPTLSHandshakeStart + (*HTTPTLSHandshakeDone)(nil), // 60: encore.engine.trace2.HTTPTLSHandshakeDone + (*HTTPWroteHeaders)(nil), // 61: encore.engine.trace2.HTTPWroteHeaders + (*HTTPWroteRequest)(nil), // 62: encore.engine.trace2.HTTPWroteRequest + (*HTTPWait100Continue)(nil), // 63: encore.engine.trace2.HTTPWait100Continue + (*HTTPClosedBodyData)(nil), // 64: encore.engine.trace2.HTTPClosedBodyData + (*LogMessage)(nil), // 65: encore.engine.trace2.LogMessage + (*LogField)(nil), // 66: encore.engine.trace2.LogField + (*StackTrace)(nil), // 67: encore.engine.trace2.StackTrace + (*StackFrame)(nil), // 68: encore.engine.trace2.StackFrame + (*Error)(nil), // 69: encore.engine.trace2.Error + nil, // 70: encore.engine.trace2.RequestSpanStart.RequestHeadersEntry + nil, // 71: encore.engine.trace2.RequestSpanEnd.ResponseHeadersEntry + (*timestamppb.Timestamp)(nil), // 72: google.protobuf.Timestamp } var file_encore_engine_trace2_trace2_proto_depIdxs = []int32{ - 1, // 0: encore.engine.trace2.SpanSummary.type:type_name -> encore.engine.trace2.SpanSummary.SpanType - 60, // 1: encore.engine.trace2.SpanSummary.started_at:type_name -> google.protobuf.Timestamp - 8, // 2: encore.engine.trace2.EventList.events:type_name -> encore.engine.trace2.TraceEvent - 6, // 3: encore.engine.trace2.TraceEvent.trace_id:type_name -> encore.engine.trace2.TraceID - 60, // 4: encore.engine.trace2.TraceEvent.event_time:type_name -> google.protobuf.Timestamp - 9, // 5: encore.engine.trace2.TraceEvent.span_start:type_name -> encore.engine.trace2.SpanStart - 10, // 6: encore.engine.trace2.TraceEvent.span_end:type_name -> encore.engine.trace2.SpanEnd - 19, // 7: encore.engine.trace2.TraceEvent.span_event:type_name -> encore.engine.trace2.SpanEvent - 6, // 8: encore.engine.trace2.SpanStart.parent_trace_id:type_name -> encore.engine.trace2.TraceID - 11, // 9: encore.engine.trace2.SpanStart.request:type_name -> encore.engine.trace2.RequestSpanStart - 13, // 10: encore.engine.trace2.SpanStart.auth:type_name -> encore.engine.trace2.AuthSpanStart - 15, // 11: encore.engine.trace2.SpanStart.pubsub_message:type_name -> encore.engine.trace2.PubsubMessageSpanStart - 17, // 12: encore.engine.trace2.SpanStart.test:type_name -> encore.engine.trace2.TestSpanStart - 57, // 13: encore.engine.trace2.SpanEnd.error:type_name -> encore.engine.trace2.Error - 55, // 14: encore.engine.trace2.SpanEnd.panic_stack:type_name -> encore.engine.trace2.StackTrace - 6, // 15: encore.engine.trace2.SpanEnd.parent_trace_id:type_name -> encore.engine.trace2.TraceID - 12, // 16: encore.engine.trace2.SpanEnd.request:type_name -> encore.engine.trace2.RequestSpanEnd - 14, // 17: encore.engine.trace2.SpanEnd.auth:type_name -> encore.engine.trace2.AuthSpanEnd - 16, // 18: encore.engine.trace2.SpanEnd.pubsub_message:type_name -> encore.engine.trace2.PubsubMessageSpanEnd - 18, // 19: encore.engine.trace2.SpanEnd.test:type_name -> encore.engine.trace2.TestSpanEnd - 58, // 20: encore.engine.trace2.RequestSpanStart.request_headers:type_name -> encore.engine.trace2.RequestSpanStart.RequestHeadersEntry - 59, // 21: encore.engine.trace2.RequestSpanEnd.response_headers:type_name -> encore.engine.trace2.RequestSpanEnd.ResponseHeadersEntry - 60, // 22: encore.engine.trace2.PubsubMessageSpanStart.publish_time:type_name -> google.protobuf.Timestamp - 53, // 23: encore.engine.trace2.SpanEvent.log_message:type_name -> encore.engine.trace2.LogMessage - 34, // 24: encore.engine.trace2.SpanEvent.body_stream:type_name -> encore.engine.trace2.BodyStream - 20, // 25: encore.engine.trace2.SpanEvent.rpc_call_start:type_name -> encore.engine.trace2.RPCCallStart - 21, // 26: encore.engine.trace2.SpanEvent.rpc_call_end:type_name -> encore.engine.trace2.RPCCallEnd - 24, // 27: encore.engine.trace2.SpanEvent.db_transaction_start:type_name -> encore.engine.trace2.DBTransactionStart - 25, // 28: encore.engine.trace2.SpanEvent.db_transaction_end:type_name -> encore.engine.trace2.DBTransactionEnd - 26, // 29: encore.engine.trace2.SpanEvent.db_query_start:type_name -> encore.engine.trace2.DBQueryStart - 27, // 30: encore.engine.trace2.SpanEvent.db_query_end:type_name -> encore.engine.trace2.DBQueryEnd - 35, // 31: encore.engine.trace2.SpanEvent.http_call_start:type_name -> encore.engine.trace2.HTTPCallStart - 36, // 32: encore.engine.trace2.SpanEvent.http_call_end:type_name -> encore.engine.trace2.HTTPCallEnd - 28, // 33: encore.engine.trace2.SpanEvent.pubsub_publish_start:type_name -> encore.engine.trace2.PubsubPublishStart - 29, // 34: encore.engine.trace2.SpanEvent.pubsub_publish_end:type_name -> encore.engine.trace2.PubsubPublishEnd - 32, // 35: encore.engine.trace2.SpanEvent.cache_call_start:type_name -> encore.engine.trace2.CacheCallStart - 33, // 36: encore.engine.trace2.SpanEvent.cache_call_end:type_name -> encore.engine.trace2.CacheCallEnd - 30, // 37: encore.engine.trace2.SpanEvent.service_init_start:type_name -> encore.engine.trace2.ServiceInitStart - 31, // 38: encore.engine.trace2.SpanEvent.service_init_end:type_name -> encore.engine.trace2.ServiceInitEnd - 55, // 39: encore.engine.trace2.RPCCallStart.stack:type_name -> encore.engine.trace2.StackTrace - 57, // 40: encore.engine.trace2.RPCCallEnd.err:type_name -> encore.engine.trace2.Error - 55, // 41: encore.engine.trace2.DBTransactionStart.stack:type_name -> encore.engine.trace2.StackTrace - 2, // 42: encore.engine.trace2.DBTransactionEnd.completion:type_name -> encore.engine.trace2.DBTransactionEnd.CompletionType - 55, // 43: encore.engine.trace2.DBTransactionEnd.stack:type_name -> encore.engine.trace2.StackTrace - 57, // 44: encore.engine.trace2.DBTransactionEnd.err:type_name -> encore.engine.trace2.Error - 55, // 45: encore.engine.trace2.DBQueryStart.stack:type_name -> encore.engine.trace2.StackTrace - 57, // 46: encore.engine.trace2.DBQueryEnd.err:type_name -> encore.engine.trace2.Error - 55, // 47: encore.engine.trace2.PubsubPublishStart.stack:type_name -> encore.engine.trace2.StackTrace - 57, // 48: encore.engine.trace2.PubsubPublishEnd.err:type_name -> encore.engine.trace2.Error - 57, // 49: encore.engine.trace2.ServiceInitEnd.err:type_name -> encore.engine.trace2.Error - 55, // 50: encore.engine.trace2.CacheCallStart.stack:type_name -> encore.engine.trace2.StackTrace - 3, // 51: encore.engine.trace2.CacheCallEnd.result:type_name -> encore.engine.trace2.CacheCallEnd.Result - 57, // 52: encore.engine.trace2.CacheCallEnd.err:type_name -> encore.engine.trace2.Error - 55, // 53: encore.engine.trace2.HTTPCallStart.stack:type_name -> encore.engine.trace2.StackTrace - 57, // 54: encore.engine.trace2.HTTPCallEnd.err:type_name -> encore.engine.trace2.Error - 37, // 55: encore.engine.trace2.HTTPCallEnd.trace_events:type_name -> encore.engine.trace2.HTTPTraceEvent - 38, // 56: encore.engine.trace2.HTTPTraceEvent.get_conn:type_name -> encore.engine.trace2.HTTPGetConn - 39, // 57: encore.engine.trace2.HTTPTraceEvent.got_conn:type_name -> encore.engine.trace2.HTTPGotConn - 40, // 58: encore.engine.trace2.HTTPTraceEvent.got_first_response_byte:type_name -> encore.engine.trace2.HTTPGotFirstResponseByte - 41, // 59: encore.engine.trace2.HTTPTraceEvent.got_1xx_response:type_name -> encore.engine.trace2.HTTPGot1xxResponse - 42, // 60: encore.engine.trace2.HTTPTraceEvent.dns_start:type_name -> encore.engine.trace2.HTTPDNSStart - 43, // 61: encore.engine.trace2.HTTPTraceEvent.dns_done:type_name -> encore.engine.trace2.HTTPDNSDone - 45, // 62: encore.engine.trace2.HTTPTraceEvent.connect_start:type_name -> encore.engine.trace2.HTTPConnectStart - 46, // 63: encore.engine.trace2.HTTPTraceEvent.connect_done:type_name -> encore.engine.trace2.HTTPConnectDone - 47, // 64: encore.engine.trace2.HTTPTraceEvent.tls_handshake_start:type_name -> encore.engine.trace2.HTTPTLSHandshakeStart - 48, // 65: encore.engine.trace2.HTTPTraceEvent.tls_handshake_done:type_name -> encore.engine.trace2.HTTPTLSHandshakeDone - 49, // 66: encore.engine.trace2.HTTPTraceEvent.wrote_headers:type_name -> encore.engine.trace2.HTTPWroteHeaders - 50, // 67: encore.engine.trace2.HTTPTraceEvent.wrote_request:type_name -> encore.engine.trace2.HTTPWroteRequest - 51, // 68: encore.engine.trace2.HTTPTraceEvent.wait_100_continue:type_name -> encore.engine.trace2.HTTPWait100Continue - 52, // 69: encore.engine.trace2.HTTPTraceEvent.closed_body:type_name -> encore.engine.trace2.HTTPClosedBodyData - 44, // 70: encore.engine.trace2.HTTPDNSDone.addrs:type_name -> encore.engine.trace2.DNSAddr - 4, // 71: encore.engine.trace2.LogMessage.level:type_name -> encore.engine.trace2.LogMessage.Level - 54, // 72: encore.engine.trace2.LogMessage.fields:type_name -> encore.engine.trace2.LogField - 55, // 73: encore.engine.trace2.LogMessage.stack:type_name -> encore.engine.trace2.StackTrace - 57, // 74: encore.engine.trace2.LogField.error:type_name -> encore.engine.trace2.Error - 60, // 75: encore.engine.trace2.LogField.time:type_name -> google.protobuf.Timestamp - 56, // 76: encore.engine.trace2.StackTrace.frames:type_name -> encore.engine.trace2.StackFrame - 55, // 77: encore.engine.trace2.Error.stack:type_name -> encore.engine.trace2.StackTrace - 78, // [78:78] is the sub-list for method output_type - 78, // [78:78] is the sub-list for method input_type - 78, // [78:78] is the sub-list for extension type_name - 78, // [78:78] is the sub-list for extension extendee - 0, // [0:78] is the sub-list for field type_name + 1, // 0: encore.engine.trace2.SpanSummary.type:type_name -> encore.engine.trace2.SpanSummary.SpanType + 72, // 1: encore.engine.trace2.SpanSummary.started_at:type_name -> google.protobuf.Timestamp + 8, // 2: encore.engine.trace2.EventList.events:type_name -> encore.engine.trace2.TraceEvent + 6, // 3: encore.engine.trace2.TraceEvent.trace_id:type_name -> encore.engine.trace2.TraceID + 72, // 4: encore.engine.trace2.TraceEvent.event_time:type_name -> google.protobuf.Timestamp + 9, // 5: encore.engine.trace2.TraceEvent.span_start:type_name -> encore.engine.trace2.SpanStart + 10, // 6: encore.engine.trace2.TraceEvent.span_end:type_name -> encore.engine.trace2.SpanEnd + 19, // 7: encore.engine.trace2.TraceEvent.span_event:type_name -> encore.engine.trace2.SpanEvent + 6, // 8: encore.engine.trace2.SpanStart.parent_trace_id:type_name -> encore.engine.trace2.TraceID + 11, // 9: encore.engine.trace2.SpanStart.request:type_name -> encore.engine.trace2.RequestSpanStart + 13, // 10: encore.engine.trace2.SpanStart.auth:type_name -> encore.engine.trace2.AuthSpanStart + 15, // 11: encore.engine.trace2.SpanStart.pubsub_message:type_name -> encore.engine.trace2.PubsubMessageSpanStart + 17, // 12: encore.engine.trace2.SpanStart.test:type_name -> encore.engine.trace2.TestSpanStart + 69, // 13: encore.engine.trace2.SpanEnd.error:type_name -> encore.engine.trace2.Error + 67, // 14: encore.engine.trace2.SpanEnd.panic_stack:type_name -> encore.engine.trace2.StackTrace + 6, // 15: encore.engine.trace2.SpanEnd.parent_trace_id:type_name -> encore.engine.trace2.TraceID + 12, // 16: encore.engine.trace2.SpanEnd.request:type_name -> encore.engine.trace2.RequestSpanEnd + 14, // 17: encore.engine.trace2.SpanEnd.auth:type_name -> encore.engine.trace2.AuthSpanEnd + 16, // 18: encore.engine.trace2.SpanEnd.pubsub_message:type_name -> encore.engine.trace2.PubsubMessageSpanEnd + 18, // 19: encore.engine.trace2.SpanEnd.test:type_name -> encore.engine.trace2.TestSpanEnd + 70, // 20: encore.engine.trace2.RequestSpanStart.request_headers:type_name -> encore.engine.trace2.RequestSpanStart.RequestHeadersEntry + 71, // 21: encore.engine.trace2.RequestSpanEnd.response_headers:type_name -> encore.engine.trace2.RequestSpanEnd.ResponseHeadersEntry + 72, // 22: encore.engine.trace2.PubsubMessageSpanStart.publish_time:type_name -> google.protobuf.Timestamp + 65, // 23: encore.engine.trace2.SpanEvent.log_message:type_name -> encore.engine.trace2.LogMessage + 46, // 24: encore.engine.trace2.SpanEvent.body_stream:type_name -> encore.engine.trace2.BodyStream + 20, // 25: encore.engine.trace2.SpanEvent.rpc_call_start:type_name -> encore.engine.trace2.RPCCallStart + 21, // 26: encore.engine.trace2.SpanEvent.rpc_call_end:type_name -> encore.engine.trace2.RPCCallEnd + 24, // 27: encore.engine.trace2.SpanEvent.db_transaction_start:type_name -> encore.engine.trace2.DBTransactionStart + 25, // 28: encore.engine.trace2.SpanEvent.db_transaction_end:type_name -> encore.engine.trace2.DBTransactionEnd + 26, // 29: encore.engine.trace2.SpanEvent.db_query_start:type_name -> encore.engine.trace2.DBQueryStart + 27, // 30: encore.engine.trace2.SpanEvent.db_query_end:type_name -> encore.engine.trace2.DBQueryEnd + 47, // 31: encore.engine.trace2.SpanEvent.http_call_start:type_name -> encore.engine.trace2.HTTPCallStart + 48, // 32: encore.engine.trace2.SpanEvent.http_call_end:type_name -> encore.engine.trace2.HTTPCallEnd + 28, // 33: encore.engine.trace2.SpanEvent.pubsub_publish_start:type_name -> encore.engine.trace2.PubsubPublishStart + 29, // 34: encore.engine.trace2.SpanEvent.pubsub_publish_end:type_name -> encore.engine.trace2.PubsubPublishEnd + 32, // 35: encore.engine.trace2.SpanEvent.cache_call_start:type_name -> encore.engine.trace2.CacheCallStart + 33, // 36: encore.engine.trace2.SpanEvent.cache_call_end:type_name -> encore.engine.trace2.CacheCallEnd + 30, // 37: encore.engine.trace2.SpanEvent.service_init_start:type_name -> encore.engine.trace2.ServiceInitStart + 31, // 38: encore.engine.trace2.SpanEvent.service_init_end:type_name -> encore.engine.trace2.ServiceInitEnd + 34, // 39: encore.engine.trace2.SpanEvent.bucket_object_upload_start:type_name -> encore.engine.trace2.BucketObjectUploadStart + 35, // 40: encore.engine.trace2.SpanEvent.bucket_object_upload_end:type_name -> encore.engine.trace2.BucketObjectUploadEnd + 36, // 41: encore.engine.trace2.SpanEvent.bucket_object_download_start:type_name -> encore.engine.trace2.BucketObjectDownloadStart + 37, // 42: encore.engine.trace2.SpanEvent.bucket_object_download_end:type_name -> encore.engine.trace2.BucketObjectDownloadEnd + 38, // 43: encore.engine.trace2.SpanEvent.bucket_object_get_attrs_start:type_name -> encore.engine.trace2.BucketObjectGetAttrsStart + 39, // 44: encore.engine.trace2.SpanEvent.bucket_object_get_attrs_end:type_name -> encore.engine.trace2.BucketObjectGetAttrsEnd + 40, // 45: encore.engine.trace2.SpanEvent.bucket_list_objects_start:type_name -> encore.engine.trace2.BucketListObjectsStart + 41, // 46: encore.engine.trace2.SpanEvent.bucket_list_objects_end:type_name -> encore.engine.trace2.BucketListObjectsEnd + 42, // 47: encore.engine.trace2.SpanEvent.bucket_delete_objects_start:type_name -> encore.engine.trace2.BucketDeleteObjectsStart + 44, // 48: encore.engine.trace2.SpanEvent.bucket_delete_objects_end:type_name -> encore.engine.trace2.BucketDeleteObjectsEnd + 67, // 49: encore.engine.trace2.RPCCallStart.stack:type_name -> encore.engine.trace2.StackTrace + 69, // 50: encore.engine.trace2.RPCCallEnd.err:type_name -> encore.engine.trace2.Error + 67, // 51: encore.engine.trace2.DBTransactionStart.stack:type_name -> encore.engine.trace2.StackTrace + 2, // 52: encore.engine.trace2.DBTransactionEnd.completion:type_name -> encore.engine.trace2.DBTransactionEnd.CompletionType + 67, // 53: encore.engine.trace2.DBTransactionEnd.stack:type_name -> encore.engine.trace2.StackTrace + 69, // 54: encore.engine.trace2.DBTransactionEnd.err:type_name -> encore.engine.trace2.Error + 67, // 55: encore.engine.trace2.DBQueryStart.stack:type_name -> encore.engine.trace2.StackTrace + 69, // 56: encore.engine.trace2.DBQueryEnd.err:type_name -> encore.engine.trace2.Error + 67, // 57: encore.engine.trace2.PubsubPublishStart.stack:type_name -> encore.engine.trace2.StackTrace + 69, // 58: encore.engine.trace2.PubsubPublishEnd.err:type_name -> encore.engine.trace2.Error + 69, // 59: encore.engine.trace2.ServiceInitEnd.err:type_name -> encore.engine.trace2.Error + 67, // 60: encore.engine.trace2.CacheCallStart.stack:type_name -> encore.engine.trace2.StackTrace + 3, // 61: encore.engine.trace2.CacheCallEnd.result:type_name -> encore.engine.trace2.CacheCallEnd.Result + 69, // 62: encore.engine.trace2.CacheCallEnd.err:type_name -> encore.engine.trace2.Error + 45, // 63: encore.engine.trace2.BucketObjectUploadStart.attrs:type_name -> encore.engine.trace2.BucketObjectAttributes + 67, // 64: encore.engine.trace2.BucketObjectUploadStart.stack:type_name -> encore.engine.trace2.StackTrace + 69, // 65: encore.engine.trace2.BucketObjectUploadEnd.err:type_name -> encore.engine.trace2.Error + 67, // 66: encore.engine.trace2.BucketObjectDownloadStart.stack:type_name -> encore.engine.trace2.StackTrace + 69, // 67: encore.engine.trace2.BucketObjectDownloadEnd.err:type_name -> encore.engine.trace2.Error + 67, // 68: encore.engine.trace2.BucketObjectGetAttrsStart.stack:type_name -> encore.engine.trace2.StackTrace + 69, // 69: encore.engine.trace2.BucketObjectGetAttrsEnd.err:type_name -> encore.engine.trace2.Error + 45, // 70: encore.engine.trace2.BucketObjectGetAttrsEnd.attrs:type_name -> encore.engine.trace2.BucketObjectAttributes + 67, // 71: encore.engine.trace2.BucketListObjectsStart.stack:type_name -> encore.engine.trace2.StackTrace + 69, // 72: encore.engine.trace2.BucketListObjectsEnd.err:type_name -> encore.engine.trace2.Error + 67, // 73: encore.engine.trace2.BucketDeleteObjectsStart.stack:type_name -> encore.engine.trace2.StackTrace + 43, // 74: encore.engine.trace2.BucketDeleteObjectsStart.entries:type_name -> encore.engine.trace2.BucketDeleteObjectEntry + 69, // 75: encore.engine.trace2.BucketDeleteObjectsEnd.err:type_name -> encore.engine.trace2.Error + 67, // 76: encore.engine.trace2.HTTPCallStart.stack:type_name -> encore.engine.trace2.StackTrace + 69, // 77: encore.engine.trace2.HTTPCallEnd.err:type_name -> encore.engine.trace2.Error + 49, // 78: encore.engine.trace2.HTTPCallEnd.trace_events:type_name -> encore.engine.trace2.HTTPTraceEvent + 50, // 79: encore.engine.trace2.HTTPTraceEvent.get_conn:type_name -> encore.engine.trace2.HTTPGetConn + 51, // 80: encore.engine.trace2.HTTPTraceEvent.got_conn:type_name -> encore.engine.trace2.HTTPGotConn + 52, // 81: encore.engine.trace2.HTTPTraceEvent.got_first_response_byte:type_name -> encore.engine.trace2.HTTPGotFirstResponseByte + 53, // 82: encore.engine.trace2.HTTPTraceEvent.got_1xx_response:type_name -> encore.engine.trace2.HTTPGot1xxResponse + 54, // 83: encore.engine.trace2.HTTPTraceEvent.dns_start:type_name -> encore.engine.trace2.HTTPDNSStart + 55, // 84: encore.engine.trace2.HTTPTraceEvent.dns_done:type_name -> encore.engine.trace2.HTTPDNSDone + 57, // 85: encore.engine.trace2.HTTPTraceEvent.connect_start:type_name -> encore.engine.trace2.HTTPConnectStart + 58, // 86: encore.engine.trace2.HTTPTraceEvent.connect_done:type_name -> encore.engine.trace2.HTTPConnectDone + 59, // 87: encore.engine.trace2.HTTPTraceEvent.tls_handshake_start:type_name -> encore.engine.trace2.HTTPTLSHandshakeStart + 60, // 88: encore.engine.trace2.HTTPTraceEvent.tls_handshake_done:type_name -> encore.engine.trace2.HTTPTLSHandshakeDone + 61, // 89: encore.engine.trace2.HTTPTraceEvent.wrote_headers:type_name -> encore.engine.trace2.HTTPWroteHeaders + 62, // 90: encore.engine.trace2.HTTPTraceEvent.wrote_request:type_name -> encore.engine.trace2.HTTPWroteRequest + 63, // 91: encore.engine.trace2.HTTPTraceEvent.wait_100_continue:type_name -> encore.engine.trace2.HTTPWait100Continue + 64, // 92: encore.engine.trace2.HTTPTraceEvent.closed_body:type_name -> encore.engine.trace2.HTTPClosedBodyData + 56, // 93: encore.engine.trace2.HTTPDNSDone.addrs:type_name -> encore.engine.trace2.DNSAddr + 4, // 94: encore.engine.trace2.LogMessage.level:type_name -> encore.engine.trace2.LogMessage.Level + 66, // 95: encore.engine.trace2.LogMessage.fields:type_name -> encore.engine.trace2.LogField + 67, // 96: encore.engine.trace2.LogMessage.stack:type_name -> encore.engine.trace2.StackTrace + 69, // 97: encore.engine.trace2.LogField.error:type_name -> encore.engine.trace2.Error + 72, // 98: encore.engine.trace2.LogField.time:type_name -> google.protobuf.Timestamp + 68, // 99: encore.engine.trace2.StackTrace.frames:type_name -> encore.engine.trace2.StackFrame + 67, // 100: encore.engine.trace2.Error.stack:type_name -> encore.engine.trace2.StackTrace + 101, // [101:101] is the sub-list for method output_type + 101, // [101:101] is the sub-list for method input_type + 101, // [101:101] is the sub-list for extension type_name + 101, // [101:101] is the sub-list for extension extendee + 0, // [0:101] is the sub-list for field type_name } func init() { file_encore_engine_trace2_trace2_proto_init() } @@ -5628,7 +6724,7 @@ func file_encore_engine_trace2_trace2_proto_init() { } } file_encore_engine_trace2_trace2_proto_msgTypes[29].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*BodyStream); i { + switch v := v.(*BucketObjectUploadStart); i { case 0: return &v.state case 1: @@ -5640,7 +6736,7 @@ func file_encore_engine_trace2_trace2_proto_init() { } } file_encore_engine_trace2_trace2_proto_msgTypes[30].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*HTTPCallStart); i { + switch v := v.(*BucketObjectUploadEnd); i { case 0: return &v.state case 1: @@ -5652,7 +6748,7 @@ func file_encore_engine_trace2_trace2_proto_init() { } } file_encore_engine_trace2_trace2_proto_msgTypes[31].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*HTTPCallEnd); i { + switch v := v.(*BucketObjectDownloadStart); i { case 0: return &v.state case 1: @@ -5664,7 +6760,7 @@ func file_encore_engine_trace2_trace2_proto_init() { } } file_encore_engine_trace2_trace2_proto_msgTypes[32].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*HTTPTraceEvent); i { + switch v := v.(*BucketObjectDownloadEnd); i { case 0: return &v.state case 1: @@ -5676,7 +6772,7 @@ func file_encore_engine_trace2_trace2_proto_init() { } } file_encore_engine_trace2_trace2_proto_msgTypes[33].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*HTTPGetConn); i { + switch v := v.(*BucketObjectGetAttrsStart); i { case 0: return &v.state case 1: @@ -5688,7 +6784,7 @@ func file_encore_engine_trace2_trace2_proto_init() { } } file_encore_engine_trace2_trace2_proto_msgTypes[34].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*HTTPGotConn); i { + switch v := v.(*BucketObjectGetAttrsEnd); i { case 0: return &v.state case 1: @@ -5700,7 +6796,7 @@ func file_encore_engine_trace2_trace2_proto_init() { } } file_encore_engine_trace2_trace2_proto_msgTypes[35].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*HTTPGotFirstResponseByte); i { + switch v := v.(*BucketListObjectsStart); i { case 0: return &v.state case 1: @@ -5712,7 +6808,7 @@ func file_encore_engine_trace2_trace2_proto_init() { } } file_encore_engine_trace2_trace2_proto_msgTypes[36].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*HTTPGot1XxResponse); i { + switch v := v.(*BucketListObjectsEnd); i { case 0: return &v.state case 1: @@ -5724,7 +6820,7 @@ func file_encore_engine_trace2_trace2_proto_init() { } } file_encore_engine_trace2_trace2_proto_msgTypes[37].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*HTTPDNSStart); i { + switch v := v.(*BucketDeleteObjectsStart); i { case 0: return &v.state case 1: @@ -5736,7 +6832,7 @@ func file_encore_engine_trace2_trace2_proto_init() { } } file_encore_engine_trace2_trace2_proto_msgTypes[38].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*HTTPDNSDone); i { + switch v := v.(*BucketDeleteObjectEntry); i { case 0: return &v.state case 1: @@ -5748,7 +6844,7 @@ func file_encore_engine_trace2_trace2_proto_init() { } } file_encore_engine_trace2_trace2_proto_msgTypes[39].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*DNSAddr); i { + switch v := v.(*BucketDeleteObjectsEnd); i { case 0: return &v.state case 1: @@ -5760,7 +6856,7 @@ func file_encore_engine_trace2_trace2_proto_init() { } } file_encore_engine_trace2_trace2_proto_msgTypes[40].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*HTTPConnectStart); i { + switch v := v.(*BucketObjectAttributes); i { case 0: return &v.state case 1: @@ -5772,7 +6868,7 @@ func file_encore_engine_trace2_trace2_proto_init() { } } file_encore_engine_trace2_trace2_proto_msgTypes[41].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*HTTPConnectDone); i { + switch v := v.(*BodyStream); i { case 0: return &v.state case 1: @@ -5784,7 +6880,7 @@ func file_encore_engine_trace2_trace2_proto_init() { } } file_encore_engine_trace2_trace2_proto_msgTypes[42].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*HTTPTLSHandshakeStart); i { + switch v := v.(*HTTPCallStart); i { case 0: return &v.state case 1: @@ -5796,7 +6892,7 @@ func file_encore_engine_trace2_trace2_proto_init() { } } file_encore_engine_trace2_trace2_proto_msgTypes[43].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*HTTPTLSHandshakeDone); i { + switch v := v.(*HTTPCallEnd); i { case 0: return &v.state case 1: @@ -5808,7 +6904,7 @@ func file_encore_engine_trace2_trace2_proto_init() { } } file_encore_engine_trace2_trace2_proto_msgTypes[44].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*HTTPWroteHeaders); i { + switch v := v.(*HTTPTraceEvent); i { case 0: return &v.state case 1: @@ -5820,7 +6916,7 @@ func file_encore_engine_trace2_trace2_proto_init() { } } file_encore_engine_trace2_trace2_proto_msgTypes[45].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*HTTPWroteRequest); i { + switch v := v.(*HTTPGetConn); i { case 0: return &v.state case 1: @@ -5832,7 +6928,7 @@ func file_encore_engine_trace2_trace2_proto_init() { } } file_encore_engine_trace2_trace2_proto_msgTypes[46].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*HTTPWait100Continue); i { + switch v := v.(*HTTPGotConn); i { case 0: return &v.state case 1: @@ -5844,7 +6940,7 @@ func file_encore_engine_trace2_trace2_proto_init() { } } file_encore_engine_trace2_trace2_proto_msgTypes[47].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*HTTPClosedBodyData); i { + switch v := v.(*HTTPGotFirstResponseByte); i { case 0: return &v.state case 1: @@ -5856,7 +6952,7 @@ func file_encore_engine_trace2_trace2_proto_init() { } } file_encore_engine_trace2_trace2_proto_msgTypes[48].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*LogMessage); i { + switch v := v.(*HTTPGot1XxResponse); i { case 0: return &v.state case 1: @@ -5868,7 +6964,7 @@ func file_encore_engine_trace2_trace2_proto_init() { } } file_encore_engine_trace2_trace2_proto_msgTypes[49].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*LogField); i { + switch v := v.(*HTTPDNSStart); i { case 0: return &v.state case 1: @@ -5880,7 +6976,7 @@ func file_encore_engine_trace2_trace2_proto_init() { } } file_encore_engine_trace2_trace2_proto_msgTypes[50].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*StackTrace); i { + switch v := v.(*HTTPDNSDone); i { case 0: return &v.state case 1: @@ -5892,7 +6988,7 @@ func file_encore_engine_trace2_trace2_proto_init() { } } file_encore_engine_trace2_trace2_proto_msgTypes[51].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*StackFrame); i { + switch v := v.(*DNSAddr); i { case 0: return &v.state case 1: @@ -5904,6 +7000,150 @@ func file_encore_engine_trace2_trace2_proto_init() { } } file_encore_engine_trace2_trace2_proto_msgTypes[52].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*HTTPConnectStart); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_encore_engine_trace2_trace2_proto_msgTypes[53].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*HTTPConnectDone); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_encore_engine_trace2_trace2_proto_msgTypes[54].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*HTTPTLSHandshakeStart); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_encore_engine_trace2_trace2_proto_msgTypes[55].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*HTTPTLSHandshakeDone); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_encore_engine_trace2_trace2_proto_msgTypes[56].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*HTTPWroteHeaders); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_encore_engine_trace2_trace2_proto_msgTypes[57].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*HTTPWroteRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_encore_engine_trace2_trace2_proto_msgTypes[58].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*HTTPWait100Continue); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_encore_engine_trace2_trace2_proto_msgTypes[59].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*HTTPClosedBodyData); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_encore_engine_trace2_trace2_proto_msgTypes[60].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*LogMessage); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_encore_engine_trace2_trace2_proto_msgTypes[61].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*LogField); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_encore_engine_trace2_trace2_proto_msgTypes[62].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*StackTrace); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_encore_engine_trace2_trace2_proto_msgTypes[63].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*StackFrame); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_encore_engine_trace2_trace2_proto_msgTypes[64].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*Error); i { case 0: return &v.state @@ -5956,6 +7196,16 @@ func file_encore_engine_trace2_trace2_proto_init() { (*SpanEvent_CacheCallEnd)(nil), (*SpanEvent_ServiceInitStart)(nil), (*SpanEvent_ServiceInitEnd)(nil), + (*SpanEvent_BucketObjectUploadStart)(nil), + (*SpanEvent_BucketObjectUploadEnd)(nil), + (*SpanEvent_BucketObjectDownloadStart)(nil), + (*SpanEvent_BucketObjectDownloadEnd)(nil), + (*SpanEvent_BucketObjectGetAttrsStart)(nil), + (*SpanEvent_BucketObjectGetAttrsEnd)(nil), + (*SpanEvent_BucketListObjectsStart)(nil), + (*SpanEvent_BucketListObjectsEnd)(nil), + (*SpanEvent_BucketDeleteObjectsStart)(nil), + (*SpanEvent_BucketDeleteObjectsEnd)(nil), } file_encore_engine_trace2_trace2_proto_msgTypes[16].OneofWrappers = []interface{}{} file_encore_engine_trace2_trace2_proto_msgTypes[20].OneofWrappers = []interface{}{} @@ -5963,8 +7213,18 @@ func file_encore_engine_trace2_trace2_proto_init() { file_encore_engine_trace2_trace2_proto_msgTypes[24].OneofWrappers = []interface{}{} file_encore_engine_trace2_trace2_proto_msgTypes[26].OneofWrappers = []interface{}{} file_encore_engine_trace2_trace2_proto_msgTypes[28].OneofWrappers = []interface{}{} + file_encore_engine_trace2_trace2_proto_msgTypes[30].OneofWrappers = []interface{}{} file_encore_engine_trace2_trace2_proto_msgTypes[31].OneofWrappers = []interface{}{} - file_encore_engine_trace2_trace2_proto_msgTypes[32].OneofWrappers = []interface{}{ + file_encore_engine_trace2_trace2_proto_msgTypes[32].OneofWrappers = []interface{}{} + file_encore_engine_trace2_trace2_proto_msgTypes[33].OneofWrappers = []interface{}{} + file_encore_engine_trace2_trace2_proto_msgTypes[34].OneofWrappers = []interface{}{} + file_encore_engine_trace2_trace2_proto_msgTypes[35].OneofWrappers = []interface{}{} + file_encore_engine_trace2_trace2_proto_msgTypes[36].OneofWrappers = []interface{}{} + file_encore_engine_trace2_trace2_proto_msgTypes[38].OneofWrappers = []interface{}{} + file_encore_engine_trace2_trace2_proto_msgTypes[39].OneofWrappers = []interface{}{} + file_encore_engine_trace2_trace2_proto_msgTypes[40].OneofWrappers = []interface{}{} + file_encore_engine_trace2_trace2_proto_msgTypes[43].OneofWrappers = []interface{}{} + file_encore_engine_trace2_trace2_proto_msgTypes[44].OneofWrappers = []interface{}{ (*HTTPTraceEvent_GetConn)(nil), (*HTTPTraceEvent_GotConn)(nil), (*HTTPTraceEvent_GotFirstResponseByte)(nil), @@ -5980,11 +7240,11 @@ func file_encore_engine_trace2_trace2_proto_init() { (*HTTPTraceEvent_Wait_100Continue)(nil), (*HTTPTraceEvent_ClosedBody)(nil), } - file_encore_engine_trace2_trace2_proto_msgTypes[38].OneofWrappers = []interface{}{} - file_encore_engine_trace2_trace2_proto_msgTypes[43].OneofWrappers = []interface{}{} - file_encore_engine_trace2_trace2_proto_msgTypes[45].OneofWrappers = []interface{}{} - file_encore_engine_trace2_trace2_proto_msgTypes[47].OneofWrappers = []interface{}{} - file_encore_engine_trace2_trace2_proto_msgTypes[49].OneofWrappers = []interface{}{ + file_encore_engine_trace2_trace2_proto_msgTypes[50].OneofWrappers = []interface{}{} + file_encore_engine_trace2_trace2_proto_msgTypes[55].OneofWrappers = []interface{}{} + file_encore_engine_trace2_trace2_proto_msgTypes[57].OneofWrappers = []interface{}{} + file_encore_engine_trace2_trace2_proto_msgTypes[59].OneofWrappers = []interface{}{} + file_encore_engine_trace2_trace2_proto_msgTypes[61].OneofWrappers = []interface{}{ (*LogField_Error)(nil), (*LogField_Str)(nil), (*LogField_Bool)(nil), @@ -5997,14 +7257,14 @@ func file_encore_engine_trace2_trace2_proto_init() { (*LogField_Float32)(nil), (*LogField_Float64)(nil), } - file_encore_engine_trace2_trace2_proto_msgTypes[52].OneofWrappers = []interface{}{} + file_encore_engine_trace2_trace2_proto_msgTypes[64].OneofWrappers = []interface{}{} type x struct{} out := protoimpl.TypeBuilder{ File: protoimpl.DescBuilder{ GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_encore_engine_trace2_trace2_proto_rawDesc, NumEnums: 5, - NumMessages: 55, + NumMessages: 67, NumExtensions: 0, NumServices: 0, }, diff --git a/proto/encore/engine/trace2/trace2.proto b/proto/encore/engine/trace2/trace2.proto index 7d1b02cc2b..038408899f 100644 --- a/proto/encore/engine/trace2/trace2.proto +++ b/proto/encore/engine/trace2/trace2.proto @@ -187,6 +187,17 @@ message SpanEvent { CacheCallEnd cache_call_end = 23; ServiceInitStart service_init_start = 24; ServiceInitEnd service_init_end = 25; + + BucketObjectUploadStart bucket_object_upload_start = 26; + BucketObjectUploadEnd bucket_object_upload_end = 27; + BucketObjectDownloadStart bucket_object_download_start = 28; + BucketObjectDownloadEnd bucket_object_download_end = 29; + BucketObjectGetAttrsStart bucket_object_get_attrs_start = 30; + BucketObjectGetAttrsEnd bucket_object_get_attrs_end = 31; + BucketListObjectsStart bucket_list_objects_start = 32; + BucketListObjectsEnd bucket_list_objects_end = 33; + BucketDeleteObjectsStart bucket_delete_objects_start = 34; + BucketDeleteObjectsEnd bucket_delete_objects_end = 35; } } @@ -268,6 +279,76 @@ message CacheCallEnd { } } +message BucketObjectUploadStart { + string bucket = 1; + string object = 2; + BucketObjectAttributes attrs = 3; + StackTrace stack = 4; +} + +message BucketObjectUploadEnd { + optional Error err = 1; + optional uint64 size = 2; +} + +message BucketObjectDownloadStart { + string bucket = 1; + string object = 2; + optional string version = 3; + StackTrace stack = 4; +} + +message BucketObjectDownloadEnd { + optional Error err = 1; + optional uint64 size = 2; +} + +message BucketObjectGetAttrsStart { + string bucket = 1; + string object = 2; + optional string version = 3; + StackTrace stack = 4; +} + +message BucketObjectGetAttrsEnd { + optional Error err = 1; + optional BucketObjectAttributes attrs = 2; +} + +message BucketListObjectsStart { + string bucket = 1; + optional string prefix = 2; + StackTrace stack = 3; +} + +message BucketListObjectsEnd { + optional Error err = 1; + uint64 observed = 2; + bool has_more = 3; +} + +message BucketDeleteObjectsStart { + string bucket = 1; + StackTrace stack = 2; + repeated BucketDeleteObjectEntry entries = 3; +} + +message BucketDeleteObjectEntry { + string object = 1; + optional string version = 2; +} + +message BucketDeleteObjectsEnd { + optional Error err = 1; +} + +message BucketObjectAttributes { + optional uint64 size = 1; + optional string version = 2; + optional string etag = 3; + optional string content_type = 4; +} + message BodyStream { bool is_response = 1; bool overflowed = 2; diff --git a/runtimes/core/src/names.rs b/runtimes/core/src/names.rs index ef2e772364..d9232d38f3 100644 --- a/runtimes/core/src/names.rs +++ b/runtimes/core/src/names.rs @@ -54,6 +54,7 @@ impl Borrow for EncoreName { &self.0 } } + impl Borrow for &EncoreName { fn borrow(&self) -> &String { &self.0 @@ -66,9 +67,39 @@ impl Display for EncoreName { } } +impl From for String { + fn from(value: EncoreName) -> Self { + value.0 + } +} + +impl From<&EncoreName> for String { + fn from(value: &EncoreName) -> Self { + value.0.clone() + } +} + #[derive(Debug, Clone, Eq, Hash, PartialEq)] pub struct CloudName(String); +impl Borrow for &CloudName { + fn borrow(&self) -> &String { + &self.0 + } +} + +impl Borrow for CloudName { + fn borrow(&self) -> &String { + &self.0 + } +} + +impl AsRef for CloudName { + fn as_ref(&self) -> &str { + &self.0 + } +} + impl Deref for CloudName { type Target = str; fn deref(&self) -> &str { @@ -94,6 +125,12 @@ impl From for String { } } +impl From<&CloudName> for String { + fn from(value: &CloudName) -> Self { + value.0.clone() + } +} + #[derive(Debug, Clone)] pub struct EndpointName { /// The full name ("service.endpoint") diff --git a/runtimes/core/src/objects/gcs/bucket.rs b/runtimes/core/src/objects/gcs/bucket.rs index 5b2529fca3..d9f62cb1c2 100644 --- a/runtimes/core/src/objects/gcs/bucket.rs +++ b/runtimes/core/src/objects/gcs/bucket.rs @@ -11,10 +11,10 @@ use tokio::io::AsyncRead; use crate::encore::runtime::v1 as pb; use crate::objects::{ - AttrsOptions, DeleteOptions, DownloadOptions, DownloadStream, Error, ListEntry, ListOptions, - ObjectAttrs, UploadOptions, + AttrsOptions, DeleteOptions, DownloadOptions, DownloadStream, Error, ExistsOptions, ListEntry, + ListOptions, ObjectAttrs, UploadOptions, }; -use crate::{objects, CloudName}; +use crate::{objects, CloudName, EncoreName}; use google_cloud_storage as gcs; use super::LazyGCSClient; @@ -22,7 +22,8 @@ use super::LazyGCSClient; #[derive(Debug)] pub struct Bucket { client: Arc, - name: CloudName, + encore_name: EncoreName, + cloud_name: CloudName, key_prefix: Option, } @@ -30,7 +31,8 @@ impl Bucket { pub(super) fn new(client: Arc, cfg: &pb::Bucket) -> Self { Self { client, - name: cfg.cloud_name.clone().into(), + encore_name: cfg.encore_name.clone().into(), + cloud_name: cfg.cloud_name.clone().into(), key_prefix: cfg.key_prefix.clone(), } } @@ -61,8 +63,15 @@ impl Bucket { } impl objects::BucketImpl for Bucket { + fn name(&self) -> &EncoreName { + &self.encore_name + } + fn object(self: Arc, name: String) -> Arc { - Arc::new(Object { bkt: self, name }) + Arc::new(Object { + bkt: self, + key: name, + }) } fn list( @@ -85,7 +94,7 @@ impl objects::BucketImpl for Bucket { }; let mut req = gcs::http::objects::list::ListObjectsRequest { - bucket: self.name.to_string(), + bucket: self.cloud_name.to_string(), max_results: Some(max_results), ..Default::default() }; @@ -150,10 +159,18 @@ impl objects::BucketImpl for Bucket { #[derive(Debug)] struct Object { bkt: Arc, - name: String, + key: String, } impl objects::ObjectImpl for Object { + fn bucket_name(&self) -> &EncoreName { + &self.bkt.encore_name + } + + fn key(&self) -> &str { + &self.key + } + fn attrs( self: Arc, options: AttrsOptions, @@ -162,8 +179,8 @@ impl objects::ObjectImpl for Object { match self.bkt.client.get().await { Ok(client) => { let mut req = gcs::http::objects::get::GetObjectRequest { - bucket: self.bkt.name.to_string(), - object: self.bkt.obj_name(Cow::Borrowed(&self.name)).into_owned(), + bucket: self.bkt.cloud_name.to_string(), + object: self.bkt.obj_name(Cow::Borrowed(&self.key)).into_owned(), ..Default::default() }; @@ -190,18 +207,18 @@ impl objects::ObjectImpl for Object { fn exists( self: Arc, - version: Option, + options: ExistsOptions, ) -> Pin> + Send>> { Box::pin(async move { match self.bkt.client.get().await { Ok(client) => { let mut req = gcs::http::objects::get::GetObjectRequest { - bucket: self.bkt.name.to_string(), - object: self.bkt.obj_name(Cow::Borrowed(&self.name)).into_owned(), + bucket: self.bkt.cloud_name.to_string(), + object: self.bkt.obj_name(Cow::Borrowed(&self.key)).into_owned(), ..Default::default() }; - if let Some(version) = version { + if let Some(version) = options.version { req.generation = Some(parse_version(version)?); } @@ -228,11 +245,11 @@ impl objects::ObjectImpl for Object { match self.bkt.client.get().await { Ok(client) => { let mut req = UploadObjectRequest { - bucket: self.bkt.name.to_string(), + bucket: self.bkt.cloud_name.to_string(), ..Default::default() }; - let cloud_name = self.bkt.obj_name(Cow::Borrowed(&self.name)); + let cloud_name = self.bkt.obj_name(Cow::Borrowed(&self.key)); let mut media = Media::new(cloud_name.into_owned()); apply_upload_opts(opts, &mut req, &mut media); @@ -283,8 +300,8 @@ impl objects::ObjectImpl for Object { match self.bkt.client.get().await { Ok(client) => { let mut req = GetObjectRequest { - bucket: self.bkt.name.to_string(), - object: self.bkt.obj_name(Cow::Borrowed(&self.name)).into_owned(), + bucket: self.bkt.cloud_name.to_string(), + object: self.bkt.obj_name(Cow::Borrowed(&self.key)).into_owned(), ..Default::default() }; @@ -316,8 +333,8 @@ impl objects::ObjectImpl for Object { match self.bkt.client.get().await { Ok(client) => { let mut req = gcs::http::objects::delete::DeleteObjectRequest { - bucket: self.bkt.name.to_string(), - object: self.bkt.obj_name(Cow::Borrowed(&self.name)).into_owned(), + bucket: self.bkt.cloud_name.to_string(), + object: self.bkt.obj_name(Cow::Borrowed(&self.key)).into_owned(), ..Default::default() }; diff --git a/runtimes/core/src/objects/manager.rs b/runtimes/core/src/objects/manager.rs index e67766728a..123e6fde9c 100644 --- a/runtimes/core/src/objects/manager.rs +++ b/runtimes/core/src/objects/manager.rs @@ -44,7 +44,7 @@ impl Manager { if let Some((cluster, bucket_cfg)) = self.bucket_cfg.get(&name) { cluster.clone().bucket(bucket_cfg) } else { - Arc::new(noop::Bucket) + Arc::new(noop::Bucket::new(name.clone())) } }; diff --git a/runtimes/core/src/objects/mod.rs b/runtimes/core/src/objects/mod.rs index 6db14076a2..ed0c28cc73 100644 --- a/runtimes/core/src/objects/mod.rs +++ b/runtimes/core/src/objects/mod.rs @@ -8,7 +8,8 @@ use tokio::io::AsyncRead; pub use manager::Manager; use crate::encore::runtime::v1 as pb; -use crate::trace::Tracer; +use crate::trace::{protocol, Tracer}; +use crate::{model, EncoreName}; mod gcs; mod manager; @@ -20,6 +21,9 @@ trait ClusterImpl: Debug + Send + Sync { } trait BucketImpl: Debug + Send + Sync { + #[allow(dead_code)] + fn name(&self) -> &EncoreName; + fn object(self: Arc, name: String) -> Arc; fn list( @@ -28,12 +32,15 @@ trait BucketImpl: Debug + Send + Sync { ) -> Pin> + Send + 'static>>; } -pub type ListStream = Box> + Send>; +type ListStream = Box> + Send>; trait ObjectImpl: Debug + Send + Sync { + fn bucket_name(&self) -> &EncoreName; + fn key(&self) -> &str; + fn exists( self: Arc, - version: Option, + options: ExistsOptions, ) -> Pin> + Send>>; fn upload( @@ -68,37 +75,143 @@ impl Bucket { pub fn object(&self, name: String) -> Object { Object { imp: self.imp.clone().object(name), - _tracer: self.tracer.clone(), + tracer: self.tracer.clone(), } } - pub async fn list(&self, options: ListOptions) -> Result { - self.imp.clone().list(options).await + pub async fn list( + &self, + options: ListOptions, + source: Option>, + ) -> Result { + let (stream, start_id) = if let Some(source) = source.as_deref() { + let start_id = + self.tracer + .bucket_list_objects_start(protocol::BucketListObjectsStart { + source, + bucket: self.imp.name(), + prefix: options.prefix.as_deref(), + }); + + let res = self.imp.clone().list(options).await; + + match res { + Ok(stream) => (stream, Some(start_id)), + Err(err) => { + self.tracer + .bucket_list_objects_end(protocol::BucketListObjectsEnd { + source, + start_id, + result: protocol::BucketListObjectsEndResult::Err(&err), + }); + return Err(err); + } + } + } else { + let stream = self.imp.clone().list(options).await?; + (stream, None) + }; + + Ok(ListIterator { + stream: stream.into(), + source, + start_id, + tracer: self.tracer.clone(), + + yielded_entries: 0, + seen_end: false, + err: None, + }) } } #[derive(Debug)] pub struct Object { - _tracer: Tracer, + tracer: Tracer, imp: Arc, } impl Object { - pub async fn exists(&self, version: Option) -> Result { - self.imp.clone().exists(version).await + pub async fn exists( + &self, + options: ExistsOptions, + source: Option>, + ) -> Result { + if let Some(source) = source.as_deref() { + let start_id = + self.tracer + .bucket_object_get_attrs_start(protocol::BucketObjectGetAttrsStart { + source, + bucket: self.imp.bucket_name(), + object: self.imp.key(), + version: options.version.as_deref(), + }); + let res = self.imp.clone().exists(options).await; + + self.tracer + .bucket_object_get_attrs_end(protocol::BucketObjectGetAttrsEnd { + start_id, + source, + result: match &res { + Ok(true) => { + protocol::BucketObjectGetAttrsEndResult::Success(Default::default()) + } + Ok(false) => protocol::BucketObjectGetAttrsEndResult::Err(&Error::NotFound), + Err(err) => protocol::BucketObjectGetAttrsEndResult::Err(err), + }, + }); + res + } else { + self.imp.clone().exists(options).await + } } pub fn upload( &self, data: Box, options: UploadOptions, + source: Option>, ) -> impl Future> + Send + 'static { - self.imp.clone().upload(data, options) + let tracer = self.tracer.clone(); + let imp = self.imp.clone(); + + async move { + if let Some(source) = source.as_deref() { + let start_id = + tracer.bucket_object_upload_start(protocol::BucketObjectUploadStart { + source, + bucket: imp.bucket_name(), + object: imp.key(), + attrs: protocol::BucketObjectAttributes { + content_type: options.content_type.as_deref(), + ..Default::default() + }, + }); + + let res = imp.upload(data, options).await; + + tracer.bucket_object_upload_end(protocol::BucketObjectUploadEnd { + start_id, + source, + result: match &res { + Ok(attrs) => { + protocol::BucketObjectUploadEndResult::Success { size: attrs.size } + } + Err(err) => protocol::BucketObjectUploadEndResult::Err(err), + }, + }); + + res + } else { + imp.upload(data, options).await + } + } } pub fn download_stream( &self, options: DownloadOptions, + _source: Option>, ) -> impl Future> + Send + 'static { self.imp.clone().download(options) } @@ -106,6 +219,47 @@ impl Object { pub fn download_all( &self, options: DownloadOptions, + source: Option>, + ) -> impl Future, Error>> + Send + 'static { + let tracer = self.tracer.clone(); + let imp = self.imp.clone(); + let start_id = if let Some(source) = source.as_deref() { + Some( + tracer.bucket_object_download_start(protocol::BucketObjectDownloadStart { + source, + bucket: imp.bucket_name(), + object: imp.key(), + version: options.version.as_deref(), + }), + ) + } else { + None + }; + + let fut = self.do_download_all(options); + async move { + let res = fut.await; + + if let (Some(start_id), Some(source)) = (start_id, source.as_deref()) { + tracer.bucket_object_download_end(protocol::BucketObjectDownloadEnd { + start_id, + source, + result: match &res { + Ok(bytes) => protocol::BucketObjectDownloadEndResult::Success { + size: bytes.len() as u64, + }, + Err(err) => protocol::BucketObjectDownloadEndResult::Err(err), + }, + }); + } + + res + } + } + + fn do_download_all( + &self, + options: DownloadOptions, ) -> impl Future, Error>> + Send + 'static { let stream = self.imp.clone().download(options); async move { @@ -119,12 +273,71 @@ impl Object { } } - pub async fn attrs(&self, options: AttrsOptions) -> Result { - self.imp.clone().attrs(options).await + pub async fn attrs( + &self, + options: AttrsOptions, + source: Option>, + ) -> Result { + if let Some(source) = source.as_deref() { + let start_id = + self.tracer + .bucket_object_get_attrs_start(protocol::BucketObjectGetAttrsStart { + source, + bucket: self.imp.bucket_name(), + object: self.imp.key(), + version: options.version.as_deref(), + }); + let res = self.imp.clone().attrs(options).await; + + self.tracer + .bucket_object_get_attrs_end(protocol::BucketObjectGetAttrsEnd { + start_id, + source, + result: match &res { + Ok(attrs) => protocol::BucketObjectGetAttrsEndResult::Success(attrs.into()), + Err(err) => protocol::BucketObjectGetAttrsEndResult::Err(err), + }, + }); + res + } else { + self.imp.clone().attrs(options).await + } } - pub async fn delete(&self, options: DeleteOptions) -> Result<(), Error> { - self.imp.clone().delete(options).await + pub async fn delete( + &self, + options: DeleteOptions, + source: Option>, + ) -> Result<(), Error> { + if let Some(source) = source.as_deref() { + let start_id = + self.tracer + .bucket_delete_objects_start(protocol::BucketDeleteObjectsStart { + source, + bucket: self.imp.bucket_name(), + objects: [protocol::BucketDeleteObjectEntry { + object: self.imp.key(), + version: options.version.as_deref(), + }] + .into_iter(), + }); + + let res = self.imp.clone().delete(options).await; + + self.tracer + .bucket_delete_objects_end(protocol::BucketDeleteObjectsEnd { + start_id, + source, + result: match &res { + Ok(()) => protocol::BucketDeleteObjectsEndResult::Success, + Err(err) => protocol::BucketDeleteObjectsEndResult::Err(err), + }, + }); + + res + } else { + self.imp.clone().delete(options).await + } } } @@ -159,6 +372,11 @@ pub struct ListEntry { pub etag: String, } +#[derive(Debug, Default)] +pub struct ExistsOptions { + pub version: Option, +} + #[derive(Debug, Default)] pub struct UploadOptions { pub content_type: Option, @@ -190,3 +408,55 @@ pub struct ListOptions { pub prefix: Option, pub limit: Option, } + +pub struct ListIterator { + stream: Pin> + Send>>, + tracer: Tracer, + start_id: Option, + source: Option>, + err: Option, + + yielded_entries: u64, + seen_end: bool, +} + +impl ListIterator { + pub async fn next(&mut self) -> Option> { + let res = self.stream.next().await; + + match &res { + None => { + self.seen_end = true; + } + Some(Ok(_)) => { + self.yielded_entries += 1; + } + Some(Err(err)) => { + if self.err.is_none() { + self.err = Some(err.to_string()); + } + } + } + + res + } +} + +impl Drop for ListIterator { + fn drop(&mut self) { + if let (Some(start_id), Some(source)) = (self.start_id, self.source.as_deref()) { + self.tracer + .bucket_list_objects_end(protocol::BucketListObjectsEnd { + start_id, + source, + result: match self.err { + Some(ref err) => protocol::BucketListObjectsEndResult::Err(err), + None => protocol::BucketListObjectsEndResult::Success { + observed: self.yielded_entries, + has_more: !self.seen_end, + }, + }, + }); + } + } +} diff --git a/runtimes/core/src/objects/noop/mod.rs b/runtimes/core/src/objects/noop/mod.rs index af830cc613..ee8fc6df13 100644 --- a/runtimes/core/src/objects/noop/mod.rs +++ b/runtimes/core/src/objects/noop/mod.rs @@ -5,29 +5,49 @@ use std::sync::Arc; use futures::future; use tokio::io::AsyncRead; -use crate::encore::runtime::v1 as pb; use crate::objects; +use crate::{encore::runtime::v1 as pb, EncoreName}; -use super::{AttrsOptions, DeleteOptions, DownloadOptions, ListOptions}; +use super::{AttrsOptions, DeleteOptions, DownloadOptions, ExistsOptions, ListOptions}; #[derive(Debug)] pub struct Cluster; #[derive(Debug)] -pub struct Bucket; +pub struct Bucket { + name: EncoreName, +} + +impl Bucket { + pub fn new(name: EncoreName) -> Self { + Self { name } + } +} #[derive(Debug)] -pub struct Object; +pub struct Object { + bkt: Arc, + name: String, +} impl objects::ClusterImpl for Cluster { - fn bucket(self: Arc, _cfg: &pb::Bucket) -> Arc { - Arc::new(Bucket) + fn bucket(self: Arc, cfg: &pb::Bucket) -> Arc { + Arc::new(Bucket { + name: cfg.encore_name.clone().into(), + }) } } impl objects::BucketImpl for Bucket { - fn object(self: Arc, _name: String) -> Arc { - Arc::new(Object) + fn name(&self) -> &EncoreName { + &self.name + } + + fn object(self: Arc, name: String) -> Arc { + Arc::new(Object { + name, + bkt: self.clone(), + }) } fn list( @@ -44,6 +64,14 @@ impl objects::BucketImpl for Bucket { } impl objects::ObjectImpl for Object { + fn bucket_name(&self) -> &EncoreName { + &self.bkt.name + } + + fn key(&self) -> &str { + &self.name + } + fn attrs( self: Arc, _options: AttrsOptions, @@ -55,7 +83,7 @@ impl objects::ObjectImpl for Object { fn exists( self: Arc, - _version: Option, + _options: ExistsOptions, ) -> Pin> + Send>> { Box::pin(future::ready(Err(objects::Error::Internal( anyhow::anyhow!("noop bucket does not support exists"), diff --git a/runtimes/core/src/objects/s3/bucket.rs b/runtimes/core/src/objects/s3/bucket.rs index 567c4839e2..6b3cfe7789 100644 --- a/runtimes/core/src/objects/s3/bucket.rs +++ b/runtimes/core/src/objects/s3/bucket.rs @@ -14,8 +14,10 @@ use tokio::io::{AsyncRead, AsyncReadExt}; use crate::encore::runtime::v1 as pb; use crate::objects::{ - self, AttrsOptions, DeleteOptions, DownloadOptions, Error, ListEntry, ListOptions, ObjectAttrs, + self, AttrsOptions, DeleteOptions, DownloadOptions, Error, ExistsOptions, ListEntry, + ListOptions, ObjectAttrs, }; +use crate::{CloudName, EncoreName}; use super::LazyS3Client; @@ -24,7 +26,8 @@ const CHUNK_SIZE: usize = 8_388_608; // 8 Mebibytes, min is 5 (5_242_880); #[derive(Debug)] pub struct Bucket { client: Arc, - name: String, + encore_name: EncoreName, + cloud_name: CloudName, key_prefix: Option, } @@ -32,7 +35,8 @@ impl Bucket { pub(super) fn new(client: Arc, cfg: &pb::Bucket) -> Self { Self { client, - name: cfg.cloud_name.clone(), + encore_name: cfg.encore_name.clone().into(), + cloud_name: cfg.cloud_name.clone().into(), key_prefix: cfg.key_prefix.clone(), } } @@ -63,10 +67,14 @@ impl Bucket { } impl objects::BucketImpl for Bucket { + fn name(&self) -> &EncoreName { + &self.encore_name + } + fn object(self: Arc, name: String) -> Arc { Arc::new(Object { bkt: self.clone(), - cloud_name: name, + name, }) } @@ -80,7 +88,7 @@ impl objects::BucketImpl for Bucket { let client = self.client.get().await.clone(); let s: objects::ListStream = Box::new(try_stream! { let mut req = client.list_objects_v2() - .bucket(self.name.clone()); + .bucket(&self.cloud_name); if let Some(key_prefix) = self.key_prefix.clone() { req = req.prefix(key_prefix); @@ -110,7 +118,7 @@ impl objects::BucketImpl for Bucket { let entry = ListEntry { name: self.strip_prefix(Cow::Owned(obj.key.unwrap_or_default())).into_owned(), size: obj.size.unwrap_or_default() as u64, - etag: obj.e_tag.unwrap_or_default(), + etag: parse_etag(obj.e_tag), }; yield entry; } @@ -125,20 +133,28 @@ impl objects::BucketImpl for Bucket { #[derive(Debug)] struct Object { bkt: Arc, - cloud_name: String, + name: String, } impl objects::ObjectImpl for Object { + fn bucket_name(&self) -> &EncoreName { + &self.bkt.encore_name + } + + fn key(&self) -> &str { + &self.name + } + fn attrs( self: Arc, options: AttrsOptions, ) -> Pin> + Send>> { Box::pin(async move { let client = self.bkt.client.get().await.clone(); - let cloud_name = self.bkt.obj_name(Cow::Borrowed(&self.cloud_name)); + let cloud_name = self.bkt.obj_name(Cow::Borrowed(&self.name)); let res = client .head_object() - .bucket(self.bkt.name.clone()) + .bucket(&self.bkt.cloud_name) .key(cloud_name) .set_version_id(options.version) .send() @@ -146,7 +162,7 @@ impl objects::ObjectImpl for Object { match res { Ok(obj) => Ok(ObjectAttrs { - name: self.cloud_name.clone(), + name: self.name.clone(), version: obj.version_id, size: obj.content_length.unwrap_or_default() as u64, content_type: obj.content_type, @@ -162,16 +178,16 @@ impl objects::ObjectImpl for Object { fn exists( self: Arc, - version: Option, + options: ExistsOptions, ) -> Pin> + Send>> { Box::pin(async move { let client = self.bkt.client.get().await.clone(); - let cloud_name = self.bkt.obj_name(Cow::Borrowed(&self.cloud_name)); + let cloud_name = self.bkt.obj_name(Cow::Borrowed(&self.name)); let res = client .head_object() - .bucket(&self.bkt.name) + .bucket(&self.bkt.cloud_name) .key(cloud_name) - .set_version_id(version) + .set_version_id(options.version) .send() .await; match res { @@ -189,7 +205,7 @@ impl objects::ObjectImpl for Object { ) -> Pin> + Send>> { Box::pin(async move { let client = self.bkt.client.get().await.clone(); - let cloud_name = self.bkt.obj_name(Cow::Borrowed(&self.cloud_name)); + let cloud_name = self.bkt.obj_name(Cow::Borrowed(&self.name)); let first_chunk = read_chunk_async(&mut data).await.map_err(|e| { Error::Other(anyhow::anyhow!("uable to read from data source: {}", e)) })?; @@ -204,7 +220,7 @@ impl objects::ObjectImpl for Object { let mut req = client .put_object() - .bucket(&self.bkt.name) + .bucket(&self.bkt.cloud_name) .key(cloud_name) .content_length(total_size as i64) .content_md5(content_md5) @@ -219,7 +235,7 @@ impl objects::ObjectImpl for Object { let resp = req.send().await.map_err(map_upload_err)?; Ok(ObjectAttrs { - name: self.cloud_name.clone(), + name: self.name.clone(), version: resp.version_id, size: total_size as u64, content_type: options.content_type, @@ -231,7 +247,7 @@ impl objects::ObjectImpl for Object { // Large file; do a multipart upload. let upload = client .create_multipart_upload() - .bucket(&self.bkt.name) + .bucket(&self.bkt.cloud_name) .key(cloud_name.to_string()) .set_content_type(options.content_type.clone()) .send() @@ -254,7 +270,7 @@ impl objects::ObjectImpl for Object { &mut data, chunk.freeze(), &upload_id, - &self.bkt.name, + &self.bkt.cloud_name, &cloud_name, &options, ) @@ -262,7 +278,7 @@ impl objects::ObjectImpl for Object { if let UploadMultipartResult::CompleteSuccess { total_size, output } = res { return Ok(ObjectAttrs { - name: self.cloud_name.clone(), + name: self.name.clone(), version: output.version_id, size: total_size, content_type: options.content_type, @@ -273,7 +289,7 @@ impl objects::ObjectImpl for Object { // Abort the upload. let fut = client .abort_multipart_upload() - .bucket(&self.bkt.name) + .bucket(&self.bkt.cloud_name) .key(cloud_name) .upload_id(upload_id) .send(); @@ -301,10 +317,10 @@ impl objects::ObjectImpl for Object { ) -> Pin> + Send>> { Box::pin(async move { let client = self.bkt.client.get().await.clone(); - let cloud_name = self.bkt.obj_name(Cow::Borrowed(&self.cloud_name)); + let cloud_name = self.bkt.obj_name(Cow::Borrowed(&self.name)); let res = client .get_object() - .bucket(&self.bkt.name) + .bucket(&self.bkt.cloud_name) .key(cloud_name.into_owned()) .set_version_id(options.version) .send() @@ -334,10 +350,10 @@ impl objects::ObjectImpl for Object { ) -> Pin> + Send>> { Box::pin(async move { let client = self.bkt.client.get().await.clone(); - let cloud_name = self.bkt.obj_name(Cow::Borrowed(&self.cloud_name)); + let cloud_name = self.bkt.obj_name(Cow::Borrowed(&self.name)); let res = client .delete_object() - .bucket(&self.bkt.name) + .bucket(&self.bkt.cloud_name) .key(cloud_name.into_owned()) .set_version_id(options.version) .send() @@ -404,7 +420,7 @@ async fn upload_multipart_chunks( reader: &mut R, first_chunk: Bytes, upload_id: &str, - bucket: &str, + bucket: &CloudName, key: &str, options: &objects::UploadOptions, ) -> UploadMultipartResult { diff --git a/runtimes/core/src/trace/protocol.rs b/runtimes/core/src/trace/protocol.rs index 596f2643ad..e85b28995f 100644 --- a/runtimes/core/src/trace/protocol.rs +++ b/runtimes/core/src/trace/protocol.rs @@ -7,7 +7,7 @@ use crate::api::{self, PValue}; use crate::model::{LogField, LogFieldValue, Request, TraceEventId}; use crate::trace::eventbuf::EventBuffer; use crate::trace::log::TraceEvent; -use crate::{model, EncoreName}; +use crate::{model, objects, EncoreName}; /// Represents a type of trace event. #[derive(Debug, Clone, Copy)] @@ -35,6 +35,18 @@ pub enum EventType { CacheCallStart = 0x14, CacheCallEnd = 0x15, BodyStream = 0x16, + TestStart = 0x17, + TestEnd = 0x18, + BucketObjectUploadStart = 0x19, + BucketObjectUploadEnd = 0x1A, + BucketObjectDownloadStart = 0x1B, + BucketObjectDownloadEnd = 0x1C, + BucketObjectGetAttrsStart = 0x1D, + BucketObjectGetAttrsEnd = 0x1E, + BucketListObjectsStart = 0x1F, + BucketListObjectsEnd = 0x20, + BucketDeleteObjectsStart = 0x21, + BucketDeleteObjectsEnd = 0x22, } // A global event id counter. @@ -445,6 +457,353 @@ impl Tracer { } } +pub struct BucketObjectUploadStart<'a> { + pub source: &'a Request, + pub bucket: &'a EncoreName, + pub object: &'a str, + pub attrs: BucketObjectAttributes<'a>, +} + +pub struct BucketObjectUploadEnd<'a, E> { + pub start_id: TraceEventId, + pub source: &'a Request, + pub result: BucketObjectUploadEndResult<'a, E>, +} + +pub enum BucketObjectUploadEndResult<'a, E> { + Success { size: u64 }, + Err(&'a E), +} + +#[derive(Default, Debug)] +pub struct BucketObjectAttributes<'a> { + pub size: Option, + pub version: Option<&'a str>, + pub etag: Option<&'a str>, + pub content_type: Option<&'a str>, +} + +impl<'a> From<&'a objects::ObjectAttrs> for BucketObjectAttributes<'a> { + fn from(attrs: &'a objects::ObjectAttrs) -> Self { + Self { + size: Some(attrs.size), + version: attrs.version.as_deref(), + etag: Some(&attrs.etag), + content_type: attrs.content_type.as_deref(), + } + } +} + +impl EventBuffer { + fn bucket_object_attrs(&mut self, attrs: &BucketObjectAttributes) { + self.u64(attrs.size.unwrap_or(0)); + self.opt_str(attrs.version); + self.opt_str(attrs.etag); + self.opt_str(attrs.content_type); + } +} + +impl Tracer { + #[inline] + pub fn bucket_object_upload_start(&self, data: BucketObjectUploadStart) -> TraceEventId { + let mut eb = BasicEventData { + correlation_event_id: None, + extra_space: 4 + 4 + 8 + data.bucket.len() + data.object.len(), + } + .into_eb(); + + eb.str(data.bucket); + eb.str(data.object); + eb.bucket_object_attrs(&data.attrs); + eb.nyi_stack_pcs(); + + self.send(EventType::BucketObjectUploadStart, data.source.span, eb) + } + + #[inline] + pub fn bucket_object_upload_end(&self, data: BucketObjectUploadEnd) + where + E: std::fmt::Display, + { + let mut eb = BasicEventData { + correlation_event_id: Some(data.start_id), + extra_space: 4 + 4 + 8, + } + .into_eb(); + + match data.result { + BucketObjectUploadEndResult::Success { size } => { + eb.u64(size); + eb.err_with_legacy_stack::(None); + } + BucketObjectUploadEndResult::Err(err) => { + eb.u64(0); + eb.err_with_legacy_stack(Some(err)); + } + } + + _ = self.send(EventType::BucketObjectUploadEnd, data.source.span, eb); + } +} + +pub struct BucketObjectDownloadStart<'a> { + pub source: &'a Request, + pub bucket: &'a EncoreName, + pub object: &'a str, + pub version: Option<&'a str>, +} + +pub struct BucketObjectDownloadEnd<'a, E> { + pub start_id: TraceEventId, + pub source: &'a Request, + pub result: BucketObjectDownloadEndResult<'a, E>, +} + +pub enum BucketObjectDownloadEndResult<'a, E> { + Success { size: u64 }, + Err(&'a E), +} + +impl Tracer { + #[inline] + pub fn bucket_object_download_start(&self, data: BucketObjectDownloadStart) -> TraceEventId { + let mut eb = BasicEventData { + correlation_event_id: None, + extra_space: 4 + 4 + 8 + data.bucket.len() + data.object.len(), + } + .into_eb(); + + eb.str(data.bucket); + eb.str(data.object); + eb.opt_str(data.version); + eb.nyi_stack_pcs(); + + self.send(EventType::BucketObjectDownloadStart, data.source.span, eb) + } + + #[inline] + pub fn bucket_object_download_end(&self, data: BucketObjectDownloadEnd) + where + E: std::fmt::Display, + { + let mut eb = BasicEventData { + correlation_event_id: Some(data.start_id), + extra_space: 4 + 4 + 8, + } + .into_eb(); + + match data.result { + BucketObjectDownloadEndResult::Success { size } => { + eb.u64(size); + eb.err_with_legacy_stack::(None); + } + BucketObjectDownloadEndResult::Err(err) => { + eb.u64(0); + eb.err_with_legacy_stack(Some(err)); + } + } + + _ = self.send(EventType::BucketObjectDownloadEnd, data.source.span, eb); + } +} + +pub struct BucketDeleteObjectsStart<'a, O> { + pub source: &'a Request, + pub bucket: &'a EncoreName, + pub objects: O, +} + +pub struct BucketDeleteObjectEntry<'a> { + pub object: &'a str, + pub version: Option<&'a str>, +} + +pub struct BucketDeleteObjectsEnd<'a, E> { + pub start_id: TraceEventId, + pub source: &'a Request, + pub result: BucketDeleteObjectsEndResult<'a, E>, +} + +pub enum BucketDeleteObjectsEndResult<'a, E> { + Success, + Err(&'a E), +} + +impl Tracer { + #[inline] + pub fn bucket_delete_objects_start<'a, O>( + &self, + data: BucketDeleteObjectsStart<'a, O>, + ) -> TraceEventId + where + O: ExactSizeIterator>, + { + let mut eb = BasicEventData { + correlation_event_id: None, + extra_space: 4 + 4 + 8 + data.bucket.len() + data.objects.len() * 8, + } + .into_eb(); + + eb.str(data.bucket); + eb.nyi_stack_pcs(); + eb.uvarint(data.objects.len() as u64); + for obj in data.objects { + eb.str(obj.object); + eb.opt_str(obj.version); + } + + self.send(EventType::BucketDeleteObjectsStart, data.source.span, eb) + } + + #[inline] + pub fn bucket_delete_objects_end(&self, data: BucketDeleteObjectsEnd) + where + E: std::fmt::Display, + { + let mut eb = BasicEventData { + correlation_event_id: Some(data.start_id), + extra_space: 4 + 4 + 8, + } + .into_eb(); + + match data.result { + BucketDeleteObjectsEndResult::Success => { + eb.err_with_legacy_stack::(None); + } + BucketDeleteObjectsEndResult::Err(err) => { + eb.err_with_legacy_stack(Some(err)); + } + } + + _ = self.send(EventType::BucketDeleteObjectsEnd, data.source.span, eb); + } +} + +pub struct BucketListObjectsStart<'a> { + pub source: &'a Request, + pub bucket: &'a EncoreName, + pub prefix: Option<&'a str>, +} + +pub struct BucketListObjectsEnd<'a, E> { + pub start_id: TraceEventId, + pub source: &'a Request, + pub result: BucketListObjectsEndResult<'a, E>, +} + +pub enum BucketListObjectsEndResult<'a, E> { + Success { observed: u64, has_more: bool }, + Err(&'a E), +} + +impl Tracer { + #[inline] + pub fn bucket_list_objects_start(&self, data: BucketListObjectsStart) -> TraceEventId { + let mut eb = BasicEventData { + correlation_event_id: None, + extra_space: 4 + + 4 + + 8 + + data.bucket.len() + + data.prefix.map(|p| p.len()).unwrap_or_default(), + } + .into_eb(); + + eb.str(data.bucket); + eb.opt_str(data.prefix); + eb.nyi_stack_pcs(); + + self.send(EventType::BucketListObjectsStart, data.source.span, eb) + } + + #[inline] + pub fn bucket_list_objects_end(&self, data: BucketListObjectsEnd) + where + E: std::fmt::Display, + { + let mut eb = BasicEventData { + correlation_event_id: Some(data.start_id), + extra_space: 4 + 4 + 8, + } + .into_eb(); + + match data.result { + BucketListObjectsEndResult::Success { observed, has_more } => { + eb.err_with_legacy_stack::(None); + eb.uvarint(observed); + eb.bool(has_more); + } + BucketListObjectsEndResult::Err(err) => { + eb.err_with_legacy_stack(Some(err)); + eb.uvarint(0u64); + eb.bool(false); + } + } + + _ = self.send(EventType::BucketListObjectsEnd, data.source.span, eb); + } +} + +pub struct BucketObjectGetAttrsStart<'a> { + pub source: &'a Request, + pub bucket: &'a EncoreName, + pub object: &'a str, + pub version: Option<&'a str>, +} + +pub struct BucketObjectGetAttrsEnd<'a, E> { + pub start_id: TraceEventId, + pub source: &'a Request, + pub result: BucketObjectGetAttrsEndResult<'a, E>, +} + +pub enum BucketObjectGetAttrsEndResult<'a, E> { + Success(BucketObjectAttributes<'a>), + Err(&'a E), +} + +impl Tracer { + #[inline] + pub fn bucket_object_get_attrs_start(&self, data: BucketObjectGetAttrsStart) -> TraceEventId { + let mut eb = BasicEventData { + correlation_event_id: None, + extra_space: 4 + 4 + 8 + data.bucket.len() + data.object.len(), + } + .into_eb(); + + eb.str(data.bucket); + eb.str(data.object); + eb.opt_str(data.version); + eb.nyi_stack_pcs(); + + self.send(EventType::BucketObjectGetAttrsStart, data.source.span, eb) + } + + #[inline] + pub fn bucket_object_get_attrs_end(&self, data: BucketObjectGetAttrsEnd) + where + E: std::fmt::Display, + { + let mut eb = BasicEventData { + correlation_event_id: Some(data.start_id), + extra_space: 4 + 4 + 8, + } + .into_eb(); + + match data.result { + BucketObjectGetAttrsEndResult::Success(attrs) => { + eb.err_with_legacy_stack::(None); + eb.bucket_object_attrs(&attrs); + } + BucketObjectGetAttrsEndResult::Err(err) => { + eb.err_with_legacy_stack(Some(err)); + } + } + + _ = self.send(EventType::BucketObjectGetAttrsEnd, data.source.span, eb); + } +} + impl Tracer { #[inline] fn send(&self, typ: EventType, span: model::SpanKey, eb: EventBuffer) -> model::TraceEventId { diff --git a/runtimes/go/appruntime/exported/trace2/events.go b/runtimes/go/appruntime/exported/trace2/events.go index 6e3619aad7..b161febbc4 100644 --- a/runtimes/go/appruntime/exported/trace2/events.go +++ b/runtimes/go/appruntime/exported/trace2/events.go @@ -16,30 +16,40 @@ import ( type EventType byte const ( - RequestSpanStart EventType = 0x01 - RequestSpanEnd EventType = 0x02 - AuthSpanStart EventType = 0x03 - AuthSpanEnd EventType = 0x04 - PubsubMessageSpanStart EventType = 0x05 - PubsubMessageSpanEnd EventType = 0x06 - DBTransactionStart EventType = 0x07 - DBTransactionEnd EventType = 0x08 - DBQueryStart EventType = 0x09 - DBQueryEnd EventType = 0x0A - RPCCallStart EventType = 0x0B - RPCCallEnd EventType = 0x0C - HTTPCallStart EventType = 0x0D - HTTPCallEnd EventType = 0x0E - LogMessage EventType = 0x0F - PubsubPublishStart EventType = 0x10 - PubsubPublishEnd EventType = 0x11 - ServiceInitStart EventType = 0x12 - ServiceInitEnd EventType = 0x13 - CacheCallStart EventType = 0x14 - CacheCallEnd EventType = 0x15 - BodyStream EventType = 0x16 - TestStart EventType = 0x17 - TestEnd EventType = 0x18 + RequestSpanStart EventType = 0x01 + RequestSpanEnd EventType = 0x02 + AuthSpanStart EventType = 0x03 + AuthSpanEnd EventType = 0x04 + PubsubMessageSpanStart EventType = 0x05 + PubsubMessageSpanEnd EventType = 0x06 + DBTransactionStart EventType = 0x07 + DBTransactionEnd EventType = 0x08 + DBQueryStart EventType = 0x09 + DBQueryEnd EventType = 0x0A + RPCCallStart EventType = 0x0B + RPCCallEnd EventType = 0x0C + HTTPCallStart EventType = 0x0D + HTTPCallEnd EventType = 0x0E + LogMessage EventType = 0x0F + PubsubPublishStart EventType = 0x10 + PubsubPublishEnd EventType = 0x11 + ServiceInitStart EventType = 0x12 + ServiceInitEnd EventType = 0x13 + CacheCallStart EventType = 0x14 + CacheCallEnd EventType = 0x15 + BodyStream EventType = 0x16 + TestStart EventType = 0x17 + TestEnd EventType = 0x18 + BucketObjectUploadStart EventType = 0x19 + BucketObjectUploadEnd EventType = 0x1A + BucketObjectDownloadStart EventType = 0x1B + BucketObjectDownloadEnd EventType = 0x1C + BucketObjectGetAttrsStart EventType = 0x1D + BucketObjectGetAttrsEnd EventType = 0x1E + BucketListObjectsStart EventType = 0x1F + BucketListObjectsEnd EventType = 0x20 + BucketDeleteObjectsStart EventType = 0x21 + BucketDeleteObjectsEnd EventType = 0x22 ) func (te EventType) String() string { @@ -92,6 +102,27 @@ func (te EventType) String() string { return "TestStart" case TestEnd: return "TestEnd" + case BucketObjectUploadStart: + return "BucketObjectUploadStart" + case BucketObjectUploadEnd: + return "BucketObjectUploadEnd" + case BucketObjectDownloadStart: + return "BucketObjectDownloadStart" + case BucketObjectDownloadEnd: + return "BucketObjectDownloadEnd" + case BucketObjectGetAttrsStart: + return "BucketObjectGetAttrsStart" + case BucketObjectGetAttrsEnd: + return "BucketObjectGetAttrsEnd" + case BucketListObjectsStart: + return "BucketListObjectsStart" + case BucketListObjectsEnd: + return "BucketListObjectsEnd" + case BucketDeleteObjectsStart: + return "BucketDeleteObjectsStart" + case BucketDeleteObjectsEnd: + return "BucketDeleteObjectsEnd" + default: return fmt.Sprintf("Unknown(%x)", byte(te)) } diff --git a/runtimes/js/encore.dev/storage/objects/bucket.ts b/runtimes/js/encore.dev/storage/objects/bucket.ts index f3b8235e45..238f79012d 100644 --- a/runtimes/js/encore.dev/storage/objects/bucket.ts +++ b/runtimes/js/encore.dev/storage/objects/bucket.ts @@ -1,7 +1,13 @@ +import { getCurrentRequest } from "../../internal/reqtrack/mod"; import * as runtime from "../../internal/runtime/mod"; import { StringLiteral } from "../../internal/utils/constraints"; export interface BucketConfig { + /** + * Whether to enable versioning of the objects in the bucket. + * Defaults to false if unset. + */ + versioned?: boolean; } /** @@ -27,10 +33,12 @@ export class Bucket { } async *list(options: ListOptions): AsyncGenerator { - const iter = await this.impl.list(); + const source = getCurrentRequest(); + const iter = await this.impl.list(options, source); while (true) { const entry = await iter.next(); if (entry === null) { + iter.markDone(); break; } yield entry; @@ -41,9 +49,10 @@ export class Bucket { * Returns whether the object exists in the bucket. * Throws an error on network failure. */ - async exists(name: string): Promise { + async exists(name: string, options?: ExistsOptions): Promise { + const source = getCurrentRequest(); const impl = this.impl.object(name); - return impl.exists(); + return impl.exists(options, source); } /** @@ -51,24 +60,27 @@ export class Bucket { * Throws an error if the object does not exist. */ async attrs(name: string, options?: AttrsOptions): Promise { + const source = getCurrentRequest(); const impl = this.impl.object(name); - return impl.attrs(options); + return impl.attrs(options, source); } /** * Uploads an object to the bucket. */ async upload(name: string, data: Buffer, options?: UploadOptions): Promise { + const source = getCurrentRequest(); const impl = this.impl.object(name); - return impl.upload(data, options); + return impl.upload(data, options, source); } /** * Downloads an object from the bucket and returns its contents. */ async download(name: string, options?: DownloadOptions): Promise { + const source = getCurrentRequest(); const impl = this.impl.object(name); - return impl.downloadAll(options); + return impl.downloadAll(options, source); } /** @@ -76,8 +88,9 @@ export class Bucket { * Throws an error on network failure. */ async remove(name: string, options?: DeleteOptions): Promise { + const source = getCurrentRequest(); const impl = this.impl.object(name); - const _ = await impl.delete(options); + const _ = await impl.delete(options, source); } } @@ -102,6 +115,16 @@ export interface AttrsOptions { version?: string; } +export interface ExistsOptions { + /** + * The object version to check for existence. + * Defaults to the lastest version if unset. + * + * If bucket versioning is not enabled, this option is ignored. + */ + version?: string; +} + export interface DeleteOptions { /** * The object version to delete. diff --git a/runtimes/js/src/objects.rs b/runtimes/js/src/objects.rs index e23a99353e..bdaf4c35e4 100644 --- a/runtimes/js/src/objects.rs +++ b/runtimes/js/src/objects.rs @@ -1,10 +1,10 @@ -use std::pin::Pin; - use encore_runtime_core::objects as core; use napi::bindgen_prelude::Buffer; use napi::{Env, JsBuffer, JsObject}; use napi_derive::napi; +use crate::api::Request; + #[napi] pub struct Bucket { bkt: core::Bucket, @@ -22,10 +22,15 @@ impl Bucket { } #[napi] - pub async fn list(&self, options: Option) -> napi::Result { + pub async fn list( + &self, + options: Option, + source: Option<&Request>, + ) -> napi::Result { let options = options.unwrap_or_default().into(); + let source = source.map(|s| s.inner.clone()); self.bkt - .list(options) + .list(options, source) .await .map_err(map_objects_err) .map(ListIterator::new) @@ -44,18 +49,29 @@ impl BucketObject { } #[napi] - pub async fn attrs(&self, options: Option) -> napi::Result { + pub async fn attrs( + &self, + options: Option, + source: Option<&Request>, + ) -> napi::Result { let options = options.unwrap_or_default().into(); + let source = source.map(|s| s.inner.clone()); self.obj - .attrs(options) + .attrs(options, source) .await .map(ObjectAttrs::from) .map_err(map_objects_err) } #[napi] - pub async fn exists(&self, version: Option) -> napi::Result { - self.obj.exists(version).await.map_err(map_objects_err) + pub async fn exists( + &self, + options: Option, + source: Option<&Request>, + ) -> napi::Result { + let opts = options.unwrap_or_default().into(); + let source = source.map(|s| s.inner.clone()); + self.obj.exists(opts, source).await.map_err(map_objects_err) } #[napi(ts_return_type = "Promise")] @@ -64,13 +80,16 @@ impl BucketObject { env: Env, data: JsBuffer, opts: Option, + source: Option<&Request>, ) -> napi::Result { // TODO: reference the data via a Ref, so that we can keep it alive throughout the upload. let data = data.into_value()?.as_ref().to_vec(); let cursor = std::io::Cursor::new(data); let opts = opts.unwrap_or_default().into(); - let fut = self.obj.upload(Box::new(cursor), opts); + let source = source.map(|s| s.inner.clone()); + + let fut = self.obj.upload(Box::new(cursor), opts, source); // We need to always execute the handler below so that we can decrement the ref count. // To do so, we need the future to be a napi::Result::Ok. So wrap the result inside that @@ -84,20 +103,33 @@ impl BucketObject { } #[napi] - pub async fn download_all(&self, options: Option) -> napi::Result { + pub async fn download_all( + &self, + options: Option, + source: Option<&Request>, + ) -> napi::Result { let options = options.unwrap_or_default().into(); + let source = source.map(|s| s.inner.clone()); let buf = self .obj - .download_all(options) + .download_all(options, source) .await .map_err(map_objects_err)?; Ok(buf.into()) } #[napi] - pub async fn delete(&self, options: Option) -> napi::Result { + pub async fn delete( + &self, + options: Option, + source: Option<&Request>, + ) -> napi::Result { let options = options.unwrap_or_default().into(); - self.obj.delete(options).await.map_err(map_objects_err)?; + let source = source.map(|s| s.inner.clone()); + self.obj + .delete(options, source) + .await + .map_err(map_objects_err)?; Ok(true) } } @@ -176,31 +208,48 @@ fn map_objects_err(err: core::Error) -> napi::Error { #[napi] pub struct ListIterator { - stream: tokio::sync::Mutex>, + stream: tokio::sync::Mutex>, } #[napi] impl ListIterator { - fn new(stream: core::ListStream) -> Self { + fn new(stream: core::ListIterator) -> Self { Self { - stream: tokio::sync::Mutex::new(stream.into()), + stream: tokio::sync::Mutex::new(Some(stream)), } } #[napi] pub async fn next(&self) -> napi::Result> { - use futures::StreamExt; let mut stream = self.stream.lock().await; - let row = stream - .next() - .await - .transpose() - .map_err(|e| napi::Error::new(napi::Status::GenericFailure, format!("{:#?}", e)))?; + if let Some(stream) = stream.as_mut() { + let row = + stream.next().await.transpose().map_err(|e| { + napi::Error::new(napi::Status::GenericFailure, format!("{:#?}", e)) + })?; + Ok(row.map(ListEntry::from)) + } else { + Err(napi::Error::new( + napi::Status::GenericFailure, + "iterator is closed", + )) + } + } - Ok(row.map(ListEntry::from)) + #[napi] + pub fn mark_done(&mut self) { + if let Some(stream) = self.stream.get_mut().take() { + drop(stream); + }; } } +#[napi(object)] +#[derive(Debug, Default)] +pub struct ExistsOptions { + pub version: Option, +} + #[napi(object)] #[derive(Debug, Default)] pub struct AttrsOptions { @@ -242,6 +291,14 @@ impl From for core::DeleteOptions { } } +impl From for core::ExistsOptions { + fn from(value: ExistsOptions) -> Self { + Self { + version: value.version, + } + } +} + impl From for core::AttrsOptions { fn from(value: AttrsOptions) -> Self { Self { diff --git a/tsparser/src/parser/resources/infra/objects.rs b/tsparser/src/parser/resources/infra/objects.rs index 5aabece296..db89d9045b 100644 --- a/tsparser/src/parser/resources/infra/objects.rs +++ b/tsparser/src/parser/resources/infra/objects.rs @@ -19,10 +19,13 @@ use crate::span_err::ErrReporter; pub struct Bucket { pub name: String, pub doc: Option, + pub versioned: bool, } #[derive(LitParser, Default)] -struct DecodedBucketConfig {} +struct DecodedBucketConfig { + pub versioned: Option, +} pub const OBJECTS_PARSER: ResourceParser = ResourceParser { name: "objects", @@ -38,7 +41,7 @@ pub const OBJECTS_PARSER: ResourceParser = ResourceParser { let r = r?; // Not yet used. - let _cfg = r.config.unwrap_or_default(); + let cfg = r.config.unwrap_or_default(); let object = match &r.bind_name { None => None, @@ -50,6 +53,7 @@ pub const OBJECTS_PARSER: ResourceParser = ResourceParser { let resource = Resource::Bucket(Lrc::new(Bucket { name: r.resource_name, doc: r.doc_comment, + versioned: cfg.versioned.unwrap_or(false), })); pass.add_resource(resource.clone()); pass.add_bind(BindData { From dfea5839cc6ba462f35976e63d08274c219e9a7f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Eriksson?= Date: Thu, 31 Oct 2024 17:01:03 +0100 Subject: [PATCH 17/37] Add bucket versioned attribute --- Cargo.lock | 12 +- proto/encore/parser/meta/v1/meta.pb.go | 254 +++++++++++++------------ proto/encore/parser/meta/v1/meta.proto | 3 +- tsparser/src/legacymeta/mod.rs | 1 + 4 files changed, 141 insertions(+), 129 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 75a0514afc..b0ac2b18ad 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -354,9 +354,9 @@ dependencies = [ [[package]] name = "aws-sdk-s3" -version = "1.58.0" +version = "1.60.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0656a79cf5e6ab0d4bb2465cd750a7a2fd7ea26c062183ed94225f5782e22365" +checksum = "0506cc60e392e33712d47717d5ae5760a3b134bf8ee7aea7e43df3d7e2669ae0" dependencies = [ "aws-credential-types", "aws-runtime", @@ -639,9 +639,9 @@ dependencies = [ [[package]] name = "aws-smithy-runtime-api" -version = "1.7.2" +version = "1.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e086682a53d3aa241192aa110fa8dfce98f2f5ac2ead0de84d41582c7e8fdb96" +checksum = "92165296a47a812b267b4f41032ff8069ab7ff783696d217f0994a0d7ab585cd" dependencies = [ "aws-smithy-async", "aws-smithy-types", @@ -656,9 +656,9 @@ dependencies = [ [[package]] name = "aws-smithy-types" -version = "1.2.8" +version = "1.2.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07c9cdc179e6afbf5d391ab08c85eac817b51c87e1892a5edb5f7bbdc64314b4" +checksum = "4fbd94a32b3a7d55d3806fe27d98d3ad393050439dd05eb53ece36ec5e3d3510" dependencies = [ "base64-simd", "bytes", diff --git a/proto/encore/parser/meta/v1/meta.pb.go b/proto/encore/parser/meta/v1/meta.pb.go index 4aadf5ab84..2b2aa851dc 100644 --- a/proto/encore/parser/meta/v1/meta.pb.go +++ b/proto/encore/parser/meta/v1/meta.pb.go @@ -2823,8 +2823,9 @@ type Bucket struct { sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` - Doc *string `protobuf:"bytes,2,opt,name=doc,proto3,oneof" json:"doc,omitempty"` + Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` + Doc *string `protobuf:"bytes,2,opt,name=doc,proto3,oneof" json:"doc,omitempty"` + Versioned bool `protobuf:"varint,3,opt,name=versioned,proto3" json:"versioned,omitempty"` } func (x *Bucket) Reset() { @@ -2873,6 +2874,13 @@ func (x *Bucket) GetDoc() string { return "" } +func (x *Bucket) GetVersioned() bool { + if x != nil { + return x.Versioned + } + return false +} + type PubSubTopic struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -4113,129 +4121,131 @@ var file_encore_parser_meta_v1_meta_proto_rawDesc = []byte{ 0x75, 0x6d, 0x62, 0x65, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, 0x06, 0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x12, 0x20, 0x0a, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, - 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x22, 0x3b, 0x0a, 0x06, 0x42, 0x75, 0x63, 0x6b, 0x65, 0x74, 0x12, + 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x22, 0x59, 0x0a, 0x06, 0x42, 0x75, 0x63, 0x6b, 0x65, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x15, 0x0a, 0x03, 0x64, 0x6f, 0x63, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, - 0x48, 0x00, 0x52, 0x03, 0x64, 0x6f, 0x63, 0x88, 0x01, 0x01, 0x42, 0x06, 0x0a, 0x04, 0x5f, 0x64, - 0x6f, 0x63, 0x22, 0xb8, 0x07, 0x0a, 0x0b, 0x50, 0x75, 0x62, 0x53, 0x75, 0x62, 0x54, 0x6f, 0x70, - 0x69, 0x63, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x15, 0x0a, 0x03, 0x64, 0x6f, 0x63, 0x18, 0x02, 0x20, - 0x01, 0x28, 0x09, 0x48, 0x00, 0x52, 0x03, 0x64, 0x6f, 0x63, 0x88, 0x01, 0x01, 0x12, 0x40, 0x0a, - 0x0c, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x03, 0x20, - 0x01, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x65, 0x6e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x70, 0x61, 0x72, - 0x73, 0x65, 0x72, 0x2e, 0x73, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x79, - 0x70, 0x65, 0x52, 0x0b, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x54, 0x79, 0x70, 0x65, 0x12, - 0x63, 0x0a, 0x12, 0x64, 0x65, 0x6c, 0x69, 0x76, 0x65, 0x72, 0x79, 0x5f, 0x67, 0x75, 0x61, 0x72, - 0x61, 0x6e, 0x74, 0x65, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x34, 0x2e, 0x65, 0x6e, - 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x70, 0x61, 0x72, 0x73, 0x65, 0x72, 0x2e, 0x6d, 0x65, 0x74, 0x61, - 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x75, 0x62, 0x53, 0x75, 0x62, 0x54, 0x6f, 0x70, 0x69, 0x63, 0x2e, - 0x44, 0x65, 0x6c, 0x69, 0x76, 0x65, 0x72, 0x79, 0x47, 0x75, 0x61, 0x72, 0x61, 0x6e, 0x74, 0x65, - 0x65, 0x52, 0x11, 0x64, 0x65, 0x6c, 0x69, 0x76, 0x65, 0x72, 0x79, 0x47, 0x75, 0x61, 0x72, 0x61, - 0x6e, 0x74, 0x65, 0x65, 0x12, 0x21, 0x0a, 0x0c, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x69, 0x6e, 0x67, - 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x6f, 0x72, 0x64, 0x65, - 0x72, 0x69, 0x6e, 0x67, 0x4b, 0x65, 0x79, 0x12, 0x4c, 0x0a, 0x0a, 0x70, 0x75, 0x62, 0x6c, 0x69, - 0x73, 0x68, 0x65, 0x72, 0x73, 0x18, 0x06, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2c, 0x2e, 0x65, 0x6e, - 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x70, 0x61, 0x72, 0x73, 0x65, 0x72, 0x2e, 0x6d, 0x65, 0x74, 0x61, - 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x75, 0x62, 0x53, 0x75, 0x62, 0x54, 0x6f, 0x70, 0x69, 0x63, 0x2e, - 0x50, 0x75, 0x62, 0x6c, 0x69, 0x73, 0x68, 0x65, 0x72, 0x52, 0x0a, 0x70, 0x75, 0x62, 0x6c, 0x69, - 0x73, 0x68, 0x65, 0x72, 0x73, 0x12, 0x55, 0x0a, 0x0d, 0x73, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, - 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x07, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2f, 0x2e, 0x65, - 0x6e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x70, 0x61, 0x72, 0x73, 0x65, 0x72, 0x2e, 0x6d, 0x65, 0x74, - 0x61, 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x75, 0x62, 0x53, 0x75, 0x62, 0x54, 0x6f, 0x70, 0x69, 0x63, - 0x2e, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x0d, 0x73, - 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x1a, 0x2e, 0x0a, 0x09, - 0x50, 0x75, 0x62, 0x6c, 0x69, 0x73, 0x68, 0x65, 0x72, 0x12, 0x21, 0x0a, 0x0c, 0x73, 0x65, 0x72, - 0x76, 0x69, 0x63, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x0b, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x1a, 0xaa, 0x02, 0x0a, - 0x0c, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x12, 0x0a, - 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, - 0x65, 0x12, 0x21, 0x0a, 0x0c, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x5f, 0x6e, 0x61, 0x6d, - 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, - 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x21, 0x0a, 0x0c, 0x61, 0x63, 0x6b, 0x5f, 0x64, 0x65, 0x61, 0x64, - 0x6c, 0x69, 0x6e, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0b, 0x61, 0x63, 0x6b, 0x44, - 0x65, 0x61, 0x64, 0x6c, 0x69, 0x6e, 0x65, 0x12, 0x2b, 0x0a, 0x11, 0x6d, 0x65, 0x73, 0x73, 0x61, - 0x67, 0x65, 0x5f, 0x72, 0x65, 0x74, 0x65, 0x6e, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x04, 0x20, 0x01, - 0x28, 0x03, 0x52, 0x10, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x52, 0x65, 0x74, 0x65, 0x6e, - 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x51, 0x0a, 0x0c, 0x72, 0x65, 0x74, 0x72, 0x79, 0x5f, 0x70, 0x6f, - 0x6c, 0x69, 0x63, 0x79, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2e, 0x2e, 0x65, 0x6e, 0x63, - 0x6f, 0x72, 0x65, 0x2e, 0x70, 0x61, 0x72, 0x73, 0x65, 0x72, 0x2e, 0x6d, 0x65, 0x74, 0x61, 0x2e, - 0x76, 0x31, 0x2e, 0x50, 0x75, 0x62, 0x53, 0x75, 0x62, 0x54, 0x6f, 0x70, 0x69, 0x63, 0x2e, 0x52, - 0x65, 0x74, 0x72, 0x79, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x52, 0x0b, 0x72, 0x65, 0x74, 0x72, - 0x79, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x12, 0x2c, 0x0a, 0x0f, 0x6d, 0x61, 0x78, 0x5f, 0x63, - 0x6f, 0x6e, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x18, 0x06, 0x20, 0x01, 0x28, 0x05, - 0x48, 0x00, 0x52, 0x0e, 0x6d, 0x61, 0x78, 0x43, 0x6f, 0x6e, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, - 0x63, 0x79, 0x88, 0x01, 0x01, 0x42, 0x12, 0x0a, 0x10, 0x5f, 0x6d, 0x61, 0x78, 0x5f, 0x63, 0x6f, - 0x6e, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x1a, 0x70, 0x0a, 0x0b, 0x52, 0x65, 0x74, - 0x72, 0x79, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x12, 0x1f, 0x0a, 0x0b, 0x6d, 0x69, 0x6e, 0x5f, - 0x62, 0x61, 0x63, 0x6b, 0x6f, 0x66, 0x66, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0a, 0x6d, - 0x69, 0x6e, 0x42, 0x61, 0x63, 0x6b, 0x6f, 0x66, 0x66, 0x12, 0x1f, 0x0a, 0x0b, 0x6d, 0x61, 0x78, - 0x5f, 0x62, 0x61, 0x63, 0x6b, 0x6f, 0x66, 0x66, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0a, - 0x6d, 0x61, 0x78, 0x42, 0x61, 0x63, 0x6b, 0x6f, 0x66, 0x66, 0x12, 0x1f, 0x0a, 0x0b, 0x6d, 0x61, - 0x78, 0x5f, 0x72, 0x65, 0x74, 0x72, 0x69, 0x65, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x03, 0x52, - 0x0a, 0x6d, 0x61, 0x78, 0x52, 0x65, 0x74, 0x72, 0x69, 0x65, 0x73, 0x22, 0x38, 0x0a, 0x11, 0x44, - 0x65, 0x6c, 0x69, 0x76, 0x65, 0x72, 0x79, 0x47, 0x75, 0x61, 0x72, 0x61, 0x6e, 0x74, 0x65, 0x65, - 0x12, 0x11, 0x0a, 0x0d, 0x41, 0x54, 0x5f, 0x4c, 0x45, 0x41, 0x53, 0x54, 0x5f, 0x4f, 0x4e, 0x43, - 0x45, 0x10, 0x00, 0x12, 0x10, 0x0a, 0x0c, 0x45, 0x58, 0x41, 0x43, 0x54, 0x4c, 0x59, 0x5f, 0x4f, - 0x4e, 0x43, 0x45, 0x10, 0x01, 0x42, 0x06, 0x0a, 0x04, 0x5f, 0x64, 0x6f, 0x63, 0x22, 0x9a, 0x03, - 0x0a, 0x0c, 0x43, 0x61, 0x63, 0x68, 0x65, 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x12, 0x12, - 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, - 0x6d, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x64, 0x6f, 0x63, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x03, 0x64, 0x6f, 0x63, 0x12, 0x4a, 0x0a, 0x09, 0x6b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, - 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2c, 0x2e, 0x65, 0x6e, 0x63, 0x6f, 0x72, 0x65, - 0x2e, 0x70, 0x61, 0x72, 0x73, 0x65, 0x72, 0x2e, 0x6d, 0x65, 0x74, 0x61, 0x2e, 0x76, 0x31, 0x2e, - 0x43, 0x61, 0x63, 0x68, 0x65, 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x2e, 0x4b, 0x65, 0x79, - 0x73, 0x70, 0x61, 0x63, 0x65, 0x52, 0x09, 0x6b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x73, - 0x12, 0x27, 0x0a, 0x0f, 0x65, 0x76, 0x69, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x70, 0x6f, 0x6c, - 0x69, 0x63, 0x79, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x65, 0x76, 0x69, 0x63, 0x74, - 0x69, 0x6f, 0x6e, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x1a, 0xee, 0x01, 0x0a, 0x08, 0x4b, 0x65, - 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x12, 0x38, 0x0a, 0x08, 0x6b, 0x65, 0x79, 0x5f, 0x74, 0x79, - 0x70, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x65, 0x6e, 0x63, 0x6f, 0x72, - 0x65, 0x2e, 0x70, 0x61, 0x72, 0x73, 0x65, 0x72, 0x2e, 0x73, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x2e, - 0x76, 0x31, 0x2e, 0x54, 0x79, 0x70, 0x65, 0x52, 0x07, 0x6b, 0x65, 0x79, 0x54, 0x79, 0x70, 0x65, - 0x12, 0x3c, 0x0a, 0x0a, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x02, - 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x65, 0x6e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x70, 0x61, - 0x72, 0x73, 0x65, 0x72, 0x2e, 0x73, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x2e, 0x76, 0x31, 0x2e, 0x54, - 0x79, 0x70, 0x65, 0x52, 0x09, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x54, 0x79, 0x70, 0x65, 0x12, 0x18, - 0x0a, 0x07, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x07, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x64, 0x6f, 0x63, 0x18, - 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x64, 0x6f, 0x63, 0x12, 0x3e, 0x0a, 0x0c, 0x70, 0x61, - 0x74, 0x68, 0x5f, 0x70, 0x61, 0x74, 0x74, 0x65, 0x72, 0x6e, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, - 0x32, 0x1b, 0x2e, 0x65, 0x6e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x70, 0x61, 0x72, 0x73, 0x65, 0x72, - 0x2e, 0x6d, 0x65, 0x74, 0x61, 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x61, 0x74, 0x68, 0x52, 0x0b, 0x70, - 0x61, 0x74, 0x68, 0x50, 0x61, 0x74, 0x74, 0x65, 0x72, 0x6e, 0x22, 0xbb, 0x03, 0x0a, 0x06, 0x4d, - 0x65, 0x74, 0x72, 0x69, 0x63, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x3f, 0x0a, 0x0a, 0x76, 0x61, 0x6c, - 0x75, 0x65, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x20, 0x2e, - 0x65, 0x6e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x70, 0x61, 0x72, 0x73, 0x65, 0x72, 0x2e, 0x73, 0x63, - 0x68, 0x65, 0x6d, 0x61, 0x2e, 0x76, 0x31, 0x2e, 0x42, 0x75, 0x69, 0x6c, 0x74, 0x69, 0x6e, 0x52, - 0x09, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x54, 0x79, 0x70, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x64, 0x6f, - 0x63, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x64, 0x6f, 0x63, 0x12, 0x3c, 0x0a, 0x04, - 0x6b, 0x69, 0x6e, 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x28, 0x2e, 0x65, 0x6e, 0x63, + 0x48, 0x00, 0x52, 0x03, 0x64, 0x6f, 0x63, 0x88, 0x01, 0x01, 0x12, 0x1c, 0x0a, 0x09, 0x76, 0x65, + 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x09, 0x76, + 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x64, 0x42, 0x06, 0x0a, 0x04, 0x5f, 0x64, 0x6f, 0x63, + 0x22, 0xb8, 0x07, 0x0a, 0x0b, 0x50, 0x75, 0x62, 0x53, 0x75, 0x62, 0x54, 0x6f, 0x70, 0x69, 0x63, + 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, + 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x15, 0x0a, 0x03, 0x64, 0x6f, 0x63, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x09, 0x48, 0x00, 0x52, 0x03, 0x64, 0x6f, 0x63, 0x88, 0x01, 0x01, 0x12, 0x40, 0x0a, 0x0c, 0x6d, + 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x1d, 0x2e, 0x65, 0x6e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x70, 0x61, 0x72, 0x73, 0x65, + 0x72, 0x2e, 0x73, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x79, 0x70, 0x65, + 0x52, 0x0b, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x54, 0x79, 0x70, 0x65, 0x12, 0x63, 0x0a, + 0x12, 0x64, 0x65, 0x6c, 0x69, 0x76, 0x65, 0x72, 0x79, 0x5f, 0x67, 0x75, 0x61, 0x72, 0x61, 0x6e, + 0x74, 0x65, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x34, 0x2e, 0x65, 0x6e, 0x63, 0x6f, + 0x72, 0x65, 0x2e, 0x70, 0x61, 0x72, 0x73, 0x65, 0x72, 0x2e, 0x6d, 0x65, 0x74, 0x61, 0x2e, 0x76, + 0x31, 0x2e, 0x50, 0x75, 0x62, 0x53, 0x75, 0x62, 0x54, 0x6f, 0x70, 0x69, 0x63, 0x2e, 0x44, 0x65, + 0x6c, 0x69, 0x76, 0x65, 0x72, 0x79, 0x47, 0x75, 0x61, 0x72, 0x61, 0x6e, 0x74, 0x65, 0x65, 0x52, + 0x11, 0x64, 0x65, 0x6c, 0x69, 0x76, 0x65, 0x72, 0x79, 0x47, 0x75, 0x61, 0x72, 0x61, 0x6e, 0x74, + 0x65, 0x65, 0x12, 0x21, 0x0a, 0x0c, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x69, 0x6e, 0x67, 0x5f, 0x6b, + 0x65, 0x79, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x69, + 0x6e, 0x67, 0x4b, 0x65, 0x79, 0x12, 0x4c, 0x0a, 0x0a, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x73, 0x68, + 0x65, 0x72, 0x73, 0x18, 0x06, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2c, 0x2e, 0x65, 0x6e, 0x63, 0x6f, + 0x72, 0x65, 0x2e, 0x70, 0x61, 0x72, 0x73, 0x65, 0x72, 0x2e, 0x6d, 0x65, 0x74, 0x61, 0x2e, 0x76, + 0x31, 0x2e, 0x50, 0x75, 0x62, 0x53, 0x75, 0x62, 0x54, 0x6f, 0x70, 0x69, 0x63, 0x2e, 0x50, 0x75, + 0x62, 0x6c, 0x69, 0x73, 0x68, 0x65, 0x72, 0x52, 0x0a, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x73, 0x68, + 0x65, 0x72, 0x73, 0x12, 0x55, 0x0a, 0x0d, 0x73, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, + 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x07, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2f, 0x2e, 0x65, 0x6e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x70, 0x61, 0x72, 0x73, 0x65, 0x72, 0x2e, 0x6d, 0x65, 0x74, 0x61, 0x2e, - 0x76, 0x31, 0x2e, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x2e, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, - 0x4b, 0x69, 0x6e, 0x64, 0x52, 0x04, 0x6b, 0x69, 0x6e, 0x64, 0x12, 0x26, 0x0a, 0x0c, 0x73, 0x65, - 0x72, 0x76, 0x69, 0x63, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, - 0x48, 0x00, 0x52, 0x0b, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x88, - 0x01, 0x01, 0x12, 0x3b, 0x0a, 0x06, 0x6c, 0x61, 0x62, 0x65, 0x6c, 0x73, 0x18, 0x06, 0x20, 0x03, - 0x28, 0x0b, 0x32, 0x23, 0x2e, 0x65, 0x6e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x70, 0x61, 0x72, 0x73, - 0x65, 0x72, 0x2e, 0x6d, 0x65, 0x74, 0x61, 0x2e, 0x76, 0x31, 0x2e, 0x4d, 0x65, 0x74, 0x72, 0x69, - 0x63, 0x2e, 0x4c, 0x61, 0x62, 0x65, 0x6c, 0x52, 0x06, 0x6c, 0x61, 0x62, 0x65, 0x6c, 0x73, 0x1a, - 0x61, 0x0a, 0x05, 0x4c, 0x61, 0x62, 0x65, 0x6c, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x34, 0x0a, 0x04, 0x74, 0x79, - 0x70, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x20, 0x2e, 0x65, 0x6e, 0x63, 0x6f, 0x72, - 0x65, 0x2e, 0x70, 0x61, 0x72, 0x73, 0x65, 0x72, 0x2e, 0x73, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x2e, - 0x76, 0x31, 0x2e, 0x42, 0x75, 0x69, 0x6c, 0x74, 0x69, 0x6e, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, - 0x12, 0x10, 0x0a, 0x03, 0x64, 0x6f, 0x63, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x64, - 0x6f, 0x63, 0x22, 0x33, 0x0a, 0x0a, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x4b, 0x69, 0x6e, 0x64, - 0x12, 0x0b, 0x0a, 0x07, 0x43, 0x4f, 0x55, 0x4e, 0x54, 0x45, 0x52, 0x10, 0x00, 0x12, 0x09, 0x0a, - 0x05, 0x47, 0x41, 0x55, 0x47, 0x45, 0x10, 0x01, 0x12, 0x0d, 0x0a, 0x09, 0x48, 0x49, 0x53, 0x54, - 0x4f, 0x47, 0x52, 0x41, 0x4d, 0x10, 0x02, 0x42, 0x0f, 0x0a, 0x0d, 0x5f, 0x73, 0x65, 0x72, 0x76, - 0x69, 0x63, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x2a, 0x1e, 0x0a, 0x04, 0x4c, 0x61, 0x6e, 0x67, - 0x12, 0x06, 0x0a, 0x02, 0x47, 0x4f, 0x10, 0x00, 0x12, 0x0e, 0x0a, 0x0a, 0x54, 0x59, 0x50, 0x45, - 0x53, 0x43, 0x52, 0x49, 0x50, 0x54, 0x10, 0x01, 0x42, 0x26, 0x5a, 0x24, 0x65, 0x6e, 0x63, 0x72, - 0x2e, 0x64, 0x65, 0x76, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x65, 0x6e, 0x63, 0x6f, 0x72, - 0x65, 0x2f, 0x70, 0x61, 0x72, 0x73, 0x65, 0x72, 0x2f, 0x6d, 0x65, 0x74, 0x61, 0x2f, 0x76, 0x31, - 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x76, 0x31, 0x2e, 0x50, 0x75, 0x62, 0x53, 0x75, 0x62, 0x54, 0x6f, 0x70, 0x69, 0x63, 0x2e, 0x53, + 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x0d, 0x73, 0x75, 0x62, + 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x1a, 0x2e, 0x0a, 0x09, 0x50, 0x75, + 0x62, 0x6c, 0x69, 0x73, 0x68, 0x65, 0x72, 0x12, 0x21, 0x0a, 0x0c, 0x73, 0x65, 0x72, 0x76, 0x69, + 0x63, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x73, + 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x1a, 0xaa, 0x02, 0x0a, 0x0c, 0x53, + 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x12, 0x0a, 0x04, 0x6e, + 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, + 0x21, 0x0a, 0x0c, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, + 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x4e, 0x61, + 0x6d, 0x65, 0x12, 0x21, 0x0a, 0x0c, 0x61, 0x63, 0x6b, 0x5f, 0x64, 0x65, 0x61, 0x64, 0x6c, 0x69, + 0x6e, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0b, 0x61, 0x63, 0x6b, 0x44, 0x65, 0x61, + 0x64, 0x6c, 0x69, 0x6e, 0x65, 0x12, 0x2b, 0x0a, 0x11, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, + 0x5f, 0x72, 0x65, 0x74, 0x65, 0x6e, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x04, 0x20, 0x01, 0x28, 0x03, + 0x52, 0x10, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x52, 0x65, 0x74, 0x65, 0x6e, 0x74, 0x69, + 0x6f, 0x6e, 0x12, 0x51, 0x0a, 0x0c, 0x72, 0x65, 0x74, 0x72, 0x79, 0x5f, 0x70, 0x6f, 0x6c, 0x69, + 0x63, 0x79, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2e, 0x2e, 0x65, 0x6e, 0x63, 0x6f, 0x72, + 0x65, 0x2e, 0x70, 0x61, 0x72, 0x73, 0x65, 0x72, 0x2e, 0x6d, 0x65, 0x74, 0x61, 0x2e, 0x76, 0x31, + 0x2e, 0x50, 0x75, 0x62, 0x53, 0x75, 0x62, 0x54, 0x6f, 0x70, 0x69, 0x63, 0x2e, 0x52, 0x65, 0x74, + 0x72, 0x79, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x52, 0x0b, 0x72, 0x65, 0x74, 0x72, 0x79, 0x50, + 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x12, 0x2c, 0x0a, 0x0f, 0x6d, 0x61, 0x78, 0x5f, 0x63, 0x6f, 0x6e, + 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x18, 0x06, 0x20, 0x01, 0x28, 0x05, 0x48, 0x00, + 0x52, 0x0e, 0x6d, 0x61, 0x78, 0x43, 0x6f, 0x6e, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, + 0x88, 0x01, 0x01, 0x42, 0x12, 0x0a, 0x10, 0x5f, 0x6d, 0x61, 0x78, 0x5f, 0x63, 0x6f, 0x6e, 0x63, + 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x1a, 0x70, 0x0a, 0x0b, 0x52, 0x65, 0x74, 0x72, 0x79, + 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x12, 0x1f, 0x0a, 0x0b, 0x6d, 0x69, 0x6e, 0x5f, 0x62, 0x61, + 0x63, 0x6b, 0x6f, 0x66, 0x66, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0a, 0x6d, 0x69, 0x6e, + 0x42, 0x61, 0x63, 0x6b, 0x6f, 0x66, 0x66, 0x12, 0x1f, 0x0a, 0x0b, 0x6d, 0x61, 0x78, 0x5f, 0x62, + 0x61, 0x63, 0x6b, 0x6f, 0x66, 0x66, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0a, 0x6d, 0x61, + 0x78, 0x42, 0x61, 0x63, 0x6b, 0x6f, 0x66, 0x66, 0x12, 0x1f, 0x0a, 0x0b, 0x6d, 0x61, 0x78, 0x5f, + 0x72, 0x65, 0x74, 0x72, 0x69, 0x65, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0a, 0x6d, + 0x61, 0x78, 0x52, 0x65, 0x74, 0x72, 0x69, 0x65, 0x73, 0x22, 0x38, 0x0a, 0x11, 0x44, 0x65, 0x6c, + 0x69, 0x76, 0x65, 0x72, 0x79, 0x47, 0x75, 0x61, 0x72, 0x61, 0x6e, 0x74, 0x65, 0x65, 0x12, 0x11, + 0x0a, 0x0d, 0x41, 0x54, 0x5f, 0x4c, 0x45, 0x41, 0x53, 0x54, 0x5f, 0x4f, 0x4e, 0x43, 0x45, 0x10, + 0x00, 0x12, 0x10, 0x0a, 0x0c, 0x45, 0x58, 0x41, 0x43, 0x54, 0x4c, 0x59, 0x5f, 0x4f, 0x4e, 0x43, + 0x45, 0x10, 0x01, 0x42, 0x06, 0x0a, 0x04, 0x5f, 0x64, 0x6f, 0x63, 0x22, 0x9a, 0x03, 0x0a, 0x0c, + 0x43, 0x61, 0x63, 0x68, 0x65, 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x12, 0x12, 0x0a, 0x04, + 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, + 0x12, 0x10, 0x0a, 0x03, 0x64, 0x6f, 0x63, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x64, + 0x6f, 0x63, 0x12, 0x4a, 0x0a, 0x09, 0x6b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x73, 0x18, + 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2c, 0x2e, 0x65, 0x6e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x70, + 0x61, 0x72, 0x73, 0x65, 0x72, 0x2e, 0x6d, 0x65, 0x74, 0x61, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x61, + 0x63, 0x68, 0x65, 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x2e, 0x4b, 0x65, 0x79, 0x73, 0x70, + 0x61, 0x63, 0x65, 0x52, 0x09, 0x6b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x73, 0x12, 0x27, + 0x0a, 0x0f, 0x65, 0x76, 0x69, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x70, 0x6f, 0x6c, 0x69, 0x63, + 0x79, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x65, 0x76, 0x69, 0x63, 0x74, 0x69, 0x6f, + 0x6e, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x1a, 0xee, 0x01, 0x0a, 0x08, 0x4b, 0x65, 0x79, 0x73, + 0x70, 0x61, 0x63, 0x65, 0x12, 0x38, 0x0a, 0x08, 0x6b, 0x65, 0x79, 0x5f, 0x74, 0x79, 0x70, 0x65, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x65, 0x6e, 0x63, 0x6f, 0x72, 0x65, 0x2e, + 0x70, 0x61, 0x72, 0x73, 0x65, 0x72, 0x2e, 0x73, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x2e, 0x76, 0x31, + 0x2e, 0x54, 0x79, 0x70, 0x65, 0x52, 0x07, 0x6b, 0x65, 0x79, 0x54, 0x79, 0x70, 0x65, 0x12, 0x3c, + 0x0a, 0x0a, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x65, 0x6e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x70, 0x61, 0x72, 0x73, + 0x65, 0x72, 0x2e, 0x73, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x79, 0x70, + 0x65, 0x52, 0x09, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x54, 0x79, 0x70, 0x65, 0x12, 0x18, 0x0a, 0x07, + 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x73, + 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x64, 0x6f, 0x63, 0x18, 0x04, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x03, 0x64, 0x6f, 0x63, 0x12, 0x3e, 0x0a, 0x0c, 0x70, 0x61, 0x74, 0x68, + 0x5f, 0x70, 0x61, 0x74, 0x74, 0x65, 0x72, 0x6e, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, + 0x2e, 0x65, 0x6e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x70, 0x61, 0x72, 0x73, 0x65, 0x72, 0x2e, 0x6d, + 0x65, 0x74, 0x61, 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x61, 0x74, 0x68, 0x52, 0x0b, 0x70, 0x61, 0x74, + 0x68, 0x50, 0x61, 0x74, 0x74, 0x65, 0x72, 0x6e, 0x22, 0xbb, 0x03, 0x0a, 0x06, 0x4d, 0x65, 0x74, + 0x72, 0x69, 0x63, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x3f, 0x0a, 0x0a, 0x76, 0x61, 0x6c, 0x75, 0x65, + 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x20, 0x2e, 0x65, 0x6e, + 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x70, 0x61, 0x72, 0x73, 0x65, 0x72, 0x2e, 0x73, 0x63, 0x68, 0x65, + 0x6d, 0x61, 0x2e, 0x76, 0x31, 0x2e, 0x42, 0x75, 0x69, 0x6c, 0x74, 0x69, 0x6e, 0x52, 0x09, 0x76, + 0x61, 0x6c, 0x75, 0x65, 0x54, 0x79, 0x70, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x64, 0x6f, 0x63, 0x18, + 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x64, 0x6f, 0x63, 0x12, 0x3c, 0x0a, 0x04, 0x6b, 0x69, + 0x6e, 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x28, 0x2e, 0x65, 0x6e, 0x63, 0x6f, 0x72, + 0x65, 0x2e, 0x70, 0x61, 0x72, 0x73, 0x65, 0x72, 0x2e, 0x6d, 0x65, 0x74, 0x61, 0x2e, 0x76, 0x31, + 0x2e, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x2e, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x4b, 0x69, + 0x6e, 0x64, 0x52, 0x04, 0x6b, 0x69, 0x6e, 0x64, 0x12, 0x26, 0x0a, 0x0c, 0x73, 0x65, 0x72, 0x76, + 0x69, 0x63, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x48, 0x00, + 0x52, 0x0b, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x88, 0x01, 0x01, + 0x12, 0x3b, 0x0a, 0x06, 0x6c, 0x61, 0x62, 0x65, 0x6c, 0x73, 0x18, 0x06, 0x20, 0x03, 0x28, 0x0b, + 0x32, 0x23, 0x2e, 0x65, 0x6e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x70, 0x61, 0x72, 0x73, 0x65, 0x72, + 0x2e, 0x6d, 0x65, 0x74, 0x61, 0x2e, 0x76, 0x31, 0x2e, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x2e, + 0x4c, 0x61, 0x62, 0x65, 0x6c, 0x52, 0x06, 0x6c, 0x61, 0x62, 0x65, 0x6c, 0x73, 0x1a, 0x61, 0x0a, + 0x05, 0x4c, 0x61, 0x62, 0x65, 0x6c, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x34, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, + 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x20, 0x2e, 0x65, 0x6e, 0x63, 0x6f, 0x72, 0x65, 0x2e, + 0x70, 0x61, 0x72, 0x73, 0x65, 0x72, 0x2e, 0x73, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x2e, 0x76, 0x31, + 0x2e, 0x42, 0x75, 0x69, 0x6c, 0x74, 0x69, 0x6e, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x10, + 0x0a, 0x03, 0x64, 0x6f, 0x63, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x64, 0x6f, 0x63, + 0x22, 0x33, 0x0a, 0x0a, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x4b, 0x69, 0x6e, 0x64, 0x12, 0x0b, + 0x0a, 0x07, 0x43, 0x4f, 0x55, 0x4e, 0x54, 0x45, 0x52, 0x10, 0x00, 0x12, 0x09, 0x0a, 0x05, 0x47, + 0x41, 0x55, 0x47, 0x45, 0x10, 0x01, 0x12, 0x0d, 0x0a, 0x09, 0x48, 0x49, 0x53, 0x54, 0x4f, 0x47, + 0x52, 0x41, 0x4d, 0x10, 0x02, 0x42, 0x0f, 0x0a, 0x0d, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, + 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x2a, 0x1e, 0x0a, 0x04, 0x4c, 0x61, 0x6e, 0x67, 0x12, 0x06, + 0x0a, 0x02, 0x47, 0x4f, 0x10, 0x00, 0x12, 0x0e, 0x0a, 0x0a, 0x54, 0x59, 0x50, 0x45, 0x53, 0x43, + 0x52, 0x49, 0x50, 0x54, 0x10, 0x01, 0x42, 0x26, 0x5a, 0x24, 0x65, 0x6e, 0x63, 0x72, 0x2e, 0x64, + 0x65, 0x76, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x65, 0x6e, 0x63, 0x6f, 0x72, 0x65, 0x2f, + 0x70, 0x61, 0x72, 0x73, 0x65, 0x72, 0x2f, 0x6d, 0x65, 0x74, 0x61, 0x2f, 0x76, 0x31, 0x62, 0x06, + 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( diff --git a/proto/encore/parser/meta/v1/meta.proto b/proto/encore/parser/meta/v1/meta.proto index 2bd4fd721d..451373d345 100644 --- a/proto/encore/parser/meta/v1/meta.proto +++ b/proto/encore/parser/meta/v1/meta.proto @@ -346,13 +346,14 @@ message SQLDatabase { message DBMigration { string filename = 1; // filename - uint64 number = 2; // migration number + uint64 number = 2; // migration number string description = 3; // descriptive name } message Bucket { string name = 1; optional string doc = 2; + bool versioned = 3; } message PubSubTopic { diff --git a/tsparser/src/legacymeta/mod.rs b/tsparser/src/legacymeta/mod.rs index ac071f2d2e..ce9feb6339 100644 --- a/tsparser/src/legacymeta/mod.rs +++ b/tsparser/src/legacymeta/mod.rs @@ -582,6 +582,7 @@ impl<'a> MetaBuilder<'a> { v1::Bucket { name: bkt.name.clone(), doc: bkt.doc.clone(), + versioned: bkt.versioned, } } From 16bfbf6a0bb684d1dfe4310882a71e3cda7e073d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Eriksson?= Date: Thu, 31 Oct 2024 17:03:05 +0100 Subject: [PATCH 18/37] Fix bucket config parsing --- tsparser/src/parser/resources/infra/objects.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/tsparser/src/parser/resources/infra/objects.rs b/tsparser/src/parser/resources/infra/objects.rs index db89d9045b..1df8b2bffe 100644 --- a/tsparser/src/parser/resources/infra/objects.rs +++ b/tsparser/src/parser/resources/infra/objects.rs @@ -1,4 +1,5 @@ use anyhow::Result; +use litparser::LitParser; use litparser_derive::LitParser; use swc_common::sync::Lrc; use swc_ecma_ast as ast; @@ -39,8 +40,6 @@ pub const OBJECTS_PARSER: ResourceParser = ResourceParser { type Res = NamedClassResourceOptionalConfig; for r in iter_references::(&module, &names) { let r = r?; - - // Not yet used. let cfg = r.config.unwrap_or_default(); let object = match &r.bind_name { From 4b76f005464d8b23e7033dc4a62deb205d41057c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Eriksson?= Date: Mon, 4 Nov 2024 08:42:21 +0100 Subject: [PATCH 19/37] More tracing tweaks --- cli/daemon/run/runtime_config2.go | 3 +- pkg/traceparser/parser.go | 5 +- proto/encore/engine/trace2/trace2.pb.go | 675 ++++++++++++------------ proto/encore/engine/trace2/trace2.proto | 1 + proto/encore/runtime/v1/infra.pb.go | 161 +++--- proto/encore/runtime/v1/infra.proto | 3 + runtimes/core/src/objects/gcs/mod.rs | 35 +- runtimes/core/src/objects/mod.rs | 7 +- runtimes/core/src/trace/protocol.rs | 5 +- 9 files changed, 469 insertions(+), 426 deletions(-) diff --git a/cli/daemon/run/runtime_config2.go b/cli/daemon/run/runtime_config2.go index c10c2c88ff..2b61e4c270 100644 --- a/cli/daemon/run/runtime_config2.go +++ b/cli/daemon/run/runtime_config2.go @@ -348,7 +348,8 @@ func (g *RuntimeConfigGenerator) initialize() error { Rid: newRid(), Provider: &runtimev1.BucketCluster_Gcs{ Gcs: &runtimev1.BucketCluster_GCS{ - Endpoint: &bktProviderConfig.GCS.Endpoint, + Endpoint: &bktProviderConfig.GCS.Endpoint, + Anonymous: true, }, }, }) diff --git a/pkg/traceparser/parser.go b/pkg/traceparser/parser.go index ecf9419fc6..69cfae71d7 100644 --- a/pkg/traceparser/parser.go +++ b/pkg/traceparser/parser.go @@ -600,8 +600,9 @@ func (tp *traceParser) bucketObjectAttrs() *tracepb2.BucketObjectAttributes { func (tp *traceParser) bucketObjectUploadEnd() *tracepb2.BucketObjectUploadEnd { return &tracepb2.BucketObjectUploadEnd{ - Size: ptrOrNil(tp.Uint64()), - Err: tp.errWithStack(), + Size: ptrOrNil(tp.Uint64()), + Version: ptrOrNil(tp.String()), + Err: tp.errWithStack(), } } diff --git a/proto/encore/engine/trace2/trace2.pb.go b/proto/encore/engine/trace2/trace2.pb.go index a4d72c671a..0ceca01e45 100644 --- a/proto/encore/engine/trace2/trace2.pb.go +++ b/proto/encore/engine/trace2/trace2.pb.go @@ -2927,8 +2927,9 @@ type BucketObjectUploadEnd struct { sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - Err *Error `protobuf:"bytes,1,opt,name=err,proto3,oneof" json:"err,omitempty"` - Size *uint64 `protobuf:"varint,2,opt,name=size,proto3,oneof" json:"size,omitempty"` + Err *Error `protobuf:"bytes,1,opt,name=err,proto3,oneof" json:"err,omitempty"` + Size *uint64 `protobuf:"varint,2,opt,name=size,proto3,oneof" json:"size,omitempty"` + Version *string `protobuf:"bytes,3,opt,name=version,proto3,oneof" json:"version,omitempty"` } func (x *BucketObjectUploadEnd) Reset() { @@ -2977,6 +2978,13 @@ func (x *BucketObjectUploadEnd) GetSize() uint64 { return 0 } +func (x *BucketObjectUploadEnd) GetVersion() string { + if x != nil && x.Version != nil { + return *x.Version + } + return "" +} + type BucketObjectDownloadStart struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -5828,347 +5836,350 @@ var file_encore_engine_trace2_trace2_proto_rawDesc = []byte{ 0x0a, 0x05, 0x73, 0x74, 0x61, 0x63, 0x6b, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x20, 0x2e, 0x65, 0x6e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x65, 0x6e, 0x67, 0x69, 0x6e, 0x65, 0x2e, 0x74, 0x72, 0x61, 0x63, 0x65, 0x32, 0x2e, 0x53, 0x74, 0x61, 0x63, 0x6b, 0x54, 0x72, 0x61, 0x63, 0x65, 0x52, - 0x05, 0x73, 0x74, 0x61, 0x63, 0x6b, 0x22, 0x75, 0x0a, 0x15, 0x42, 0x75, 0x63, 0x6b, 0x65, 0x74, - 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x55, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x45, 0x6e, 0x64, 0x12, - 0x32, 0x0a, 0x03, 0x65, 0x72, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x65, - 0x6e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x65, 0x6e, 0x67, 0x69, 0x6e, 0x65, 0x2e, 0x74, 0x72, 0x61, - 0x63, 0x65, 0x32, 0x2e, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x48, 0x00, 0x52, 0x03, 0x65, 0x72, 0x72, - 0x88, 0x01, 0x01, 0x12, 0x17, 0x0a, 0x04, 0x73, 0x69, 0x7a, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, - 0x04, 0x48, 0x01, 0x52, 0x04, 0x73, 0x69, 0x7a, 0x65, 0x88, 0x01, 0x01, 0x42, 0x06, 0x0a, 0x04, - 0x5f, 0x65, 0x72, 0x72, 0x42, 0x07, 0x0a, 0x05, 0x5f, 0x73, 0x69, 0x7a, 0x65, 0x22, 0xae, 0x01, - 0x0a, 0x19, 0x42, 0x75, 0x63, 0x6b, 0x65, 0x74, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x44, 0x6f, - 0x77, 0x6e, 0x6c, 0x6f, 0x61, 0x64, 0x53, 0x74, 0x61, 0x72, 0x74, 0x12, 0x16, 0x0a, 0x06, 0x62, + 0x05, 0x73, 0x74, 0x61, 0x63, 0x6b, 0x22, 0xa0, 0x01, 0x0a, 0x15, 0x42, 0x75, 0x63, 0x6b, 0x65, + 0x74, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x55, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x45, 0x6e, 0x64, + 0x12, 0x32, 0x0a, 0x03, 0x65, 0x72, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, + 0x65, 0x6e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x65, 0x6e, 0x67, 0x69, 0x6e, 0x65, 0x2e, 0x74, 0x72, + 0x61, 0x63, 0x65, 0x32, 0x2e, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x48, 0x00, 0x52, 0x03, 0x65, 0x72, + 0x72, 0x88, 0x01, 0x01, 0x12, 0x17, 0x0a, 0x04, 0x73, 0x69, 0x7a, 0x65, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x04, 0x48, 0x01, 0x52, 0x04, 0x73, 0x69, 0x7a, 0x65, 0x88, 0x01, 0x01, 0x12, 0x1d, 0x0a, + 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x48, 0x02, + 0x52, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x88, 0x01, 0x01, 0x42, 0x06, 0x0a, 0x04, + 0x5f, 0x65, 0x72, 0x72, 0x42, 0x07, 0x0a, 0x05, 0x5f, 0x73, 0x69, 0x7a, 0x65, 0x42, 0x0a, 0x0a, + 0x08, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x22, 0xae, 0x01, 0x0a, 0x19, 0x42, 0x75, + 0x63, 0x6b, 0x65, 0x74, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x44, 0x6f, 0x77, 0x6e, 0x6c, 0x6f, + 0x61, 0x64, 0x53, 0x74, 0x61, 0x72, 0x74, 0x12, 0x16, 0x0a, 0x06, 0x62, 0x75, 0x63, 0x6b, 0x65, + 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x62, 0x75, 0x63, 0x6b, 0x65, 0x74, 0x12, + 0x16, 0x0a, 0x06, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x06, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x12, 0x1d, 0x0a, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, + 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x48, 0x00, 0x52, 0x07, 0x76, 0x65, 0x72, 0x73, + 0x69, 0x6f, 0x6e, 0x88, 0x01, 0x01, 0x12, 0x36, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x63, 0x6b, 0x18, + 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x20, 0x2e, 0x65, 0x6e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x65, + 0x6e, 0x67, 0x69, 0x6e, 0x65, 0x2e, 0x74, 0x72, 0x61, 0x63, 0x65, 0x32, 0x2e, 0x53, 0x74, 0x61, + 0x63, 0x6b, 0x54, 0x72, 0x61, 0x63, 0x65, 0x52, 0x05, 0x73, 0x74, 0x61, 0x63, 0x6b, 0x42, 0x0a, + 0x0a, 0x08, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x22, 0x77, 0x0a, 0x17, 0x42, 0x75, + 0x63, 0x6b, 0x65, 0x74, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x44, 0x6f, 0x77, 0x6e, 0x6c, 0x6f, + 0x61, 0x64, 0x45, 0x6e, 0x64, 0x12, 0x32, 0x0a, 0x03, 0x65, 0x72, 0x72, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x65, 0x6e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x65, 0x6e, 0x67, 0x69, + 0x6e, 0x65, 0x2e, 0x74, 0x72, 0x61, 0x63, 0x65, 0x32, 0x2e, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x48, + 0x00, 0x52, 0x03, 0x65, 0x72, 0x72, 0x88, 0x01, 0x01, 0x12, 0x17, 0x0a, 0x04, 0x73, 0x69, 0x7a, + 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x48, 0x01, 0x52, 0x04, 0x73, 0x69, 0x7a, 0x65, 0x88, + 0x01, 0x01, 0x42, 0x06, 0x0a, 0x04, 0x5f, 0x65, 0x72, 0x72, 0x42, 0x07, 0x0a, 0x05, 0x5f, 0x73, + 0x69, 0x7a, 0x65, 0x22, 0xae, 0x01, 0x0a, 0x19, 0x42, 0x75, 0x63, 0x6b, 0x65, 0x74, 0x4f, 0x62, + 0x6a, 0x65, 0x63, 0x74, 0x47, 0x65, 0x74, 0x41, 0x74, 0x74, 0x72, 0x73, 0x53, 0x74, 0x61, 0x72, + 0x74, 0x12, 0x16, 0x0a, 0x06, 0x62, 0x75, 0x63, 0x6b, 0x65, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x06, 0x62, 0x75, 0x63, 0x6b, 0x65, 0x74, 0x12, 0x16, 0x0a, 0x06, 0x6f, 0x62, 0x6a, + 0x65, 0x63, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x6f, 0x62, 0x6a, 0x65, 0x63, + 0x74, 0x12, 0x1d, 0x0a, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, + 0x28, 0x09, 0x48, 0x00, 0x52, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x88, 0x01, 0x01, + 0x12, 0x36, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x63, 0x6b, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x20, 0x2e, 0x65, 0x6e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x65, 0x6e, 0x67, 0x69, 0x6e, 0x65, 0x2e, + 0x74, 0x72, 0x61, 0x63, 0x65, 0x32, 0x2e, 0x53, 0x74, 0x61, 0x63, 0x6b, 0x54, 0x72, 0x61, 0x63, + 0x65, 0x52, 0x05, 0x73, 0x74, 0x61, 0x63, 0x6b, 0x42, 0x0a, 0x0a, 0x08, 0x5f, 0x76, 0x65, 0x72, + 0x73, 0x69, 0x6f, 0x6e, 0x22, 0xa8, 0x01, 0x0a, 0x17, 0x42, 0x75, 0x63, 0x6b, 0x65, 0x74, 0x4f, + 0x62, 0x6a, 0x65, 0x63, 0x74, 0x47, 0x65, 0x74, 0x41, 0x74, 0x74, 0x72, 0x73, 0x45, 0x6e, 0x64, + 0x12, 0x32, 0x0a, 0x03, 0x65, 0x72, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, + 0x65, 0x6e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x65, 0x6e, 0x67, 0x69, 0x6e, 0x65, 0x2e, 0x74, 0x72, + 0x61, 0x63, 0x65, 0x32, 0x2e, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x48, 0x00, 0x52, 0x03, 0x65, 0x72, + 0x72, 0x88, 0x01, 0x01, 0x12, 0x47, 0x0a, 0x05, 0x61, 0x74, 0x74, 0x72, 0x73, 0x18, 0x02, 0x20, + 0x01, 0x28, 0x0b, 0x32, 0x2c, 0x2e, 0x65, 0x6e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x65, 0x6e, 0x67, + 0x69, 0x6e, 0x65, 0x2e, 0x74, 0x72, 0x61, 0x63, 0x65, 0x32, 0x2e, 0x42, 0x75, 0x63, 0x6b, 0x65, + 0x74, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, + 0x73, 0x48, 0x01, 0x52, 0x05, 0x61, 0x74, 0x74, 0x72, 0x73, 0x88, 0x01, 0x01, 0x42, 0x06, 0x0a, + 0x04, 0x5f, 0x65, 0x72, 0x72, 0x42, 0x08, 0x0a, 0x06, 0x5f, 0x61, 0x74, 0x74, 0x72, 0x73, 0x22, + 0x90, 0x01, 0x0a, 0x16, 0x42, 0x75, 0x63, 0x6b, 0x65, 0x74, 0x4c, 0x69, 0x73, 0x74, 0x4f, 0x62, + 0x6a, 0x65, 0x63, 0x74, 0x73, 0x53, 0x74, 0x61, 0x72, 0x74, 0x12, 0x16, 0x0a, 0x06, 0x62, 0x75, + 0x63, 0x6b, 0x65, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x62, 0x75, 0x63, 0x6b, + 0x65, 0x74, 0x12, 0x1b, 0x0a, 0x06, 0x70, 0x72, 0x65, 0x66, 0x69, 0x78, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x09, 0x48, 0x00, 0x52, 0x06, 0x70, 0x72, 0x65, 0x66, 0x69, 0x78, 0x88, 0x01, 0x01, 0x12, + 0x36, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x63, 0x6b, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x20, + 0x2e, 0x65, 0x6e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x65, 0x6e, 0x67, 0x69, 0x6e, 0x65, 0x2e, 0x74, + 0x72, 0x61, 0x63, 0x65, 0x32, 0x2e, 0x53, 0x74, 0x61, 0x63, 0x6b, 0x54, 0x72, 0x61, 0x63, 0x65, + 0x52, 0x05, 0x73, 0x74, 0x61, 0x63, 0x6b, 0x42, 0x09, 0x0a, 0x07, 0x5f, 0x70, 0x72, 0x65, 0x66, + 0x69, 0x78, 0x22, 0x89, 0x01, 0x0a, 0x14, 0x42, 0x75, 0x63, 0x6b, 0x65, 0x74, 0x4c, 0x69, 0x73, + 0x74, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x73, 0x45, 0x6e, 0x64, 0x12, 0x32, 0x0a, 0x03, 0x65, + 0x72, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x65, 0x6e, 0x63, 0x6f, 0x72, + 0x65, 0x2e, 0x65, 0x6e, 0x67, 0x69, 0x6e, 0x65, 0x2e, 0x74, 0x72, 0x61, 0x63, 0x65, 0x32, 0x2e, + 0x45, 0x72, 0x72, 0x6f, 0x72, 0x48, 0x00, 0x52, 0x03, 0x65, 0x72, 0x72, 0x88, 0x01, 0x01, 0x12, + 0x1a, 0x0a, 0x08, 0x6f, 0x62, 0x73, 0x65, 0x72, 0x76, 0x65, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x04, 0x52, 0x08, 0x6f, 0x62, 0x73, 0x65, 0x72, 0x76, 0x65, 0x64, 0x12, 0x19, 0x0a, 0x08, 0x68, + 0x61, 0x73, 0x5f, 0x6d, 0x6f, 0x72, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x68, + 0x61, 0x73, 0x4d, 0x6f, 0x72, 0x65, 0x42, 0x06, 0x0a, 0x04, 0x5f, 0x65, 0x72, 0x72, 0x22, 0xb3, + 0x01, 0x0a, 0x18, 0x42, 0x75, 0x63, 0x6b, 0x65, 0x74, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x4f, + 0x62, 0x6a, 0x65, 0x63, 0x74, 0x73, 0x53, 0x74, 0x61, 0x72, 0x74, 0x12, 0x16, 0x0a, 0x06, 0x62, 0x75, 0x63, 0x6b, 0x65, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x62, 0x75, 0x63, - 0x6b, 0x65, 0x74, 0x12, 0x16, 0x0a, 0x06, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x18, 0x02, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x06, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x12, 0x1d, 0x0a, 0x07, 0x76, - 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x48, 0x00, 0x52, 0x07, - 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x88, 0x01, 0x01, 0x12, 0x36, 0x0a, 0x05, 0x73, 0x74, - 0x61, 0x63, 0x6b, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x20, 0x2e, 0x65, 0x6e, 0x63, 0x6f, - 0x72, 0x65, 0x2e, 0x65, 0x6e, 0x67, 0x69, 0x6e, 0x65, 0x2e, 0x74, 0x72, 0x61, 0x63, 0x65, 0x32, - 0x2e, 0x53, 0x74, 0x61, 0x63, 0x6b, 0x54, 0x72, 0x61, 0x63, 0x65, 0x52, 0x05, 0x73, 0x74, 0x61, - 0x63, 0x6b, 0x42, 0x0a, 0x0a, 0x08, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x22, 0x77, - 0x0a, 0x17, 0x42, 0x75, 0x63, 0x6b, 0x65, 0x74, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x44, 0x6f, - 0x77, 0x6e, 0x6c, 0x6f, 0x61, 0x64, 0x45, 0x6e, 0x64, 0x12, 0x32, 0x0a, 0x03, 0x65, 0x72, 0x72, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x65, 0x6e, 0x63, 0x6f, 0x72, 0x65, 0x2e, - 0x65, 0x6e, 0x67, 0x69, 0x6e, 0x65, 0x2e, 0x74, 0x72, 0x61, 0x63, 0x65, 0x32, 0x2e, 0x45, 0x72, - 0x72, 0x6f, 0x72, 0x48, 0x00, 0x52, 0x03, 0x65, 0x72, 0x72, 0x88, 0x01, 0x01, 0x12, 0x17, 0x0a, - 0x04, 0x73, 0x69, 0x7a, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x48, 0x01, 0x52, 0x04, 0x73, - 0x69, 0x7a, 0x65, 0x88, 0x01, 0x01, 0x42, 0x06, 0x0a, 0x04, 0x5f, 0x65, 0x72, 0x72, 0x42, 0x07, - 0x0a, 0x05, 0x5f, 0x73, 0x69, 0x7a, 0x65, 0x22, 0xae, 0x01, 0x0a, 0x19, 0x42, 0x75, 0x63, 0x6b, - 0x65, 0x74, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x47, 0x65, 0x74, 0x41, 0x74, 0x74, 0x72, 0x73, - 0x53, 0x74, 0x61, 0x72, 0x74, 0x12, 0x16, 0x0a, 0x06, 0x62, 0x75, 0x63, 0x6b, 0x65, 0x74, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x62, 0x75, 0x63, 0x6b, 0x65, 0x74, 0x12, 0x16, 0x0a, - 0x06, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x6f, - 0x62, 0x6a, 0x65, 0x63, 0x74, 0x12, 0x1d, 0x0a, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, - 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x48, 0x00, 0x52, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, - 0x6e, 0x88, 0x01, 0x01, 0x12, 0x36, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x63, 0x6b, 0x18, 0x04, 0x20, - 0x01, 0x28, 0x0b, 0x32, 0x20, 0x2e, 0x65, 0x6e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x65, 0x6e, 0x67, - 0x69, 0x6e, 0x65, 0x2e, 0x74, 0x72, 0x61, 0x63, 0x65, 0x32, 0x2e, 0x53, 0x74, 0x61, 0x63, 0x6b, - 0x54, 0x72, 0x61, 0x63, 0x65, 0x52, 0x05, 0x73, 0x74, 0x61, 0x63, 0x6b, 0x42, 0x0a, 0x0a, 0x08, - 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x22, 0xa8, 0x01, 0x0a, 0x17, 0x42, 0x75, 0x63, - 0x6b, 0x65, 0x74, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x47, 0x65, 0x74, 0x41, 0x74, 0x74, 0x72, - 0x73, 0x45, 0x6e, 0x64, 0x12, 0x32, 0x0a, 0x03, 0x65, 0x72, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, - 0x0b, 0x32, 0x1b, 0x2e, 0x65, 0x6e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x65, 0x6e, 0x67, 0x69, 0x6e, - 0x65, 0x2e, 0x74, 0x72, 0x61, 0x63, 0x65, 0x32, 0x2e, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x48, 0x00, - 0x52, 0x03, 0x65, 0x72, 0x72, 0x88, 0x01, 0x01, 0x12, 0x47, 0x0a, 0x05, 0x61, 0x74, 0x74, 0x72, - 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2c, 0x2e, 0x65, 0x6e, 0x63, 0x6f, 0x72, 0x65, - 0x2e, 0x65, 0x6e, 0x67, 0x69, 0x6e, 0x65, 0x2e, 0x74, 0x72, 0x61, 0x63, 0x65, 0x32, 0x2e, 0x42, - 0x75, 0x63, 0x6b, 0x65, 0x74, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x41, 0x74, 0x74, 0x72, 0x69, - 0x62, 0x75, 0x74, 0x65, 0x73, 0x48, 0x01, 0x52, 0x05, 0x61, 0x74, 0x74, 0x72, 0x73, 0x88, 0x01, - 0x01, 0x42, 0x06, 0x0a, 0x04, 0x5f, 0x65, 0x72, 0x72, 0x42, 0x08, 0x0a, 0x06, 0x5f, 0x61, 0x74, - 0x74, 0x72, 0x73, 0x22, 0x90, 0x01, 0x0a, 0x16, 0x42, 0x75, 0x63, 0x6b, 0x65, 0x74, 0x4c, 0x69, - 0x73, 0x74, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x73, 0x53, 0x74, 0x61, 0x72, 0x74, 0x12, 0x16, - 0x0a, 0x06, 0x62, 0x75, 0x63, 0x6b, 0x65, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, - 0x62, 0x75, 0x63, 0x6b, 0x65, 0x74, 0x12, 0x1b, 0x0a, 0x06, 0x70, 0x72, 0x65, 0x66, 0x69, 0x78, - 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x48, 0x00, 0x52, 0x06, 0x70, 0x72, 0x65, 0x66, 0x69, 0x78, - 0x88, 0x01, 0x01, 0x12, 0x36, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x63, 0x6b, 0x18, 0x03, 0x20, 0x01, + 0x6b, 0x65, 0x74, 0x12, 0x36, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x63, 0x6b, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x20, 0x2e, 0x65, 0x6e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x65, 0x6e, 0x67, 0x69, 0x6e, 0x65, 0x2e, 0x74, 0x72, 0x61, 0x63, 0x65, 0x32, 0x2e, 0x53, 0x74, 0x61, 0x63, 0x6b, 0x54, - 0x72, 0x61, 0x63, 0x65, 0x52, 0x05, 0x73, 0x74, 0x61, 0x63, 0x6b, 0x42, 0x09, 0x0a, 0x07, 0x5f, - 0x70, 0x72, 0x65, 0x66, 0x69, 0x78, 0x22, 0x89, 0x01, 0x0a, 0x14, 0x42, 0x75, 0x63, 0x6b, 0x65, - 0x74, 0x4c, 0x69, 0x73, 0x74, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x73, 0x45, 0x6e, 0x64, 0x12, - 0x32, 0x0a, 0x03, 0x65, 0x72, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x65, + 0x72, 0x61, 0x63, 0x65, 0x52, 0x05, 0x73, 0x74, 0x61, 0x63, 0x6b, 0x12, 0x47, 0x0a, 0x07, 0x65, + 0x6e, 0x74, 0x72, 0x69, 0x65, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2d, 0x2e, 0x65, 0x6e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x65, 0x6e, 0x67, 0x69, 0x6e, 0x65, 0x2e, 0x74, 0x72, 0x61, - 0x63, 0x65, 0x32, 0x2e, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x48, 0x00, 0x52, 0x03, 0x65, 0x72, 0x72, - 0x88, 0x01, 0x01, 0x12, 0x1a, 0x0a, 0x08, 0x6f, 0x62, 0x73, 0x65, 0x72, 0x76, 0x65, 0x64, 0x18, - 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, 0x08, 0x6f, 0x62, 0x73, 0x65, 0x72, 0x76, 0x65, 0x64, 0x12, - 0x19, 0x0a, 0x08, 0x68, 0x61, 0x73, 0x5f, 0x6d, 0x6f, 0x72, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, - 0x08, 0x52, 0x07, 0x68, 0x61, 0x73, 0x4d, 0x6f, 0x72, 0x65, 0x42, 0x06, 0x0a, 0x04, 0x5f, 0x65, - 0x72, 0x72, 0x22, 0xb3, 0x01, 0x0a, 0x18, 0x42, 0x75, 0x63, 0x6b, 0x65, 0x74, 0x44, 0x65, 0x6c, - 0x65, 0x74, 0x65, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x73, 0x53, 0x74, 0x61, 0x72, 0x74, 0x12, - 0x16, 0x0a, 0x06, 0x62, 0x75, 0x63, 0x6b, 0x65, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x06, 0x62, 0x75, 0x63, 0x6b, 0x65, 0x74, 0x12, 0x36, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x63, 0x6b, - 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x20, 0x2e, 0x65, 0x6e, 0x63, 0x6f, 0x72, 0x65, 0x2e, + 0x63, 0x65, 0x32, 0x2e, 0x42, 0x75, 0x63, 0x6b, 0x65, 0x74, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, + 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x07, 0x65, 0x6e, 0x74, + 0x72, 0x69, 0x65, 0x73, 0x22, 0x5c, 0x0a, 0x17, 0x42, 0x75, 0x63, 0x6b, 0x65, 0x74, 0x44, 0x65, + 0x6c, 0x65, 0x74, 0x65, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, + 0x16, 0x0a, 0x06, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x06, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x12, 0x1d, 0x0a, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, + 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x48, 0x00, 0x52, 0x07, 0x76, 0x65, 0x72, 0x73, + 0x69, 0x6f, 0x6e, 0x88, 0x01, 0x01, 0x42, 0x0a, 0x0a, 0x08, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, + 0x6f, 0x6e, 0x22, 0x54, 0x0a, 0x16, 0x42, 0x75, 0x63, 0x6b, 0x65, 0x74, 0x44, 0x65, 0x6c, 0x65, + 0x74, 0x65, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x73, 0x45, 0x6e, 0x64, 0x12, 0x32, 0x0a, 0x03, + 0x65, 0x72, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x65, 0x6e, 0x63, 0x6f, + 0x72, 0x65, 0x2e, 0x65, 0x6e, 0x67, 0x69, 0x6e, 0x65, 0x2e, 0x74, 0x72, 0x61, 0x63, 0x65, 0x32, + 0x2e, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x48, 0x00, 0x52, 0x03, 0x65, 0x72, 0x72, 0x88, 0x01, 0x01, + 0x42, 0x06, 0x0a, 0x04, 0x5f, 0x65, 0x72, 0x72, 0x22, 0xc0, 0x01, 0x0a, 0x16, 0x42, 0x75, 0x63, + 0x6b, 0x65, 0x74, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, + 0x74, 0x65, 0x73, 0x12, 0x17, 0x0a, 0x04, 0x73, 0x69, 0x7a, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x04, 0x48, 0x00, 0x52, 0x04, 0x73, 0x69, 0x7a, 0x65, 0x88, 0x01, 0x01, 0x12, 0x1d, 0x0a, 0x07, + 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x48, 0x01, 0x52, + 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x88, 0x01, 0x01, 0x12, 0x17, 0x0a, 0x04, 0x65, + 0x74, 0x61, 0x67, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x48, 0x02, 0x52, 0x04, 0x65, 0x74, 0x61, + 0x67, 0x88, 0x01, 0x01, 0x12, 0x26, 0x0a, 0x0c, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x5f, + 0x74, 0x79, 0x70, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x48, 0x03, 0x52, 0x0b, 0x63, 0x6f, + 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x54, 0x79, 0x70, 0x65, 0x88, 0x01, 0x01, 0x42, 0x07, 0x0a, 0x05, + 0x5f, 0x73, 0x69, 0x7a, 0x65, 0x42, 0x0a, 0x0a, 0x08, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, + 0x6e, 0x42, 0x07, 0x0a, 0x05, 0x5f, 0x65, 0x74, 0x61, 0x67, 0x42, 0x0f, 0x0a, 0x0d, 0x5f, 0x63, + 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x22, 0x61, 0x0a, 0x0a, 0x42, + 0x6f, 0x64, 0x79, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x12, 0x1f, 0x0a, 0x0b, 0x69, 0x73, 0x5f, + 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0a, + 0x69, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1e, 0x0a, 0x0a, 0x6f, 0x76, + 0x65, 0x72, 0x66, 0x6c, 0x6f, 0x77, 0x65, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0a, + 0x6f, 0x76, 0x65, 0x72, 0x66, 0x6c, 0x6f, 0x77, 0x65, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x64, 0x61, + 0x74, 0x61, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x04, 0x64, 0x61, 0x74, 0x61, 0x22, 0xd5, + 0x01, 0x0a, 0x0d, 0x48, 0x54, 0x54, 0x50, 0x43, 0x61, 0x6c, 0x6c, 0x53, 0x74, 0x61, 0x72, 0x74, + 0x12, 0x3b, 0x0a, 0x1a, 0x63, 0x6f, 0x72, 0x72, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, + 0x70, 0x61, 0x72, 0x65, 0x6e, 0x74, 0x5f, 0x73, 0x70, 0x61, 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x04, 0x52, 0x17, 0x63, 0x6f, 0x72, 0x72, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x6f, + 0x6e, 0x50, 0x61, 0x72, 0x65, 0x6e, 0x74, 0x53, 0x70, 0x61, 0x6e, 0x49, 0x64, 0x12, 0x16, 0x0a, + 0x06, 0x6d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x6d, + 0x65, 0x74, 0x68, 0x6f, 0x64, 0x12, 0x10, 0x0a, 0x03, 0x75, 0x72, 0x6c, 0x18, 0x03, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x03, 0x75, 0x72, 0x6c, 0x12, 0x36, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x63, 0x6b, + 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x20, 0x2e, 0x65, 0x6e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x65, 0x6e, 0x67, 0x69, 0x6e, 0x65, 0x2e, 0x74, 0x72, 0x61, 0x63, 0x65, 0x32, 0x2e, 0x53, 0x74, 0x61, 0x63, 0x6b, 0x54, 0x72, 0x61, 0x63, 0x65, 0x52, 0x05, 0x73, 0x74, 0x61, 0x63, 0x6b, 0x12, - 0x47, 0x0a, 0x07, 0x65, 0x6e, 0x74, 0x72, 0x69, 0x65, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, - 0x32, 0x2d, 0x2e, 0x65, 0x6e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x65, 0x6e, 0x67, 0x69, 0x6e, 0x65, - 0x2e, 0x74, 0x72, 0x61, 0x63, 0x65, 0x32, 0x2e, 0x42, 0x75, 0x63, 0x6b, 0x65, 0x74, 0x44, 0x65, - 0x6c, 0x65, 0x74, 0x65, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, - 0x07, 0x65, 0x6e, 0x74, 0x72, 0x69, 0x65, 0x73, 0x22, 0x5c, 0x0a, 0x17, 0x42, 0x75, 0x63, 0x6b, - 0x65, 0x74, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x45, 0x6e, - 0x74, 0x72, 0x79, 0x12, 0x16, 0x0a, 0x06, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x06, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x12, 0x1d, 0x0a, 0x07, 0x76, - 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x48, 0x00, 0x52, 0x07, - 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x88, 0x01, 0x01, 0x42, 0x0a, 0x0a, 0x08, 0x5f, 0x76, - 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x22, 0x54, 0x0a, 0x16, 0x42, 0x75, 0x63, 0x6b, 0x65, 0x74, - 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x73, 0x45, 0x6e, 0x64, - 0x12, 0x32, 0x0a, 0x03, 0x65, 0x72, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, - 0x65, 0x6e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x65, 0x6e, 0x67, 0x69, 0x6e, 0x65, 0x2e, 0x74, 0x72, - 0x61, 0x63, 0x65, 0x32, 0x2e, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x48, 0x00, 0x52, 0x03, 0x65, 0x72, - 0x72, 0x88, 0x01, 0x01, 0x42, 0x06, 0x0a, 0x04, 0x5f, 0x65, 0x72, 0x72, 0x22, 0xc0, 0x01, 0x0a, - 0x16, 0x42, 0x75, 0x63, 0x6b, 0x65, 0x74, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x41, 0x74, 0x74, - 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x12, 0x17, 0x0a, 0x04, 0x73, 0x69, 0x7a, 0x65, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x04, 0x48, 0x00, 0x52, 0x04, 0x73, 0x69, 0x7a, 0x65, 0x88, 0x01, 0x01, - 0x12, 0x1d, 0x0a, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, - 0x09, 0x48, 0x01, 0x52, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x88, 0x01, 0x01, 0x12, - 0x17, 0x0a, 0x04, 0x65, 0x74, 0x61, 0x67, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x48, 0x02, 0x52, - 0x04, 0x65, 0x74, 0x61, 0x67, 0x88, 0x01, 0x01, 0x12, 0x26, 0x0a, 0x0c, 0x63, 0x6f, 0x6e, 0x74, - 0x65, 0x6e, 0x74, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x48, 0x03, - 0x52, 0x0b, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x54, 0x79, 0x70, 0x65, 0x88, 0x01, 0x01, - 0x42, 0x07, 0x0a, 0x05, 0x5f, 0x73, 0x69, 0x7a, 0x65, 0x42, 0x0a, 0x0a, 0x08, 0x5f, 0x76, 0x65, - 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x42, 0x07, 0x0a, 0x05, 0x5f, 0x65, 0x74, 0x61, 0x67, 0x42, 0x0f, - 0x0a, 0x0d, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x22, - 0x61, 0x0a, 0x0a, 0x42, 0x6f, 0x64, 0x79, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x12, 0x1f, 0x0a, - 0x0b, 0x69, 0x73, 0x5f, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x08, 0x52, 0x0a, 0x69, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1e, - 0x0a, 0x0a, 0x6f, 0x76, 0x65, 0x72, 0x66, 0x6c, 0x6f, 0x77, 0x65, 0x64, 0x18, 0x02, 0x20, 0x01, - 0x28, 0x08, 0x52, 0x0a, 0x6f, 0x76, 0x65, 0x72, 0x66, 0x6c, 0x6f, 0x77, 0x65, 0x64, 0x12, 0x12, - 0x0a, 0x04, 0x64, 0x61, 0x74, 0x61, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x04, 0x64, 0x61, - 0x74, 0x61, 0x22, 0xd5, 0x01, 0x0a, 0x0d, 0x48, 0x54, 0x54, 0x50, 0x43, 0x61, 0x6c, 0x6c, 0x53, - 0x74, 0x61, 0x72, 0x74, 0x12, 0x3b, 0x0a, 0x1a, 0x63, 0x6f, 0x72, 0x72, 0x65, 0x6c, 0x61, 0x74, - 0x69, 0x6f, 0x6e, 0x5f, 0x70, 0x61, 0x72, 0x65, 0x6e, 0x74, 0x5f, 0x73, 0x70, 0x61, 0x6e, 0x5f, - 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x17, 0x63, 0x6f, 0x72, 0x72, 0x65, 0x6c, - 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x50, 0x61, 0x72, 0x65, 0x6e, 0x74, 0x53, 0x70, 0x61, 0x6e, 0x49, - 0x64, 0x12, 0x16, 0x0a, 0x06, 0x6d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x06, 0x6d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x12, 0x10, 0x0a, 0x03, 0x75, 0x72, 0x6c, - 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x75, 0x72, 0x6c, 0x12, 0x36, 0x0a, 0x05, 0x73, - 0x74, 0x61, 0x63, 0x6b, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x20, 0x2e, 0x65, 0x6e, 0x63, - 0x6f, 0x72, 0x65, 0x2e, 0x65, 0x6e, 0x67, 0x69, 0x6e, 0x65, 0x2e, 0x74, 0x72, 0x61, 0x63, 0x65, - 0x32, 0x2e, 0x53, 0x74, 0x61, 0x63, 0x6b, 0x54, 0x72, 0x61, 0x63, 0x65, 0x52, 0x05, 0x73, 0x74, - 0x61, 0x63, 0x6b, 0x12, 0x25, 0x0a, 0x0e, 0x73, 0x74, 0x61, 0x72, 0x74, 0x5f, 0x6e, 0x61, 0x6e, - 0x6f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0d, 0x73, 0x74, 0x61, - 0x72, 0x74, 0x4e, 0x61, 0x6e, 0x6f, 0x74, 0x69, 0x6d, 0x65, 0x22, 0xc8, 0x01, 0x0a, 0x0b, 0x48, - 0x54, 0x54, 0x50, 0x43, 0x61, 0x6c, 0x6c, 0x45, 0x6e, 0x64, 0x12, 0x24, 0x0a, 0x0b, 0x73, 0x74, - 0x61, 0x74, 0x75, 0x73, 0x5f, 0x63, 0x6f, 0x64, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x48, - 0x00, 0x52, 0x0a, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x43, 0x6f, 0x64, 0x65, 0x88, 0x01, 0x01, - 0x12, 0x32, 0x0a, 0x03, 0x65, 0x72, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, - 0x65, 0x6e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x65, 0x6e, 0x67, 0x69, 0x6e, 0x65, 0x2e, 0x74, 0x72, - 0x61, 0x63, 0x65, 0x32, 0x2e, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x48, 0x01, 0x52, 0x03, 0x65, 0x72, - 0x72, 0x88, 0x01, 0x01, 0x12, 0x47, 0x0a, 0x0c, 0x74, 0x72, 0x61, 0x63, 0x65, 0x5f, 0x65, 0x76, - 0x65, 0x6e, 0x74, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x24, 0x2e, 0x65, 0x6e, 0x63, - 0x6f, 0x72, 0x65, 0x2e, 0x65, 0x6e, 0x67, 0x69, 0x6e, 0x65, 0x2e, 0x74, 0x72, 0x61, 0x63, 0x65, - 0x32, 0x2e, 0x48, 0x54, 0x54, 0x50, 0x54, 0x72, 0x61, 0x63, 0x65, 0x45, 0x76, 0x65, 0x6e, 0x74, - 0x52, 0x0b, 0x74, 0x72, 0x61, 0x63, 0x65, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x42, 0x0e, 0x0a, - 0x0c, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x5f, 0x63, 0x6f, 0x64, 0x65, 0x42, 0x06, 0x0a, - 0x04, 0x5f, 0x65, 0x72, 0x72, 0x22, 0x90, 0x09, 0x0a, 0x0e, 0x48, 0x54, 0x54, 0x50, 0x54, 0x72, - 0x61, 0x63, 0x65, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x6e, 0x61, 0x6e, 0x6f, - 0x74, 0x69, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x08, 0x6e, 0x61, 0x6e, 0x6f, - 0x74, 0x69, 0x6d, 0x65, 0x12, 0x3e, 0x0a, 0x08, 0x67, 0x65, 0x74, 0x5f, 0x63, 0x6f, 0x6e, 0x6e, - 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x21, 0x2e, 0x65, 0x6e, 0x63, 0x6f, 0x72, 0x65, 0x2e, - 0x65, 0x6e, 0x67, 0x69, 0x6e, 0x65, 0x2e, 0x74, 0x72, 0x61, 0x63, 0x65, 0x32, 0x2e, 0x48, 0x54, - 0x54, 0x50, 0x47, 0x65, 0x74, 0x43, 0x6f, 0x6e, 0x6e, 0x48, 0x00, 0x52, 0x07, 0x67, 0x65, 0x74, - 0x43, 0x6f, 0x6e, 0x6e, 0x12, 0x3e, 0x0a, 0x08, 0x67, 0x6f, 0x74, 0x5f, 0x63, 0x6f, 0x6e, 0x6e, - 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x21, 0x2e, 0x65, 0x6e, 0x63, 0x6f, 0x72, 0x65, 0x2e, + 0x25, 0x0a, 0x0e, 0x73, 0x74, 0x61, 0x72, 0x74, 0x5f, 0x6e, 0x61, 0x6e, 0x6f, 0x74, 0x69, 0x6d, + 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0d, 0x73, 0x74, 0x61, 0x72, 0x74, 0x4e, 0x61, + 0x6e, 0x6f, 0x74, 0x69, 0x6d, 0x65, 0x22, 0xc8, 0x01, 0x0a, 0x0b, 0x48, 0x54, 0x54, 0x50, 0x43, + 0x61, 0x6c, 0x6c, 0x45, 0x6e, 0x64, 0x12, 0x24, 0x0a, 0x0b, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, + 0x5f, 0x63, 0x6f, 0x64, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x48, 0x00, 0x52, 0x0a, 0x73, + 0x74, 0x61, 0x74, 0x75, 0x73, 0x43, 0x6f, 0x64, 0x65, 0x88, 0x01, 0x01, 0x12, 0x32, 0x0a, 0x03, + 0x65, 0x72, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x65, 0x6e, 0x63, 0x6f, + 0x72, 0x65, 0x2e, 0x65, 0x6e, 0x67, 0x69, 0x6e, 0x65, 0x2e, 0x74, 0x72, 0x61, 0x63, 0x65, 0x32, + 0x2e, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x48, 0x01, 0x52, 0x03, 0x65, 0x72, 0x72, 0x88, 0x01, 0x01, + 0x12, 0x47, 0x0a, 0x0c, 0x74, 0x72, 0x61, 0x63, 0x65, 0x5f, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x73, + 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x24, 0x2e, 0x65, 0x6e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x65, 0x6e, 0x67, 0x69, 0x6e, 0x65, 0x2e, 0x74, 0x72, 0x61, 0x63, 0x65, 0x32, 0x2e, 0x48, 0x54, - 0x54, 0x50, 0x47, 0x6f, 0x74, 0x43, 0x6f, 0x6e, 0x6e, 0x48, 0x00, 0x52, 0x07, 0x67, 0x6f, 0x74, - 0x43, 0x6f, 0x6e, 0x6e, 0x12, 0x67, 0x0a, 0x17, 0x67, 0x6f, 0x74, 0x5f, 0x66, 0x69, 0x72, 0x73, - 0x74, 0x5f, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x5f, 0x62, 0x79, 0x74, 0x65, 0x18, - 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2e, 0x2e, 0x65, 0x6e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x65, - 0x6e, 0x67, 0x69, 0x6e, 0x65, 0x2e, 0x74, 0x72, 0x61, 0x63, 0x65, 0x32, 0x2e, 0x48, 0x54, 0x54, - 0x50, 0x47, 0x6f, 0x74, 0x46, 0x69, 0x72, 0x73, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, - 0x65, 0x42, 0x79, 0x74, 0x65, 0x48, 0x00, 0x52, 0x14, 0x67, 0x6f, 0x74, 0x46, 0x69, 0x72, 0x73, - 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x42, 0x79, 0x74, 0x65, 0x12, 0x54, 0x0a, - 0x10, 0x67, 0x6f, 0x74, 0x5f, 0x31, 0x78, 0x78, 0x5f, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, - 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x28, 0x2e, 0x65, 0x6e, 0x63, 0x6f, 0x72, 0x65, + 0x54, 0x50, 0x54, 0x72, 0x61, 0x63, 0x65, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x52, 0x0b, 0x74, 0x72, + 0x61, 0x63, 0x65, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x42, 0x0e, 0x0a, 0x0c, 0x5f, 0x73, 0x74, + 0x61, 0x74, 0x75, 0x73, 0x5f, 0x63, 0x6f, 0x64, 0x65, 0x42, 0x06, 0x0a, 0x04, 0x5f, 0x65, 0x72, + 0x72, 0x22, 0x90, 0x09, 0x0a, 0x0e, 0x48, 0x54, 0x54, 0x50, 0x54, 0x72, 0x61, 0x63, 0x65, 0x45, + 0x76, 0x65, 0x6e, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x6e, 0x61, 0x6e, 0x6f, 0x74, 0x69, 0x6d, 0x65, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x08, 0x6e, 0x61, 0x6e, 0x6f, 0x74, 0x69, 0x6d, 0x65, + 0x12, 0x3e, 0x0a, 0x08, 0x67, 0x65, 0x74, 0x5f, 0x63, 0x6f, 0x6e, 0x6e, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x0b, 0x32, 0x21, 0x2e, 0x65, 0x6e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x65, 0x6e, 0x67, 0x69, + 0x6e, 0x65, 0x2e, 0x74, 0x72, 0x61, 0x63, 0x65, 0x32, 0x2e, 0x48, 0x54, 0x54, 0x50, 0x47, 0x65, + 0x74, 0x43, 0x6f, 0x6e, 0x6e, 0x48, 0x00, 0x52, 0x07, 0x67, 0x65, 0x74, 0x43, 0x6f, 0x6e, 0x6e, + 0x12, 0x3e, 0x0a, 0x08, 0x67, 0x6f, 0x74, 0x5f, 0x63, 0x6f, 0x6e, 0x6e, 0x18, 0x03, 0x20, 0x01, + 0x28, 0x0b, 0x32, 0x21, 0x2e, 0x65, 0x6e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x65, 0x6e, 0x67, 0x69, + 0x6e, 0x65, 0x2e, 0x74, 0x72, 0x61, 0x63, 0x65, 0x32, 0x2e, 0x48, 0x54, 0x54, 0x50, 0x47, 0x6f, + 0x74, 0x43, 0x6f, 0x6e, 0x6e, 0x48, 0x00, 0x52, 0x07, 0x67, 0x6f, 0x74, 0x43, 0x6f, 0x6e, 0x6e, + 0x12, 0x67, 0x0a, 0x17, 0x67, 0x6f, 0x74, 0x5f, 0x66, 0x69, 0x72, 0x73, 0x74, 0x5f, 0x72, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x5f, 0x62, 0x79, 0x74, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x2e, 0x2e, 0x65, 0x6e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x65, 0x6e, 0x67, 0x69, 0x6e, + 0x65, 0x2e, 0x74, 0x72, 0x61, 0x63, 0x65, 0x32, 0x2e, 0x48, 0x54, 0x54, 0x50, 0x47, 0x6f, 0x74, + 0x46, 0x69, 0x72, 0x73, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x42, 0x79, 0x74, + 0x65, 0x48, 0x00, 0x52, 0x14, 0x67, 0x6f, 0x74, 0x46, 0x69, 0x72, 0x73, 0x74, 0x52, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x42, 0x79, 0x74, 0x65, 0x12, 0x54, 0x0a, 0x10, 0x67, 0x6f, 0x74, + 0x5f, 0x31, 0x78, 0x78, 0x5f, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x18, 0x05, 0x20, + 0x01, 0x28, 0x0b, 0x32, 0x28, 0x2e, 0x65, 0x6e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x65, 0x6e, 0x67, + 0x69, 0x6e, 0x65, 0x2e, 0x74, 0x72, 0x61, 0x63, 0x65, 0x32, 0x2e, 0x48, 0x54, 0x54, 0x50, 0x47, + 0x6f, 0x74, 0x31, 0x78, 0x78, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x48, 0x00, 0x52, + 0x0e, 0x67, 0x6f, 0x74, 0x31, 0x78, 0x78, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, + 0x41, 0x0a, 0x09, 0x64, 0x6e, 0x73, 0x5f, 0x73, 0x74, 0x61, 0x72, 0x74, 0x18, 0x06, 0x20, 0x01, + 0x28, 0x0b, 0x32, 0x22, 0x2e, 0x65, 0x6e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x65, 0x6e, 0x67, 0x69, + 0x6e, 0x65, 0x2e, 0x74, 0x72, 0x61, 0x63, 0x65, 0x32, 0x2e, 0x48, 0x54, 0x54, 0x50, 0x44, 0x4e, + 0x53, 0x53, 0x74, 0x61, 0x72, 0x74, 0x48, 0x00, 0x52, 0x08, 0x64, 0x6e, 0x73, 0x53, 0x74, 0x61, + 0x72, 0x74, 0x12, 0x3e, 0x0a, 0x08, 0x64, 0x6e, 0x73, 0x5f, 0x64, 0x6f, 0x6e, 0x65, 0x18, 0x07, + 0x20, 0x01, 0x28, 0x0b, 0x32, 0x21, 0x2e, 0x65, 0x6e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x65, 0x6e, + 0x67, 0x69, 0x6e, 0x65, 0x2e, 0x74, 0x72, 0x61, 0x63, 0x65, 0x32, 0x2e, 0x48, 0x54, 0x54, 0x50, + 0x44, 0x4e, 0x53, 0x44, 0x6f, 0x6e, 0x65, 0x48, 0x00, 0x52, 0x07, 0x64, 0x6e, 0x73, 0x44, 0x6f, + 0x6e, 0x65, 0x12, 0x4d, 0x0a, 0x0d, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x5f, 0x73, 0x74, + 0x61, 0x72, 0x74, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x26, 0x2e, 0x65, 0x6e, 0x63, 0x6f, + 0x72, 0x65, 0x2e, 0x65, 0x6e, 0x67, 0x69, 0x6e, 0x65, 0x2e, 0x74, 0x72, 0x61, 0x63, 0x65, 0x32, + 0x2e, 0x48, 0x54, 0x54, 0x50, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x53, 0x74, 0x61, 0x72, + 0x74, 0x48, 0x00, 0x52, 0x0c, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x53, 0x74, 0x61, 0x72, + 0x74, 0x12, 0x4a, 0x0a, 0x0c, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x5f, 0x64, 0x6f, 0x6e, + 0x65, 0x18, 0x09, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x25, 0x2e, 0x65, 0x6e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x65, 0x6e, 0x67, 0x69, 0x6e, 0x65, 0x2e, 0x74, 0x72, 0x61, 0x63, 0x65, 0x32, 0x2e, 0x48, - 0x54, 0x54, 0x50, 0x47, 0x6f, 0x74, 0x31, 0x78, 0x78, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, - 0x65, 0x48, 0x00, 0x52, 0x0e, 0x67, 0x6f, 0x74, 0x31, 0x78, 0x78, 0x52, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x12, 0x41, 0x0a, 0x09, 0x64, 0x6e, 0x73, 0x5f, 0x73, 0x74, 0x61, 0x72, 0x74, - 0x18, 0x06, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x22, 0x2e, 0x65, 0x6e, 0x63, 0x6f, 0x72, 0x65, 0x2e, - 0x65, 0x6e, 0x67, 0x69, 0x6e, 0x65, 0x2e, 0x74, 0x72, 0x61, 0x63, 0x65, 0x32, 0x2e, 0x48, 0x54, - 0x54, 0x50, 0x44, 0x4e, 0x53, 0x53, 0x74, 0x61, 0x72, 0x74, 0x48, 0x00, 0x52, 0x08, 0x64, 0x6e, - 0x73, 0x53, 0x74, 0x61, 0x72, 0x74, 0x12, 0x3e, 0x0a, 0x08, 0x64, 0x6e, 0x73, 0x5f, 0x64, 0x6f, - 0x6e, 0x65, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x21, 0x2e, 0x65, 0x6e, 0x63, 0x6f, 0x72, + 0x54, 0x54, 0x50, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x44, 0x6f, 0x6e, 0x65, 0x48, 0x00, + 0x52, 0x0b, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x44, 0x6f, 0x6e, 0x65, 0x12, 0x5d, 0x0a, + 0x13, 0x74, 0x6c, 0x73, 0x5f, 0x68, 0x61, 0x6e, 0x64, 0x73, 0x68, 0x61, 0x6b, 0x65, 0x5f, 0x73, + 0x74, 0x61, 0x72, 0x74, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2b, 0x2e, 0x65, 0x6e, 0x63, + 0x6f, 0x72, 0x65, 0x2e, 0x65, 0x6e, 0x67, 0x69, 0x6e, 0x65, 0x2e, 0x74, 0x72, 0x61, 0x63, 0x65, + 0x32, 0x2e, 0x48, 0x54, 0x54, 0x50, 0x54, 0x4c, 0x53, 0x48, 0x61, 0x6e, 0x64, 0x73, 0x68, 0x61, + 0x6b, 0x65, 0x53, 0x74, 0x61, 0x72, 0x74, 0x48, 0x00, 0x52, 0x11, 0x74, 0x6c, 0x73, 0x48, 0x61, + 0x6e, 0x64, 0x73, 0x68, 0x61, 0x6b, 0x65, 0x53, 0x74, 0x61, 0x72, 0x74, 0x12, 0x5a, 0x0a, 0x12, + 0x74, 0x6c, 0x73, 0x5f, 0x68, 0x61, 0x6e, 0x64, 0x73, 0x68, 0x61, 0x6b, 0x65, 0x5f, 0x64, 0x6f, + 0x6e, 0x65, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2a, 0x2e, 0x65, 0x6e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x65, 0x6e, 0x67, 0x69, 0x6e, 0x65, 0x2e, 0x74, 0x72, 0x61, 0x63, 0x65, 0x32, 0x2e, - 0x48, 0x54, 0x54, 0x50, 0x44, 0x4e, 0x53, 0x44, 0x6f, 0x6e, 0x65, 0x48, 0x00, 0x52, 0x07, 0x64, - 0x6e, 0x73, 0x44, 0x6f, 0x6e, 0x65, 0x12, 0x4d, 0x0a, 0x0d, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, - 0x74, 0x5f, 0x73, 0x74, 0x61, 0x72, 0x74, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x26, 0x2e, - 0x65, 0x6e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x65, 0x6e, 0x67, 0x69, 0x6e, 0x65, 0x2e, 0x74, 0x72, - 0x61, 0x63, 0x65, 0x32, 0x2e, 0x48, 0x54, 0x54, 0x50, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, - 0x53, 0x74, 0x61, 0x72, 0x74, 0x48, 0x00, 0x52, 0x0c, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, - 0x53, 0x74, 0x61, 0x72, 0x74, 0x12, 0x4a, 0x0a, 0x0c, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, - 0x5f, 0x64, 0x6f, 0x6e, 0x65, 0x18, 0x09, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x25, 0x2e, 0x65, 0x6e, - 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x65, 0x6e, 0x67, 0x69, 0x6e, 0x65, 0x2e, 0x74, 0x72, 0x61, 0x63, - 0x65, 0x32, 0x2e, 0x48, 0x54, 0x54, 0x50, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x44, 0x6f, - 0x6e, 0x65, 0x48, 0x00, 0x52, 0x0b, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x44, 0x6f, 0x6e, - 0x65, 0x12, 0x5d, 0x0a, 0x13, 0x74, 0x6c, 0x73, 0x5f, 0x68, 0x61, 0x6e, 0x64, 0x73, 0x68, 0x61, - 0x6b, 0x65, 0x5f, 0x73, 0x74, 0x61, 0x72, 0x74, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2b, + 0x48, 0x54, 0x54, 0x50, 0x54, 0x4c, 0x53, 0x48, 0x61, 0x6e, 0x64, 0x73, 0x68, 0x61, 0x6b, 0x65, + 0x44, 0x6f, 0x6e, 0x65, 0x48, 0x00, 0x52, 0x10, 0x74, 0x6c, 0x73, 0x48, 0x61, 0x6e, 0x64, 0x73, + 0x68, 0x61, 0x6b, 0x65, 0x44, 0x6f, 0x6e, 0x65, 0x12, 0x4d, 0x0a, 0x0d, 0x77, 0x72, 0x6f, 0x74, + 0x65, 0x5f, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x73, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x26, 0x2e, 0x65, 0x6e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x65, 0x6e, 0x67, 0x69, 0x6e, 0x65, 0x2e, + 0x74, 0x72, 0x61, 0x63, 0x65, 0x32, 0x2e, 0x48, 0x54, 0x54, 0x50, 0x57, 0x72, 0x6f, 0x74, 0x65, + 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x73, 0x48, 0x00, 0x52, 0x0c, 0x77, 0x72, 0x6f, 0x74, 0x65, + 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x73, 0x12, 0x4d, 0x0a, 0x0d, 0x77, 0x72, 0x6f, 0x74, 0x65, + 0x5f, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x18, 0x0d, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x26, 0x2e, 0x65, 0x6e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x65, 0x6e, 0x67, 0x69, 0x6e, 0x65, 0x2e, 0x74, - 0x72, 0x61, 0x63, 0x65, 0x32, 0x2e, 0x48, 0x54, 0x54, 0x50, 0x54, 0x4c, 0x53, 0x48, 0x61, 0x6e, - 0x64, 0x73, 0x68, 0x61, 0x6b, 0x65, 0x53, 0x74, 0x61, 0x72, 0x74, 0x48, 0x00, 0x52, 0x11, 0x74, - 0x6c, 0x73, 0x48, 0x61, 0x6e, 0x64, 0x73, 0x68, 0x61, 0x6b, 0x65, 0x53, 0x74, 0x61, 0x72, 0x74, - 0x12, 0x5a, 0x0a, 0x12, 0x74, 0x6c, 0x73, 0x5f, 0x68, 0x61, 0x6e, 0x64, 0x73, 0x68, 0x61, 0x6b, - 0x65, 0x5f, 0x64, 0x6f, 0x6e, 0x65, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2a, 0x2e, 0x65, - 0x6e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x65, 0x6e, 0x67, 0x69, 0x6e, 0x65, 0x2e, 0x74, 0x72, 0x61, - 0x63, 0x65, 0x32, 0x2e, 0x48, 0x54, 0x54, 0x50, 0x54, 0x4c, 0x53, 0x48, 0x61, 0x6e, 0x64, 0x73, - 0x68, 0x61, 0x6b, 0x65, 0x44, 0x6f, 0x6e, 0x65, 0x48, 0x00, 0x52, 0x10, 0x74, 0x6c, 0x73, 0x48, - 0x61, 0x6e, 0x64, 0x73, 0x68, 0x61, 0x6b, 0x65, 0x44, 0x6f, 0x6e, 0x65, 0x12, 0x4d, 0x0a, 0x0d, - 0x77, 0x72, 0x6f, 0x74, 0x65, 0x5f, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x73, 0x18, 0x0c, 0x20, - 0x01, 0x28, 0x0b, 0x32, 0x26, 0x2e, 0x65, 0x6e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x65, 0x6e, 0x67, - 0x69, 0x6e, 0x65, 0x2e, 0x74, 0x72, 0x61, 0x63, 0x65, 0x32, 0x2e, 0x48, 0x54, 0x54, 0x50, 0x57, - 0x72, 0x6f, 0x74, 0x65, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x73, 0x48, 0x00, 0x52, 0x0c, 0x77, - 0x72, 0x6f, 0x74, 0x65, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x73, 0x12, 0x4d, 0x0a, 0x0d, 0x77, - 0x72, 0x6f, 0x74, 0x65, 0x5f, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x18, 0x0d, 0x20, 0x01, - 0x28, 0x0b, 0x32, 0x26, 0x2e, 0x65, 0x6e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x65, 0x6e, 0x67, 0x69, - 0x6e, 0x65, 0x2e, 0x74, 0x72, 0x61, 0x63, 0x65, 0x32, 0x2e, 0x48, 0x54, 0x54, 0x50, 0x57, 0x72, - 0x6f, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x48, 0x00, 0x52, 0x0c, 0x77, 0x72, - 0x6f, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x57, 0x0a, 0x11, 0x77, 0x61, - 0x69, 0x74, 0x5f, 0x31, 0x30, 0x30, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x69, 0x6e, 0x75, 0x65, 0x18, - 0x0e, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x29, 0x2e, 0x65, 0x6e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x65, - 0x6e, 0x67, 0x69, 0x6e, 0x65, 0x2e, 0x74, 0x72, 0x61, 0x63, 0x65, 0x32, 0x2e, 0x48, 0x54, 0x54, - 0x50, 0x57, 0x61, 0x69, 0x74, 0x31, 0x30, 0x30, 0x43, 0x6f, 0x6e, 0x74, 0x69, 0x6e, 0x75, 0x65, - 0x48, 0x00, 0x52, 0x0f, 0x77, 0x61, 0x69, 0x74, 0x31, 0x30, 0x30, 0x43, 0x6f, 0x6e, 0x74, 0x69, - 0x6e, 0x75, 0x65, 0x12, 0x4b, 0x0a, 0x0b, 0x63, 0x6c, 0x6f, 0x73, 0x65, 0x64, 0x5f, 0x62, 0x6f, - 0x64, 0x79, 0x18, 0x0f, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x28, 0x2e, 0x65, 0x6e, 0x63, 0x6f, 0x72, - 0x65, 0x2e, 0x65, 0x6e, 0x67, 0x69, 0x6e, 0x65, 0x2e, 0x74, 0x72, 0x61, 0x63, 0x65, 0x32, 0x2e, - 0x48, 0x54, 0x54, 0x50, 0x43, 0x6c, 0x6f, 0x73, 0x65, 0x64, 0x42, 0x6f, 0x64, 0x79, 0x44, 0x61, - 0x74, 0x61, 0x48, 0x00, 0x52, 0x0a, 0x63, 0x6c, 0x6f, 0x73, 0x65, 0x64, 0x42, 0x6f, 0x64, 0x79, - 0x42, 0x06, 0x0a, 0x04, 0x64, 0x61, 0x74, 0x61, 0x22, 0x2a, 0x0a, 0x0b, 0x48, 0x54, 0x54, 0x50, - 0x47, 0x65, 0x74, 0x43, 0x6f, 0x6e, 0x6e, 0x12, 0x1b, 0x0a, 0x09, 0x68, 0x6f, 0x73, 0x74, 0x5f, - 0x70, 0x6f, 0x72, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x68, 0x6f, 0x73, 0x74, - 0x50, 0x6f, 0x72, 0x74, 0x22, 0x6a, 0x0a, 0x0b, 0x48, 0x54, 0x54, 0x50, 0x47, 0x6f, 0x74, 0x43, - 0x6f, 0x6e, 0x6e, 0x12, 0x16, 0x0a, 0x06, 0x72, 0x65, 0x75, 0x73, 0x65, 0x64, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x08, 0x52, 0x06, 0x72, 0x65, 0x75, 0x73, 0x65, 0x64, 0x12, 0x19, 0x0a, 0x08, 0x77, - 0x61, 0x73, 0x5f, 0x69, 0x64, 0x6c, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x77, - 0x61, 0x73, 0x49, 0x64, 0x6c, 0x65, 0x12, 0x28, 0x0a, 0x10, 0x69, 0x64, 0x6c, 0x65, 0x5f, 0x64, - 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x6e, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x03, - 0x52, 0x0e, 0x69, 0x64, 0x6c, 0x65, 0x44, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x4e, 0x73, - 0x22, 0x1a, 0x0a, 0x18, 0x48, 0x54, 0x54, 0x50, 0x47, 0x6f, 0x74, 0x46, 0x69, 0x72, 0x73, 0x74, - 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x42, 0x79, 0x74, 0x65, 0x22, 0x28, 0x0a, 0x12, - 0x48, 0x54, 0x54, 0x50, 0x47, 0x6f, 0x74, 0x31, 0x78, 0x78, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, - 0x73, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x63, 0x6f, 0x64, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, - 0x52, 0x04, 0x63, 0x6f, 0x64, 0x65, 0x22, 0x22, 0x0a, 0x0c, 0x48, 0x54, 0x54, 0x50, 0x44, 0x4e, - 0x53, 0x53, 0x74, 0x61, 0x72, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x68, 0x6f, 0x73, 0x74, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x68, 0x6f, 0x73, 0x74, 0x22, 0x61, 0x0a, 0x0b, 0x48, 0x54, - 0x54, 0x50, 0x44, 0x4e, 0x53, 0x44, 0x6f, 0x6e, 0x65, 0x12, 0x15, 0x0a, 0x03, 0x65, 0x72, 0x72, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x48, 0x00, 0x52, 0x03, 0x65, 0x72, 0x72, 0x88, 0x01, 0x01, - 0x12, 0x33, 0x0a, 0x05, 0x61, 0x64, 0x64, 0x72, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, - 0x1d, 0x2e, 0x65, 0x6e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x65, 0x6e, 0x67, 0x69, 0x6e, 0x65, 0x2e, - 0x74, 0x72, 0x61, 0x63, 0x65, 0x32, 0x2e, 0x44, 0x4e, 0x53, 0x41, 0x64, 0x64, 0x72, 0x52, 0x05, - 0x61, 0x64, 0x64, 0x72, 0x73, 0x42, 0x06, 0x0a, 0x04, 0x5f, 0x65, 0x72, 0x72, 0x22, 0x19, 0x0a, - 0x07, 0x44, 0x4e, 0x53, 0x41, 0x64, 0x64, 0x72, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x70, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x0c, 0x52, 0x02, 0x69, 0x70, 0x22, 0x40, 0x0a, 0x10, 0x48, 0x54, 0x54, 0x50, - 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x53, 0x74, 0x61, 0x72, 0x74, 0x12, 0x18, 0x0a, 0x07, - 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x6e, - 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x12, 0x12, 0x0a, 0x04, 0x61, 0x64, 0x64, 0x72, 0x18, 0x02, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x61, 0x64, 0x64, 0x72, 0x22, 0x51, 0x0a, 0x0f, 0x48, 0x54, - 0x54, 0x50, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x44, 0x6f, 0x6e, 0x65, 0x12, 0x18, 0x0a, - 0x07, 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, - 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x12, 0x12, 0x0a, 0x04, 0x61, 0x64, 0x64, 0x72, 0x18, - 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x61, 0x64, 0x64, 0x72, 0x12, 0x10, 0x0a, 0x03, 0x65, - 0x72, 0x72, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x03, 0x65, 0x72, 0x72, 0x22, 0x17, 0x0a, - 0x15, 0x48, 0x54, 0x54, 0x50, 0x54, 0x4c, 0x53, 0x48, 0x61, 0x6e, 0x64, 0x73, 0x68, 0x61, 0x6b, - 0x65, 0x53, 0x74, 0x61, 0x72, 0x74, 0x22, 0xcb, 0x01, 0x0a, 0x14, 0x48, 0x54, 0x54, 0x50, 0x54, - 0x4c, 0x53, 0x48, 0x61, 0x6e, 0x64, 0x73, 0x68, 0x61, 0x6b, 0x65, 0x44, 0x6f, 0x6e, 0x65, 0x12, - 0x15, 0x0a, 0x03, 0x65, 0x72, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x48, 0x00, 0x52, 0x03, - 0x65, 0x72, 0x72, 0x88, 0x01, 0x01, 0x12, 0x1f, 0x0a, 0x0b, 0x74, 0x6c, 0x73, 0x5f, 0x76, 0x65, - 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0a, 0x74, 0x6c, 0x73, - 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x21, 0x0a, 0x0c, 0x63, 0x69, 0x70, 0x68, 0x65, - 0x72, 0x5f, 0x73, 0x75, 0x69, 0x74, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0b, 0x63, - 0x69, 0x70, 0x68, 0x65, 0x72, 0x53, 0x75, 0x69, 0x74, 0x65, 0x12, 0x1f, 0x0a, 0x0b, 0x73, 0x65, - 0x72, 0x76, 0x65, 0x72, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x0a, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x2f, 0x0a, 0x13, 0x6e, - 0x65, 0x67, 0x6f, 0x74, 0x69, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, - 0x6f, 0x6c, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x12, 0x6e, 0x65, 0x67, 0x6f, 0x74, 0x69, - 0x61, 0x74, 0x65, 0x64, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x42, 0x06, 0x0a, 0x04, - 0x5f, 0x65, 0x72, 0x72, 0x22, 0x12, 0x0a, 0x10, 0x48, 0x54, 0x54, 0x50, 0x57, 0x72, 0x6f, 0x74, - 0x65, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x73, 0x22, 0x31, 0x0a, 0x10, 0x48, 0x54, 0x54, 0x50, - 0x57, 0x72, 0x6f, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x15, 0x0a, 0x03, - 0x65, 0x72, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x48, 0x00, 0x52, 0x03, 0x65, 0x72, 0x72, - 0x88, 0x01, 0x01, 0x42, 0x06, 0x0a, 0x04, 0x5f, 0x65, 0x72, 0x72, 0x22, 0x15, 0x0a, 0x13, 0x48, - 0x54, 0x54, 0x50, 0x57, 0x61, 0x69, 0x74, 0x31, 0x30, 0x30, 0x43, 0x6f, 0x6e, 0x74, 0x69, 0x6e, - 0x75, 0x65, 0x22, 0x33, 0x0a, 0x12, 0x48, 0x54, 0x54, 0x50, 0x43, 0x6c, 0x6f, 0x73, 0x65, 0x64, - 0x42, 0x6f, 0x64, 0x79, 0x44, 0x61, 0x74, 0x61, 0x12, 0x15, 0x0a, 0x03, 0x65, 0x72, 0x72, 0x18, + 0x72, 0x61, 0x63, 0x65, 0x32, 0x2e, 0x48, 0x54, 0x54, 0x50, 0x57, 0x72, 0x6f, 0x74, 0x65, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x48, 0x00, 0x52, 0x0c, 0x77, 0x72, 0x6f, 0x74, 0x65, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x57, 0x0a, 0x11, 0x77, 0x61, 0x69, 0x74, 0x5f, 0x31, + 0x30, 0x30, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x69, 0x6e, 0x75, 0x65, 0x18, 0x0e, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x29, 0x2e, 0x65, 0x6e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x65, 0x6e, 0x67, 0x69, 0x6e, + 0x65, 0x2e, 0x74, 0x72, 0x61, 0x63, 0x65, 0x32, 0x2e, 0x48, 0x54, 0x54, 0x50, 0x57, 0x61, 0x69, + 0x74, 0x31, 0x30, 0x30, 0x43, 0x6f, 0x6e, 0x74, 0x69, 0x6e, 0x75, 0x65, 0x48, 0x00, 0x52, 0x0f, + 0x77, 0x61, 0x69, 0x74, 0x31, 0x30, 0x30, 0x43, 0x6f, 0x6e, 0x74, 0x69, 0x6e, 0x75, 0x65, 0x12, + 0x4b, 0x0a, 0x0b, 0x63, 0x6c, 0x6f, 0x73, 0x65, 0x64, 0x5f, 0x62, 0x6f, 0x64, 0x79, 0x18, 0x0f, + 0x20, 0x01, 0x28, 0x0b, 0x32, 0x28, 0x2e, 0x65, 0x6e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x65, 0x6e, + 0x67, 0x69, 0x6e, 0x65, 0x2e, 0x74, 0x72, 0x61, 0x63, 0x65, 0x32, 0x2e, 0x48, 0x54, 0x54, 0x50, + 0x43, 0x6c, 0x6f, 0x73, 0x65, 0x64, 0x42, 0x6f, 0x64, 0x79, 0x44, 0x61, 0x74, 0x61, 0x48, 0x00, + 0x52, 0x0a, 0x63, 0x6c, 0x6f, 0x73, 0x65, 0x64, 0x42, 0x6f, 0x64, 0x79, 0x42, 0x06, 0x0a, 0x04, + 0x64, 0x61, 0x74, 0x61, 0x22, 0x2a, 0x0a, 0x0b, 0x48, 0x54, 0x54, 0x50, 0x47, 0x65, 0x74, 0x43, + 0x6f, 0x6e, 0x6e, 0x12, 0x1b, 0x0a, 0x09, 0x68, 0x6f, 0x73, 0x74, 0x5f, 0x70, 0x6f, 0x72, 0x74, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x68, 0x6f, 0x73, 0x74, 0x50, 0x6f, 0x72, 0x74, + 0x22, 0x6a, 0x0a, 0x0b, 0x48, 0x54, 0x54, 0x50, 0x47, 0x6f, 0x74, 0x43, 0x6f, 0x6e, 0x6e, 0x12, + 0x16, 0x0a, 0x06, 0x72, 0x65, 0x75, 0x73, 0x65, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, + 0x06, 0x72, 0x65, 0x75, 0x73, 0x65, 0x64, 0x12, 0x19, 0x0a, 0x08, 0x77, 0x61, 0x73, 0x5f, 0x69, + 0x64, 0x6c, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x77, 0x61, 0x73, 0x49, 0x64, + 0x6c, 0x65, 0x12, 0x28, 0x0a, 0x10, 0x69, 0x64, 0x6c, 0x65, 0x5f, 0x64, 0x75, 0x72, 0x61, 0x74, + 0x69, 0x6f, 0x6e, 0x5f, 0x6e, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0e, 0x69, 0x64, + 0x6c, 0x65, 0x44, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x4e, 0x73, 0x22, 0x1a, 0x0a, 0x18, + 0x48, 0x54, 0x54, 0x50, 0x47, 0x6f, 0x74, 0x46, 0x69, 0x72, 0x73, 0x74, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x42, 0x79, 0x74, 0x65, 0x22, 0x28, 0x0a, 0x12, 0x48, 0x54, 0x54, 0x50, + 0x47, 0x6f, 0x74, 0x31, 0x78, 0x78, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x12, + 0x0a, 0x04, 0x63, 0x6f, 0x64, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x04, 0x63, 0x6f, + 0x64, 0x65, 0x22, 0x22, 0x0a, 0x0c, 0x48, 0x54, 0x54, 0x50, 0x44, 0x4e, 0x53, 0x53, 0x74, 0x61, + 0x72, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x68, 0x6f, 0x73, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x04, 0x68, 0x6f, 0x73, 0x74, 0x22, 0x61, 0x0a, 0x0b, 0x48, 0x54, 0x54, 0x50, 0x44, 0x4e, + 0x53, 0x44, 0x6f, 0x6e, 0x65, 0x12, 0x15, 0x0a, 0x03, 0x65, 0x72, 0x72, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x0c, 0x48, 0x00, 0x52, 0x03, 0x65, 0x72, 0x72, 0x88, 0x01, 0x01, 0x12, 0x33, 0x0a, 0x05, + 0x61, 0x64, 0x64, 0x72, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x65, 0x6e, + 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x65, 0x6e, 0x67, 0x69, 0x6e, 0x65, 0x2e, 0x74, 0x72, 0x61, 0x63, + 0x65, 0x32, 0x2e, 0x44, 0x4e, 0x53, 0x41, 0x64, 0x64, 0x72, 0x52, 0x05, 0x61, 0x64, 0x64, 0x72, + 0x73, 0x42, 0x06, 0x0a, 0x04, 0x5f, 0x65, 0x72, 0x72, 0x22, 0x19, 0x0a, 0x07, 0x44, 0x4e, 0x53, + 0x41, 0x64, 0x64, 0x72, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x70, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, + 0x52, 0x02, 0x69, 0x70, 0x22, 0x40, 0x0a, 0x10, 0x48, 0x54, 0x54, 0x50, 0x43, 0x6f, 0x6e, 0x6e, + 0x65, 0x63, 0x74, 0x53, 0x74, 0x61, 0x72, 0x74, 0x12, 0x18, 0x0a, 0x07, 0x6e, 0x65, 0x74, 0x77, + 0x6f, 0x72, 0x6b, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x6e, 0x65, 0x74, 0x77, 0x6f, + 0x72, 0x6b, 0x12, 0x12, 0x0a, 0x04, 0x61, 0x64, 0x64, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x04, 0x61, 0x64, 0x64, 0x72, 0x22, 0x51, 0x0a, 0x0f, 0x48, 0x54, 0x54, 0x50, 0x43, 0x6f, + 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x44, 0x6f, 0x6e, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x6e, 0x65, 0x74, + 0x77, 0x6f, 0x72, 0x6b, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x6e, 0x65, 0x74, 0x77, + 0x6f, 0x72, 0x6b, 0x12, 0x12, 0x0a, 0x04, 0x61, 0x64, 0x64, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x04, 0x61, 0x64, 0x64, 0x72, 0x12, 0x10, 0x0a, 0x03, 0x65, 0x72, 0x72, 0x18, 0x03, + 0x20, 0x01, 0x28, 0x0c, 0x52, 0x03, 0x65, 0x72, 0x72, 0x22, 0x17, 0x0a, 0x15, 0x48, 0x54, 0x54, + 0x50, 0x54, 0x4c, 0x53, 0x48, 0x61, 0x6e, 0x64, 0x73, 0x68, 0x61, 0x6b, 0x65, 0x53, 0x74, 0x61, + 0x72, 0x74, 0x22, 0xcb, 0x01, 0x0a, 0x14, 0x48, 0x54, 0x54, 0x50, 0x54, 0x4c, 0x53, 0x48, 0x61, + 0x6e, 0x64, 0x73, 0x68, 0x61, 0x6b, 0x65, 0x44, 0x6f, 0x6e, 0x65, 0x12, 0x15, 0x0a, 0x03, 0x65, + 0x72, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x48, 0x00, 0x52, 0x03, 0x65, 0x72, 0x72, 0x88, + 0x01, 0x01, 0x12, 0x1f, 0x0a, 0x0b, 0x74, 0x6c, 0x73, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, + 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0a, 0x74, 0x6c, 0x73, 0x56, 0x65, 0x72, 0x73, + 0x69, 0x6f, 0x6e, 0x12, 0x21, 0x0a, 0x0c, 0x63, 0x69, 0x70, 0x68, 0x65, 0x72, 0x5f, 0x73, 0x75, + 0x69, 0x74, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0b, 0x63, 0x69, 0x70, 0x68, 0x65, + 0x72, 0x53, 0x75, 0x69, 0x74, 0x65, 0x12, 0x1f, 0x0a, 0x0b, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, + 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x73, 0x65, 0x72, + 0x76, 0x65, 0x72, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x2f, 0x0a, 0x13, 0x6e, 0x65, 0x67, 0x6f, 0x74, + 0x69, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x18, 0x05, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x12, 0x6e, 0x65, 0x67, 0x6f, 0x74, 0x69, 0x61, 0x74, 0x65, 0x64, + 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x42, 0x06, 0x0a, 0x04, 0x5f, 0x65, 0x72, 0x72, + 0x22, 0x12, 0x0a, 0x10, 0x48, 0x54, 0x54, 0x50, 0x57, 0x72, 0x6f, 0x74, 0x65, 0x48, 0x65, 0x61, + 0x64, 0x65, 0x72, 0x73, 0x22, 0x31, 0x0a, 0x10, 0x48, 0x54, 0x54, 0x50, 0x57, 0x72, 0x6f, 0x74, + 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x15, 0x0a, 0x03, 0x65, 0x72, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x48, 0x00, 0x52, 0x03, 0x65, 0x72, 0x72, 0x88, 0x01, 0x01, 0x42, - 0x06, 0x0a, 0x04, 0x5f, 0x65, 0x72, 0x72, 0x22, 0x8a, 0x02, 0x0a, 0x0a, 0x4c, 0x6f, 0x67, 0x4d, - 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x3c, 0x0a, 0x05, 0x6c, 0x65, 0x76, 0x65, 0x6c, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x26, 0x2e, 0x65, 0x6e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x65, - 0x6e, 0x67, 0x69, 0x6e, 0x65, 0x2e, 0x74, 0x72, 0x61, 0x63, 0x65, 0x32, 0x2e, 0x4c, 0x6f, 0x67, - 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x2e, 0x4c, 0x65, 0x76, 0x65, 0x6c, 0x52, 0x05, 0x6c, - 0x65, 0x76, 0x65, 0x6c, 0x12, 0x10, 0x0a, 0x03, 0x6d, 0x73, 0x67, 0x18, 0x02, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x03, 0x6d, 0x73, 0x67, 0x12, 0x36, 0x0a, 0x06, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x73, - 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1e, 0x2e, 0x65, 0x6e, 0x63, 0x6f, 0x72, 0x65, 0x2e, - 0x65, 0x6e, 0x67, 0x69, 0x6e, 0x65, 0x2e, 0x74, 0x72, 0x61, 0x63, 0x65, 0x32, 0x2e, 0x4c, 0x6f, - 0x67, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x52, 0x06, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x73, 0x12, 0x36, - 0x0a, 0x05, 0x73, 0x74, 0x61, 0x63, 0x6b, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x20, 0x2e, - 0x65, 0x6e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x65, 0x6e, 0x67, 0x69, 0x6e, 0x65, 0x2e, 0x74, 0x72, - 0x61, 0x63, 0x65, 0x32, 0x2e, 0x53, 0x74, 0x61, 0x63, 0x6b, 0x54, 0x72, 0x61, 0x63, 0x65, 0x52, - 0x05, 0x73, 0x74, 0x61, 0x63, 0x6b, 0x22, 0x3c, 0x0a, 0x05, 0x4c, 0x65, 0x76, 0x65, 0x6c, 0x12, - 0x09, 0x0a, 0x05, 0x44, 0x45, 0x42, 0x55, 0x47, 0x10, 0x00, 0x12, 0x08, 0x0a, 0x04, 0x49, 0x4e, - 0x46, 0x4f, 0x10, 0x01, 0x12, 0x09, 0x0a, 0x05, 0x45, 0x52, 0x52, 0x4f, 0x52, 0x10, 0x02, 0x12, - 0x08, 0x0a, 0x04, 0x57, 0x41, 0x52, 0x4e, 0x10, 0x03, 0x12, 0x09, 0x0a, 0x05, 0x54, 0x52, 0x41, - 0x43, 0x45, 0x10, 0x04, 0x22, 0xd8, 0x02, 0x0a, 0x08, 0x4c, 0x6f, 0x67, 0x46, 0x69, 0x65, 0x6c, - 0x64, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, - 0x6b, 0x65, 0x79, 0x12, 0x33, 0x0a, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x18, 0x02, 0x20, 0x01, - 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x65, 0x6e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x65, 0x6e, 0x67, 0x69, - 0x6e, 0x65, 0x2e, 0x74, 0x72, 0x61, 0x63, 0x65, 0x32, 0x2e, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x48, - 0x00, 0x52, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x12, 0x12, 0x0a, 0x03, 0x73, 0x74, 0x72, 0x18, - 0x03, 0x20, 0x01, 0x28, 0x09, 0x48, 0x00, 0x52, 0x03, 0x73, 0x74, 0x72, 0x12, 0x14, 0x0a, 0x04, - 0x62, 0x6f, 0x6f, 0x6c, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x48, 0x00, 0x52, 0x04, 0x62, 0x6f, - 0x6f, 0x6c, 0x12, 0x30, 0x0a, 0x04, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x05, 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, 0x48, 0x00, 0x52, 0x04, - 0x74, 0x69, 0x6d, 0x65, 0x12, 0x12, 0x0a, 0x03, 0x64, 0x75, 0x72, 0x18, 0x06, 0x20, 0x01, 0x28, - 0x03, 0x48, 0x00, 0x52, 0x03, 0x64, 0x75, 0x72, 0x12, 0x14, 0x0a, 0x04, 0x75, 0x75, 0x69, 0x64, - 0x18, 0x07, 0x20, 0x01, 0x28, 0x0c, 0x48, 0x00, 0x52, 0x04, 0x75, 0x75, 0x69, 0x64, 0x12, 0x14, - 0x0a, 0x04, 0x6a, 0x73, 0x6f, 0x6e, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0c, 0x48, 0x00, 0x52, 0x04, - 0x6a, 0x73, 0x6f, 0x6e, 0x12, 0x12, 0x0a, 0x03, 0x69, 0x6e, 0x74, 0x18, 0x09, 0x20, 0x01, 0x28, - 0x03, 0x48, 0x00, 0x52, 0x03, 0x69, 0x6e, 0x74, 0x12, 0x14, 0x0a, 0x04, 0x75, 0x69, 0x6e, 0x74, - 0x18, 0x0a, 0x20, 0x01, 0x28, 0x04, 0x48, 0x00, 0x52, 0x04, 0x75, 0x69, 0x6e, 0x74, 0x12, 0x1a, - 0x0a, 0x07, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x33, 0x32, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x02, 0x48, - 0x00, 0x52, 0x07, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x33, 0x32, 0x12, 0x1a, 0x0a, 0x07, 0x66, 0x6c, - 0x6f, 0x61, 0x74, 0x36, 0x34, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x01, 0x48, 0x00, 0x52, 0x07, 0x66, - 0x6c, 0x6f, 0x61, 0x74, 0x36, 0x34, 0x42, 0x07, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x22, - 0x58, 0x0a, 0x0a, 0x53, 0x74, 0x61, 0x63, 0x6b, 0x54, 0x72, 0x61, 0x63, 0x65, 0x12, 0x10, 0x0a, - 0x03, 0x70, 0x63, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x03, 0x52, 0x03, 0x70, 0x63, 0x73, 0x12, - 0x38, 0x0a, 0x06, 0x66, 0x72, 0x61, 0x6d, 0x65, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, - 0x20, 0x2e, 0x65, 0x6e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x65, 0x6e, 0x67, 0x69, 0x6e, 0x65, 0x2e, - 0x74, 0x72, 0x61, 0x63, 0x65, 0x32, 0x2e, 0x53, 0x74, 0x61, 0x63, 0x6b, 0x46, 0x72, 0x61, 0x6d, - 0x65, 0x52, 0x06, 0x66, 0x72, 0x61, 0x6d, 0x65, 0x73, 0x22, 0x50, 0x0a, 0x0a, 0x53, 0x74, 0x61, - 0x63, 0x6b, 0x46, 0x72, 0x61, 0x6d, 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x66, 0x69, 0x6c, 0x65, 0x6e, - 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x66, 0x69, 0x6c, 0x65, 0x6e, - 0x61, 0x6d, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x66, 0x75, 0x6e, 0x63, 0x18, 0x02, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x04, 0x66, 0x75, 0x6e, 0x63, 0x12, 0x12, 0x0a, 0x04, 0x6c, 0x69, 0x6e, 0x65, 0x18, - 0x03, 0x20, 0x01, 0x28, 0x05, 0x52, 0x04, 0x6c, 0x69, 0x6e, 0x65, 0x22, 0x60, 0x0a, 0x05, 0x45, - 0x72, 0x72, 0x6f, 0x72, 0x12, 0x10, 0x0a, 0x03, 0x6d, 0x73, 0x67, 0x18, 0x01, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x03, 0x6d, 0x73, 0x67, 0x12, 0x3b, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x63, 0x6b, 0x18, - 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x20, 0x2e, 0x65, 0x6e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x65, - 0x6e, 0x67, 0x69, 0x6e, 0x65, 0x2e, 0x74, 0x72, 0x61, 0x63, 0x65, 0x32, 0x2e, 0x53, 0x74, 0x61, - 0x63, 0x6b, 0x54, 0x72, 0x61, 0x63, 0x65, 0x48, 0x00, 0x52, 0x05, 0x73, 0x74, 0x61, 0x63, 0x6b, - 0x88, 0x01, 0x01, 0x42, 0x08, 0x0a, 0x06, 0x5f, 0x73, 0x74, 0x61, 0x63, 0x6b, 0x2a, 0xb1, 0x02, - 0x0a, 0x12, 0x48, 0x54, 0x54, 0x50, 0x54, 0x72, 0x61, 0x63, 0x65, 0x45, 0x76, 0x65, 0x6e, 0x74, - 0x43, 0x6f, 0x64, 0x65, 0x12, 0x0b, 0x0a, 0x07, 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x10, - 0x00, 0x12, 0x0c, 0x0a, 0x08, 0x47, 0x45, 0x54, 0x5f, 0x43, 0x4f, 0x4e, 0x4e, 0x10, 0x01, 0x12, - 0x0c, 0x0a, 0x08, 0x47, 0x4f, 0x54, 0x5f, 0x43, 0x4f, 0x4e, 0x4e, 0x10, 0x02, 0x12, 0x1b, 0x0a, - 0x17, 0x47, 0x4f, 0x54, 0x5f, 0x46, 0x49, 0x52, 0x53, 0x54, 0x5f, 0x52, 0x45, 0x53, 0x50, 0x4f, - 0x4e, 0x53, 0x45, 0x5f, 0x42, 0x59, 0x54, 0x45, 0x10, 0x03, 0x12, 0x14, 0x0a, 0x10, 0x47, 0x4f, - 0x54, 0x5f, 0x31, 0x58, 0x58, 0x5f, 0x52, 0x45, 0x53, 0x50, 0x4f, 0x4e, 0x53, 0x45, 0x10, 0x04, - 0x12, 0x0d, 0x0a, 0x09, 0x44, 0x4e, 0x53, 0x5f, 0x53, 0x54, 0x41, 0x52, 0x54, 0x10, 0x05, 0x12, - 0x0c, 0x0a, 0x08, 0x44, 0x4e, 0x53, 0x5f, 0x44, 0x4f, 0x4e, 0x45, 0x10, 0x06, 0x12, 0x11, 0x0a, - 0x0d, 0x43, 0x4f, 0x4e, 0x4e, 0x45, 0x43, 0x54, 0x5f, 0x53, 0x54, 0x41, 0x52, 0x54, 0x10, 0x07, - 0x12, 0x10, 0x0a, 0x0c, 0x43, 0x4f, 0x4e, 0x4e, 0x45, 0x43, 0x54, 0x5f, 0x44, 0x4f, 0x4e, 0x45, - 0x10, 0x08, 0x12, 0x17, 0x0a, 0x13, 0x54, 0x4c, 0x53, 0x5f, 0x48, 0x41, 0x4e, 0x44, 0x53, 0x48, - 0x41, 0x4b, 0x45, 0x5f, 0x53, 0x54, 0x41, 0x52, 0x54, 0x10, 0x09, 0x12, 0x16, 0x0a, 0x12, 0x54, - 0x4c, 0x53, 0x5f, 0x48, 0x41, 0x4e, 0x44, 0x53, 0x48, 0x41, 0x4b, 0x45, 0x5f, 0x44, 0x4f, 0x4e, - 0x45, 0x10, 0x0a, 0x12, 0x11, 0x0a, 0x0d, 0x57, 0x52, 0x4f, 0x54, 0x45, 0x5f, 0x48, 0x45, 0x41, - 0x44, 0x45, 0x52, 0x53, 0x10, 0x0b, 0x12, 0x11, 0x0a, 0x0d, 0x57, 0x52, 0x4f, 0x54, 0x45, 0x5f, - 0x52, 0x45, 0x51, 0x55, 0x45, 0x53, 0x54, 0x10, 0x0c, 0x12, 0x15, 0x0a, 0x11, 0x57, 0x41, 0x49, - 0x54, 0x5f, 0x31, 0x30, 0x30, 0x5f, 0x43, 0x4f, 0x4e, 0x54, 0x49, 0x4e, 0x55, 0x45, 0x10, 0x0d, - 0x12, 0x0f, 0x0a, 0x0b, 0x43, 0x4c, 0x4f, 0x53, 0x45, 0x44, 0x5f, 0x42, 0x4f, 0x44, 0x59, 0x10, - 0x0e, 0x42, 0x25, 0x5a, 0x23, 0x65, 0x6e, 0x63, 0x72, 0x2e, 0x64, 0x65, 0x76, 0x2f, 0x70, 0x72, - 0x6f, 0x74, 0x6f, 0x2f, 0x65, 0x6e, 0x63, 0x6f, 0x72, 0x65, 0x2f, 0x65, 0x6e, 0x67, 0x69, 0x6e, - 0x65, 0x2f, 0x74, 0x72, 0x61, 0x63, 0x65, 0x32, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x06, 0x0a, 0x04, 0x5f, 0x65, 0x72, 0x72, 0x22, 0x15, 0x0a, 0x13, 0x48, 0x54, 0x54, 0x50, 0x57, + 0x61, 0x69, 0x74, 0x31, 0x30, 0x30, 0x43, 0x6f, 0x6e, 0x74, 0x69, 0x6e, 0x75, 0x65, 0x22, 0x33, + 0x0a, 0x12, 0x48, 0x54, 0x54, 0x50, 0x43, 0x6c, 0x6f, 0x73, 0x65, 0x64, 0x42, 0x6f, 0x64, 0x79, + 0x44, 0x61, 0x74, 0x61, 0x12, 0x15, 0x0a, 0x03, 0x65, 0x72, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x0c, 0x48, 0x00, 0x52, 0x03, 0x65, 0x72, 0x72, 0x88, 0x01, 0x01, 0x42, 0x06, 0x0a, 0x04, 0x5f, + 0x65, 0x72, 0x72, 0x22, 0x8a, 0x02, 0x0a, 0x0a, 0x4c, 0x6f, 0x67, 0x4d, 0x65, 0x73, 0x73, 0x61, + 0x67, 0x65, 0x12, 0x3c, 0x0a, 0x05, 0x6c, 0x65, 0x76, 0x65, 0x6c, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x0e, 0x32, 0x26, 0x2e, 0x65, 0x6e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x65, 0x6e, 0x67, 0x69, 0x6e, + 0x65, 0x2e, 0x74, 0x72, 0x61, 0x63, 0x65, 0x32, 0x2e, 0x4c, 0x6f, 0x67, 0x4d, 0x65, 0x73, 0x73, + 0x61, 0x67, 0x65, 0x2e, 0x4c, 0x65, 0x76, 0x65, 0x6c, 0x52, 0x05, 0x6c, 0x65, 0x76, 0x65, 0x6c, + 0x12, 0x10, 0x0a, 0x03, 0x6d, 0x73, 0x67, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6d, + 0x73, 0x67, 0x12, 0x36, 0x0a, 0x06, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x73, 0x18, 0x03, 0x20, 0x03, + 0x28, 0x0b, 0x32, 0x1e, 0x2e, 0x65, 0x6e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x65, 0x6e, 0x67, 0x69, + 0x6e, 0x65, 0x2e, 0x74, 0x72, 0x61, 0x63, 0x65, 0x32, 0x2e, 0x4c, 0x6f, 0x67, 0x46, 0x69, 0x65, + 0x6c, 0x64, 0x52, 0x06, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x73, 0x12, 0x36, 0x0a, 0x05, 0x73, 0x74, + 0x61, 0x63, 0x6b, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x20, 0x2e, 0x65, 0x6e, 0x63, 0x6f, + 0x72, 0x65, 0x2e, 0x65, 0x6e, 0x67, 0x69, 0x6e, 0x65, 0x2e, 0x74, 0x72, 0x61, 0x63, 0x65, 0x32, + 0x2e, 0x53, 0x74, 0x61, 0x63, 0x6b, 0x54, 0x72, 0x61, 0x63, 0x65, 0x52, 0x05, 0x73, 0x74, 0x61, + 0x63, 0x6b, 0x22, 0x3c, 0x0a, 0x05, 0x4c, 0x65, 0x76, 0x65, 0x6c, 0x12, 0x09, 0x0a, 0x05, 0x44, + 0x45, 0x42, 0x55, 0x47, 0x10, 0x00, 0x12, 0x08, 0x0a, 0x04, 0x49, 0x4e, 0x46, 0x4f, 0x10, 0x01, + 0x12, 0x09, 0x0a, 0x05, 0x45, 0x52, 0x52, 0x4f, 0x52, 0x10, 0x02, 0x12, 0x08, 0x0a, 0x04, 0x57, + 0x41, 0x52, 0x4e, 0x10, 0x03, 0x12, 0x09, 0x0a, 0x05, 0x54, 0x52, 0x41, 0x43, 0x45, 0x10, 0x04, + 0x22, 0xd8, 0x02, 0x0a, 0x08, 0x4c, 0x6f, 0x67, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x12, 0x10, 0x0a, + 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, + 0x33, 0x0a, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, + 0x2e, 0x65, 0x6e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x65, 0x6e, 0x67, 0x69, 0x6e, 0x65, 0x2e, 0x74, + 0x72, 0x61, 0x63, 0x65, 0x32, 0x2e, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x48, 0x00, 0x52, 0x05, 0x65, + 0x72, 0x72, 0x6f, 0x72, 0x12, 0x12, 0x0a, 0x03, 0x73, 0x74, 0x72, 0x18, 0x03, 0x20, 0x01, 0x28, + 0x09, 0x48, 0x00, 0x52, 0x03, 0x73, 0x74, 0x72, 0x12, 0x14, 0x0a, 0x04, 0x62, 0x6f, 0x6f, 0x6c, + 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x48, 0x00, 0x52, 0x04, 0x62, 0x6f, 0x6f, 0x6c, 0x12, 0x30, + 0x0a, 0x04, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x05, 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, 0x48, 0x00, 0x52, 0x04, 0x74, 0x69, 0x6d, 0x65, + 0x12, 0x12, 0x0a, 0x03, 0x64, 0x75, 0x72, 0x18, 0x06, 0x20, 0x01, 0x28, 0x03, 0x48, 0x00, 0x52, + 0x03, 0x64, 0x75, 0x72, 0x12, 0x14, 0x0a, 0x04, 0x75, 0x75, 0x69, 0x64, 0x18, 0x07, 0x20, 0x01, + 0x28, 0x0c, 0x48, 0x00, 0x52, 0x04, 0x75, 0x75, 0x69, 0x64, 0x12, 0x14, 0x0a, 0x04, 0x6a, 0x73, + 0x6f, 0x6e, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0c, 0x48, 0x00, 0x52, 0x04, 0x6a, 0x73, 0x6f, 0x6e, + 0x12, 0x12, 0x0a, 0x03, 0x69, 0x6e, 0x74, 0x18, 0x09, 0x20, 0x01, 0x28, 0x03, 0x48, 0x00, 0x52, + 0x03, 0x69, 0x6e, 0x74, 0x12, 0x14, 0x0a, 0x04, 0x75, 0x69, 0x6e, 0x74, 0x18, 0x0a, 0x20, 0x01, + 0x28, 0x04, 0x48, 0x00, 0x52, 0x04, 0x75, 0x69, 0x6e, 0x74, 0x12, 0x1a, 0x0a, 0x07, 0x66, 0x6c, + 0x6f, 0x61, 0x74, 0x33, 0x32, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x02, 0x48, 0x00, 0x52, 0x07, 0x66, + 0x6c, 0x6f, 0x61, 0x74, 0x33, 0x32, 0x12, 0x1a, 0x0a, 0x07, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x36, + 0x34, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x01, 0x48, 0x00, 0x52, 0x07, 0x66, 0x6c, 0x6f, 0x61, 0x74, + 0x36, 0x34, 0x42, 0x07, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x22, 0x58, 0x0a, 0x0a, 0x53, + 0x74, 0x61, 0x63, 0x6b, 0x54, 0x72, 0x61, 0x63, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x70, 0x63, 0x73, + 0x18, 0x01, 0x20, 0x03, 0x28, 0x03, 0x52, 0x03, 0x70, 0x63, 0x73, 0x12, 0x38, 0x0a, 0x06, 0x66, + 0x72, 0x61, 0x6d, 0x65, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x20, 0x2e, 0x65, 0x6e, + 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x65, 0x6e, 0x67, 0x69, 0x6e, 0x65, 0x2e, 0x74, 0x72, 0x61, 0x63, + 0x65, 0x32, 0x2e, 0x53, 0x74, 0x61, 0x63, 0x6b, 0x46, 0x72, 0x61, 0x6d, 0x65, 0x52, 0x06, 0x66, + 0x72, 0x61, 0x6d, 0x65, 0x73, 0x22, 0x50, 0x0a, 0x0a, 0x53, 0x74, 0x61, 0x63, 0x6b, 0x46, 0x72, + 0x61, 0x6d, 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x66, 0x69, 0x6c, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x66, 0x69, 0x6c, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x12, + 0x12, 0x0a, 0x04, 0x66, 0x75, 0x6e, 0x63, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x66, + 0x75, 0x6e, 0x63, 0x12, 0x12, 0x0a, 0x04, 0x6c, 0x69, 0x6e, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, + 0x05, 0x52, 0x04, 0x6c, 0x69, 0x6e, 0x65, 0x22, 0x60, 0x0a, 0x05, 0x45, 0x72, 0x72, 0x6f, 0x72, + 0x12, 0x10, 0x0a, 0x03, 0x6d, 0x73, 0x67, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6d, + 0x73, 0x67, 0x12, 0x3b, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x63, 0x6b, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x20, 0x2e, 0x65, 0x6e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x65, 0x6e, 0x67, 0x69, 0x6e, + 0x65, 0x2e, 0x74, 0x72, 0x61, 0x63, 0x65, 0x32, 0x2e, 0x53, 0x74, 0x61, 0x63, 0x6b, 0x54, 0x72, + 0x61, 0x63, 0x65, 0x48, 0x00, 0x52, 0x05, 0x73, 0x74, 0x61, 0x63, 0x6b, 0x88, 0x01, 0x01, 0x42, + 0x08, 0x0a, 0x06, 0x5f, 0x73, 0x74, 0x61, 0x63, 0x6b, 0x2a, 0xb1, 0x02, 0x0a, 0x12, 0x48, 0x54, + 0x54, 0x50, 0x54, 0x72, 0x61, 0x63, 0x65, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x43, 0x6f, 0x64, 0x65, + 0x12, 0x0b, 0x0a, 0x07, 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x10, 0x00, 0x12, 0x0c, 0x0a, + 0x08, 0x47, 0x45, 0x54, 0x5f, 0x43, 0x4f, 0x4e, 0x4e, 0x10, 0x01, 0x12, 0x0c, 0x0a, 0x08, 0x47, + 0x4f, 0x54, 0x5f, 0x43, 0x4f, 0x4e, 0x4e, 0x10, 0x02, 0x12, 0x1b, 0x0a, 0x17, 0x47, 0x4f, 0x54, + 0x5f, 0x46, 0x49, 0x52, 0x53, 0x54, 0x5f, 0x52, 0x45, 0x53, 0x50, 0x4f, 0x4e, 0x53, 0x45, 0x5f, + 0x42, 0x59, 0x54, 0x45, 0x10, 0x03, 0x12, 0x14, 0x0a, 0x10, 0x47, 0x4f, 0x54, 0x5f, 0x31, 0x58, + 0x58, 0x5f, 0x52, 0x45, 0x53, 0x50, 0x4f, 0x4e, 0x53, 0x45, 0x10, 0x04, 0x12, 0x0d, 0x0a, 0x09, + 0x44, 0x4e, 0x53, 0x5f, 0x53, 0x54, 0x41, 0x52, 0x54, 0x10, 0x05, 0x12, 0x0c, 0x0a, 0x08, 0x44, + 0x4e, 0x53, 0x5f, 0x44, 0x4f, 0x4e, 0x45, 0x10, 0x06, 0x12, 0x11, 0x0a, 0x0d, 0x43, 0x4f, 0x4e, + 0x4e, 0x45, 0x43, 0x54, 0x5f, 0x53, 0x54, 0x41, 0x52, 0x54, 0x10, 0x07, 0x12, 0x10, 0x0a, 0x0c, + 0x43, 0x4f, 0x4e, 0x4e, 0x45, 0x43, 0x54, 0x5f, 0x44, 0x4f, 0x4e, 0x45, 0x10, 0x08, 0x12, 0x17, + 0x0a, 0x13, 0x54, 0x4c, 0x53, 0x5f, 0x48, 0x41, 0x4e, 0x44, 0x53, 0x48, 0x41, 0x4b, 0x45, 0x5f, + 0x53, 0x54, 0x41, 0x52, 0x54, 0x10, 0x09, 0x12, 0x16, 0x0a, 0x12, 0x54, 0x4c, 0x53, 0x5f, 0x48, + 0x41, 0x4e, 0x44, 0x53, 0x48, 0x41, 0x4b, 0x45, 0x5f, 0x44, 0x4f, 0x4e, 0x45, 0x10, 0x0a, 0x12, + 0x11, 0x0a, 0x0d, 0x57, 0x52, 0x4f, 0x54, 0x45, 0x5f, 0x48, 0x45, 0x41, 0x44, 0x45, 0x52, 0x53, + 0x10, 0x0b, 0x12, 0x11, 0x0a, 0x0d, 0x57, 0x52, 0x4f, 0x54, 0x45, 0x5f, 0x52, 0x45, 0x51, 0x55, + 0x45, 0x53, 0x54, 0x10, 0x0c, 0x12, 0x15, 0x0a, 0x11, 0x57, 0x41, 0x49, 0x54, 0x5f, 0x31, 0x30, + 0x30, 0x5f, 0x43, 0x4f, 0x4e, 0x54, 0x49, 0x4e, 0x55, 0x45, 0x10, 0x0d, 0x12, 0x0f, 0x0a, 0x0b, + 0x43, 0x4c, 0x4f, 0x53, 0x45, 0x44, 0x5f, 0x42, 0x4f, 0x44, 0x59, 0x10, 0x0e, 0x42, 0x25, 0x5a, + 0x23, 0x65, 0x6e, 0x63, 0x72, 0x2e, 0x64, 0x65, 0x76, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, + 0x65, 0x6e, 0x63, 0x6f, 0x72, 0x65, 0x2f, 0x65, 0x6e, 0x67, 0x69, 0x6e, 0x65, 0x2f, 0x74, 0x72, + 0x61, 0x63, 0x65, 0x32, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( diff --git a/proto/encore/engine/trace2/trace2.proto b/proto/encore/engine/trace2/trace2.proto index 038408899f..bc70592c6a 100644 --- a/proto/encore/engine/trace2/trace2.proto +++ b/proto/encore/engine/trace2/trace2.proto @@ -289,6 +289,7 @@ message BucketObjectUploadStart { message BucketObjectUploadEnd { optional Error err = 1; optional uint64 size = 2; + optional string version = 3; } message BucketObjectDownloadStart { diff --git a/proto/encore/runtime/v1/infra.pb.go b/proto/encore/runtime/v1/infra.pb.go index 89c26d907d..c641f909f7 100644 --- a/proto/encore/runtime/v1/infra.pb.go +++ b/proto/encore/runtime/v1/infra.pb.go @@ -2377,6 +2377,8 @@ type BucketCluster_GCS struct { // Endpoint override, if any. Defaults to https://storage.googleapis.com if unset. Endpoint *string `protobuf:"bytes,1,opt,name=endpoint,proto3,oneof" json:"endpoint,omitempty"` + // Whether to connect anonymously or if a service account should be resolved. + Anonymous bool `protobuf:"varint,2,opt,name=anonymous,proto3" json:"anonymous,omitempty"` } func (x *BucketCluster_GCS) Reset() { @@ -2418,6 +2420,13 @@ func (x *BucketCluster_GCS) GetEndpoint() string { return "" } +func (x *BucketCluster_GCS) GetAnonymous() bool { + if x != nil { + return x.Anonymous + } + return false +} + // CORS describes the CORS configuration for a gateway. type Gateway_CORS struct { state protoimpl.MessageState @@ -2932,7 +2941,7 @@ var file_encore_runtime_v1_infra_proto_rawDesc = []byte{ 0x72, 0x76, 0x69, 0x63, 0x65, 0x5f, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x42, 0x14, 0x0a, 0x12, 0x5f, 0x70, 0x75, 0x73, 0x68, 0x5f, 0x6a, 0x77, 0x74, 0x5f, 0x61, 0x75, 0x64, 0x69, 0x65, 0x6e, 0x63, 0x65, 0x42, 0x11, 0x0a, 0x0f, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x5f, - 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x22, 0xd4, 0x02, 0x0a, 0x0d, 0x42, 0x75, 0x63, 0x6b, 0x65, + 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x22, 0xf2, 0x02, 0x0a, 0x0d, 0x42, 0x75, 0x63, 0x6b, 0x65, 0x74, 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x12, 0x10, 0x0a, 0x03, 0x72, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x72, 0x69, 0x64, 0x12, 0x33, 0x0a, 0x07, 0x62, 0x75, 0x63, 0x6b, 0x65, 0x74, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x65, 0x6e, @@ -2949,83 +2958,85 @@ var file_encore_runtime_v1_infra_proto_rawDesc = []byte{ 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x72, 0x65, 0x67, 0x69, 0x6f, 0x6e, 0x12, 0x1f, 0x0a, 0x08, 0x65, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x48, 0x00, 0x52, 0x08, 0x65, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x88, 0x01, 0x01, 0x42, - 0x0b, 0x0a, 0x09, 0x5f, 0x65, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x1a, 0x33, 0x0a, 0x03, + 0x0b, 0x0a, 0x09, 0x5f, 0x65, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x1a, 0x51, 0x0a, 0x03, 0x47, 0x43, 0x53, 0x12, 0x1f, 0x0a, 0x08, 0x65, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x48, 0x00, 0x52, 0x08, 0x65, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, - 0x74, 0x88, 0x01, 0x01, 0x42, 0x0b, 0x0a, 0x09, 0x5f, 0x65, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, - 0x74, 0x42, 0x0a, 0x0a, 0x08, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x22, 0x8d, 0x01, - 0x0a, 0x06, 0x42, 0x75, 0x63, 0x6b, 0x65, 0x74, 0x12, 0x10, 0x0a, 0x03, 0x72, 0x69, 0x64, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x72, 0x69, 0x64, 0x12, 0x1f, 0x0a, 0x0b, 0x65, 0x6e, - 0x63, 0x6f, 0x72, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x0a, 0x65, 0x6e, 0x63, 0x6f, 0x72, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x1d, 0x0a, 0x0a, 0x63, - 0x6c, 0x6f, 0x75, 0x64, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x09, 0x63, 0x6c, 0x6f, 0x75, 0x64, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x22, 0x0a, 0x0a, 0x6b, 0x65, - 0x79, 0x5f, 0x70, 0x72, 0x65, 0x66, 0x69, 0x78, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x48, 0x00, - 0x52, 0x09, 0x6b, 0x65, 0x79, 0x50, 0x72, 0x65, 0x66, 0x69, 0x78, 0x88, 0x01, 0x01, 0x42, 0x0d, - 0x0a, 0x0b, 0x5f, 0x6b, 0x65, 0x79, 0x5f, 0x70, 0x72, 0x65, 0x66, 0x69, 0x78, 0x22, 0xb9, 0x06, - 0x0a, 0x07, 0x47, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x72, 0x69, 0x64, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x72, 0x69, 0x64, 0x12, 0x1f, 0x0a, 0x0b, 0x65, - 0x6e, 0x63, 0x6f, 0x72, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x0a, 0x65, 0x6e, 0x63, 0x6f, 0x72, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x19, 0x0a, 0x08, - 0x62, 0x61, 0x73, 0x65, 0x5f, 0x75, 0x72, 0x6c, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, - 0x62, 0x61, 0x73, 0x65, 0x55, 0x72, 0x6c, 0x12, 0x1c, 0x0a, 0x09, 0x68, 0x6f, 0x73, 0x74, 0x6e, - 0x61, 0x6d, 0x65, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x09, 0x52, 0x09, 0x68, 0x6f, 0x73, 0x74, - 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x12, 0x33, 0x0a, 0x04, 0x63, 0x6f, 0x72, 0x73, 0x18, 0x05, 0x20, - 0x01, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x65, 0x6e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x72, 0x75, 0x6e, - 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x2e, - 0x43, 0x4f, 0x52, 0x53, 0x52, 0x04, 0x63, 0x6f, 0x72, 0x73, 0x1a, 0xcd, 0x04, 0x0a, 0x04, 0x43, - 0x4f, 0x52, 0x53, 0x12, 0x14, 0x0a, 0x05, 0x64, 0x65, 0x62, 0x75, 0x67, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x08, 0x52, 0x05, 0x64, 0x65, 0x62, 0x75, 0x67, 0x12, 0x2f, 0x0a, 0x13, 0x64, 0x69, 0x73, - 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x63, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x73, - 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x12, 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x43, - 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x73, 0x12, 0x58, 0x0a, 0x0f, 0x61, 0x6c, - 0x6c, 0x6f, 0x77, 0x65, 0x64, 0x5f, 0x6f, 0x72, 0x69, 0x67, 0x69, 0x6e, 0x73, 0x18, 0x03, 0x20, - 0x01, 0x28, 0x0b, 0x32, 0x2d, 0x2e, 0x65, 0x6e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x72, 0x75, 0x6e, - 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x2e, - 0x43, 0x4f, 0x52, 0x53, 0x41, 0x6c, 0x6c, 0x6f, 0x77, 0x65, 0x64, 0x4f, 0x72, 0x69, 0x67, 0x69, - 0x6e, 0x73, 0x48, 0x00, 0x52, 0x0e, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x65, 0x64, 0x4f, 0x72, 0x69, - 0x67, 0x69, 0x6e, 0x73, 0x12, 0x59, 0x0a, 0x29, 0x75, 0x6e, 0x73, 0x61, 0x66, 0x65, 0x5f, 0x61, - 0x6c, 0x6c, 0x6f, 0x77, 0x5f, 0x61, 0x6c, 0x6c, 0x5f, 0x6f, 0x72, 0x69, 0x67, 0x69, 0x6e, 0x73, - 0x5f, 0x77, 0x69, 0x74, 0x68, 0x5f, 0x63, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, - 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x48, 0x00, 0x52, 0x24, 0x75, 0x6e, 0x73, 0x61, 0x66, - 0x65, 0x41, 0x6c, 0x6c, 0x6f, 0x77, 0x41, 0x6c, 0x6c, 0x4f, 0x72, 0x69, 0x67, 0x69, 0x6e, 0x73, - 0x57, 0x69, 0x74, 0x68, 0x43, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x73, 0x12, - 0x7c, 0x0a, 0x23, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x65, 0x64, 0x5f, 0x6f, 0x72, 0x69, 0x67, 0x69, - 0x6e, 0x73, 0x5f, 0x77, 0x69, 0x74, 0x68, 0x6f, 0x75, 0x74, 0x5f, 0x63, 0x72, 0x65, 0x64, 0x65, - 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x73, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2d, 0x2e, 0x65, - 0x6e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, - 0x2e, 0x47, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x2e, 0x43, 0x4f, 0x52, 0x53, 0x41, 0x6c, 0x6c, - 0x6f, 0x77, 0x65, 0x64, 0x4f, 0x72, 0x69, 0x67, 0x69, 0x6e, 0x73, 0x52, 0x20, 0x61, 0x6c, 0x6c, - 0x6f, 0x77, 0x65, 0x64, 0x4f, 0x72, 0x69, 0x67, 0x69, 0x6e, 0x73, 0x57, 0x69, 0x74, 0x68, 0x6f, - 0x75, 0x74, 0x43, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x73, 0x12, 0x32, 0x0a, - 0x15, 0x65, 0x78, 0x74, 0x72, 0x61, 0x5f, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x65, 0x64, 0x5f, 0x68, - 0x65, 0x61, 0x64, 0x65, 0x72, 0x73, 0x18, 0x06, 0x20, 0x03, 0x28, 0x09, 0x52, 0x13, 0x65, 0x78, - 0x74, 0x72, 0x61, 0x41, 0x6c, 0x6c, 0x6f, 0x77, 0x65, 0x64, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, - 0x73, 0x12, 0x32, 0x0a, 0x15, 0x65, 0x78, 0x74, 0x72, 0x61, 0x5f, 0x65, 0x78, 0x70, 0x6f, 0x73, - 0x65, 0x64, 0x5f, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x73, 0x18, 0x07, 0x20, 0x03, 0x28, 0x09, - 0x52, 0x13, 0x65, 0x78, 0x74, 0x72, 0x61, 0x45, 0x78, 0x70, 0x6f, 0x73, 0x65, 0x64, 0x48, 0x65, - 0x61, 0x64, 0x65, 0x72, 0x73, 0x12, 0x3f, 0x0a, 0x1c, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x5f, 0x70, - 0x72, 0x69, 0x76, 0x61, 0x74, 0x65, 0x5f, 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x5f, 0x61, - 0x63, 0x63, 0x65, 0x73, 0x73, 0x18, 0x08, 0x20, 0x01, 0x28, 0x08, 0x52, 0x19, 0x61, 0x6c, 0x6c, - 0x6f, 0x77, 0x50, 0x72, 0x69, 0x76, 0x61, 0x74, 0x65, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, - 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, 0x42, 0x22, 0x0a, 0x20, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x65, - 0x64, 0x5f, 0x6f, 0x72, 0x69, 0x67, 0x69, 0x6e, 0x73, 0x5f, 0x77, 0x69, 0x74, 0x68, 0x5f, 0x63, - 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x73, 0x1a, 0x3d, 0x0a, 0x12, 0x43, 0x4f, + 0x74, 0x88, 0x01, 0x01, 0x12, 0x1c, 0x0a, 0x09, 0x61, 0x6e, 0x6f, 0x6e, 0x79, 0x6d, 0x6f, 0x75, + 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x09, 0x61, 0x6e, 0x6f, 0x6e, 0x79, 0x6d, 0x6f, + 0x75, 0x73, 0x42, 0x0b, 0x0a, 0x09, 0x5f, 0x65, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x42, + 0x0a, 0x0a, 0x08, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x22, 0x8d, 0x01, 0x0a, 0x06, + 0x42, 0x75, 0x63, 0x6b, 0x65, 0x74, 0x12, 0x10, 0x0a, 0x03, 0x72, 0x69, 0x64, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x03, 0x72, 0x69, 0x64, 0x12, 0x1f, 0x0a, 0x0b, 0x65, 0x6e, 0x63, 0x6f, + 0x72, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x65, + 0x6e, 0x63, 0x6f, 0x72, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x1d, 0x0a, 0x0a, 0x63, 0x6c, 0x6f, + 0x75, 0x64, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x63, + 0x6c, 0x6f, 0x75, 0x64, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x22, 0x0a, 0x0a, 0x6b, 0x65, 0x79, 0x5f, + 0x70, 0x72, 0x65, 0x66, 0x69, 0x78, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x48, 0x00, 0x52, 0x09, + 0x6b, 0x65, 0x79, 0x50, 0x72, 0x65, 0x66, 0x69, 0x78, 0x88, 0x01, 0x01, 0x42, 0x0d, 0x0a, 0x0b, + 0x5f, 0x6b, 0x65, 0x79, 0x5f, 0x70, 0x72, 0x65, 0x66, 0x69, 0x78, 0x22, 0xb9, 0x06, 0x0a, 0x07, + 0x47, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x72, 0x69, 0x64, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x72, 0x69, 0x64, 0x12, 0x1f, 0x0a, 0x0b, 0x65, 0x6e, 0x63, + 0x6f, 0x72, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, + 0x65, 0x6e, 0x63, 0x6f, 0x72, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x19, 0x0a, 0x08, 0x62, 0x61, + 0x73, 0x65, 0x5f, 0x75, 0x72, 0x6c, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x62, 0x61, + 0x73, 0x65, 0x55, 0x72, 0x6c, 0x12, 0x1c, 0x0a, 0x09, 0x68, 0x6f, 0x73, 0x74, 0x6e, 0x61, 0x6d, + 0x65, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x09, 0x52, 0x09, 0x68, 0x6f, 0x73, 0x74, 0x6e, 0x61, + 0x6d, 0x65, 0x73, 0x12, 0x33, 0x0a, 0x04, 0x63, 0x6f, 0x72, 0x73, 0x18, 0x05, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x1f, 0x2e, 0x65, 0x6e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, + 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x2e, 0x43, 0x4f, + 0x52, 0x53, 0x52, 0x04, 0x63, 0x6f, 0x72, 0x73, 0x1a, 0xcd, 0x04, 0x0a, 0x04, 0x43, 0x4f, 0x52, + 0x53, 0x12, 0x14, 0x0a, 0x05, 0x64, 0x65, 0x62, 0x75, 0x67, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, + 0x52, 0x05, 0x64, 0x65, 0x62, 0x75, 0x67, 0x12, 0x2f, 0x0a, 0x13, 0x64, 0x69, 0x73, 0x61, 0x62, + 0x6c, 0x65, 0x5f, 0x63, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x73, 0x18, 0x02, + 0x20, 0x01, 0x28, 0x08, 0x52, 0x12, 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x43, 0x72, 0x65, + 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x73, 0x12, 0x58, 0x0a, 0x0f, 0x61, 0x6c, 0x6c, 0x6f, + 0x77, 0x65, 0x64, 0x5f, 0x6f, 0x72, 0x69, 0x67, 0x69, 0x6e, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x2d, 0x2e, 0x65, 0x6e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, + 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x2e, 0x43, 0x4f, 0x52, 0x53, 0x41, 0x6c, 0x6c, 0x6f, 0x77, 0x65, 0x64, 0x4f, 0x72, 0x69, 0x67, 0x69, 0x6e, 0x73, - 0x12, 0x27, 0x0a, 0x0f, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x65, 0x64, 0x5f, 0x6f, 0x72, 0x69, 0x67, - 0x69, 0x6e, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0e, 0x61, 0x6c, 0x6c, 0x6f, 0x77, - 0x65, 0x64, 0x4f, 0x72, 0x69, 0x67, 0x69, 0x6e, 0x73, 0x2a, 0x7d, 0x0a, 0x0a, 0x53, 0x65, 0x72, - 0x76, 0x65, 0x72, 0x4b, 0x69, 0x6e, 0x64, 0x12, 0x1b, 0x0a, 0x17, 0x53, 0x45, 0x52, 0x56, 0x45, - 0x52, 0x5f, 0x4b, 0x49, 0x4e, 0x44, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, - 0x45, 0x44, 0x10, 0x00, 0x12, 0x17, 0x0a, 0x13, 0x53, 0x45, 0x52, 0x56, 0x45, 0x52, 0x5f, 0x4b, - 0x49, 0x4e, 0x44, 0x5f, 0x50, 0x52, 0x49, 0x4d, 0x41, 0x52, 0x59, 0x10, 0x01, 0x12, 0x1b, 0x0a, - 0x17, 0x53, 0x45, 0x52, 0x56, 0x45, 0x52, 0x5f, 0x4b, 0x49, 0x4e, 0x44, 0x5f, 0x48, 0x4f, 0x54, - 0x5f, 0x53, 0x54, 0x41, 0x4e, 0x44, 0x42, 0x59, 0x10, 0x02, 0x12, 0x1c, 0x0a, 0x18, 0x53, 0x45, - 0x52, 0x56, 0x45, 0x52, 0x5f, 0x4b, 0x49, 0x4e, 0x44, 0x5f, 0x52, 0x45, 0x41, 0x44, 0x5f, 0x52, - 0x45, 0x50, 0x4c, 0x49, 0x43, 0x41, 0x10, 0x03, 0x42, 0x2c, 0x5a, 0x2a, 0x65, 0x6e, 0x63, 0x72, - 0x2e, 0x64, 0x65, 0x76, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x65, 0x6e, 0x63, 0x6f, 0x72, - 0x65, 0x2f, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2f, 0x76, 0x31, 0x3b, 0x72, 0x75, 0x6e, - 0x74, 0x69, 0x6d, 0x65, 0x76, 0x31, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x48, 0x00, 0x52, 0x0e, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x65, 0x64, 0x4f, 0x72, 0x69, 0x67, 0x69, + 0x6e, 0x73, 0x12, 0x59, 0x0a, 0x29, 0x75, 0x6e, 0x73, 0x61, 0x66, 0x65, 0x5f, 0x61, 0x6c, 0x6c, + 0x6f, 0x77, 0x5f, 0x61, 0x6c, 0x6c, 0x5f, 0x6f, 0x72, 0x69, 0x67, 0x69, 0x6e, 0x73, 0x5f, 0x77, + 0x69, 0x74, 0x68, 0x5f, 0x63, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x73, 0x18, + 0x04, 0x20, 0x01, 0x28, 0x08, 0x48, 0x00, 0x52, 0x24, 0x75, 0x6e, 0x73, 0x61, 0x66, 0x65, 0x41, + 0x6c, 0x6c, 0x6f, 0x77, 0x41, 0x6c, 0x6c, 0x4f, 0x72, 0x69, 0x67, 0x69, 0x6e, 0x73, 0x57, 0x69, + 0x74, 0x68, 0x43, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x73, 0x12, 0x7c, 0x0a, + 0x23, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x65, 0x64, 0x5f, 0x6f, 0x72, 0x69, 0x67, 0x69, 0x6e, 0x73, + 0x5f, 0x77, 0x69, 0x74, 0x68, 0x6f, 0x75, 0x74, 0x5f, 0x63, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, + 0x69, 0x61, 0x6c, 0x73, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2d, 0x2e, 0x65, 0x6e, 0x63, + 0x6f, 0x72, 0x65, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x47, + 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x2e, 0x43, 0x4f, 0x52, 0x53, 0x41, 0x6c, 0x6c, 0x6f, 0x77, + 0x65, 0x64, 0x4f, 0x72, 0x69, 0x67, 0x69, 0x6e, 0x73, 0x52, 0x20, 0x61, 0x6c, 0x6c, 0x6f, 0x77, + 0x65, 0x64, 0x4f, 0x72, 0x69, 0x67, 0x69, 0x6e, 0x73, 0x57, 0x69, 0x74, 0x68, 0x6f, 0x75, 0x74, + 0x43, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x73, 0x12, 0x32, 0x0a, 0x15, 0x65, + 0x78, 0x74, 0x72, 0x61, 0x5f, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x65, 0x64, 0x5f, 0x68, 0x65, 0x61, + 0x64, 0x65, 0x72, 0x73, 0x18, 0x06, 0x20, 0x03, 0x28, 0x09, 0x52, 0x13, 0x65, 0x78, 0x74, 0x72, + 0x61, 0x41, 0x6c, 0x6c, 0x6f, 0x77, 0x65, 0x64, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x73, 0x12, + 0x32, 0x0a, 0x15, 0x65, 0x78, 0x74, 0x72, 0x61, 0x5f, 0x65, 0x78, 0x70, 0x6f, 0x73, 0x65, 0x64, + 0x5f, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x73, 0x18, 0x07, 0x20, 0x03, 0x28, 0x09, 0x52, 0x13, + 0x65, 0x78, 0x74, 0x72, 0x61, 0x45, 0x78, 0x70, 0x6f, 0x73, 0x65, 0x64, 0x48, 0x65, 0x61, 0x64, + 0x65, 0x72, 0x73, 0x12, 0x3f, 0x0a, 0x1c, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x5f, 0x70, 0x72, 0x69, + 0x76, 0x61, 0x74, 0x65, 0x5f, 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x5f, 0x61, 0x63, 0x63, + 0x65, 0x73, 0x73, 0x18, 0x08, 0x20, 0x01, 0x28, 0x08, 0x52, 0x19, 0x61, 0x6c, 0x6c, 0x6f, 0x77, + 0x50, 0x72, 0x69, 0x76, 0x61, 0x74, 0x65, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x41, 0x63, + 0x63, 0x65, 0x73, 0x73, 0x42, 0x22, 0x0a, 0x20, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x65, 0x64, 0x5f, + 0x6f, 0x72, 0x69, 0x67, 0x69, 0x6e, 0x73, 0x5f, 0x77, 0x69, 0x74, 0x68, 0x5f, 0x63, 0x72, 0x65, + 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x73, 0x1a, 0x3d, 0x0a, 0x12, 0x43, 0x4f, 0x52, 0x53, + 0x41, 0x6c, 0x6c, 0x6f, 0x77, 0x65, 0x64, 0x4f, 0x72, 0x69, 0x67, 0x69, 0x6e, 0x73, 0x12, 0x27, + 0x0a, 0x0f, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x65, 0x64, 0x5f, 0x6f, 0x72, 0x69, 0x67, 0x69, 0x6e, + 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0e, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x65, 0x64, + 0x4f, 0x72, 0x69, 0x67, 0x69, 0x6e, 0x73, 0x2a, 0x7d, 0x0a, 0x0a, 0x53, 0x65, 0x72, 0x76, 0x65, + 0x72, 0x4b, 0x69, 0x6e, 0x64, 0x12, 0x1b, 0x0a, 0x17, 0x53, 0x45, 0x52, 0x56, 0x45, 0x52, 0x5f, + 0x4b, 0x49, 0x4e, 0x44, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, + 0x10, 0x00, 0x12, 0x17, 0x0a, 0x13, 0x53, 0x45, 0x52, 0x56, 0x45, 0x52, 0x5f, 0x4b, 0x49, 0x4e, + 0x44, 0x5f, 0x50, 0x52, 0x49, 0x4d, 0x41, 0x52, 0x59, 0x10, 0x01, 0x12, 0x1b, 0x0a, 0x17, 0x53, + 0x45, 0x52, 0x56, 0x45, 0x52, 0x5f, 0x4b, 0x49, 0x4e, 0x44, 0x5f, 0x48, 0x4f, 0x54, 0x5f, 0x53, + 0x54, 0x41, 0x4e, 0x44, 0x42, 0x59, 0x10, 0x02, 0x12, 0x1c, 0x0a, 0x18, 0x53, 0x45, 0x52, 0x56, + 0x45, 0x52, 0x5f, 0x4b, 0x49, 0x4e, 0x44, 0x5f, 0x52, 0x45, 0x41, 0x44, 0x5f, 0x52, 0x45, 0x50, + 0x4c, 0x49, 0x43, 0x41, 0x10, 0x03, 0x42, 0x2c, 0x5a, 0x2a, 0x65, 0x6e, 0x63, 0x72, 0x2e, 0x64, + 0x65, 0x76, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x65, 0x6e, 0x63, 0x6f, 0x72, 0x65, 0x2f, + 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2f, 0x76, 0x31, 0x3b, 0x72, 0x75, 0x6e, 0x74, 0x69, + 0x6d, 0x65, 0x76, 0x31, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( diff --git a/proto/encore/runtime/v1/infra.proto b/proto/encore/runtime/v1/infra.proto index 0ace7f8a66..a16c7f3130 100644 --- a/proto/encore/runtime/v1/infra.proto +++ b/proto/encore/runtime/v1/infra.proto @@ -328,6 +328,9 @@ message BucketCluster { message GCS { // Endpoint override, if any. Defaults to https://storage.googleapis.com if unset. optional string endpoint = 1; + + // Whether to connect anonymously or if a service account should be resolved. + bool anonymous = 2; } } diff --git a/runtimes/core/src/objects/gcs/mod.rs b/runtimes/core/src/objects/gcs/mod.rs index e6b9d19099..cb38139407 100644 --- a/runtimes/core/src/objects/gcs/mod.rs +++ b/runtimes/core/src/objects/gcs/mod.rs @@ -17,6 +17,10 @@ pub struct Cluster { impl Cluster { pub fn new(cfg: pb::bucket_cluster::Gcs) -> Self { let client = Arc::new(LazyGCSClient::new(cfg)); + + // Begin initializing the client in the background. + tokio::spawn(client.clone().begin_initialize()); + Self { client } } } @@ -47,19 +51,28 @@ impl LazyGCSClient { } async fn get(&self) -> &anyhow::Result> { - self.cell - .get_or_init(|| async { - let mut config = gcs::client::ClientConfig::default() - .with_auth() - .await - .context("get client config")?; + self.cell.get_or_init(|| initialize(&self.cfg)).await + } + + async fn begin_initialize(self: Arc) { + self.get().await; + } +} - if let Some(endpoint) = &self.cfg.endpoint { - config.storage_endpoint.clone_from(endpoint); - } +async fn initialize(cfg: &pb::bucket_cluster::Gcs) -> anyhow::Result> { + let mut config = gcs::client::ClientConfig::default(); + if let Some(endpoint) = &cfg.endpoint { + config.storage_endpoint.clone_from(endpoint); + } - Ok(Arc::new(gcs::client::Client::new(config))) - }) + if cfg.anonymous { + config = config.anonymous(); + } else { + config = config + .with_auth() .await + .context("unable to resolve client config")?; } + + Ok(Arc::new(gcs::client::Client::new(config))) } diff --git a/runtimes/core/src/objects/mod.rs b/runtimes/core/src/objects/mod.rs index ed0c28cc73..cc1ccd2362 100644 --- a/runtimes/core/src/objects/mod.rs +++ b/runtimes/core/src/objects/mod.rs @@ -194,9 +194,10 @@ impl Object { start_id, source, result: match &res { - Ok(attrs) => { - protocol::BucketObjectUploadEndResult::Success { size: attrs.size } - } + Ok(attrs) => protocol::BucketObjectUploadEndResult::Success { + size: attrs.size, + version: attrs.version.as_deref(), + }, Err(err) => protocol::BucketObjectUploadEndResult::Err(err), }, }); diff --git a/runtimes/core/src/trace/protocol.rs b/runtimes/core/src/trace/protocol.rs index e85b28995f..36211e4138 100644 --- a/runtimes/core/src/trace/protocol.rs +++ b/runtimes/core/src/trace/protocol.rs @@ -471,7 +471,7 @@ pub struct BucketObjectUploadEnd<'a, E> { } pub enum BucketObjectUploadEndResult<'a, E> { - Success { size: u64 }, + Success { size: u64, version: Option<&'a str> }, Err(&'a E), } @@ -532,8 +532,9 @@ impl Tracer { .into_eb(); match data.result { - BucketObjectUploadEndResult::Success { size } => { + BucketObjectUploadEndResult::Success { size, version } => { eb.u64(size); + eb.opt_str(version); eb.err_with_legacy_stack::(None); } BucketObjectUploadEndResult::Err(err) => { From cb1f66a2276a529d42018b94c9fe8f68c21618ba Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Eriksson?= Date: Tue, 5 Nov 2024 10:37:37 +0100 Subject: [PATCH 20/37] Tracing protocol improvements --- pkg/traceparser/binreader.go | 8 + pkg/traceparser/parser.go | 6 +- runtimes/core/src/trace/protocol.rs | 10 +- .../go/appruntime/exported/config/config.go | 5 +- .../exported/trace/mock_trace/mock_trace.go | 331 ++++++++++++++++++ .../go/appruntime/exported/trace2/events.go | 297 ++++++++++++++++ runtimes/go/appruntime/exported/trace2/log.go | 16 + .../go/appruntime/exported/trace2/logger.go | 11 + .../traceprovider/mock_trace/mock_trace.go | 130 +++++++ 9 files changed, 804 insertions(+), 10 deletions(-) create mode 100644 runtimes/go/appruntime/exported/trace/mock_trace/mock_trace.go diff --git a/pkg/traceparser/binreader.go b/pkg/traceparser/binreader.go index 63367d411b..f891fb6ba8 100644 --- a/pkg/traceparser/binreader.go +++ b/pkg/traceparser/binreader.go @@ -70,6 +70,14 @@ func (tr *traceReader) String() string { return s } +func (tr *traceReader) OptString() *string { + return ptrOrNil(tr.String()) +} + +func (tr *traceReader) OptUVarint() *uint64 { + return ptrOrNil(tr.UVarint()) +} + func (tr *traceReader) ByteString() []byte { size := tr.UVarint() if (size) == 0 { diff --git a/pkg/traceparser/parser.go b/pkg/traceparser/parser.go index 69cfae71d7..2effec3891 100644 --- a/pkg/traceparser/parser.go +++ b/pkg/traceparser/parser.go @@ -591,7 +591,7 @@ func (tp *traceParser) bucketObjectUploadStart() *tracepb2.BucketObjectUploadSta func (tp *traceParser) bucketObjectAttrs() *tracepb2.BucketObjectAttributes { return &tracepb2.BucketObjectAttributes{ - Size: ptrOrNil(tp.Uint64()), + Size: ptrOrNil(tp.UVarint()), Version: ptrOrNil(tp.String()), Etag: ptrOrNil(tp.String()), ContentType: ptrOrNil(tp.String()), @@ -600,8 +600,8 @@ func (tp *traceParser) bucketObjectAttrs() *tracepb2.BucketObjectAttributes { func (tp *traceParser) bucketObjectUploadEnd() *tracepb2.BucketObjectUploadEnd { return &tracepb2.BucketObjectUploadEnd{ - Size: ptrOrNil(tp.Uint64()), - Version: ptrOrNil(tp.String()), + Size: tp.OptUVarint(), + Version: tp.OptString(), Err: tp.errWithStack(), } } diff --git a/runtimes/core/src/trace/protocol.rs b/runtimes/core/src/trace/protocol.rs index 36211e4138..c8da590f7f 100644 --- a/runtimes/core/src/trace/protocol.rs +++ b/runtimes/core/src/trace/protocol.rs @@ -496,7 +496,7 @@ impl<'a> From<&'a objects::ObjectAttrs> for BucketObjectAttributes<'a> { impl EventBuffer { fn bucket_object_attrs(&mut self, attrs: &BucketObjectAttributes) { - self.u64(attrs.size.unwrap_or(0)); + self.uvarint(attrs.size.unwrap_or(0)); self.opt_str(attrs.version); self.opt_str(attrs.etag); self.opt_str(attrs.content_type); @@ -533,12 +533,12 @@ impl Tracer { match data.result { BucketObjectUploadEndResult::Success { size, version } => { - eb.u64(size); + eb.uvarint(size); eb.opt_str(version); eb.err_with_legacy_stack::(None); } BucketObjectUploadEndResult::Err(err) => { - eb.u64(0); + eb.uvarint(0u64); eb.err_with_legacy_stack(Some(err)); } } @@ -595,11 +595,11 @@ impl Tracer { match data.result { BucketObjectDownloadEndResult::Success { size } => { - eb.u64(size); + eb.uvarint(size); eb.err_with_legacy_stack::(None); } BucketObjectDownloadEndResult::Err(err) => { - eb.u64(0); + eb.uvarint(0u64); eb.err_with_legacy_stack(Some(err)); } } diff --git a/runtimes/go/appruntime/exported/config/config.go b/runtimes/go/appruntime/exported/config/config.go index d7709601bd..136cb0c944 100644 --- a/runtimes/go/appruntime/exported/config/config.go +++ b/runtimes/go/appruntime/exported/config/config.go @@ -58,7 +58,7 @@ type Runtime struct { RedisServers []*RedisServer `json:"redis_servers,omitempty"` RedisDatabases []*RedisDatabase `json:"redis_databases,omitempty"` BucketProviders []*BucketProvider `json:"bucket_providers,omitempty"` - Buckets []*Bucket `json:"buckets,omitempty"` + Buckets map[string]*Bucket `json:"buckets,omitempty"` Metrics *Metrics `json:"metrics,omitempty"` Gateways []Gateway `json:"gateways,omitempty"` // Gateways defines the gateways which should be served by the container HostedServices []string `json:"hosted_services,omitempty"` // List of services to be hosted within this container (zero length means all services, unless there's a gateway running) @@ -407,7 +407,8 @@ type S3BucketProvider struct { } type GCSBucketProvider struct { - Endpoint string `json:"endpoint"` + Endpoint string `json:"endpoint"` + Anonymous bool `json:"anonymous"` } type Bucket struct { diff --git a/runtimes/go/appruntime/exported/trace/mock_trace/mock_trace.go b/runtimes/go/appruntime/exported/trace/mock_trace/mock_trace.go new file mode 100644 index 0000000000..ef1cdbd983 --- /dev/null +++ b/runtimes/go/appruntime/exported/trace/mock_trace/mock_trace.go @@ -0,0 +1,331 @@ +// Code generated by MockGen. DO NOT EDIT. +// Source: ./logger.go + +// Package mock_trace is a generated GoMock package. +package mock_trace + +import ( + context "context" + http "net/http" + reflect "reflect" + + model "encore.dev/appruntime/exported/model" + trace "encore.dev/appruntime/exported/trace" + gomock "github.com/golang/mock/gomock" +) + +// MockLogger is a mock of Logger interface. +type MockLogger struct { + ctrl *gomock.Controller + recorder *MockLoggerMockRecorder +} + +// MockLoggerMockRecorder is the mock recorder for MockLogger. +type MockLoggerMockRecorder struct { + mock *MockLogger +} + +// NewMockLogger creates a new mock instance. +func NewMockLogger(ctrl *gomock.Controller) *MockLogger { + mock := &MockLogger{ctrl: ctrl} + mock.recorder = &MockLoggerMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *MockLogger) EXPECT() *MockLoggerMockRecorder { + return m.recorder +} + +// Add mocks base method. +func (m *MockLogger) Add(event trace.EventType, data []byte) { + m.ctrl.T.Helper() + m.ctrl.Call(m, "Add", event, data) +} + +// Add indicates an expected call of Add. +func (mr *MockLoggerMockRecorder) Add(event, data interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Add", reflect.TypeOf((*MockLogger)(nil).Add), event, data) +} + +// BeginAuth mocks base method. +func (m *MockLogger) BeginAuth(call *model.AuthCall, goid uint32) { + m.ctrl.T.Helper() + m.ctrl.Call(m, "BeginAuth", call, goid) +} + +// BeginAuth indicates an expected call of BeginAuth. +func (mr *MockLoggerMockRecorder) BeginAuth(call, goid interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "BeginAuth", reflect.TypeOf((*MockLogger)(nil).BeginAuth), call, goid) +} + +// BeginCall mocks base method. +func (m *MockLogger) BeginCall(call *model.APICall, goid uint32) { + m.ctrl.T.Helper() + m.ctrl.Call(m, "BeginCall", call, goid) +} + +// BeginCall indicates an expected call of BeginCall. +func (mr *MockLoggerMockRecorder) BeginCall(call, goid interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "BeginCall", reflect.TypeOf((*MockLogger)(nil).BeginCall), call, goid) +} + +// BeginRequest mocks base method. +func (m *MockLogger) BeginRequest(req *model.Request, goid uint32) { + m.ctrl.T.Helper() + m.ctrl.Call(m, "BeginRequest", req, goid) +} + +// BeginRequest indicates an expected call of BeginRequest. +func (mr *MockLoggerMockRecorder) BeginRequest(req, goid interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "BeginRequest", reflect.TypeOf((*MockLogger)(nil).BeginRequest), req, goid) +} + +// BodyStream mocks base method. +func (m *MockLogger) BodyStream(p trace.BodyStreamParams) { + m.ctrl.T.Helper() + m.ctrl.Call(m, "BodyStream", p) +} + +// BodyStream indicates an expected call of BodyStream. +func (mr *MockLoggerMockRecorder) BodyStream(p interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "BodyStream", reflect.TypeOf((*MockLogger)(nil).BodyStream), p) +} + +// CacheOpEnd mocks base method. +func (m *MockLogger) CacheOpEnd(p trace.CacheOpEndParams) { + m.ctrl.T.Helper() + m.ctrl.Call(m, "CacheOpEnd", p) +} + +// CacheOpEnd indicates an expected call of CacheOpEnd. +func (mr *MockLoggerMockRecorder) CacheOpEnd(p interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CacheOpEnd", reflect.TypeOf((*MockLogger)(nil).CacheOpEnd), p) +} + +// CacheOpStart mocks base method. +func (m *MockLogger) CacheOpStart(p trace.CacheOpStartParams) { + m.ctrl.T.Helper() + m.ctrl.Call(m, "CacheOpStart", p) +} + +// CacheOpStart indicates an expected call of CacheOpStart. +func (mr *MockLoggerMockRecorder) CacheOpStart(p interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CacheOpStart", reflect.TypeOf((*MockLogger)(nil).CacheOpStart), p) +} + +// DBQueryEnd mocks base method. +func (m *MockLogger) DBQueryEnd(queryID uint64, err error) { + m.ctrl.T.Helper() + m.ctrl.Call(m, "DBQueryEnd", queryID, err) +} + +// DBQueryEnd indicates an expected call of DBQueryEnd. +func (mr *MockLoggerMockRecorder) DBQueryEnd(queryID, err interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DBQueryEnd", reflect.TypeOf((*MockLogger)(nil).DBQueryEnd), queryID, err) +} + +// DBQueryStart mocks base method. +func (m *MockLogger) DBQueryStart(p trace.DBQueryStartParams) { + m.ctrl.T.Helper() + m.ctrl.Call(m, "DBQueryStart", p) +} + +// DBQueryStart indicates an expected call of DBQueryStart. +func (mr *MockLoggerMockRecorder) DBQueryStart(p interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DBQueryStart", reflect.TypeOf((*MockLogger)(nil).DBQueryStart), p) +} + +// DBTxEnd mocks base method. +func (m *MockLogger) DBTxEnd(p trace.DBTxEndParams) { + m.ctrl.T.Helper() + m.ctrl.Call(m, "DBTxEnd", p) +} + +// DBTxEnd indicates an expected call of DBTxEnd. +func (mr *MockLoggerMockRecorder) DBTxEnd(p interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DBTxEnd", reflect.TypeOf((*MockLogger)(nil).DBTxEnd), p) +} + +// DBTxStart mocks base method. +func (m *MockLogger) DBTxStart(p trace.DBTxStartParams) { + m.ctrl.T.Helper() + m.ctrl.Call(m, "DBTxStart", p) +} + +// DBTxStart indicates an expected call of DBTxStart. +func (mr *MockLoggerMockRecorder) DBTxStart(p interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DBTxStart", reflect.TypeOf((*MockLogger)(nil).DBTxStart), p) +} + +// FinishAuth mocks base method. +func (m *MockLogger) FinishAuth(call *model.AuthCall, uid model.UID, err error) { + m.ctrl.T.Helper() + m.ctrl.Call(m, "FinishAuth", call, uid, err) +} + +// FinishAuth indicates an expected call of FinishAuth. +func (mr *MockLoggerMockRecorder) FinishAuth(call, uid, err interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "FinishAuth", reflect.TypeOf((*MockLogger)(nil).FinishAuth), call, uid, err) +} + +// FinishCall mocks base method. +func (m *MockLogger) FinishCall(call *model.APICall, err error) { + m.ctrl.T.Helper() + m.ctrl.Call(m, "FinishCall", call, err) +} + +// FinishCall indicates an expected call of FinishCall. +func (mr *MockLoggerMockRecorder) FinishCall(call, err interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "FinishCall", reflect.TypeOf((*MockLogger)(nil).FinishCall), call, err) +} + +// FinishRequest mocks base method. +func (m *MockLogger) FinishRequest(req *model.Request, resp *model.Response) { + m.ctrl.T.Helper() + m.ctrl.Call(m, "FinishRequest", req, resp) +} + +// FinishRequest indicates an expected call of FinishRequest. +func (mr *MockLoggerMockRecorder) FinishRequest(req, resp interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "FinishRequest", reflect.TypeOf((*MockLogger)(nil).FinishRequest), req, resp) +} + +// GetAndClear mocks base method. +func (m *MockLogger) GetAndClear() []byte { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetAndClear") + ret0, _ := ret[0].([]byte) + return ret0 +} + +// GetAndClear indicates an expected call of GetAndClear. +func (mr *MockLoggerMockRecorder) GetAndClear() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetAndClear", reflect.TypeOf((*MockLogger)(nil).GetAndClear)) +} + +// GoClear mocks base method. +func (m *MockLogger) GoClear(spanID model.SpanID, goctr uint32) { + m.ctrl.T.Helper() + m.ctrl.Call(m, "GoClear", spanID, goctr) +} + +// GoClear indicates an expected call of GoClear. +func (mr *MockLoggerMockRecorder) GoClear(spanID, goctr interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GoClear", reflect.TypeOf((*MockLogger)(nil).GoClear), spanID, goctr) +} + +// GoEnd mocks base method. +func (m *MockLogger) GoEnd(spanID model.SpanID, goctr uint32) { + m.ctrl.T.Helper() + m.ctrl.Call(m, "GoEnd", spanID, goctr) +} + +// GoEnd indicates an expected call of GoEnd. +func (mr *MockLoggerMockRecorder) GoEnd(spanID, goctr interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GoEnd", reflect.TypeOf((*MockLogger)(nil).GoEnd), spanID, goctr) +} + +// GoStart mocks base method. +func (m *MockLogger) GoStart(spanID model.SpanID, goctr uint32) { + m.ctrl.T.Helper() + m.ctrl.Call(m, "GoStart", spanID, goctr) +} + +// GoStart indicates an expected call of GoStart. +func (mr *MockLoggerMockRecorder) GoStart(spanID, goctr interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GoStart", reflect.TypeOf((*MockLogger)(nil).GoStart), spanID, goctr) +} + +// HTTPBeginRoundTrip mocks base method. +func (m *MockLogger) HTTPBeginRoundTrip(httpReq *http.Request, req *model.Request, goid uint32) (context.Context, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "HTTPBeginRoundTrip", httpReq, req, goid) + ret0, _ := ret[0].(context.Context) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// HTTPBeginRoundTrip indicates an expected call of HTTPBeginRoundTrip. +func (mr *MockLoggerMockRecorder) HTTPBeginRoundTrip(httpReq, req, goid interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "HTTPBeginRoundTrip", reflect.TypeOf((*MockLogger)(nil).HTTPBeginRoundTrip), httpReq, req, goid) +} + +// HTTPCompleteRoundTrip mocks base method. +func (m *MockLogger) HTTPCompleteRoundTrip(req *http.Request, resp *http.Response, err error) { + m.ctrl.T.Helper() + m.ctrl.Call(m, "HTTPCompleteRoundTrip", req, resp, err) +} + +// HTTPCompleteRoundTrip indicates an expected call of HTTPCompleteRoundTrip. +func (mr *MockLoggerMockRecorder) HTTPCompleteRoundTrip(req, resp, err interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "HTTPCompleteRoundTrip", reflect.TypeOf((*MockLogger)(nil).HTTPCompleteRoundTrip), req, resp, err) +} + +// PublishEnd mocks base method. +func (m *MockLogger) PublishEnd(publishID uint64, messageID string, err error) { + m.ctrl.T.Helper() + m.ctrl.Call(m, "PublishEnd", publishID, messageID, err) +} + +// PublishEnd indicates an expected call of PublishEnd. +func (mr *MockLoggerMockRecorder) PublishEnd(publishID, messageID, err interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "PublishEnd", reflect.TypeOf((*MockLogger)(nil).PublishEnd), publishID, messageID, err) +} + +// PublishStart mocks base method. +func (m *MockLogger) PublishStart(topic string, msg []byte, spanID model.SpanID, goid uint32, publishID uint64, skipFrames int) { + m.ctrl.T.Helper() + m.ctrl.Call(m, "PublishStart", topic, msg, spanID, goid, publishID, skipFrames) +} + +// PublishStart indicates an expected call of PublishStart. +func (mr *MockLoggerMockRecorder) PublishStart(topic, msg, spanID, goid, publishID, skipFrames interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "PublishStart", reflect.TypeOf((*MockLogger)(nil).PublishStart), topic, msg, spanID, goid, publishID, skipFrames) +} + +// ServiceInitEnd mocks base method. +func (m *MockLogger) ServiceInitEnd(initCtr uint64, err error) { + m.ctrl.T.Helper() + m.ctrl.Call(m, "ServiceInitEnd", initCtr, err) +} + +// ServiceInitEnd indicates an expected call of ServiceInitEnd. +func (mr *MockLoggerMockRecorder) ServiceInitEnd(initCtr, err interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ServiceInitEnd", reflect.TypeOf((*MockLogger)(nil).ServiceInitEnd), initCtr, err) +} + +// ServiceInitStart mocks base method. +func (m *MockLogger) ServiceInitStart(p trace.ServiceInitStartParams) { + m.ctrl.T.Helper() + m.ctrl.Call(m, "ServiceInitStart", p) +} + +// ServiceInitStart indicates an expected call of ServiceInitStart. +func (mr *MockLoggerMockRecorder) ServiceInitStart(p interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ServiceInitStart", reflect.TypeOf((*MockLogger)(nil).ServiceInitStart), p) +} diff --git a/runtimes/go/appruntime/exported/trace2/events.go b/runtimes/go/appruntime/exported/trace2/events.go index b161febbc4..581743ddae 100644 --- a/runtimes/go/appruntime/exported/trace2/events.go +++ b/runtimes/go/appruntime/exported/trace2/events.go @@ -741,6 +741,303 @@ func (l *Log) BodyStream(p BodyStreamParams) { }) } +type BucketObjectUploadStartParams struct { + EventParams + Bucket string + Object string + Attrs BucketObjectAttributes + Stack stack.Stack +} + +type BucketObjectAttributes struct { + Size *uint64 + Version *string + ETag *string + ContentType *string +} + +func (l *Log) BucketObjectUploadStart(p BucketObjectUploadStartParams) EventID { + tb := l.newEvent(eventData{ + Common: p.EventParams, + ExtraSpace: 64, + }) + + tb.String(p.Bucket) + tb.String(p.Object) + tb.bucketObjectAttrs(&p.Attrs) + tb.Stack(p.Stack) + + return l.Add(Event{ + Type: BucketObjectUploadStart, + TraceID: p.TraceID, + SpanID: p.SpanID, + Data: tb, + }) +} + +func (tb *EventBuffer) bucketObjectAttrs(attrs *BucketObjectAttributes) { + tb.OptUVarint(attrs.Size) + tb.OptString(attrs.Version) + tb.OptString(attrs.ETag) + tb.OptString(attrs.ContentType) +} + +type BucketObjectUploadEndParams struct { + EventParams + StartID EventID + + Err error + // Set iff err == nil + Size uint64 + Version *string +} + +func (l *Log) BucketObjectUploadEnd(p BucketObjectUploadEndParams) { + tb := l.newEvent(eventData{ + Common: p.EventParams, + CorrelationEventID: p.StartID, + ExtraSpace: 64, + }) + + tb.UVarint(p.Size) + tb.OptString(p.Version) + tb.ErrWithStack(p.Err) + + l.Add(Event{ + Type: BucketObjectUploadEnd, + TraceID: p.TraceID, + SpanID: p.SpanID, + Data: tb, + }) +} + +type BucketObjectDownloadStartParams struct { + EventParams + Bucket string + Object string + Version *string + Stack stack.Stack +} + +func (l *Log) BucketObjectDownloadStart(p BucketObjectDownloadStartParams) EventID { + tb := l.newEvent(eventData{ + Common: p.EventParams, + ExtraSpace: 64, + }) + + tb.String(p.Bucket) + tb.String(p.Object) + tb.OptString(p.Version) + tb.Stack(p.Stack) + + return l.Add(Event{ + Type: BucketObjectDownloadStart, + TraceID: p.TraceID, + SpanID: p.SpanID, + Data: tb, + }) +} + +type BucketObjectDownloadEndParams struct { + EventParams + StartID EventID + + Err error + // Set iff err == nil + Size uint64 +} + +func (l *Log) BucketObjectDownloadEnd(p BucketObjectDownloadEndParams) { + tb := l.newEvent(eventData{ + Common: p.EventParams, + CorrelationEventID: p.StartID, + ExtraSpace: 4 + 4 + 8, + }) + + tb.UVarint(p.Size) + tb.ErrWithStack(p.Err) + + l.Add(Event{ + Type: BucketObjectDownloadEnd, + TraceID: p.TraceID, + SpanID: p.SpanID, + Data: tb, + }) +} + +type BucketObjectGetAttrsStartParams struct { + EventParams + Bucket string + Object string + Version *string + Stack stack.Stack +} + +func (l *Log) BucketObjectGetAttrsStart(p BucketObjectGetAttrsStartParams) EventID { + tb := l.newEvent(eventData{ + Common: p.EventParams, + ExtraSpace: 64, + }) + + tb.String(p.Bucket) + tb.String(p.Object) + tb.OptString(p.Version) + tb.Stack(p.Stack) + + return l.Add(Event{ + Type: BucketObjectGetAttrsStart, + TraceID: p.TraceID, + SpanID: p.SpanID, + Data: tb, + }) +} + +type BucketObjectGetAttrsEndParams struct { + EventParams + StartID EventID + + Err error + // Set iff err == nil + Attrs *BucketObjectAttributes +} + +func (l *Log) BucketObjectGetAttrsEnd(p BucketObjectGetAttrsEndParams) { + tb := l.newEvent(eventData{ + Common: p.EventParams, + CorrelationEventID: p.StartID, + ExtraSpace: 4 + 4 + 8, + }) + + tb.ErrWithStack(p.Err) + if p.Err == nil { + tb.bucketObjectAttrs(p.Attrs) + } + + l.Add(Event{ + Type: BucketObjectGetAttrsEnd, + TraceID: p.TraceID, + SpanID: p.SpanID, + Data: tb, + }) +} + +type BucketListObjectsStartParams struct { + EventParams + Bucket string + Prefix *string + Stack stack.Stack +} + +func (l *Log) BucketListObjectsStart(p BucketListObjectsStartParams) EventID { + tb := l.newEvent(eventData{ + Common: p.EventParams, + ExtraSpace: 64, + }) + + tb.String(p.Bucket) + tb.OptString(p.Prefix) + tb.Stack(p.Stack) + + return l.Add(Event{ + Type: BucketListObjectsStart, + TraceID: p.TraceID, + SpanID: p.SpanID, + Data: tb, + }) +} + +type BucketListObjectsEndParams struct { + EventParams + StartID EventID + + Err error + // Set iff err == nil + Observed uint64 + HasMore bool +} + +func (l *Log) BucketListObjectsEnd(p BucketListObjectsEndParams) { + tb := l.newEvent(eventData{ + Common: p.EventParams, + CorrelationEventID: p.StartID, + ExtraSpace: 4 + 4 + 8, + }) + + tb.ErrWithStack(p.Err) + tb.UVarint(p.Observed) + tb.Bool(p.HasMore) + + l.Add(Event{ + Type: BucketListObjectsEnd, + TraceID: p.TraceID, + SpanID: p.SpanID, + Data: tb, + }) +} + +type BucketDeleteObjectsStartParams struct { + EventParams + Bucket string + Objects []BucketDeleteObjectsEntry + Stack stack.Stack +} + +type BucketDeleteObjectsEntry struct { + Object string + Version *string +} + +func (l *Log) BucketDeleteObjectsStart(p BucketDeleteObjectsStartParams) EventID { + tb := l.newEvent(eventData{ + Common: p.EventParams, + ExtraSpace: 64, + }) + + tb.String(p.Bucket) + tb.Stack(p.Stack) + tb.UVarint(uint64(len(p.Objects))) + for _, e := range p.Objects { + tb.String(e.Object) + tb.OptString(e.Version) + } + + return l.Add(Event{ + Type: BucketDeleteObjectsStart, + TraceID: p.TraceID, + SpanID: p.SpanID, + Data: tb, + }) +} + +type BucketDeleteObjectsEndParams struct { + EventParams + StartID EventID + + Err error + // Set iff err == nil + Observed uint64 + HasMore bool +} + +func (l *Log) BucketDeleteObjectsEnd(p BucketDeleteObjectsEndParams) { + tb := l.newEvent(eventData{ + Common: p.EventParams, + CorrelationEventID: p.StartID, + ExtraSpace: 4 + 4 + 8, + }) + + tb.ErrWithStack(p.Err) + tb.UVarint(p.Observed) + tb.Bool(p.HasMore) + + l.Add(Event{ + Type: BucketDeleteObjectsEnd, + TraceID: p.TraceID, + SpanID: p.SpanID, + Data: tb, + }) +} + func (l *Log) logHeaders(tb *EventBuffer, headers http.Header) { tb.UVarint(uint64(len(headers))) for k, v := range headers { diff --git a/runtimes/go/appruntime/exported/trace2/log.go b/runtimes/go/appruntime/exported/trace2/log.go index 2134dc030c..3fc9b1227f 100644 --- a/runtimes/go/appruntime/exported/trace2/log.go +++ b/runtimes/go/appruntime/exported/trace2/log.go @@ -284,6 +284,22 @@ func (tb *EventBuffer) EventID(id EventID) { tb.UVarint(uint64(id)) } +func (tb *EventBuffer) OptString(s *string) { + if s != nil { + tb.String(*s) + } else { + tb.String("") + } +} + +func (tb *EventBuffer) OptUVarint(i *uint64) { + if i != nil { + tb.UVarint(*i) + } else { + tb.UVarint(0) + } +} + func (tb *EventBuffer) Uint64(x uint64) { tb.buf = append(tb.buf, byte(x), diff --git a/runtimes/go/appruntime/exported/trace2/logger.go b/runtimes/go/appruntime/exported/trace2/logger.go index ff574d7aa7..5e0c26f915 100644 --- a/runtimes/go/appruntime/exported/trace2/logger.go +++ b/runtimes/go/appruntime/exported/trace2/logger.go @@ -44,4 +44,15 @@ type Logger interface { LogMessage(LogMessageParams) HTTPBeginRoundTrip(httpReq *http.Request, req *model.Request, goid uint32) (context.Context, error) HTTPCompleteRoundTrip(req *http.Request, resp *http.Response, goid uint32, err error) + + BucketObjectUploadStart(BucketObjectUploadStartParams) EventID + BucketObjectUploadEnd(BucketObjectUploadEndParams) + BucketObjectDownloadStart(BucketObjectDownloadStartParams) EventID + BucketObjectDownloadEnd(BucketObjectDownloadEndParams) + BucketObjectGetAttrsStart(BucketObjectGetAttrsStartParams) EventID + BucketObjectGetAttrsEnd(BucketObjectGetAttrsEndParams) + BucketListObjectsStart(BucketListObjectsStartParams) EventID + BucketListObjectsEnd(BucketListObjectsEndParams) + BucketDeleteObjectsStart(BucketDeleteObjectsStartParams) EventID + BucketDeleteObjectsEnd(BucketDeleteObjectsEndParams) } diff --git a/runtimes/go/appruntime/shared/traceprovider/mock_trace/mock_trace.go b/runtimes/go/appruntime/shared/traceprovider/mock_trace/mock_trace.go index ed610453cc..7e05c7c1d2 100644 --- a/runtimes/go/appruntime/shared/traceprovider/mock_trace/mock_trace.go +++ b/runtimes/go/appruntime/shared/traceprovider/mock_trace/mock_trace.go @@ -89,6 +89,136 @@ func (mr *MockLoggerMockRecorder) BodyStream(arg0 interface{}) *gomock.Call { return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "BodyStream", reflect.TypeOf((*MockLogger)(nil).BodyStream), arg0) } +// BucketDeleteObjectsEnd mocks base method. +func (m *MockLogger) BucketDeleteObjectsEnd(arg0 trace2.BucketDeleteObjectsEndParams) { + m.ctrl.T.Helper() + m.ctrl.Call(m, "BucketDeleteObjectsEnd", arg0) +} + +// BucketDeleteObjectsEnd indicates an expected call of BucketDeleteObjectsEnd. +func (mr *MockLoggerMockRecorder) BucketDeleteObjectsEnd(arg0 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "BucketDeleteObjectsEnd", reflect.TypeOf((*MockLogger)(nil).BucketDeleteObjectsEnd), arg0) +} + +// BucketDeleteObjectsStart mocks base method. +func (m *MockLogger) BucketDeleteObjectsStart(arg0 trace2.BucketDeleteObjectsStartParams) trace2.EventID { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "BucketDeleteObjectsStart", arg0) + ret0, _ := ret[0].(trace2.EventID) + return ret0 +} + +// BucketDeleteObjectsStart indicates an expected call of BucketDeleteObjectsStart. +func (mr *MockLoggerMockRecorder) BucketDeleteObjectsStart(arg0 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "BucketDeleteObjectsStart", reflect.TypeOf((*MockLogger)(nil).BucketDeleteObjectsStart), arg0) +} + +// BucketListObjectsEnd mocks base method. +func (m *MockLogger) BucketListObjectsEnd(arg0 trace2.BucketListObjectsEndParams) { + m.ctrl.T.Helper() + m.ctrl.Call(m, "BucketListObjectsEnd", arg0) +} + +// BucketListObjectsEnd indicates an expected call of BucketListObjectsEnd. +func (mr *MockLoggerMockRecorder) BucketListObjectsEnd(arg0 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "BucketListObjectsEnd", reflect.TypeOf((*MockLogger)(nil).BucketListObjectsEnd), arg0) +} + +// BucketListObjectsStart mocks base method. +func (m *MockLogger) BucketListObjectsStart(arg0 trace2.BucketListObjectsStartParams) trace2.EventID { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "BucketListObjectsStart", arg0) + ret0, _ := ret[0].(trace2.EventID) + return ret0 +} + +// BucketListObjectsStart indicates an expected call of BucketListObjectsStart. +func (mr *MockLoggerMockRecorder) BucketListObjectsStart(arg0 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "BucketListObjectsStart", reflect.TypeOf((*MockLogger)(nil).BucketListObjectsStart), arg0) +} + +// BucketObjectDownloadEnd mocks base method. +func (m *MockLogger) BucketObjectDownloadEnd(arg0 trace2.BucketObjectDownloadEndParams) { + m.ctrl.T.Helper() + m.ctrl.Call(m, "BucketObjectDownloadEnd", arg0) +} + +// BucketObjectDownloadEnd indicates an expected call of BucketObjectDownloadEnd. +func (mr *MockLoggerMockRecorder) BucketObjectDownloadEnd(arg0 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "BucketObjectDownloadEnd", reflect.TypeOf((*MockLogger)(nil).BucketObjectDownloadEnd), arg0) +} + +// BucketObjectDownloadStart mocks base method. +func (m *MockLogger) BucketObjectDownloadStart(arg0 trace2.BucketObjectDownloadStartParams) trace2.EventID { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "BucketObjectDownloadStart", arg0) + ret0, _ := ret[0].(trace2.EventID) + return ret0 +} + +// BucketObjectDownloadStart indicates an expected call of BucketObjectDownloadStart. +func (mr *MockLoggerMockRecorder) BucketObjectDownloadStart(arg0 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "BucketObjectDownloadStart", reflect.TypeOf((*MockLogger)(nil).BucketObjectDownloadStart), arg0) +} + +// BucketObjectGetAttrsEnd mocks base method. +func (m *MockLogger) BucketObjectGetAttrsEnd(arg0 trace2.BucketObjectGetAttrsEndParams) { + m.ctrl.T.Helper() + m.ctrl.Call(m, "BucketObjectGetAttrsEnd", arg0) +} + +// BucketObjectGetAttrsEnd indicates an expected call of BucketObjectGetAttrsEnd. +func (mr *MockLoggerMockRecorder) BucketObjectGetAttrsEnd(arg0 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "BucketObjectGetAttrsEnd", reflect.TypeOf((*MockLogger)(nil).BucketObjectGetAttrsEnd), arg0) +} + +// BucketObjectGetAttrsStart mocks base method. +func (m *MockLogger) BucketObjectGetAttrsStart(arg0 trace2.BucketObjectGetAttrsStartParams) trace2.EventID { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "BucketObjectGetAttrsStart", arg0) + ret0, _ := ret[0].(trace2.EventID) + return ret0 +} + +// BucketObjectGetAttrsStart indicates an expected call of BucketObjectGetAttrsStart. +func (mr *MockLoggerMockRecorder) BucketObjectGetAttrsStart(arg0 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "BucketObjectGetAttrsStart", reflect.TypeOf((*MockLogger)(nil).BucketObjectGetAttrsStart), arg0) +} + +// BucketObjectUploadEnd mocks base method. +func (m *MockLogger) BucketObjectUploadEnd(arg0 trace2.BucketObjectUploadEndParams) { + m.ctrl.T.Helper() + m.ctrl.Call(m, "BucketObjectUploadEnd", arg0) +} + +// BucketObjectUploadEnd indicates an expected call of BucketObjectUploadEnd. +func (mr *MockLoggerMockRecorder) BucketObjectUploadEnd(arg0 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "BucketObjectUploadEnd", reflect.TypeOf((*MockLogger)(nil).BucketObjectUploadEnd), arg0) +} + +// BucketObjectUploadStart mocks base method. +func (m *MockLogger) BucketObjectUploadStart(arg0 trace2.BucketObjectUploadStartParams) trace2.EventID { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "BucketObjectUploadStart", arg0) + ret0, _ := ret[0].(trace2.EventID) + return ret0 +} + +// BucketObjectUploadStart indicates an expected call of BucketObjectUploadStart. +func (mr *MockLoggerMockRecorder) BucketObjectUploadStart(arg0 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "BucketObjectUploadStart", reflect.TypeOf((*MockLogger)(nil).BucketObjectUploadStart), arg0) +} + // CacheCallEnd mocks base method. func (m *MockLogger) CacheCallEnd(arg0 trace2.CacheCallEndParams) { m.ctrl.T.Helper() From 580d55d88271161faafebd5f9d711cf9b821645d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Eriksson?= Date: Wed, 6 Nov 2024 11:39:28 +0100 Subject: [PATCH 21/37] v2/parser: add bucket parsing --- v2/app/validate.go | 5 ++ v2/app/validate_objects.go | 38 ++++++++ v2/parser/infra/objects/bucket.go | 89 +++++++++++++++++++ v2/parser/infra/objects/errors.go | 58 +++++++++++++ v2/parser/infra/objects/usage.go | 119 ++++++++++++++++++++++++++ v2/parser/infra/objects/usage_test.go | 91 ++++++++++++++++++++ v2/parser/parser.go | 3 + v2/parser/resource/resource.go | 1 + v2/parser/resource/resource_string.go | 13 +-- 9 files changed, 411 insertions(+), 6 deletions(-) create mode 100644 v2/app/validate_objects.go create mode 100644 v2/parser/infra/objects/bucket.go create mode 100644 v2/parser/infra/objects/errors.go create mode 100644 v2/parser/infra/objects/usage.go create mode 100644 v2/parser/infra/objects/usage_test.go diff --git a/v2/app/validate.go b/v2/app/validate.go index 856db7895f..3a9b401245 100644 --- a/v2/app/validate.go +++ b/v2/app/validate.go @@ -7,6 +7,7 @@ import ( "encr.dev/v2/parser" "encr.dev/v2/parser/apis/authhandler" "encr.dev/v2/parser/apis/middleware" + "encr.dev/v2/parser/infra/objects" "encr.dev/v2/parser/infra/pubsub" "encr.dev/v2/parser/infra/secrets" "encr.dev/v2/parser/infra/sqldb" @@ -30,6 +31,7 @@ func (d *Desc) validate(pc *parsectx.Context, result *parser.Result) { d.validateCrons(pc, result) d.validateDatabases(pc, result) d.validatePubSub(pc, result) + d.validateObjects(pc, result) // Validate all resources are defined within a service for _, b := range result.AllBinds() { @@ -38,6 +40,9 @@ func (d *Desc) validate(pc *parsectx.Context, result *parser.Result) { case *pubsub.Topic: // We allow pubsub topics to be declared outside of service code continue + case *objects.Bucket: + // We allow buckets to be declared outside of service code + continue case *middleware.Middleware: // Middleware is also allowed to be declared outside of service code if it's global (validateMiddleware checks this already) continue diff --git a/v2/app/validate_objects.go b/v2/app/validate_objects.go new file mode 100644 index 0000000000..def00ffc39 --- /dev/null +++ b/v2/app/validate_objects.go @@ -0,0 +1,38 @@ +package app + +import ( + "encr.dev/pkg/errors" + "encr.dev/v2/internals/parsectx" + "encr.dev/v2/parser" + "encr.dev/v2/parser/infra/objects" +) + +func (d *Desc) validateObjects(pc *parsectx.Context, result *parser.Result) { + buckets := make(map[string]*objects.Bucket) + + for _, res := range d.Parse.Resources() { + switch res := res.(type) { + case *objects.Bucket: + if existing, ok := buckets[res.Name]; ok { + pc.Errs.Add(objects.ErrBucketNameNotUnique. + AtGoNode(existing.AST.Args[0], errors.AsHelp("originally defined here")). + AtGoNode(res.AST.Args[0], errors.AsError("duplicated here")), + ) + } else { + buckets[res.Name] = res + } + + // Make sure any BucketRef calls are within a service. + for _, use := range d.Parse.Usages(res) { + if _, ok := use.(*objects.RefUsage); ok { + errTxt := "used here" + if _, ok := d.ServiceForPath(use.DeclaredIn().FSPath); !ok && !use.DeclaredIn().TestFile { + pc.Errs.Add(objects.ErrBucketRefOutsideService. + AtGoNode(use, errors.AsError(errTxt)), + ) + } + } + } + } + } +} diff --git a/v2/parser/infra/objects/bucket.go b/v2/parser/infra/objects/bucket.go new file mode 100644 index 0000000000..47c13c7e8d --- /dev/null +++ b/v2/parser/infra/objects/bucket.go @@ -0,0 +1,89 @@ +package objects + +import ( + "go/ast" + "go/token" + + "encr.dev/pkg/paths" + "encr.dev/v2/internals/pkginfo" + literals "encr.dev/v2/parser/infra/internal/literals" + parseutil "encr.dev/v2/parser/infra/internal/parseutil" + "encr.dev/v2/parser/resource" + "encr.dev/v2/parser/resource/resourceparser" +) + +type Bucket struct { + AST *ast.CallExpr + File *pkginfo.File + Name string // The unique name of the bucket + Doc string // The documentation on the bucket + Versioned bool +} + +func (t *Bucket) Kind() resource.Kind { return resource.Bucket } +func (t *Bucket) Package() *pkginfo.Package { return t.File.Pkg } +func (t *Bucket) ASTExpr() ast.Expr { return t.AST } +func (t *Bucket) ResourceName() string { return t.Name } +func (t *Bucket) Pos() token.Pos { return t.AST.Pos() } +func (t *Bucket) End() token.Pos { return t.AST.End() } +func (t *Bucket) SortKey() string { return t.Name } + +var BucketParser = &resourceparser.Parser{ + Name: "Bucket", + + InterestingImports: []paths.Pkg{"encore.dev/storage/objects"}, + Run: func(p *resourceparser.Pass) { + name := pkginfo.QualifiedName{Name: "NewBucket", PkgPath: "encore.dev/storage/objects"} + + spec := &parseutil.ReferenceSpec{ + MinTypeArgs: 0, + MaxTypeArgs: 0, + Parse: parseBucket, + } + + parseutil.FindPkgNameRefs(p.Pkg, []pkginfo.QualifiedName{name}, func(file *pkginfo.File, name pkginfo.QualifiedName, stack []ast.Node) { + parseutil.ParseReference(p, spec, parseutil.ReferenceData{ + File: file, + Stack: stack, + ResourceFunc: name, + }) + }) + }, +} + +func parseBucket(d parseutil.ReferenceInfo) { + errs := d.Pass.Errs + + if len(d.Call.Args) != 2 { + errs.Add(errNewBucketArgCount(len(d.Call.Args)).AtGoNode(d.Call)) + return + } + + bucketName := parseutil.ParseResourceName(d.Pass.Errs, "objects.NewBucket", "bucket name", + d.Call.Args[0], parseutil.KebabName, "") + if bucketName == "" { + // we already reported the error inside ParseResourceName + return + } + + cfgLit, ok := literals.ParseStruct(d.Pass.Errs, d.File, "objects.BucketConfig", d.Call.Args[1]) + if !ok { + return // error reported by ParseStruct + } + + // Decode the config + type decodedConfig struct { + Versioned bool `literal:",optional"` + } + config := literals.Decode[decodedConfig](d.Pass.Errs, cfgLit, nil) + + bkt := &Bucket{ + AST: d.Call, + File: d.File, + Name: bucketName, + Doc: d.Doc, + Versioned: config.Versioned, + } + d.Pass.RegisterResource(bkt) + d.Pass.AddBind(d.File, d.Ident, bkt) +} diff --git a/v2/parser/infra/objects/errors.go b/v2/parser/infra/objects/errors.go new file mode 100644 index 0000000000..0875f768c1 --- /dev/null +++ b/v2/parser/infra/objects/errors.go @@ -0,0 +1,58 @@ +package objects + +import ( + "encr.dev/pkg/errors" +) + +const ( + objectsNewBucketHelp = "For example `objects.NewBucket(\"my-bucket\", objects.BucketConfig{ Versioned: false })`" + + objectsBucketUsageHelp = "The bucket can only be referenced by calling methods on it, or by using objects.BucketRef." +) + +var ( + errRange = errors.Range( + "pubsub", + "For more information on Object Storage, see https://encore.dev/docs/primitives/object-storage", + ) + + errNewBucketArgCount = errRange.Newf( + "Invalid objects.NewBucket call", + "A call to objects.NewBucket requires 2 arguments; the bucket name and the config object, got %d arguments.", + errors.PrependDetails(objectsNewBucketHelp), + ) + + errInvalidBucketUsage = errRange.New( + "Invalid reference to objects.Bucket", + "A reference to an objects.Bucket is not permissible here.", + errors.PrependDetails(objectsBucketUsageHelp), + ) + + ErrBucketNameNotUnique = errRange.New( + "Duplicate bucket name", + "An object storage bucket name must be unique.", + + errors.PrependDetails("If you wish to reuse the same bucket, then you can export the original Bucket object and reference it from here."), + ) + + ErrUnableToIdentifyServicesInvolved = errRange.New( + "Unable to identify services involved", + "Unable to identify services involved in the PubSub subscription.", + errors.MarkAsInternalError(), + ) + + errBucketRefNoTypeArgs = errRange.New( + "Invalid call to objects.BucketRef", + "A type argument indicating the requested permissions must be provided.", + ) + + errBucketRefInvalidPerms = errRange.New( + "Unrecognized permissions in call to objects.BucketRef", + "The supported permissions are objects.Writer/Reader.", + ) + + ErrBucketRefOutsideService = errRange.New( + "Call to objects.BucketRef outside service", + "objects.BucketRef can only be called from within a service.", + ) +) diff --git a/v2/parser/infra/objects/usage.go b/v2/parser/infra/objects/usage.go new file mode 100644 index 0000000000..b69bd8a903 --- /dev/null +++ b/v2/parser/infra/objects/usage.go @@ -0,0 +1,119 @@ +package objects + +import ( + "encr.dev/pkg/option" + "encr.dev/v2/internals/perr" + "encr.dev/v2/internals/pkginfo" + "encr.dev/v2/internals/schema" + "encr.dev/v2/internals/schema/schemautil" + "encr.dev/v2/parser/resource/usage" +) + +type MethodUsage struct { + usage.Base + Perm Perm +} + +type RefUsage struct { + usage.Base + Perms []Perm +} + +func (u *RefUsage) HasPerm(perm Perm) bool { + for _, p := range u.Perms { + if p == perm { + return true + } + } + return false +} + +type Perm string + +const ( + ListObjects Perm = "list-objects" + ReadObjectContents Perm = "read-object-contents" + WriteObject Perm = "write-object" + UpdateObjectMetadata Perm = "update-object-metadata" + GetObjectMetadata Perm = "get-object-metadata" + DeleteObject Perm = "delete-object" +) + +func ResolveBucketUsage(data usage.ResolveData, topic *Bucket) usage.Usage { + switch expr := data.Expr.(type) { + case *usage.MethodCall: + var perm Perm + switch expr.Method { + case "Upload": + perm = WriteObject + default: + return nil + } + return &MethodUsage{ + Base: usage.Base{ + File: expr.File, + Bind: expr.Bind, + Expr: expr, + }, + Perm: perm, + } + + case *usage.FuncArg: + switch { + case option.Contains(expr.PkgFunc, pkginfo.Q("encore.dev/storage/objects", "BucketRef")): + return parseBucketRef(data.Errs, expr) + } + } + + data.Errs.Add(errInvalidBucketUsage.AtGoNode(data.Expr)) + return nil +} + +func parseBucketRef(errs *perr.List, expr *usage.FuncArg) usage.Usage { + if len(expr.TypeArgs) < 1 { + errs.Add(errBucketRefNoTypeArgs.AtGoNode(expr.Call)) + return nil + } + + checkUsage := func(typ schema.Type) (usage.Usage, bool) { + if schemautil.IsNamed(typ, "encore.dev/storage/objects", "Uploader") { + return &RefUsage{ + Base: usage.Base{ + File: expr.File, + Bind: expr.Bind, + Expr: expr, + }, + Perms: []Perm{WriteObject}, + }, true + } + return nil, false + } + + // Do we have a simple usage directly as the type argument? + if u, ok := checkUsage(expr.TypeArgs[0]); ok { + return u + } + + // Determine if we have a custom ref type, + // either in the form "type Foo = pubsub.Publisher[Msg]" + // or in the form "type Foo interface { pubsub.Publisher[Msg] }" + if named, ok := expr.TypeArgs[0].(schema.NamedType); ok { + underlying := named.Decl().Type + if u, ok := checkUsage(underlying); ok { + return u + } + + // Otherwise make sure the interface only embeds the one supported type we have (pubsub.Publisher). + // We'll need to extend this in the future to support multiple permissions. + if iface, ok := underlying.(schema.InterfaceType); ok { + if len(iface.EmbeddedIfaces) == 1 && len(iface.Methods) == 0 && len(iface.TypeLists) == 0 { + if u, ok := checkUsage(iface.EmbeddedIfaces[0]); ok { + return u + } + } + } + } + + errs.Add(errBucketRefInvalidPerms.AtGoNode(expr.Call)) + return nil +} diff --git a/v2/parser/infra/objects/usage_test.go b/v2/parser/infra/objects/usage_test.go new file mode 100644 index 0000000000..6434583572 --- /dev/null +++ b/v2/parser/infra/objects/usage_test.go @@ -0,0 +1,91 @@ +package objects_test + +import ( + "testing" + + "encr.dev/v2/parser/infra/objects" + "encr.dev/v2/parser/resource/usage" + "encr.dev/v2/parser/resource/usage/usagetest" +) + +func TestResolveBucketUsage(t *testing.T) { + tests := []usagetest.Case{ + { + Name: "none", + Code: ` +var bkt = objects.NewBucket("bucket", objects.BucketConfig{}) + +`, + Want: []usage.Usage{}, + }, + { + Name: "publish", + Code: ` +var bkt = objects.NewBucket("bucket", objects.BucketConfig{}) + +func Foo() { bkt.Upload(context.Background(), "key") } + +`, + Want: []usage.Usage{&objects.MethodUsage{Perm: objects.WriteObject}}, + }, + { + Name: "ref", + Code: ` +var bkt = objects.NewBucket("bucket", objects.BucketConfig{}) + +var ref = objects.BucketRef[objects.Uploader](bkt) +`, + Want: []usage.Usage{&objects.RefUsage{ + Perms: []objects.Perm{objects.WriteObject}, + }}, + }, + { + Name: "custom_ref_alias", + Code: ` +var bkt = objects.NewBucket("bucket", objects.BucketConfig{}) + +type MyRef = objects.Uploader + +var ref = objects.BucketRef[MyRef](bkt) +`, + Want: []usage.Usage{&objects.RefUsage{ + Perms: []objects.Perm{objects.WriteObject}, + }}, + }, + { + Name: "custom_ref_interface", + Code: ` +var bkt = objects.NewBucket("bucket", objects.BucketConfig{}) + +type MyRef interface { objects.Uploader } + +var ref = objects.BucketRef[MyRef](bkt) +`, + Want: []usage.Usage{&objects.RefUsage{ + Perms: []objects.Perm{objects.WriteObject}, + }}, + }, + { + Name: "invalid_ref", + Code: ` +var bkt = objects.NewBucket("bucket", objects.BucketConfig{}) + +type MyRef interface { objects.Uploader; ~int | string; Publish() int } + +var ref = objects.BucketRef[MyRef](bkt) +`, + WantErrs: []string{"Unrecognized permissions in call to objects.BucketRef"}, + }, + { + Name: "invalid_ref_2", + Code: ` +var bkt = objects.NewBucket("bucket", objects.BucketConfig{}) + +var ref = objects.BucketRef[string](bkt) +`, + WantErrs: []string{"Unrecognized permissions in call to objects.BucketRef"}, + }, + } + + usagetest.Run(t, []string{"encore.dev/storage/objects"}, tests) +} diff --git a/v2/parser/parser.go b/v2/parser/parser.go index 0d11a4e37c..898ba2a755 100644 --- a/v2/parser/parser.go +++ b/v2/parser/parser.go @@ -18,6 +18,7 @@ import ( "encr.dev/v2/parser/infra/config" "encr.dev/v2/parser/infra/crons" "encr.dev/v2/parser/infra/metrics" + "encr.dev/v2/parser/infra/objects" "encr.dev/v2/parser/infra/pubsub" "encr.dev/v2/parser/infra/secrets" "encr.dev/v2/parser/infra/sqldb" @@ -148,6 +149,7 @@ var allParsers = []*resourceparser.Parser{ sqldb.DatabaseParser, sqldb.MigrationParser, sqldb.NamedParser, + objects.BucketParser, } func newUsageResolver() *usage.Resolver { @@ -157,6 +159,7 @@ func newUsageResolver() *usage.Resolver { usage.RegisterUsageResolver[*config.Load](r, config.ResolveConfigUsage) usage.RegisterUsageResolver[*pubsub.Topic](r, pubsub.ResolveTopicUsage) usage.RegisterUsageResolver[*sqldb.Database](r, sqldb.ResolveDatabaseUsage) + usage.RegisterUsageResolver[*objects.Bucket](r, objects.ResolveBucketUsage) // API Framework usage.RegisterUsageResolver[*api.Endpoint](r, api.ResolveEndpointUsage) diff --git a/v2/parser/resource/resource.go b/v2/parser/resource/resource.go index a37aa3c00b..4dc281211a 100644 --- a/v2/parser/resource/resource.go +++ b/v2/parser/resource/resource.go @@ -21,6 +21,7 @@ const ( CacheKeyspace ConfigLoad Secrets + Bucket // API Framework Resources APIEndpoint diff --git a/v2/parser/resource/resource_string.go b/v2/parser/resource/resource_string.go index a9916a1788..bd1eaac365 100644 --- a/v2/parser/resource/resource_string.go +++ b/v2/parser/resource/resource_string.go @@ -18,15 +18,16 @@ func _() { _ = x[CacheKeyspace-7] _ = x[ConfigLoad-8] _ = x[Secrets-9] - _ = x[APIEndpoint-10] - _ = x[AuthHandler-11] - _ = x[Middleware-12] - _ = x[ServiceStruct-13] + _ = x[Bucket-10] + _ = x[APIEndpoint-11] + _ = x[AuthHandler-12] + _ = x[Middleware-13] + _ = x[ServiceStruct-14] } -const _Kind_name = "UnknownPubSubTopicPubSubSubscriptionSQLDatabaseMetricCronJobCacheClusterCacheKeyspaceConfigLoadSecretsAPIEndpointAuthHandlerMiddlewareServiceStruct" +const _Kind_name = "UnknownPubSubTopicPubSubSubscriptionSQLDatabaseMetricCronJobCacheClusterCacheKeyspaceConfigLoadSecretsBucketAPIEndpointAuthHandlerMiddlewareServiceStruct" -var _Kind_index = [...]uint8{0, 7, 18, 36, 47, 53, 60, 72, 85, 95, 102, 113, 124, 134, 147} +var _Kind_index = [...]uint8{0, 7, 18, 36, 47, 53, 60, 72, 85, 95, 102, 108, 119, 130, 140, 153} func (i Kind) String() string { if i < 0 || i >= Kind(len(_Kind_index)-1) { From 89d24cd1ac16edbd7d5095cf70a4d82c4f1eb2af Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Eriksson?= Date: Wed, 6 Nov 2024 11:39:46 +0100 Subject: [PATCH 22/37] wip object storage impl --- runtimes/go/go.mod | 1 + runtimes/go/go.sum | 6 + runtimes/go/storage/objects/bucket.go | 122 ++++++++++++++++++ .../go/storage/objects/internal/gcp/bucket.go | 80 ++++++++++++ .../go/storage/objects/internal/noop/noop.go | 28 ++++ .../storage/objects/internal/types/private.go | 25 ++++ .../storage/objects/internal/types/public.go | 9 ++ .../go/storage/objects/manager_internal.go | 68 ++++++++++ runtimes/go/storage/objects/objects.go | 10 ++ runtimes/go/storage/objects/package.go | 6 + runtimes/go/storage/objects/provider_gcp.go | 13 ++ runtimes/go/storage/objects/refs.go | 53 ++++++++ runtimes/go/storage/objects/types.go | 7 + .../storage/objects/zzz_singleton_internal.go | 24 ++++ 14 files changed, 452 insertions(+) create mode 100644 runtimes/go/storage/objects/bucket.go create mode 100644 runtimes/go/storage/objects/internal/gcp/bucket.go create mode 100644 runtimes/go/storage/objects/internal/noop/noop.go create mode 100644 runtimes/go/storage/objects/internal/types/private.go create mode 100644 runtimes/go/storage/objects/internal/types/public.go create mode 100644 runtimes/go/storage/objects/manager_internal.go create mode 100644 runtimes/go/storage/objects/objects.go create mode 100644 runtimes/go/storage/objects/package.go create mode 100644 runtimes/go/storage/objects/provider_gcp.go create mode 100644 runtimes/go/storage/objects/refs.go create mode 100644 runtimes/go/storage/objects/types.go create mode 100644 runtimes/go/storage/objects/zzz_singleton_internal.go diff --git a/runtimes/go/go.mod b/runtimes/go/go.mod index 4587459864..471cd6637e 100644 --- a/runtimes/go/go.mod +++ b/runtimes/go/go.mod @@ -6,6 +6,7 @@ require ( cloud.google.com/go/compute/metadata v0.5.0 cloud.google.com/go/monitoring v1.20.4 cloud.google.com/go/pubsub v1.41.0 + cloud.google.com/go/storage v1.41.0 github.com/Azure/azure-sdk-for-go/sdk/azcore v1.1.3 github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.1.0 github.com/Azure/azure-sdk-for-go/sdk/messaging/azservicebus v1.1.0 diff --git a/runtimes/go/go.sum b/runtimes/go/go.sum index da75bfaa7b..b9ea92a64e 100644 --- a/runtimes/go/go.sum +++ b/runtimes/go/go.sum @@ -17,6 +17,8 @@ cloud.google.com/go/monitoring v1.20.4 h1:zwcViK7mT9SV0kzKqLOI3spRadvsmvw/R9z1MH cloud.google.com/go/monitoring v1.20.4/go.mod h1:v7F/UcLRw15EX7xq565N7Ae5tnYEE28+Cl717aTXG4c= cloud.google.com/go/pubsub v1.41.0 h1:ZPaM/CvTO6T+1tQOs/jJ4OEMpjtel0PTLV7j1JK+ZrI= cloud.google.com/go/pubsub v1.41.0/go.mod h1:g+YzC6w/3N91tzG66e2BZtp7WrpBBMXVa3Y9zVoOGpk= +cloud.google.com/go/storage v1.41.0 h1:RusiwatSu6lHeEXe3kglxakAmAbfV+rhtPqA6i8RBx0= +cloud.google.com/go/storage v1.41.0/go.mod h1:J1WCa/Z2FcgdEDuPUY8DxT5I+d9mFKsCepp5vR6Sq80= github.com/Azure/azure-sdk-for-go/sdk/azcore v1.1.3 h1:8LoU8N2lIUzkmstvwXvVfniMZlFbesfT2AmA1aqvRr8= github.com/Azure/azure-sdk-for-go/sdk/azcore v1.1.3/go.mod h1:uGG2W01BaETf0Ozp+QxxKJdMBNRWPdstHG0Fmdwn1/U= github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.1.0 h1:QkAcEIAKbNL4KoFr4SathZPhDhF4mVwpBMFlYjyAqy8= @@ -149,6 +151,8 @@ github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeN github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/google/martian/v3 v3.3.3 h1:DIhPTQrbPkgs2yJYdXU/eNACCG5DVQjySNRNlflZ9Fc= +github.com/google/martian/v3 v3.3.3/go.mod h1:iEPrYcgCF7jA9OtScMFQyAlZZ4YXTKEtJ1E6RWzmBA0= github.com/google/s2a-go v0.1.8 h1:zZDs9gcbt9ZPLV0ndSyQk6Kacx2g/X+SKYovpnz3SMM= github.com/google/s2a-go v0.1.8/go.mod h1:6iNWHTpQ+nfNRN5E00MSdfDwVesa8hhS32PhPO8deJA= github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= @@ -329,6 +333,8 @@ golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8T golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028 h1:+cNy6SZtPcJQH3LJVLOSmiC7MMxXNOb3PU/VUEz+EhU= +golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028/go.mod h1:NDW/Ps6MPRej6fsCIbMTohpP40sJ/P/vI1MoTEGwX90= google.golang.org/api v0.191.0 h1:cJcF09Z+4HAB2t5qTQM1ZtfL/PemsLFkcFG67qq2afk= google.golang.org/api v0.191.0/go.mod h1:tD5dsFGxFza0hnQveGfVk9QQYKcfp+VzgRqyXFxE0+E= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= diff --git a/runtimes/go/storage/objects/bucket.go b/runtimes/go/storage/objects/bucket.go new file mode 100644 index 0000000000..9ed7e31431 --- /dev/null +++ b/runtimes/go/storage/objects/bucket.go @@ -0,0 +1,122 @@ +package objects + +import ( + "context" + + "encore.dev/appruntime/exported/config" + "encore.dev/storage/objects/internal/noop" + "encore.dev/storage/objects/internal/types" +) + +// Bucket represents an object storage bucket, containing a set of files. +// +// See NewBucket for more information on how to declare a Bucket. +type Bucket struct { + mgr *Manager + staticCfg BucketConfig // The config as defined in the applications source code + runtimeCfg *config.Bucket // The config for this running instance of the application + impl types.BucketImpl +} + +func newBucket(mgr *Manager, name string, cfg BucketConfig) *Bucket { + // if mgr.static.Testing { + // return &Bucket{ + // staticCfg: cfg, + // mgr: mgr, + // runtimeCfg: &config.Bucket{EncoreName: name}, + // impl: test.NewBucket(mgr.ts, name), + // } + // } + + // Look up the bkt configuration + bkt, ok := mgr.runtime.Buckets[name] + if !ok { + // If we don't have a topic configuration for this topic, it means that the topic was not registered for this instance + // thus we should default to the noop implementation. + return &Bucket{ + staticCfg: cfg, + mgr: mgr, + runtimeCfg: &config.Bucket{EncoreName: name}, + impl: &noop.BucketImpl{}, + } + } + + // Look up the provider config + provider := mgr.runtime.BucketProviders[bkt.ProviderID] + + tried := make([]string, 0, len(mgr.providers)) + for _, p := range mgr.providers { + if p.Matches(provider) { + impl := p.NewBucket(provider, cfg, bkt) + return &Bucket{ + staticCfg: cfg, + mgr: mgr, + runtimeCfg: bkt, + impl: impl, + } + } + tried = append(tried, p.ProviderName()) + } + + mgr.rootLogger.Fatal().Msgf("unsupported Object Storage provider for provider[%d], tried: %v", + bkt.ProviderID, tried) + panic("unreachable") +} + +// BucketMeta contains metadata about a bucket. +// The fields should not be modified by the caller. +// Additional fields may be added in the future. +type BucketMeta struct { + // Name is the name of the bucket, as provided in the constructor to NewTopic. + Name string + // Config is the bucket's configuration. + Config BucketConfig +} + +// Meta returns metadata about the topic. +func (b *Bucket) Meta() BucketMeta { + return BucketMeta{ + Name: b.runtimeCfg.EncoreName, + Config: b.staticCfg, + } +} + +func (b *Bucket) Upload(ctx context.Context, object string) *Writer { + return &Writer{ + bkt: b, + + ctx: ctx, + obj: object, + } +} + +type Writer struct { + bkt *Bucket + + ctx context.Context + obj string + + // Initialized on first write + u types.Uploader +} + +func (w *Writer) Write(p []byte) (int, error) { + u := w.initUpload() + return u.Write(p) +} + +func (w *Writer) Close() error { + u := w.initUpload() + _, err := u.Complete() + return err +} + +func (w *Writer) initUpload() types.Uploader { + if w.u == nil { + w.u = w.bkt.impl.NewUpload(types.UploadData{ + Ctx: w.ctx, + Object: w.obj, + }) + } + return w.u +} diff --git a/runtimes/go/storage/objects/internal/gcp/bucket.go b/runtimes/go/storage/objects/internal/gcp/bucket.go new file mode 100644 index 0000000000..328990fb2a --- /dev/null +++ b/runtimes/go/storage/objects/internal/gcp/bucket.go @@ -0,0 +1,80 @@ +package gcp + +import ( + "context" + "fmt" + "strconv" + + "cloud.google.com/go/storage" + + "encore.dev/appruntime/exported/config" + "encore.dev/storage/objects/internal/types" +) + +type Manager struct { + ctx context.Context + runtime *config.Runtime + client *storage.Client +} + +func NewManager(ctx context.Context, static *config.Static, runtime *config.Runtime) *Manager { + client, err := storage.NewClient(ctx) + if err != nil { + panic(fmt.Sprintf("failed to create object storage client: %s", err)) + } + return &Manager{ctx: ctx, runtime: runtime, client: client} +} + +type bucket struct { + mgr *Manager + cfg *config.Bucket + handle *storage.BucketHandle +} + +func (mgr *Manager) ProviderName() string { return "gcp" } + +func (mgr *Manager) Matches(cfg *config.BucketProvider) bool { + return cfg.GCS != nil +} + +func (mgr *Manager) NewBucket(provider *config.BucketProvider, staticCfg types.BucketConfig, runtimeCfg *config.Bucket) types.BucketImpl { + handle := mgr.client.Bucket(runtimeCfg.CloudName) + return &bucket{mgr, runtimeCfg, handle} +} + +func (b *bucket) NewUpload(data types.UploadData) types.Uploader { + ctx, cancel := context.WithCancelCause(data.Ctx) + w := b.handle.Object(data.Object).NewWriter(ctx) + // TODO set ChunkSize, attributes, etc + + u := &uploader{ + cancel: cancel, + w: w, + } + return u +} + +type uploader struct { + cancel context.CancelCauseFunc + w *storage.Writer +} + +func (u *uploader) Write(p []byte) (int, error) { + return u.w.Write(p) +} + +func (u *uploader) Complete() (*types.Attrs, error) { + if err := u.w.Close(); err != nil { + return nil, err + } + + attrs := u.w.Attrs() + return &types.Attrs{ + Version: strconv.FormatInt(attrs.Generation, 10), + // TODO fill this in + }, nil +} + +func (u *uploader) Abort(err error) { + u.cancel(err) +} diff --git a/runtimes/go/storage/objects/internal/noop/noop.go b/runtimes/go/storage/objects/internal/noop/noop.go new file mode 100644 index 0000000000..d3af4c17ee --- /dev/null +++ b/runtimes/go/storage/objects/internal/noop/noop.go @@ -0,0 +1,28 @@ +package noop + +import ( + "fmt" + + "encore.dev/storage/objects/internal/types" +) + +type BucketImpl struct { + EncoreName string +} + +func (b *BucketImpl) NewUpload(data types.UploadData) types.Uploader { + return &noopUploader{} +} + +type noopUploader struct{} + +func (*noopUploader) Write(p []byte) (int, error) { + return 0, fmt.Errorf("cannot upload to noop bucket") +} + +func (*noopUploader) Complete() (*types.Attrs, error) { + return nil, fmt.Errorf("cannot upload to noop bucket") +} + +func (*noopUploader) Abort(err error) { +} diff --git a/runtimes/go/storage/objects/internal/types/private.go b/runtimes/go/storage/objects/internal/types/private.go new file mode 100644 index 0000000000..85f3cdb36a --- /dev/null +++ b/runtimes/go/storage/objects/internal/types/private.go @@ -0,0 +1,25 @@ +package types + +import ( + "context" + "io" +) + +type Uploader interface { + io.Writer + Abort(err error) + Complete() (*Attrs, error) +} + +type Attrs struct { + Version string +} + +type BucketImpl interface { + NewUpload(data UploadData) Uploader +} + +type UploadData struct { + Ctx context.Context + Object string +} diff --git a/runtimes/go/storage/objects/internal/types/public.go b/runtimes/go/storage/objects/internal/types/public.go new file mode 100644 index 0000000000..e330b537d0 --- /dev/null +++ b/runtimes/go/storage/objects/internal/types/public.go @@ -0,0 +1,9 @@ +package types + +type BucketConfig struct { + // Whether objects stored in the bucket should be versioned. + // + // If true, the bucket will store multiple versions of each object + // whenever it changes, as opposed to overwriting the old version. + Versioned bool +} diff --git a/runtimes/go/storage/objects/manager_internal.go b/runtimes/go/storage/objects/manager_internal.go new file mode 100644 index 0000000000..51528db8ad --- /dev/null +++ b/runtimes/go/storage/objects/manager_internal.go @@ -0,0 +1,68 @@ +package objects + +import ( + "context" + + jsoniter "github.com/json-iterator/go" + "github.com/rs/zerolog" + + "encore.dev/appruntime/exported/config" + "encore.dev/appruntime/shared/reqtrack" + "encore.dev/appruntime/shared/shutdown" + "encore.dev/appruntime/shared/testsupport" + "encore.dev/storage/objects/internal/types" +) + +type Manager struct { + ctx context.Context + cancelCtx func() + static *config.Static + runtime *config.Runtime + rt *reqtrack.RequestTracker + ts *testsupport.Manager + rootLogger zerolog.Logger + json jsoniter.API + providers []provider +} + +func NewManager(static *config.Static, runtime *config.Runtime, rt *reqtrack.RequestTracker, + ts *testsupport.Manager, rootLogger zerolog.Logger, json jsoniter.API) *Manager { + mgr := &Manager{ + ctx: context.Background(), + static: static, + runtime: runtime, + rt: rt, + ts: ts, + rootLogger: rootLogger, + json: json, + } + + for _, p := range providerRegistry { + mgr.providers = append(mgr.providers, p(mgr)) + } + + return mgr +} + +// Shutdown stops the manager from fetching new messages and processing them. +func (mgr *Manager) Shutdown(p *shutdown.Process) error { + // Once it's time to force-close tasks, cancel the base context. + go func() { + <-p.ForceCloseTasks.Done() + mgr.cancelCtx() + }() + + return nil +} + +type provider interface { + ProviderName() string + Matches(providerCfg *config.BucketProvider) bool + NewBucket(providerCfg *config.BucketProvider, staticCfg BucketConfig, runtimeCfg *config.Bucket) types.BucketImpl +} + +var providerRegistry []func(*Manager) provider + +func registerProvider(p func(mgr *Manager) provider) { + providerRegistry = append(providerRegistry, p) +} diff --git a/runtimes/go/storage/objects/objects.go b/runtimes/go/storage/objects/objects.go new file mode 100644 index 0000000000..f58bc6fc2a --- /dev/null +++ b/runtimes/go/storage/objects/objects.go @@ -0,0 +1,10 @@ +//go:build encore_app + +package objects + +// NewBucket declares a new object storage bucket. +// +// See https://encore.dev/docs/develop/object-storage for more information. +func NewBucket(name string, cfg BucketConfig) *Bucket { + return newBucket(Singleton, name, cfg) +} diff --git a/runtimes/go/storage/objects/package.go b/runtimes/go/storage/objects/package.go new file mode 100644 index 0000000000..cd26c822d9 --- /dev/null +++ b/runtimes/go/storage/objects/package.go @@ -0,0 +1,6 @@ +// Package objects provides Encore applications with the ability +// to create and use Object Storage buckets (like for example Amazon S3) +// for storing and retrieving files in a cloud-agnostic manner. +// +// For more information see https://encore.dev/docs/develop/object-storage +package objects diff --git a/runtimes/go/storage/objects/provider_gcp.go b/runtimes/go/storage/objects/provider_gcp.go new file mode 100644 index 0000000000..0388113028 --- /dev/null +++ b/runtimes/go/storage/objects/provider_gcp.go @@ -0,0 +1,13 @@ +//go:build !encore_no_gcp && !encore_no_encorecloud + +package objects + +import ( + "encore.dev/storage/objects/internal/gcp" +) + +func init() { + registerProvider(func(mgr *Manager) provider { + return gcp.NewManager(mgr.ctx, mgr.static, mgr.runtime) + }) +} diff --git a/runtimes/go/storage/objects/refs.go b/runtimes/go/storage/objects/refs.go new file mode 100644 index 0000000000..51317d4bf4 --- /dev/null +++ b/runtimes/go/storage/objects/refs.go @@ -0,0 +1,53 @@ +package objects + +import "context" + +// BucketPerms is the type constraint for all permission-declaring +// interfaces that can be used with BucketRef. +type BucketPerms interface { + Meta() BucketMeta +} + +// Uploader is the interface for uploading objects to a bucket. +// It can be used in conjunction with [BucketRef] to declare +// a reference that can upload objects to the bucket. +// +// For example: +// +// var MyBucket = objects.NewBucket(...) +// var ref = objects.BucketRef[objects.Uploader](MyBucket) +// +// The ref object can then be used to upload objects and can be +// passed around freely within the service, without being subject +// to Encore's static analysis restrictions that apply to MyBucket. +type Uploader interface { + // Upload begins uploading an object to the bucket. + Upload(ctx context.Context, object string) *Writer + + // Meta returns metadata about the bucket. + Meta() BucketMeta +} + +// BucketRef returns an interface reference to a bucket, +// that can be freely passed around within a service +// without being subject to Encore's typical static analysis +// restrictions that normally apply to *Bucket objects. +// +// This works because using BucketRef effectively declares +// which operations you want to be able to perform since the +// type argument P must be a permission-declaring interface (implementing BucketPerms). +// +// The returned reference is scoped down to those permissions. +// +// For example: +// +// var MyBucket = objects.NewBucket(...) +// var ref = objects.BucketRef[objects.Uploader](MyBucket) +// // ref.Publish(...) can now be used to publish messages to MyTopic. +func BucketRef[P BucketPerms](bucket *Bucket) P { + return any(bucketRef{Bucket: bucket}).(P) +} + +type bucketRef struct { + *Bucket +} diff --git a/runtimes/go/storage/objects/types.go b/runtimes/go/storage/objects/types.go new file mode 100644 index 0000000000..2177b1cb75 --- /dev/null +++ b/runtimes/go/storage/objects/types.go @@ -0,0 +1,7 @@ +package objects + +import ( + "encore.dev/storage/objects/internal/types" +) + +type BucketConfig = types.BucketConfig diff --git a/runtimes/go/storage/objects/zzz_singleton_internal.go b/runtimes/go/storage/objects/zzz_singleton_internal.go new file mode 100644 index 0000000000..824d03e39e --- /dev/null +++ b/runtimes/go/storage/objects/zzz_singleton_internal.go @@ -0,0 +1,24 @@ +//go:build encore_app + +package objects + +import ( + "encore.dev/appruntime/shared/appconf" + "encore.dev/appruntime/shared/jsonapi" + "encore.dev/appruntime/shared/reqtrack" + "encore.dev/appruntime/shared/shutdown" + "encore.dev/appruntime/shared/testsupport" +) + +// Initialize the singleton instance. +// NOTE: This file is named zzz_singleton_internal.go so that +// the init function is initialized after all the providers +// have been registered. + +//publicapigen:drop +var Singleton *Manager + +func init() { + Singleton = NewManager(appconf.Static, appconf.Runtime, reqtrack.Singleton, testsupport.Singleton, jsonapi.Default) + shutdown.Singleton.RegisterShutdownHandler(Singleton.Shutdown) +} From 13761a5abe5c9b3950e69628ce45e8d02486bdc9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Eriksson?= Date: Wed, 6 Nov 2024 18:55:51 +0100 Subject: [PATCH 23/37] Work on cloud storage for Go --- go.mod | 4 +- pkg/rtconfgen/convert.go | 34 +++ runtimes/core/src/objects/gcs/bucket.rs | 2 +- runtimes/core/src/objects/s3/bucket.rs | 4 +- runtimes/go/go.mod | 2 +- runtimes/go/storage/objects/bucket.go | 226 +++++++++++++++--- .../go/storage/objects/internal/gcp/bucket.go | 80 ------- .../go/storage/objects/internal/noop/noop.go | 28 --- .../objects/internal/providers/gcp/bucket.go | 199 +++++++++++++++ .../objects/internal/providers/noop/noop.go | 34 +++ .../objects/internal/providers/providers.go | 1 + .../storage/objects/internal/types/private.go | 25 -- .../storage/objects/internal/types/public.go | 9 - .../storage/objects/internal/types/types.go | 81 +++++++ .../go/storage/objects/manager_internal.go | 24 +- runtimes/go/storage/objects/objects.go | 14 +- runtimes/go/storage/objects/options.go | 90 +++++++ runtimes/go/storage/objects/provider_gcp.go | 11 +- runtimes/go/storage/objects/refs.go | 112 ++++++++- .../go/storage/objects/registry_internal.go | 20 ++ runtimes/go/storage/objects/types.go | 7 - .../storage/objects/zzz_singleton_internal.go | 5 +- v2/app/legacymeta/legacymeta.go | 47 ++++ v2/app/validate_objects.go | 11 +- v2/parser/infra/objects/errors.go | 9 +- v2/parser/infra/objects/usage.go | 73 +++++- 26 files changed, 913 insertions(+), 239 deletions(-) delete mode 100644 runtimes/go/storage/objects/internal/gcp/bucket.go delete mode 100644 runtimes/go/storage/objects/internal/noop/noop.go create mode 100644 runtimes/go/storage/objects/internal/providers/gcp/bucket.go create mode 100644 runtimes/go/storage/objects/internal/providers/noop/noop.go create mode 100644 runtimes/go/storage/objects/internal/providers/providers.go delete mode 100644 runtimes/go/storage/objects/internal/types/private.go delete mode 100644 runtimes/go/storage/objects/internal/types/public.go create mode 100644 runtimes/go/storage/objects/internal/types/types.go create mode 100644 runtimes/go/storage/objects/options.go create mode 100644 runtimes/go/storage/objects/registry_internal.go delete mode 100644 runtimes/go/storage/objects/types.go diff --git a/go.mod b/go.mod index a58506b1aa..c931e6ee4b 100644 --- a/go.mod +++ b/go.mod @@ -1,6 +1,8 @@ module encr.dev -go 1.22.2 +go 1.23.0 + +toolchain go1.23.2 require ( cloud.google.com/go/storage v1.43.0 diff --git a/pkg/rtconfgen/convert.go b/pkg/rtconfgen/convert.go index 697e1b60c5..39dee973e5 100644 --- a/pkg/rtconfgen/convert.go +++ b/pkg/rtconfgen/convert.go @@ -51,6 +51,7 @@ func (c *legacyConverter) Convert() (*config.Runtime, error) { DynamicExperiments: nil, Gateways: []config.Gateway{}, PubsubTopics: make(map[string]*config.PubsubTopic), + Buckets: make(map[string]*config.Bucket), CORS: &config.CORS{}, } @@ -375,6 +376,39 @@ func (c *legacyConverter) Convert() (*config.Runtime, error) { } } } + + // Cloud Storage + { + for _, cluster := range c.in.Infra.Resources.BucketClusters { + p := &config.BucketProvider{} + switch prov := cluster.Provider.(type) { + case *runtimev1.BucketCluster_S3_: + p.S3 = &config.S3BucketProvider{ + Endpoint: prov.S3.GetEndpoint(), + Region: prov.S3.GetRegion(), + } + case *runtimev1.BucketCluster_Gcs: + p.GCS = &config.GCSBucketProvider{ + Endpoint: prov.Gcs.GetEndpoint(), + Anonymous: prov.Gcs.Anonymous, + } + default: + c.setErrf("unknown object storage provider type %T", prov) + continue + } + + providerID := len(cfg.BucketProviders) + cfg.BucketProviders = append(cfg.BucketProviders, p) + for _, bkt := range cluster.Buckets { + cfg.Buckets[bkt.EncoreName] = &config.Bucket{ + ProviderID: providerID, + EncoreName: bkt.EncoreName, + CloudName: bkt.CloudName, + } + } + + } + } } // Observability. diff --git a/runtimes/core/src/objects/gcs/bucket.rs b/runtimes/core/src/objects/gcs/bucket.rs index d9f62cb1c2..56ca0bc511 100644 --- a/runtimes/core/src/objects/gcs/bucket.rs +++ b/runtimes/core/src/objects/gcs/bucket.rs @@ -344,7 +344,7 @@ impl objects::ObjectImpl for Object { match client.delete_object(&req).await.map_err(map_err) { Ok(_) => Ok(()), - Err(Error::NotFound) => Ok(()), + Err(Error::NotFound) => Err(Error::NotFound), Err(err) => Err(err), } } diff --git a/runtimes/core/src/objects/s3/bucket.rs b/runtimes/core/src/objects/s3/bucket.rs index 6b3cfe7789..a7082135cc 100644 --- a/runtimes/core/src/objects/s3/bucket.rs +++ b/runtimes/core/src/objects/s3/bucket.rs @@ -360,7 +360,9 @@ impl objects::ObjectImpl for Object { .await; match res { Ok(_) => Ok(()), - Err(SdkError::ServiceError(err)) if err.raw().status().as_u16() == 404 => Ok(()), + Err(SdkError::ServiceError(err)) if err.raw().status().as_u16() == 404 => { + Err(Error::NotFound) + } Err(err) => Err(Error::Other(err.into())), } }) diff --git a/runtimes/go/go.mod b/runtimes/go/go.mod index 471cd6637e..5b37c50d1a 100644 --- a/runtimes/go/go.mod +++ b/runtimes/go/go.mod @@ -1,6 +1,6 @@ module encore.dev -go 1.21.0 +go 1.23.0 require ( cloud.google.com/go/compute/metadata v0.5.0 diff --git a/runtimes/go/storage/objects/bucket.go b/runtimes/go/storage/objects/bucket.go index 9ed7e31431..a1637326ac 100644 --- a/runtimes/go/storage/objects/bucket.go +++ b/runtimes/go/storage/objects/bucket.go @@ -2,9 +2,11 @@ package objects import ( "context" + "errors" + "iter" "encore.dev/appruntime/exported/config" - "encore.dev/storage/objects/internal/noop" + "encore.dev/storage/objects/internal/providers/noop" "encore.dev/storage/objects/internal/types" ) @@ -13,28 +15,24 @@ import ( // See NewBucket for more information on how to declare a Bucket. type Bucket struct { mgr *Manager - staticCfg BucketConfig // The config as defined in the applications source code runtimeCfg *config.Bucket // The config for this running instance of the application impl types.BucketImpl } -func newBucket(mgr *Manager, name string, cfg BucketConfig) *Bucket { - // if mgr.static.Testing { - // return &Bucket{ - // staticCfg: cfg, - // mgr: mgr, - // runtimeCfg: &config.Bucket{EncoreName: name}, - // impl: test.NewBucket(mgr.ts, name), - // } - // } +type BucketConfig struct { + // Whether objects stored in the bucket should be versioned. + // + // If true, the bucket will store multiple versions of each object + // whenever it changes, as opposed to overwriting the old version. + Versioned bool +} +func newBucket(mgr *Manager, name string) *Bucket { // Look up the bkt configuration bkt, ok := mgr.runtime.Buckets[name] if !ok { - // If we don't have a topic configuration for this topic, it means that the topic was not registered for this instance - // thus we should default to the noop implementation. + // No runtime config; return the noop implementation. return &Bucket{ - staticCfg: cfg, mgr: mgr, runtimeCfg: &config.Bucket{EncoreName: name}, impl: &noop.BucketImpl{}, @@ -47,9 +45,8 @@ func newBucket(mgr *Manager, name string, cfg BucketConfig) *Bucket { tried := make([]string, 0, len(mgr.providers)) for _, p := range mgr.providers { if p.Matches(provider) { - impl := p.NewBucket(provider, cfg, bkt) + impl := p.NewBucket(provider, bkt) return &Bucket{ - staticCfg: cfg, mgr: mgr, runtimeCfg: bkt, impl: impl, @@ -63,30 +60,17 @@ func newBucket(mgr *Manager, name string, cfg BucketConfig) *Bucket { panic("unreachable") } -// BucketMeta contains metadata about a bucket. -// The fields should not be modified by the caller. -// Additional fields may be added in the future. -type BucketMeta struct { - // Name is the name of the bucket, as provided in the constructor to NewTopic. - Name string - // Config is the bucket's configuration. - Config BucketConfig -} - -// Meta returns metadata about the topic. -func (b *Bucket) Meta() BucketMeta { - return BucketMeta{ - Name: b.runtimeCfg.EncoreName, - Config: b.staticCfg, +func (b *Bucket) Upload(ctx context.Context, object string, options ...UploadOption) *Writer { + var opt uploadOptions + for _, o := range options { + o.uploadOption(&opt) } -} -func (b *Bucket) Upload(ctx context.Context, object string) *Writer { return &Writer{ bkt: b, - ctx: ctx, obj: object, + opt: opt, } } @@ -96,6 +80,8 @@ type Writer struct { ctx context.Context obj string + opt uploadOptions + // Initialized on first write u types.Uploader } @@ -113,10 +99,180 @@ func (w *Writer) Close() error { func (w *Writer) initUpload() types.Uploader { if w.u == nil { - w.u = w.bkt.impl.NewUpload(types.UploadData{ + u, err := w.bkt.impl.Upload(types.UploadData{ Ctx: w.ctx, Object: w.obj, + Attrs: w.opt.attrs, }) + if err != nil { + w.u = &errUploader{err: err} + } else { + w.u = u + } } + return w.u } + +type errUploader struct { + err error +} + +func (e *errUploader) Write(p []byte) (int, error) { + return 0, e.err +} +func (e *errUploader) Abort(err error) {} +func (e *errUploader) Complete() (*types.ObjectAttrs, error) { + return nil, e.err +} + +var _ types.Uploader = &errUploader{} + +func (b *Bucket) Download(ctx context.Context, object string, options ...DownloadOption) *Reader { + var opt downloadOptions + for _, o := range options { + o.downloadOption(&opt) + } + + r, err := b.impl.Download(types.DownloadData{ + Ctx: ctx, + Object: object, + Version: opt.version, + }) + return &Reader{r: r, err: err} +} + +type Reader struct { + err error // any error encountered + r types.Downloader +} + +func (r *Reader) Err() error { + return r.err +} + +func (r *Reader) Read(p []byte) (int, error) { + if r.err != nil { + return 0, r.err + } + + n, err := r.r.Read(p) + r.err = err + return n, err +} + +func (r *Reader) Close() error { + if r.err != nil { + return r.err + } + + r.err = r.r.Close() + return r.err +} + +type Query struct { + // Prefix indicates to only return objects + // whose name starts with the given prefix. + Prefix string + + // Maximum number of objects to return. Zero means no limit. + Limit int64 +} + +func mapQuery(ctx context.Context, q *Query) types.ListData { + return types.ListData{ + Ctx: ctx, + Prefix: q.Prefix, + Limit: q.Limit, + } +} + +type ObjectAttrs struct { + Version string +} + +func mapAttrs(attrs *types.ObjectAttrs) *ObjectAttrs { + return &ObjectAttrs{ + Version: attrs.Version, + } +} + +type ListEntry struct { + Name string + Size int64 + ETag string +} + +func mapListEntry(entry *types.ListEntry) *ListEntry { + return &ListEntry{ + Name: entry.Name, + Size: entry.Size, + ETag: entry.ETag, + } +} + +func (b *Bucket) List(ctx context.Context, query *Query, options ...ListOption) iter.Seq2[*ListEntry, error] { + return func(yield func(*ListEntry, error) bool) { + iter := b.impl.List(mapQuery(ctx, query)) + for entry, err := range iter { + if err != nil { + if !yield(nil, err) { + return + } + } + if !yield(mapListEntry(entry), nil) { + return + } + } + } +} + +// Remove removes an object from the bucket. +func (b *Bucket) Remove(ctx context.Context, object string, options ...RemoveOption) error { + return b.impl.Remove(types.RemoveData{ + Ctx: ctx, + Object: object, + }) +} + +var ErrObjectNotFound = types.ErrObjectNotExist + +// Attrs returns the attributes of an object in the bucket. +// If the object does not exist, it returns ErrObjectNotFound. +func (b *Bucket) Attrs(ctx context.Context, object string, options ...AttrsOption) (*ObjectAttrs, error) { + var opt attrsOptions + for _, o := range options { + o.attrsOption(&opt) + } + + attrs, err := b.impl.Attrs(types.AttrsData{ + Ctx: ctx, + Object: object, + Version: opt.version, + }) + if err != nil { + return nil, err + } + + return mapAttrs(attrs), nil +} + +// Exists reports whether an object exists in the bucket. +func (b *Bucket) Exists(ctx context.Context, object string, options ...ExistsOption) (bool, error) { + var opt existsOptions + for _, o := range options { + o.existsOption(&opt) + } + + _, err := b.impl.Attrs(types.AttrsData{ + Ctx: ctx, + Object: object, + Version: opt.version, + }) + if errors.Is(err, ErrObjectNotFound) { + return false, nil + } else if err != nil { + return false, err + } + return true, nil +} diff --git a/runtimes/go/storage/objects/internal/gcp/bucket.go b/runtimes/go/storage/objects/internal/gcp/bucket.go deleted file mode 100644 index 328990fb2a..0000000000 --- a/runtimes/go/storage/objects/internal/gcp/bucket.go +++ /dev/null @@ -1,80 +0,0 @@ -package gcp - -import ( - "context" - "fmt" - "strconv" - - "cloud.google.com/go/storage" - - "encore.dev/appruntime/exported/config" - "encore.dev/storage/objects/internal/types" -) - -type Manager struct { - ctx context.Context - runtime *config.Runtime - client *storage.Client -} - -func NewManager(ctx context.Context, static *config.Static, runtime *config.Runtime) *Manager { - client, err := storage.NewClient(ctx) - if err != nil { - panic(fmt.Sprintf("failed to create object storage client: %s", err)) - } - return &Manager{ctx: ctx, runtime: runtime, client: client} -} - -type bucket struct { - mgr *Manager - cfg *config.Bucket - handle *storage.BucketHandle -} - -func (mgr *Manager) ProviderName() string { return "gcp" } - -func (mgr *Manager) Matches(cfg *config.BucketProvider) bool { - return cfg.GCS != nil -} - -func (mgr *Manager) NewBucket(provider *config.BucketProvider, staticCfg types.BucketConfig, runtimeCfg *config.Bucket) types.BucketImpl { - handle := mgr.client.Bucket(runtimeCfg.CloudName) - return &bucket{mgr, runtimeCfg, handle} -} - -func (b *bucket) NewUpload(data types.UploadData) types.Uploader { - ctx, cancel := context.WithCancelCause(data.Ctx) - w := b.handle.Object(data.Object).NewWriter(ctx) - // TODO set ChunkSize, attributes, etc - - u := &uploader{ - cancel: cancel, - w: w, - } - return u -} - -type uploader struct { - cancel context.CancelCauseFunc - w *storage.Writer -} - -func (u *uploader) Write(p []byte) (int, error) { - return u.w.Write(p) -} - -func (u *uploader) Complete() (*types.Attrs, error) { - if err := u.w.Close(); err != nil { - return nil, err - } - - attrs := u.w.Attrs() - return &types.Attrs{ - Version: strconv.FormatInt(attrs.Generation, 10), - // TODO fill this in - }, nil -} - -func (u *uploader) Abort(err error) { - u.cancel(err) -} diff --git a/runtimes/go/storage/objects/internal/noop/noop.go b/runtimes/go/storage/objects/internal/noop/noop.go deleted file mode 100644 index d3af4c17ee..0000000000 --- a/runtimes/go/storage/objects/internal/noop/noop.go +++ /dev/null @@ -1,28 +0,0 @@ -package noop - -import ( - "fmt" - - "encore.dev/storage/objects/internal/types" -) - -type BucketImpl struct { - EncoreName string -} - -func (b *BucketImpl) NewUpload(data types.UploadData) types.Uploader { - return &noopUploader{} -} - -type noopUploader struct{} - -func (*noopUploader) Write(p []byte) (int, error) { - return 0, fmt.Errorf("cannot upload to noop bucket") -} - -func (*noopUploader) Complete() (*types.Attrs, error) { - return nil, fmt.Errorf("cannot upload to noop bucket") -} - -func (*noopUploader) Abort(err error) { -} diff --git a/runtimes/go/storage/objects/internal/providers/gcp/bucket.go b/runtimes/go/storage/objects/internal/providers/gcp/bucket.go new file mode 100644 index 0000000000..bdcad27caf --- /dev/null +++ b/runtimes/go/storage/objects/internal/providers/gcp/bucket.go @@ -0,0 +1,199 @@ +package gcp + +import ( + "context" + "errors" + "fmt" + "iter" + "strconv" + + "cloud.google.com/go/storage" + "google.golang.org/api/iterator" + "google.golang.org/api/option" + + "encore.dev/appruntime/exported/config" + "encore.dev/storage/objects/internal/types" +) + +type Manager struct { + ctx context.Context + runtime *config.Runtime + clients map[*config.BucketProvider]*storage.Client +} + +func NewManager(ctx context.Context, runtime *config.Runtime) *Manager { + return &Manager{ctx: ctx, runtime: runtime, clients: make(map[*config.BucketProvider]*storage.Client)} +} + +type bucket struct { + client *storage.Client + cfg *config.Bucket + handle *storage.BucketHandle +} + +func (mgr *Manager) ProviderName() string { return "gcp" } + +func (mgr *Manager) Matches(cfg *config.BucketProvider) bool { + return cfg.GCS != nil +} + +func (mgr *Manager) NewBucket(provider *config.BucketProvider, runtimeCfg *config.Bucket) types.BucketImpl { + client := mgr.clientForProvider(provider) + handle := client.Bucket(runtimeCfg.CloudName) + return &bucket{client, runtimeCfg, handle} +} + +func (b *bucket) Download(data types.DownloadData) (types.Downloader, error) { + obj := b.handle.Object(data.Object) + if data.Version != "" { + if gen, err := strconv.ParseInt(data.Version, 10, 64); err == nil { + obj = obj.Generation(gen) + } + } + r, err := obj.NewReader(data.Ctx) + return r, mapErr(err) +} + +func (b *bucket) Upload(data types.UploadData) (types.Uploader, error) { + ctx, cancel := context.WithCancelCause(data.Ctx) + w := b.handle.Object(data.Object).NewWriter(ctx) + w.ContentType = data.Attrs.ContentType + + u := &uploader{ + cancel: cancel, + w: w, + } + return u, nil +} + +type uploader struct { + cancel context.CancelCauseFunc + w *storage.Writer +} + +func (u *uploader) Write(p []byte) (int, error) { + n, err := u.w.Write(p) + return n, mapErr(err) +} + +func (u *uploader) Complete() (*types.ObjectAttrs, error) { + if err := u.w.Close(); err != nil { + return nil, mapErr(err) + } + + attrs := u.w.Attrs() + return mapAttrs(attrs), nil +} + +func (u *uploader) Abort(err error) { + u.cancel(err) +} + +func mapAttrs(attrs *storage.ObjectAttrs) *types.ObjectAttrs { + if attrs == nil { + return nil + } + return &types.ObjectAttrs{ + Version: strconv.FormatInt(attrs.Generation, 10), + ContentType: attrs.ContentType, + Size: attrs.Size, + ETag: attrs.Etag, + } +} + +func mapListEntry(attrs *storage.ObjectAttrs) *types.ListEntry { + return &types.ListEntry{ + Name: attrs.Name, + Size: attrs.Size, + ETag: attrs.Etag, + } +} + +func (b *bucket) List(data types.ListData) iter.Seq2[*types.ListEntry, error] { + iter := b.handle.Objects(data.Ctx, &storage.Query{ + Prefix: data.Prefix, + }) + var n int64 + return func(yield func(*types.ListEntry, error) bool) { + for { + res, err := iter.Next() + if err == iterator.Done { + return + } + + // Are we over the limit? + if data.Limit != 0 && n >= data.Limit { + return + } + n++ + + var entry *types.ListEntry + if res != nil { + entry = mapListEntry(res) + } + + if !yield(entry, mapErr(err)) { + return + } + } + } +} + +func (b *bucket) Remove(data types.RemoveData) error { + obj := b.handle.Object(data.Object) + + if data.Version != "" { + if gen, err := strconv.ParseInt(data.Version, 10, 64); err == nil { + obj = obj.Generation(gen) + } + } + + err := obj.Delete(data.Ctx) + return mapErr(err) +} + +func (b *bucket) Attrs(data types.AttrsData) (*types.ObjectAttrs, error) { + obj := b.handle.Object(data.Object) + + if data.Version != "" { + if gen, err := strconv.ParseInt(data.Version, 10, 64); err == nil { + obj = obj.Generation(gen) + } + } + + resp, err := obj.Attrs(data.Ctx) + return mapAttrs(resp), mapErr(err) +} + +func (mgr *Manager) clientForProvider(prov *config.BucketProvider) *storage.Client { + if client, ok := mgr.clients[prov]; ok { + return client + } + + var opts []option.ClientOption + if prov.GCS.Anonymous { + opts = append(opts, option.WithoutAuthentication()) + } + if prov.GCS.Endpoint != "" { + opts = append(opts, option.WithEndpoint(prov.GCS.Endpoint)) + } + + client, err := storage.NewClient(mgr.ctx, opts...) + if err != nil { + panic(fmt.Sprintf("failed to create object storage client: %s", err)) + } + + mgr.clients[prov] = client + return client +} + +func mapErr(err error) error { + switch { + case err == nil: + return nil + case errors.Is(err, storage.ErrObjectNotExist): + return types.ErrObjectNotExist + default: + return err + } +} diff --git a/runtimes/go/storage/objects/internal/providers/noop/noop.go b/runtimes/go/storage/objects/internal/providers/noop/noop.go new file mode 100644 index 0000000000..2e5951ef6e --- /dev/null +++ b/runtimes/go/storage/objects/internal/providers/noop/noop.go @@ -0,0 +1,34 @@ +package noop + +import ( + "fmt" + "iter" + + "encore.dev/storage/objects/internal/types" +) + +type BucketImpl struct { + EncoreName string +} + +func (b *BucketImpl) Download(data types.DownloadData) (types.Downloader, error) { + return nil, fmt.Errorf("cannot download from noop bucket") +} + +func (b *BucketImpl) Upload(data types.UploadData) (types.Uploader, error) { + return nil, fmt.Errorf("cannot upload to noop bucket") +} + +func (b *BucketImpl) List(data types.ListData) iter.Seq2[*types.ListEntry, error] { + return func(yield func(*types.ListEntry, error) bool) { + yield(nil, fmt.Errorf("cannot list objects from noop bucket")) + } +} + +func (b *BucketImpl) Remove(data types.RemoveData) error { + return fmt.Errorf("cannot remove from noop bucket") +} + +func (b *BucketImpl) Attrs(data types.AttrsData) (*types.ObjectAttrs, error) { + return nil, fmt.Errorf("cannot get attributes from noop bucket") +} diff --git a/runtimes/go/storage/objects/internal/providers/providers.go b/runtimes/go/storage/objects/internal/providers/providers.go new file mode 100644 index 0000000000..cf2a3badeb --- /dev/null +++ b/runtimes/go/storage/objects/internal/providers/providers.go @@ -0,0 +1 @@ +package providers diff --git a/runtimes/go/storage/objects/internal/types/private.go b/runtimes/go/storage/objects/internal/types/private.go deleted file mode 100644 index 85f3cdb36a..0000000000 --- a/runtimes/go/storage/objects/internal/types/private.go +++ /dev/null @@ -1,25 +0,0 @@ -package types - -import ( - "context" - "io" -) - -type Uploader interface { - io.Writer - Abort(err error) - Complete() (*Attrs, error) -} - -type Attrs struct { - Version string -} - -type BucketImpl interface { - NewUpload(data UploadData) Uploader -} - -type UploadData struct { - Ctx context.Context - Object string -} diff --git a/runtimes/go/storage/objects/internal/types/public.go b/runtimes/go/storage/objects/internal/types/public.go deleted file mode 100644 index e330b537d0..0000000000 --- a/runtimes/go/storage/objects/internal/types/public.go +++ /dev/null @@ -1,9 +0,0 @@ -package types - -type BucketConfig struct { - // Whether objects stored in the bucket should be versioned. - // - // If true, the bucket will store multiple versions of each object - // whenever it changes, as opposed to overwriting the old version. - Versioned bool -} diff --git a/runtimes/go/storage/objects/internal/types/types.go b/runtimes/go/storage/objects/internal/types/types.go new file mode 100644 index 0000000000..da8610fbc9 --- /dev/null +++ b/runtimes/go/storage/objects/internal/types/types.go @@ -0,0 +1,81 @@ +package types + +import ( + "context" + "errors" + "io" + "iter" +) + +type BucketImpl interface { + Upload(data UploadData) (Uploader, error) + Download(data DownloadData) (Downloader, error) + List(data ListData) iter.Seq2[*ListEntry, error] + Remove(data RemoveData) error + Attrs(data AttrsData) (*ObjectAttrs, error) +} + +type UploadData struct { + Ctx context.Context + Object string + + Attrs UploadAttrs +} + +type UploadAttrs struct { + ContentType string +} + +type Uploader interface { + io.Writer + Abort(err error) + Complete() (*ObjectAttrs, error) +} + +type DownloadData struct { + Ctx context.Context + Object string + + // Non-zero to download a specific version + Version string +} + +type Downloader interface { + io.Reader + io.Closer +} + +type ObjectAttrs struct { + Version string + ContentType string + Size int64 + ETag string +} + +type ListData struct { + Ctx context.Context + Prefix string + Limit int64 +} + +type ListEntry struct { + Name string + Size int64 + ETag string +} + +type RemoveData struct { + Ctx context.Context + Object string + + Version string // non-zero means specific version +} + +type AttrsData struct { + Ctx context.Context + Object string + + Version string // non-zero means specific version +} + +var ErrObjectNotExist = errors.New("objects: object doesn't exist") diff --git a/runtimes/go/storage/objects/manager_internal.go b/runtimes/go/storage/objects/manager_internal.go index 51528db8ad..e45f92d868 100644 --- a/runtimes/go/storage/objects/manager_internal.go +++ b/runtimes/go/storage/objects/manager_internal.go @@ -3,14 +3,12 @@ package objects import ( "context" - jsoniter "github.com/json-iterator/go" "github.com/rs/zerolog" "encore.dev/appruntime/exported/config" "encore.dev/appruntime/shared/reqtrack" "encore.dev/appruntime/shared/shutdown" "encore.dev/appruntime/shared/testsupport" - "encore.dev/storage/objects/internal/types" ) type Manager struct { @@ -21,24 +19,24 @@ type Manager struct { rt *reqtrack.RequestTracker ts *testsupport.Manager rootLogger zerolog.Logger - json jsoniter.API providers []provider } func NewManager(static *config.Static, runtime *config.Runtime, rt *reqtrack.RequestTracker, - ts *testsupport.Manager, rootLogger zerolog.Logger, json jsoniter.API) *Manager { + ts *testsupport.Manager, rootLogger zerolog.Logger) *Manager { + ctx, cancel := context.WithCancel(context.Background()) mgr := &Manager{ - ctx: context.Background(), + ctx: ctx, + cancelCtx: cancel, static: static, runtime: runtime, rt: rt, ts: ts, rootLogger: rootLogger, - json: json, } for _, p := range providerRegistry { - mgr.providers = append(mgr.providers, p(mgr)) + mgr.providers = append(mgr.providers, p(mgr.ctx, mgr.runtime)) } return mgr @@ -54,15 +52,3 @@ func (mgr *Manager) Shutdown(p *shutdown.Process) error { return nil } - -type provider interface { - ProviderName() string - Matches(providerCfg *config.BucketProvider) bool - NewBucket(providerCfg *config.BucketProvider, staticCfg BucketConfig, runtimeCfg *config.Bucket) types.BucketImpl -} - -var providerRegistry []func(*Manager) provider - -func registerProvider(p func(mgr *Manager) provider) { - providerRegistry = append(providerRegistry, p) -} diff --git a/runtimes/go/storage/objects/objects.go b/runtimes/go/storage/objects/objects.go index f58bc6fc2a..0ac7f7003a 100644 --- a/runtimes/go/storage/objects/objects.go +++ b/runtimes/go/storage/objects/objects.go @@ -6,5 +6,17 @@ package objects // // See https://encore.dev/docs/develop/object-storage for more information. func NewBucket(name string, cfg BucketConfig) *Bucket { - return newBucket(Singleton, name, cfg) + return newBucket(Singleton, name) +} + +// constStr is a string that can only be provided as a constant. +// +//publicapigen:keep +type constStr string + +// Named returns a database object connected to the database with the given name. +// +// The name must be a string literal constant, to facilitate static analysis. +func Named(name constStr) *Bucket { + return newBucket(Singleton, string(name)) } diff --git a/runtimes/go/storage/objects/options.go b/runtimes/go/storage/objects/options.go new file mode 100644 index 0000000000..f256552f51 --- /dev/null +++ b/runtimes/go/storage/objects/options.go @@ -0,0 +1,90 @@ +package objects + +import "encore.dev/storage/objects/internal/types" + +type DownloadOption interface { + downloadOption(*downloadOptions) +} + +func WithVersion(version string) withVersionOption { + return withVersionOption{version: version} +} + +//publicapigen:keep +type withVersionOption struct { + version string +} + +//publicapigen:keep +func (o withVersionOption) downloadOptions(opts *downloadOptions) { opts.version = o.version } +func (o withVersionOption) removeOptions(opts *removeOptions) { opts.version = o.version } +func (o withVersionOption) attrsOptions(opts *attrsOptions) { opts.version = o.version } +func (o withVersionOption) existsOptions(opts *existsOptions) { opts.version = o.version } + +//publicapigen:keep +type downloadOptions struct { + version string +} + +type UploadOption interface { + uploadOption(*uploadOptions) +} + +type UploadAttrs struct { + ContentType string +} + +func WithUploadAttrs(attrs UploadAttrs) withUploadAttrsOption { + return withUploadAttrsOption{attrs: attrs} +} + +//publicapigen:keep +type withUploadAttrsOption struct { + attrs UploadAttrs +} + +//publicapigen:keep +func (o withUploadAttrsOption) uploadOption(opts *uploadOptions) { + opts.attrs = types.UploadAttrs{ + ContentType: o.attrs.ContentType, + } +} + +//publicapigen:keep +type uploadOptions struct { + attrs types.UploadAttrs +} + +type ListOption interface { + listOption(*listOptions) +} + +//publicapigen:keep +type listOptions struct{} + +type RemoveOption interface { + removeOption(*removeOptions) +} + +//publicapigen:keep +type removeOptions struct { + version string +} + +type AttrsOption interface { + attrsOption(*attrsOptions) +} + +//publicapigen:keep +type attrsOptions struct { + version string +} + +type ExistsOption interface { + existsOption(*existsOptions) +} + +//publicapigen:keep +type existsOptions struct { + version string +} diff --git a/runtimes/go/storage/objects/provider_gcp.go b/runtimes/go/storage/objects/provider_gcp.go index 0388113028..5a451a5059 100644 --- a/runtimes/go/storage/objects/provider_gcp.go +++ b/runtimes/go/storage/objects/provider_gcp.go @@ -1,13 +1,16 @@ -//go:build !encore_no_gcp && !encore_no_encorecloud +//go:build !encore_no_gcp || !encore_no_encorecloud || !encore_no_local package objects import ( - "encore.dev/storage/objects/internal/gcp" + "context" + + "encore.dev/appruntime/exported/config" + "encore.dev/storage/objects/internal/providers/gcp" ) func init() { - registerProvider(func(mgr *Manager) provider { - return gcp.NewManager(mgr.ctx, mgr.static, mgr.runtime) + registerProvider(func(ctx context.Context, runtimeCfg *config.Runtime) provider { + return gcp.NewManager(ctx, runtimeCfg) }) } diff --git a/runtimes/go/storage/objects/refs.go b/runtimes/go/storage/objects/refs.go index 51317d4bf4..d589c7f42d 100644 --- a/runtimes/go/storage/objects/refs.go +++ b/runtimes/go/storage/objects/refs.go @@ -1,11 +1,24 @@ package objects -import "context" +import ( + "context" + "iter" +) // BucketPerms is the type constraint for all permission-declaring // interfaces that can be used with BucketRef. type BucketPerms interface { - Meta() BucketMeta + perms() +} + +// ReadWriter is a utility permission interface that provides +// all the read-write permissions. +type ReadWriter interface { + Uploader + Downloader + Remover + Lister + Attrser } // Uploader is the interface for uploading objects to a bucket. @@ -22,10 +35,88 @@ type BucketPerms interface { // to Encore's static analysis restrictions that apply to MyBucket. type Uploader interface { // Upload begins uploading an object to the bucket. - Upload(ctx context.Context, object string) *Writer + Upload(ctx context.Context, object string, options ...UploadOption) *Writer + + perms() +} + +// Downloader is the interface for downloading objects from a bucket. +// It can be used in conjunction with [BucketRef] to declare +// a reference that can download objects from the bucket. +// +// For example: +// +// var MyBucket = objects.NewBucket(...) +// var ref = objects.BucketRef[objects.Downloader](MyBucket) +// +// The ref object can then be used to download objects and can be +// passed around freely within the service, without being subject +// to Encore's static analysis restrictions that apply to MyBucket. +type Downloader interface { + // Download downloads an object from the bucket. + Download(ctx context.Context, object string, options ...DownloadOption) *Reader - // Meta returns metadata about the bucket. - Meta() BucketMeta + perms() +} + +// Lister is the interface for listing objects in a bucket. +// It can be used in conjunction with [BucketRef] to declare +// a reference that can list objects in the bucket. +// +// For example: +// +// var MyBucket = objects.NewBucket(...) +// var ref = objects.BucketRef[objects.Lister](MyBucket) +// +// The ref object can then be used to list objects and can be +// passed around freely within the service, without being subject +// to Encore's static analysis restrictions that apply to MyBucket. +type Lister interface { + // List lists objects in the bucket. + List(ctx context.Context, query *Query, options ...ListOption) iter.Seq2[*ListEntry, error] + + perms() +} + +// Remove is the interface for removing objects from a bucket. +// It can be used in conjunction with [BucketRef] to declare +// a reference that can remove objects from the bucket. +// +// For example: +// +// var MyBucket = objects.NewBucket(...) +// var ref = objects.BucketRef[objects.Remover](MyBucket) +// +// The ref object can then be used to remove objects and can be +// passed around freely within the service, without being subject +// to Encore's static analysis restrictions that apply to MyBucket. +type Remover interface { + // Remove removes an object from the bucket. + Remove(ctx context.Context, object string, options ...RemoveOption) error + + perms() +} + +// Attrser is the interface for resolving objects' attributes in a bucket. +// It can be used in conjunction with [BucketRef] to declare +// a reference that can check object attributes in a bucket. +// +// For example: +// +// var MyBucket = objects.NewBucket(...) +// var ref = objects.BucketRef[objects.Attrser](MyBucket) +// +// The ref object can then be used to remove objects and can be +// passed around freely within the service, without being subject +// to Encore's static analysis restrictions that apply to MyBucket. +type Attrser interface { + // Attrs resolves the attributes of an object. + Attrs(ctx context.Context, object string, options ...AttrsOption) (*ObjectAttrs, error) + + // Exists checks whether an object exists in the bucket. + Exists(ctx context.Context, object string, options ...ExistsOption) (bool, error) + + perms() } // BucketRef returns an interface reference to a bucket, @@ -42,8 +133,13 @@ type Uploader interface { // For example: // // var MyBucket = objects.NewBucket(...) -// var ref = objects.BucketRef[objects.Uploader](MyBucket) -// // ref.Publish(...) can now be used to publish messages to MyTopic. +// var ref = objects.BucketRef[objects.ReadWriter](MyBucket) +// // ref.Upload(...) can now be used to upload objects to MyBucket. +// +// Multiple permissions can be combined by defining a custom interface +// that embeds multiple permission interfaces: +// +// var ref = objects.BucketRef[interface { objects.Uploader; objects.Downloader }](MyBucket) func BucketRef[P BucketPerms](bucket *Bucket) P { return any(bucketRef{Bucket: bucket}).(P) } @@ -51,3 +147,5 @@ func BucketRef[P BucketPerms](bucket *Bucket) P { type bucketRef struct { *Bucket } + +func (r bucketRef) perms() {} diff --git a/runtimes/go/storage/objects/registry_internal.go b/runtimes/go/storage/objects/registry_internal.go new file mode 100644 index 0000000000..51451815f7 --- /dev/null +++ b/runtimes/go/storage/objects/registry_internal.go @@ -0,0 +1,20 @@ +package objects + +import ( + "context" + + "encore.dev/appruntime/exported/config" + "encore.dev/storage/objects/internal/types" +) + +type provider interface { + ProviderName() string + Matches(providerCfg *config.BucketProvider) bool + NewBucket(providerCfg *config.BucketProvider, runtimeCfg *config.Bucket) types.BucketImpl +} + +var providerRegistry []func(context.Context, *config.Runtime) provider + +func registerProvider(p func(context.Context, *config.Runtime) provider) { + providerRegistry = append(providerRegistry, p) +} diff --git a/runtimes/go/storage/objects/types.go b/runtimes/go/storage/objects/types.go deleted file mode 100644 index 2177b1cb75..0000000000 --- a/runtimes/go/storage/objects/types.go +++ /dev/null @@ -1,7 +0,0 @@ -package objects - -import ( - "encore.dev/storage/objects/internal/types" -) - -type BucketConfig = types.BucketConfig diff --git a/runtimes/go/storage/objects/zzz_singleton_internal.go b/runtimes/go/storage/objects/zzz_singleton_internal.go index 824d03e39e..eb057ebcdb 100644 --- a/runtimes/go/storage/objects/zzz_singleton_internal.go +++ b/runtimes/go/storage/objects/zzz_singleton_internal.go @@ -4,7 +4,7 @@ package objects import ( "encore.dev/appruntime/shared/appconf" - "encore.dev/appruntime/shared/jsonapi" + "encore.dev/appruntime/shared/logging" "encore.dev/appruntime/shared/reqtrack" "encore.dev/appruntime/shared/shutdown" "encore.dev/appruntime/shared/testsupport" @@ -19,6 +19,7 @@ import ( var Singleton *Manager func init() { - Singleton = NewManager(appconf.Static, appconf.Runtime, reqtrack.Singleton, testsupport.Singleton, jsonapi.Default) + Singleton = NewManager(appconf.Static, appconf.Runtime, reqtrack.Singleton, + testsupport.Singleton, logging.RootLogger) shutdown.Singleton.RegisterShutdownHandler(Singleton.Shutdown) } diff --git a/v2/app/legacymeta/legacymeta.go b/v2/app/legacymeta/legacymeta.go index aebea93f9b..91ec36c8c0 100644 --- a/v2/app/legacymeta/legacymeta.go +++ b/v2/app/legacymeta/legacymeta.go @@ -3,6 +3,7 @@ package legacymeta import ( "cmp" "fmt" + "go/token" gotoken "go/token" "slices" "sort" @@ -23,6 +24,7 @@ import ( "encr.dev/v2/parser/infra/config" "encr.dev/v2/parser/infra/crons" "encr.dev/v2/parser/infra/metrics" + "encr.dev/v2/parser/infra/objects" "encr.dev/v2/parser/infra/pubsub" "encr.dev/v2/parser/infra/secrets" "encr.dev/v2/parser/infra/sqldb" @@ -323,6 +325,51 @@ func (b *builder) Build() *meta.Data { } md.PubsubTopics = append(md.PubsubTopics, topic) + case *objects.Bucket: + bkt := &meta.Bucket{ + Name: r.Name, + Doc: zeroNil(r.Doc), + Versioned: r.Versioned, + } + md.Buckets = append(md.Buckets, bkt) + + permsBySvc := make(map[string][]objects.Perm) + addPerms := func(svcName string, perms ...objects.Perm) { + permsBySvc[svcName] = append(permsBySvc[svcName], perms...) + } + + // Record all the permissions. + for _, u := range b.app.Parse.Usages(r) { + switch u := u.(type) { + case *objects.MethodUsage: + if svc, ok := b.app.ServiceForPath(u.DeclaredIn().FSPath); ok { + addPerms(svc.Name, u.Perm) + } + case *objects.RefUsage: + if svc, ok := b.app.ServiceForPath(u.DeclaredIn().FSPath); ok { + addPerms(svc.Name, u.Perms...) + } + } + } + + // Collect the perms + for svcName, perms := range permsBySvc { + if svc, ok := svcByName[svcName]; ok { + ops := fns.Map(perms, func(p objects.Perm) meta.BucketUsage_Operation { + op, ok := p.ToMeta() + if !ok { + b.errs.Addf(token.NoPos, "unsupported permission %v", p) + } + return op + }) + slices.Sort(ops) + svc.Buckets = append(svc.Buckets, &meta.BucketUsage{ + Bucket: bkt.Name, + Operations: ops, + }) + } + } + case *caches.Cluster: cluster := &meta.CacheCluster{ Name: r.Name, diff --git a/v2/app/validate_objects.go b/v2/app/validate_objects.go index def00ffc39..25335b9105 100644 --- a/v2/app/validate_objects.go +++ b/v2/app/validate_objects.go @@ -24,13 +24,22 @@ func (d *Desc) validateObjects(pc *parsectx.Context, result *parser.Result) { // Make sure any BucketRef calls are within a service. for _, use := range d.Parse.Usages(res) { - if _, ok := use.(*objects.RefUsage); ok { + switch use := use.(type) { + case *objects.RefUsage: errTxt := "used here" if _, ok := d.ServiceForPath(use.DeclaredIn().FSPath); !ok && !use.DeclaredIn().TestFile { pc.Errs.Add(objects.ErrBucketRefOutsideService. AtGoNode(use, errors.AsError(errTxt)), ) } + + case *objects.MethodUsage: + errTxt := "used here" + if _, ok := d.ServiceForPath(use.DeclaredIn().FSPath); !ok && !use.DeclaredIn().TestFile { + pc.Errs.Add(objects.ErrUnsupportedOperationOutsideService(use.Method). + AtGoNode(use, errors.AsError(errTxt)), + ) + } } } } diff --git a/v2/parser/infra/objects/errors.go b/v2/parser/infra/objects/errors.go index 0875f768c1..dc8dc7bfc5 100644 --- a/v2/parser/infra/objects/errors.go +++ b/v2/parser/infra/objects/errors.go @@ -35,10 +35,9 @@ var ( errors.PrependDetails("If you wish to reuse the same bucket, then you can export the original Bucket object and reference it from here."), ) - ErrUnableToIdentifyServicesInvolved = errRange.New( - "Unable to identify services involved", - "Unable to identify services involved in the PubSub subscription.", - errors.MarkAsInternalError(), + ErrUnsupportedOperationOutsideService = errRange.Newf( + "Unsupported bucket operation outside of service", + "The %s operation can only be performed within a service. Use objects.BucketRef to pass the bucket reference to other, non-service components.", ) errBucketRefNoTypeArgs = errRange.New( @@ -48,7 +47,7 @@ var ( errBucketRefInvalidPerms = errRange.New( "Unrecognized permissions in call to objects.BucketRef", - "The supported permissions are objects.Writer/Reader.", + "The supported permissions are objects.Uploader/Downloader.", ) ErrBucketRefOutsideService = errRange.New( diff --git a/v2/parser/infra/objects/usage.go b/v2/parser/infra/objects/usage.go index b69bd8a903..d51bfa8c81 100644 --- a/v2/parser/infra/objects/usage.go +++ b/v2/parser/infra/objects/usage.go @@ -7,11 +7,14 @@ import ( "encr.dev/v2/internals/schema" "encr.dev/v2/internals/schema/schemautil" "encr.dev/v2/parser/resource/usage" + + meta "encr.dev/proto/encore/parser/meta/v1" ) type MethodUsage struct { usage.Base - Perm Perm + Method string + Perm Perm } type RefUsage struct { @@ -39,6 +42,25 @@ const ( DeleteObject Perm = "delete-object" ) +func (p Perm) ToMeta() (meta.BucketUsage_Operation, bool) { + switch p { + case ListObjects: + return meta.BucketUsage_LIST_OBJECTS, true + case ReadObjectContents: + return meta.BucketUsage_READ_OBJECT_CONTENTS, true + case WriteObject: + return meta.BucketUsage_WRITE_OBJECT, true + case UpdateObjectMetadata: + return meta.BucketUsage_UPDATE_OBJECT_METADATA, true + case GetObjectMetadata: + return meta.BucketUsage_GET_OBJECT_METADATA, true + case DeleteObject: + return meta.BucketUsage_DELETE_OBJECT, true + default: + return meta.BucketUsage_UNKNOWN, false + } +} + func ResolveBucketUsage(data usage.ResolveData, topic *Bucket) usage.Usage { switch expr := data.Expr.(type) { case *usage.MethodCall: @@ -46,16 +68,24 @@ func ResolveBucketUsage(data usage.ResolveData, topic *Bucket) usage.Usage { switch expr.Method { case "Upload": perm = WriteObject + case "Download": + perm = ReadObjectContents + case "List": + perm = ListObjects + case "Remove": + perm = DeleteObject default: return nil } + return &MethodUsage{ Base: usage.Base{ File: expr.File, Bind: expr.Bind, Expr: expr, }, - Perm: perm, + Method: expr.Method, + Perm: perm, } case *usage.FuncArg: @@ -76,17 +106,32 @@ func parseBucketRef(errs *perr.List, expr *usage.FuncArg) usage.Usage { } checkUsage := func(typ schema.Type) (usage.Usage, bool) { - if schemautil.IsNamed(typ, "encore.dev/storage/objects", "Uploader") { - return &RefUsage{ - Base: usage.Base{ - File: expr.File, - Bind: expr.Bind, - Expr: expr, - }, - Perms: []Perm{WriteObject}, - }, true + var perms []Perm + switch { + case isNamed(typ, "Uploader"): + perms = []Perm{WriteObject} + case isNamed(typ, "Downloader"): + perms = []Perm{ReadObjectContents} + case isNamed(typ, "Lister"): + perms = []Perm{ListObjects} + case isNamed(typ, "Remover"): + perms = []Perm{DeleteObject} + case isNamed(typ, "Attrser"): + perms = []Perm{GetObjectMetadata} + case isNamed(typ, "ReadWriter"): + perms = []Perm{WriteObject, ReadObjectContents, ListObjects, DeleteObject, GetObjectMetadata, UpdateObjectMetadata} + default: + return nil, false } - return nil, false + + return &RefUsage{ + Base: usage.Base{ + File: expr.File, + Bind: expr.Bind, + Expr: expr, + }, + Perms: perms, + }, true } // Do we have a simple usage directly as the type argument? @@ -117,3 +162,7 @@ func parseBucketRef(errs *perr.List, expr *usage.FuncArg) usage.Usage { errs.Add(errBucketRefInvalidPerms.AtGoNode(expr.Call)) return nil } + +func isNamed(typ schema.Type, name string) bool { + return schemautil.IsNamed(typ, "encore.dev/storage/objects", name) +} From 8afef3f7ecef0b8a12e3e32fe3de286c02b0e07c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Eriksson?= Date: Thu, 7 Nov 2024 13:01:39 +0100 Subject: [PATCH 24/37] object storage tracing --- pkg/rtconfgen/convert.go | 1 + .../go/appruntime/exported/config/config.go | 1 + .../go/appruntime/exported/trace2/events.go | 5 - runtimes/go/storage/objects/bucket.go | 370 ++++++++++++++++-- .../objects/internal/providers/gcp/bucket.go | 41 +- .../storage/objects/internal/types/types.go | 31 +- runtimes/go/storage/objects/options.go | 21 + 7 files changed, 417 insertions(+), 53 deletions(-) diff --git a/pkg/rtconfgen/convert.go b/pkg/rtconfgen/convert.go index 39dee973e5..00d074fa50 100644 --- a/pkg/rtconfgen/convert.go +++ b/pkg/rtconfgen/convert.go @@ -404,6 +404,7 @@ func (c *legacyConverter) Convert() (*config.Runtime, error) { ProviderID: providerID, EncoreName: bkt.EncoreName, CloudName: bkt.CloudName, + KeyPrefix: bkt.GetKeyPrefix(), } } diff --git a/runtimes/go/appruntime/exported/config/config.go b/runtimes/go/appruntime/exported/config/config.go index 136cb0c944..77a6d4bf7f 100644 --- a/runtimes/go/appruntime/exported/config/config.go +++ b/runtimes/go/appruntime/exported/config/config.go @@ -415,6 +415,7 @@ type Bucket struct { ProviderID int `json:"cluster_id"` // the index into (*Runtime).BucketProviders EncoreName string `json:"encore_name"` // the Encore name for the bucket CloudName string `json:"cloud_name"` // the cloud name for the bucket + KeyPrefix string `json:"key_prefix"` // the prefix to use for all keys in the bucket } type Metrics struct { diff --git a/runtimes/go/appruntime/exported/trace2/events.go b/runtimes/go/appruntime/exported/trace2/events.go index 581743ddae..6211b872c8 100644 --- a/runtimes/go/appruntime/exported/trace2/events.go +++ b/runtimes/go/appruntime/exported/trace2/events.go @@ -1014,9 +1014,6 @@ type BucketDeleteObjectsEndParams struct { StartID EventID Err error - // Set iff err == nil - Observed uint64 - HasMore bool } func (l *Log) BucketDeleteObjectsEnd(p BucketDeleteObjectsEndParams) { @@ -1027,8 +1024,6 @@ func (l *Log) BucketDeleteObjectsEnd(p BucketDeleteObjectsEndParams) { }) tb.ErrWithStack(p.Err) - tb.UVarint(p.Observed) - tb.Bool(p.HasMore) l.Add(Event{ Type: BucketDeleteObjectsEnd, diff --git a/runtimes/go/storage/objects/bucket.go b/runtimes/go/storage/objects/bucket.go index a1637326ac..12a294f088 100644 --- a/runtimes/go/storage/objects/bucket.go +++ b/runtimes/go/storage/objects/bucket.go @@ -4,8 +4,12 @@ import ( "context" "errors" "iter" + "strings" "encore.dev/appruntime/exported/config" + "encore.dev/appruntime/exported/stack" + "encore.dev/appruntime/exported/trace2" + "encore.dev/appruntime/shared/reqtrack" "encore.dev/storage/objects/internal/providers/noop" "encore.dev/storage/objects/internal/types" ) @@ -17,6 +21,10 @@ type Bucket struct { mgr *Manager runtimeCfg *config.Bucket // The config for this running instance of the application impl types.BucketImpl + name string + + // Prefix to prepend to all cloud names. + baseCloudPrefix string } type BucketConfig struct { @@ -36,6 +44,7 @@ func newBucket(mgr *Manager, name string) *Bucket { mgr: mgr, runtimeCfg: &config.Bucket{EncoreName: name}, impl: &noop.BucketImpl{}, + name: name, } } @@ -47,9 +56,11 @@ func newBucket(mgr *Manager, name string) *Bucket { if p.Matches(provider) { impl := p.NewBucket(provider, bkt) return &Bucket{ - mgr: mgr, - runtimeCfg: bkt, - impl: impl, + mgr: mgr, + runtimeCfg: bkt, + impl: impl, + name: name, + baseCloudPrefix: bkt.KeyPrefix, } } tried = append(tried, p.ProviderName()) @@ -66,12 +77,32 @@ func (b *Bucket) Upload(ctx context.Context, object string, options ...UploadOpt o.uploadOption(&opt) } - return &Writer{ + w := &Writer{ bkt: b, ctx: ctx, obj: object, opt: opt, } + + curr := b.mgr.rt.Current() + if curr.Req != nil && curr.Trace != nil { + w.curr = curr + w.startEventID = curr.Trace.BucketObjectUploadStart(trace2.BucketObjectUploadStartParams{ + EventParams: trace2.EventParams{ + TraceID: curr.Req.TraceID, + SpanID: curr.Req.SpanID, + Goid: curr.Goctr, + }, + Bucket: b.name, + Object: object, + Attrs: trace2.BucketObjectAttributes{ + ContentType: ptrOrNil(opt.attrs.ContentType), + }, + Stack: stack.Build(1), + }) + } + + return w } type Writer struct { @@ -84,6 +115,10 @@ type Writer struct { // Initialized on first write u types.Uploader + + // Set if tracing + curr reqtrack.Current + startEventID trace2.EventID } func (w *Writer) Write(p []byte) (int, error) { @@ -93,7 +128,26 @@ func (w *Writer) Write(p []byte) (int, error) { func (w *Writer) Close() error { u := w.initUpload() - _, err := u.Complete() + attrs, err := u.Complete() + + if w.curr.Trace != nil { + params := trace2.BucketObjectUploadEndParams{ + StartID: w.startEventID, + EventParams: trace2.EventParams{ + TraceID: w.curr.Req.TraceID, + SpanID: w.curr.Req.SpanID, + Goid: w.curr.Goctr, + }, + Err: err, + } + + if attrs != nil { + params.Size = uint64(attrs.Size) + params.Version = ptrOrNil(attrs.Version) + } + w.curr.Trace.BucketObjectUploadEnd(params) + } + return err } @@ -101,8 +155,11 @@ func (w *Writer) initUpload() types.Uploader { if w.u == nil { u, err := w.bkt.impl.Upload(types.UploadData{ Ctx: w.ctx, - Object: w.obj, + Object: w.bkt.toCloudObject(w.obj), Attrs: w.opt.attrs, + Pre: types.Preconditions{ + NotExists: w.opt.pre.NotExists, + }, }) if err != nil { w.u = &errUploader{err: err} @@ -134,17 +191,39 @@ func (b *Bucket) Download(ctx context.Context, object string, options ...Downloa o.downloadOption(&opt) } + var startEventID trace2.EventID + curr := b.mgr.rt.Current() + if curr.Req != nil && curr.Trace != nil { + startEventID = curr.Trace.BucketObjectDownloadStart(trace2.BucketObjectDownloadStartParams{ + EventParams: trace2.EventParams{ + TraceID: curr.Req.TraceID, + SpanID: curr.Req.SpanID, + Goid: curr.Goctr, + }, + Bucket: b.name, + Object: object, + Version: ptrOrNil(opt.version), + Stack: stack.Build(1), + }) + } + r, err := b.impl.Download(types.DownloadData{ Ctx: ctx, - Object: object, + Object: b.toCloudObject(object), Version: opt.version, }) - return &Reader{r: r, err: err} + return &Reader{r: r, err: err, curr: curr, startEventID: startEventID} } type Reader struct { - err error // any error encountered - r types.Downloader + err error // any error encountered + r types.Downloader + totalRead uint64 + + // Set if traced + traceCompleted bool + curr reqtrack.Current + startEventID trace2.EventID } func (r *Reader) Err() error { @@ -158,10 +237,12 @@ func (r *Reader) Read(p []byte) (int, error) { n, err := r.r.Read(p) r.err = err + r.totalRead += uint64(n) return n, err } func (r *Reader) Close() error { + defer r.completeTrace() if r.err != nil { return r.err } @@ -170,6 +251,26 @@ func (r *Reader) Close() error { return r.err } +func (r *Reader) completeTrace() { + if r.traceCompleted { + return + } + + r.traceCompleted = true + if r.curr.Trace != nil && r.startEventID != 0 { + r.curr.Trace.BucketObjectDownloadEnd(trace2.BucketObjectDownloadEndParams{ + StartID: r.startEventID, + EventParams: trace2.EventParams{ + TraceID: r.curr.Req.TraceID, + SpanID: r.curr.Req.SpanID, + Goid: r.curr.Goctr, + }, + Err: r.err, + Size: r.totalRead, + }) + } +} + type Query struct { // Prefix indicates to only return objects // whose name starts with the given prefix. @@ -179,21 +280,29 @@ type Query struct { Limit int64 } -func mapQuery(ctx context.Context, q *Query) types.ListData { +func (b *Bucket) mapQuery(ctx context.Context, q *Query) types.ListData { return types.ListData{ Ctx: ctx, - Prefix: q.Prefix, + Prefix: b.baseCloudPrefix + q.Prefix, Limit: q.Limit, } } type ObjectAttrs struct { - Version string + Name string + Version string + ContentType string + Size int64 + ETag string } -func mapAttrs(attrs *types.ObjectAttrs) *ObjectAttrs { +func (b *Bucket) mapAttrs(attrs *types.ObjectAttrs) *ObjectAttrs { return &ObjectAttrs{ - Version: attrs.Version, + Name: b.fromCloudObject(attrs.Object), + Version: attrs.Version, + ContentType: attrs.ContentType, + Size: attrs.Size, + ETag: attrs.ETag, } } @@ -203,9 +312,9 @@ type ListEntry struct { ETag string } -func mapListEntry(entry *types.ListEntry) *ListEntry { +func (b *Bucket) mapListEntry(entry *types.ListEntry) *ListEntry { return &ListEntry{ - Name: entry.Name, + Name: b.fromCloudObject(entry.Object), Size: entry.Size, ETag: entry.ETag, } @@ -213,14 +322,52 @@ func mapListEntry(entry *types.ListEntry) *ListEntry { func (b *Bucket) List(ctx context.Context, query *Query, options ...ListOption) iter.Seq2[*ListEntry, error] { return func(yield func(*ListEntry, error) bool) { - iter := b.impl.List(mapQuery(ctx, query)) + // Tracing state + var ( + listErr error + observed uint64 + hasMore bool + ) + + curr := b.mgr.rt.Current() + if curr.Req != nil && curr.Trace != nil { + startEventID := curr.Trace.BucketListObjectsStart(trace2.BucketListObjectsStartParams{ + EventParams: trace2.EventParams{ + TraceID: curr.Req.TraceID, + SpanID: curr.Req.SpanID, + Goid: curr.Goctr, + }, + Bucket: b.name, + Prefix: ptrOrNil(query.Prefix), + Stack: stack.Build(1), + }) + + defer curr.Trace.BucketListObjectsEnd(trace2.BucketListObjectsEndParams{ + StartID: startEventID, + EventParams: trace2.EventParams{ + TraceID: curr.Req.TraceID, + SpanID: curr.Req.SpanID, + Goid: curr.Goctr, + }, + Err: listErr, + Observed: observed, + HasMore: hasMore, + }) + } + + iter := b.impl.List(b.mapQuery(ctx, query)) for entry, err := range iter { if err != nil { + listErr = err if !yield(nil, err) { return } } - if !yield(mapListEntry(entry), nil) { + + observed++ + if !yield(b.mapListEntry(entry), nil) { + // Consumer didn't want any more entries; set hasMore = true + hasMore = true return } } @@ -229,13 +376,54 @@ func (b *Bucket) List(ctx context.Context, query *Query, options ...ListOption) // Remove removes an object from the bucket. func (b *Bucket) Remove(ctx context.Context, object string, options ...RemoveOption) error { - return b.impl.Remove(types.RemoveData{ - Ctx: ctx, - Object: object, + var opts removeOptions + for _, o := range options { + o.removeOption(&opts) + } + + var removeErr error + curr := b.mgr.rt.Current() + if curr.Req != nil && curr.Trace != nil { + startEventID := curr.Trace.BucketDeleteObjectsStart(trace2.BucketDeleteObjectsStartParams{ + EventParams: trace2.EventParams{ + TraceID: curr.Req.TraceID, + SpanID: curr.Req.SpanID, + Goid: curr.Goctr, + }, + Bucket: b.name, + Objects: []trace2.BucketDeleteObjectsEntry{ + { + Object: object, + Version: ptrOrNil(opts.version), + }, + }, + Stack: stack.Build(1), + }) + + defer curr.Trace.BucketDeleteObjectsEnd(trace2.BucketDeleteObjectsEndParams{ + StartID: startEventID, + EventParams: trace2.EventParams{ + TraceID: curr.Req.TraceID, + SpanID: curr.Req.SpanID, + Goid: curr.Goctr, + }, + Err: removeErr, + }) + } + + removeErr = b.impl.Remove(types.RemoveData{ + Ctx: ctx, + Object: b.toCloudObject(object), + Version: opts.version, }) + + return removeErr } -var ErrObjectNotFound = types.ErrObjectNotExist +var ( + ErrObjectNotFound = types.ErrObjectNotExist + ErrPreconditionFailed = types.ErrPreconditionFailed +) // Attrs returns the attributes of an object in the bucket. // If the object does not exist, it returns ErrObjectNotFound. @@ -245,16 +433,58 @@ func (b *Bucket) Attrs(ctx context.Context, object string, options ...AttrsOptio o.attrsOption(&opt) } - attrs, err := b.impl.Attrs(types.AttrsData{ + var ( + attrs *types.ObjectAttrs + attrsErr error + ) + + curr := b.mgr.rt.Current() + if curr.Req != nil && curr.Trace != nil { + startEventID := curr.Trace.BucketObjectGetAttrsStart(trace2.BucketObjectGetAttrsStartParams{ + EventParams: trace2.EventParams{ + TraceID: curr.Req.TraceID, + SpanID: curr.Req.SpanID, + Goid: curr.Goctr, + }, + Bucket: b.name, + Object: object, + Version: ptrOrNil(opt.version), + Stack: stack.Build(1), + }) + + defer func() { + params := trace2.BucketObjectGetAttrsEndParams{ + StartID: startEventID, + EventParams: trace2.EventParams{ + TraceID: curr.Req.TraceID, + SpanID: curr.Req.SpanID, + Goid: curr.Goctr, + }, + Err: attrsErr, + } + if attrs != nil { + size := uint64(attrs.Size) + params.Attrs = &trace2.BucketObjectAttributes{ + Size: &size, + Version: ptrOrNil(attrs.Version), + ETag: ptrOrNil(attrs.ETag), + ContentType: ptrOrNil(attrs.ContentType), + } + } + curr.Trace.BucketObjectGetAttrsEnd(params) + }() + } + + attrs, attrsErr = b.impl.Attrs(types.AttrsData{ Ctx: ctx, - Object: object, + Object: b.toCloudObject(object), Version: opt.version, }) - if err != nil { - return nil, err + if attrsErr != nil { + return nil, attrsErr } - return mapAttrs(attrs), nil + return b.mapAttrs(attrs), nil } // Exists reports whether an object exists in the bucket. @@ -264,15 +494,89 @@ func (b *Bucket) Exists(ctx context.Context, object string, options ...ExistsOpt o.existsOption(&opt) } - _, err := b.impl.Attrs(types.AttrsData{ + var ( + attrs *types.ObjectAttrs + attrsErr error + ) + + curr := b.mgr.rt.Current() + if curr.Req != nil && curr.Trace != nil { + startEventID := curr.Trace.BucketObjectGetAttrsStart(trace2.BucketObjectGetAttrsStartParams{ + EventParams: trace2.EventParams{ + TraceID: curr.Req.TraceID, + SpanID: curr.Req.SpanID, + Goid: curr.Goctr, + }, + Bucket: b.name, + Object: object, + Version: ptrOrNil(opt.version), + Stack: stack.Build(1), + }) + + defer func() { + params := trace2.BucketObjectGetAttrsEndParams{ + StartID: startEventID, + EventParams: trace2.EventParams{ + TraceID: curr.Req.TraceID, + SpanID: curr.Req.SpanID, + Goid: curr.Goctr, + }, + Err: attrsErr, + } + if attrs != nil { + size := uint64(attrs.Size) + params.Attrs = &trace2.BucketObjectAttributes{ + Size: &size, + Version: ptrOrNil(attrs.Version), + ETag: ptrOrNil(attrs.ETag), + ContentType: ptrOrNil(attrs.ContentType), + } + } + curr.Trace.BucketObjectGetAttrsEnd(params) + }() + } + + attrs, attrsErr = b.impl.Attrs(types.AttrsData{ Ctx: ctx, - Object: object, + Object: b.toCloudObject(object), Version: opt.version, }) - if errors.Is(err, ErrObjectNotFound) { + if errors.Is(attrsErr, ErrObjectNotFound) { return false, nil - } else if err != nil { - return false, err + } else if attrsErr != nil { + return false, attrsErr } return true, nil } + +func (b *Bucket) toCloudObject(object string) types.CloudObject { + return types.CloudObject(b.cloudPrefix() + object) +} + +// cloudPrefix computes the cloud prefix to use. +// It adds the current test name as a prefix when running tests, for test isolation. +func (b *Bucket) cloudPrefix() string { + prefix := b.baseCloudPrefix + + if b.mgr.static.Testing { + test := b.mgr.ts.CurrentTest() + if prefix != "" { + prefix += "/" + } + prefix += test.Name() + "/__test__/" + } + + return prefix +} + +func (b *Bucket) fromCloudObject(object types.CloudObject) string { + return strings.TrimPrefix(string(object), b.cloudPrefix()) +} + +func ptrOrNil[V comparable](val V) *V { + var zero V + if val != zero { + return &val + } + return nil +} diff --git a/runtimes/go/storage/objects/internal/providers/gcp/bucket.go b/runtimes/go/storage/objects/internal/providers/gcp/bucket.go index bdcad27caf..db45da8e7d 100644 --- a/runtimes/go/storage/objects/internal/providers/gcp/bucket.go +++ b/runtimes/go/storage/objects/internal/providers/gcp/bucket.go @@ -5,11 +5,15 @@ import ( "errors" "fmt" "iter" + "net/http" "strconv" "cloud.google.com/go/storage" + "google.golang.org/api/googleapi" "google.golang.org/api/iterator" "google.golang.org/api/option" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" "encore.dev/appruntime/exported/config" "encore.dev/storage/objects/internal/types" @@ -44,7 +48,7 @@ func (mgr *Manager) NewBucket(provider *config.BucketProvider, runtimeCfg *confi } func (b *bucket) Download(data types.DownloadData) (types.Downloader, error) { - obj := b.handle.Object(data.Object) + obj := b.handle.Object(data.Object.String()) if data.Version != "" { if gen, err := strconv.ParseInt(data.Version, 10, 64); err == nil { obj = obj.Generation(gen) @@ -56,7 +60,15 @@ func (b *bucket) Download(data types.DownloadData) (types.Downloader, error) { func (b *bucket) Upload(data types.UploadData) (types.Uploader, error) { ctx, cancel := context.WithCancelCause(data.Ctx) - w := b.handle.Object(data.Object).NewWriter(ctx) + obj := b.handle.Object(data.Object.String()) + + if data.Pre.NotExists { + obj = obj.If(storage.Conditions{ + DoesNotExist: true, + }) + } + + w := obj.NewWriter(ctx) w.ContentType = data.Attrs.ContentType u := &uploader{ @@ -94,6 +106,7 @@ func mapAttrs(attrs *storage.ObjectAttrs) *types.ObjectAttrs { return nil } return &types.ObjectAttrs{ + Object: types.CloudObject(attrs.Name), Version: strconv.FormatInt(attrs.Generation, 10), ContentType: attrs.ContentType, Size: attrs.Size, @@ -103,9 +116,9 @@ func mapAttrs(attrs *storage.ObjectAttrs) *types.ObjectAttrs { func mapListEntry(attrs *storage.ObjectAttrs) *types.ListEntry { return &types.ListEntry{ - Name: attrs.Name, - Size: attrs.Size, - ETag: attrs.Etag, + Object: types.CloudObject(attrs.Name), + Size: attrs.Size, + ETag: attrs.Etag, } } @@ -140,7 +153,7 @@ func (b *bucket) List(data types.ListData) iter.Seq2[*types.ListEntry, error] { } func (b *bucket) Remove(data types.RemoveData) error { - obj := b.handle.Object(data.Object) + obj := b.handle.Object(data.Object.String()) if data.Version != "" { if gen, err := strconv.ParseInt(data.Version, 10, 64); err == nil { @@ -153,7 +166,7 @@ func (b *bucket) Remove(data types.RemoveData) error { } func (b *bucket) Attrs(data types.AttrsData) (*types.ObjectAttrs, error) { - obj := b.handle.Object(data.Object) + obj := b.handle.Object(data.Object.String()) if data.Version != "" { if gen, err := strconv.ParseInt(data.Version, 10, 64); err == nil { @@ -194,6 +207,20 @@ func mapErr(err error) error { case errors.Is(err, storage.ErrObjectNotExist): return types.ErrObjectNotExist default: + // Handle precondition failures + { + var e *googleapi.Error + if ok := errors.As(err, &e); ok && e.Code == http.StatusPreconditionFailed { + return types.ErrPreconditionFailed + } + } + + { + if s, ok := status.FromError(err); ok && s.Code() == codes.AlreadyExists || s.Code() == codes.FailedPrecondition { + return types.ErrPreconditionFailed + } + } + return err } } diff --git a/runtimes/go/storage/objects/internal/types/types.go b/runtimes/go/storage/objects/internal/types/types.go index da8610fbc9..2ea0f0bf2a 100644 --- a/runtimes/go/storage/objects/internal/types/types.go +++ b/runtimes/go/storage/objects/internal/types/types.go @@ -15,11 +15,22 @@ type BucketImpl interface { Attrs(data AttrsData) (*ObjectAttrs, error) } +// CloudObject is the cloud name for an object. +// It can differ from the logical name when using a prefix bucket. +type CloudObject string + +func (o CloudObject) String() string { return string(o) } + type UploadData struct { Ctx context.Context - Object string + Object CloudObject Attrs UploadAttrs + Pre Preconditions +} + +type Preconditions struct { + NotExists bool } type UploadAttrs struct { @@ -34,7 +45,7 @@ type Uploader interface { type DownloadData struct { Ctx context.Context - Object string + Object CloudObject // Non-zero to download a specific version Version string @@ -46,6 +57,7 @@ type Downloader interface { } type ObjectAttrs struct { + Object CloudObject Version string ContentType string Size int64 @@ -59,23 +71,26 @@ type ListData struct { } type ListEntry struct { - Name string - Size int64 - ETag string + Object CloudObject + Size int64 + ETag string } type RemoveData struct { Ctx context.Context - Object string + Object CloudObject Version string // non-zero means specific version } type AttrsData struct { Ctx context.Context - Object string + Object CloudObject Version string // non-zero means specific version } -var ErrObjectNotExist = errors.New("objects: object doesn't exist") +var ( + ErrObjectNotExist = errors.New("objects: object doesn't exist") + ErrPreconditionFailed = errors.New("objects: precondition failed") +) diff --git a/runtimes/go/storage/objects/options.go b/runtimes/go/storage/objects/options.go index f256552f51..9e93b5d9fd 100644 --- a/runtimes/go/storage/objects/options.go +++ b/runtimes/go/storage/objects/options.go @@ -30,6 +30,26 @@ type UploadOption interface { uploadOption(*uploadOptions) } +// WithPreconditions is an UploadOption for only uploading an object +// if certain preconditions are met. +func WithPreconditions(pre Preconditions) withPreconditionsOption { + return withPreconditionsOption{pre: pre} +} + +type Preconditions struct { + NotExists bool +} + +//publicapigen:keep +type withPreconditionsOption struct { + pre Preconditions +} + +//publicapigen:keep +func (o withPreconditionsOption) uploadOption(opts *uploadOptions) { + opts.pre = o.pre +} + type UploadAttrs struct { ContentType string } @@ -53,6 +73,7 @@ func (o withUploadAttrsOption) uploadOption(opts *uploadOptions) { //publicapigen:keep type uploadOptions struct { attrs types.UploadAttrs + pre Preconditions } type ListOption interface { From 0d687a20e88a43e6f957a5063334b0ca8d0d918a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Eriksson?= Date: Tue, 12 Nov 2024 11:46:42 +0100 Subject: [PATCH 25/37] More work on Go implementation --- pkg/rtconfgen/convert.go | 2 +- .../go/appruntime/exported/config/config.go | 6 ++-- runtimes/go/go.mod | 19 +++++++---- runtimes/go/go.sum | 34 ++++++++++++------- .../internal/providers/{gcp => gcs}/bucket.go | 4 +-- .../objects/internal/providers/providers.go | 1 - .../{provider_gcp.go => provider_gcs.go} | 4 +-- 7 files changed, 43 insertions(+), 27 deletions(-) rename runtimes/go/storage/objects/internal/providers/{gcp => gcs}/bucket.go (98%) delete mode 100644 runtimes/go/storage/objects/internal/providers/providers.go rename runtimes/go/storage/objects/{provider_gcp.go => provider_gcs.go} (73%) diff --git a/pkg/rtconfgen/convert.go b/pkg/rtconfgen/convert.go index 00d074fa50..13d29c0217 100644 --- a/pkg/rtconfgen/convert.go +++ b/pkg/rtconfgen/convert.go @@ -384,8 +384,8 @@ func (c *legacyConverter) Convert() (*config.Runtime, error) { switch prov := cluster.Provider.(type) { case *runtimev1.BucketCluster_S3_: p.S3 = &config.S3BucketProvider{ - Endpoint: prov.S3.GetEndpoint(), Region: prov.S3.GetRegion(), + Endpoint: prov.S3.Endpoint, } case *runtimev1.BucketCluster_Gcs: p.GCS = &config.GCSBucketProvider{ diff --git a/runtimes/go/appruntime/exported/config/config.go b/runtimes/go/appruntime/exported/config/config.go index 77a6d4bf7f..ac5604b63b 100644 --- a/runtimes/go/appruntime/exported/config/config.go +++ b/runtimes/go/appruntime/exported/config/config.go @@ -402,8 +402,10 @@ type BucketProvider struct { } type S3BucketProvider struct { - Endpoint string `json:"endpoint"` - Region string `json:"region"` + Region string `json:"region"` + // The endpoint to use. If nil, the default endpoint for the region is used. + // Must be set for non-AWS endpoints. + Endpoint *string `json:"endpoint"` } type GCSBucketProvider struct { diff --git a/runtimes/go/go.mod b/runtimes/go/go.mod index 5b37c50d1a..c525e33826 100644 --- a/runtimes/go/go.mod +++ b/runtimes/go/go.mod @@ -12,9 +12,10 @@ require ( github.com/Azure/azure-sdk-for-go/sdk/messaging/azservicebus v1.1.0 github.com/DataDog/datadog-api-client-go/v2 v2.9.0 github.com/alicebob/miniredis/v2 v2.23.0 - github.com/aws/aws-sdk-go-v2 v1.24.1 + github.com/aws/aws-sdk-go-v2 v1.32.4 github.com/aws/aws-sdk-go-v2/config v1.26.6 github.com/aws/aws-sdk-go-v2/service/cloudwatch v1.32.2 + github.com/aws/aws-sdk-go-v2/service/s3 v1.66.3 github.com/aws/aws-sdk-go-v2/service/sns v1.26.7 github.com/aws/aws-sdk-go-v2/service/sqs v1.29.7 github.com/benbjohnson/clock v1.3.3 @@ -40,6 +41,7 @@ require ( golang.org/x/time v0.6.0 google.golang.org/api v0.191.0 google.golang.org/genproto/googleapis/api v0.0.0-20240725223205-93522f1f2a9f + google.golang.org/grpc v1.64.1 google.golang.org/protobuf v1.34.2 ) @@ -52,17 +54,21 @@ require ( github.com/AzureAD/microsoft-authentication-library-for-go v0.7.0 // indirect github.com/DataDog/zstd v1.5.0 // indirect github.com/alicebob/gopher-json v0.0.0-20200520072559-a9ecdc9d1d3a // indirect + github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.6.6 // indirect github.com/aws/aws-sdk-go-v2/credentials v1.16.16 // indirect github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.14.11 // indirect - github.com/aws/aws-sdk-go-v2/internal/configsources v1.2.10 // indirect - github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.5.10 // indirect + github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.23 // indirect + github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.23 // indirect github.com/aws/aws-sdk-go-v2/internal/ini v1.7.3 // indirect - github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.10.4 // indirect - github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.10.10 // indirect + github.com/aws/aws-sdk-go-v2/internal/v4a v1.3.23 // indirect + github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.12.0 // indirect + github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.4.4 // indirect + github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.4 // indirect + github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.18.4 // indirect github.com/aws/aws-sdk-go-v2/service/sso v1.18.7 // indirect github.com/aws/aws-sdk-go-v2/service/ssooidc v1.21.7 // indirect github.com/aws/aws-sdk-go-v2/service/sts v1.26.7 // indirect - github.com/aws/smithy-go v1.19.0 // indirect + github.com/aws/smithy-go v1.22.0 // indirect github.com/cespare/xxhash/v2 v2.2.0 // indirect github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect github.com/dnaeon/go-vcr v1.2.0 // indirect @@ -103,6 +109,5 @@ require ( golang.org/x/text v0.16.0 // indirect google.golang.org/genproto v0.0.0-20240730163845-b1a4ccb954bf // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20240730163845-b1a4ccb954bf // indirect - google.golang.org/grpc v1.64.1 // indirect nhooyr.io/websocket v1.8.7 // indirect ) diff --git a/runtimes/go/go.sum b/runtimes/go/go.sum index b9ea92a64e..26989ba848 100644 --- a/runtimes/go/go.sum +++ b/runtimes/go/go.sum @@ -38,26 +38,36 @@ github.com/alicebob/gopher-json v0.0.0-20200520072559-a9ecdc9d1d3a h1:HbKu58rmZp github.com/alicebob/gopher-json v0.0.0-20200520072559-a9ecdc9d1d3a/go.mod h1:SGnFV6hVsYE877CKEZ6tDNTjaSXYUk6QqoIK6PrAtcc= github.com/alicebob/miniredis/v2 v2.23.0 h1:+lwAJYjvvdIVg6doFHuotFjueJ/7KY10xo/vm3X3Scw= github.com/alicebob/miniredis/v2 v2.23.0/go.mod h1:XNqvJdQJv5mSuVMc0ynneafpnL/zv52acZ6kqeS0t88= -github.com/aws/aws-sdk-go-v2 v1.24.1 h1:xAojnj+ktS95YZlDf0zxWBkbFtymPeDP+rvUQIH3uAU= -github.com/aws/aws-sdk-go-v2 v1.24.1/go.mod h1:LNh45Br1YAkEKaAqvmE1m8FUx6a5b/V0oAKV7of29b4= +github.com/aws/aws-sdk-go-v2 v1.32.4 h1:S13INUiTxgrPueTmrm5DZ+MiAo99zYzHEFh1UNkOxNE= +github.com/aws/aws-sdk-go-v2 v1.32.4/go.mod h1:2SK5n0a2karNTv5tbP1SjsX0uhttou00v/HpXKM1ZUo= +github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.6.6 h1:pT3hpW0cOHRJx8Y0DfJUEQuqPild8jRGmSFmBgvydr0= +github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.6.6/go.mod h1:j/I2++U0xX+cr44QjHay4Cvxj6FUbnxrgmqN3H1jTZA= github.com/aws/aws-sdk-go-v2/config v1.26.6 h1:Z/7w9bUqlRI0FFQpetVuFYEsjzE3h7fpU6HuGmfPL/o= github.com/aws/aws-sdk-go-v2/config v1.26.6/go.mod h1:uKU6cnDmYCvJ+pxO9S4cWDb2yWWIH5hra+32hVh1MI4= github.com/aws/aws-sdk-go-v2/credentials v1.16.16 h1:8q6Rliyv0aUFAVtzaldUEcS+T5gbadPbWdV1WcAddK8= github.com/aws/aws-sdk-go-v2/credentials v1.16.16/go.mod h1:UHVZrdUsv63hPXFo1H7c5fEneoVo9UXiz36QG1GEPi0= github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.14.11 h1:c5I5iH+DZcH3xOIMlz3/tCKJDaHFwYEmxvlh2fAcFo8= github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.14.11/go.mod h1:cRrYDYAMUohBJUtUnOhydaMHtiK/1NZ0Otc9lIb6O0Y= -github.com/aws/aws-sdk-go-v2/internal/configsources v1.2.10 h1:vF+Zgd9s+H4vOXd5BMaPWykta2a6Ih0AKLq/X6NYKn4= -github.com/aws/aws-sdk-go-v2/internal/configsources v1.2.10/go.mod h1:6BkRjejp/GR4411UGqkX8+wFMbFbqsUIimfK4XjOKR4= -github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.5.10 h1:nYPe006ktcqUji8S2mqXf9c/7NdiKriOwMvWQHgYztw= -github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.5.10/go.mod h1:6UV4SZkVvmODfXKql4LCbaZUpF7HO2BX38FgBf9ZOLw= +github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.23 h1:A2w6m6Tmr+BNXjDsr7M90zkWjsu4JXHwrzPg235STs4= +github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.23/go.mod h1:35EVp9wyeANdujZruvHiQUAo9E3vbhnIO1mTCAxMlY0= +github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.23 h1:pgYW9FCabt2M25MoHYCfMrVY2ghiiBKYWUVXfwZs+sU= +github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.23/go.mod h1:c48kLgzO19wAu3CPkDWC28JbaJ+hfQlsdl7I2+oqIbk= github.com/aws/aws-sdk-go-v2/internal/ini v1.7.3 h1:n3GDfwqF2tzEkXlv5cuy4iy7LpKDtqDMcNLfZDu9rls= github.com/aws/aws-sdk-go-v2/internal/ini v1.7.3/go.mod h1:6fQQgfuGmw8Al/3M2IgIllycxV7ZW7WCdVSqfBeUiCY= +github.com/aws/aws-sdk-go-v2/internal/v4a v1.3.23 h1:1SZBDiRzzs3sNhOMVApyWPduWYGAX0imGy06XiBnCAM= +github.com/aws/aws-sdk-go-v2/internal/v4a v1.3.23/go.mod h1:i9TkxgbZmHVh2S0La6CAXtnyFhlCX/pJ0JsOvBAS6Mk= github.com/aws/aws-sdk-go-v2/service/cloudwatch v1.32.2 h1:vQfCIHSDouEvbE4EuDrlCGKcrtABEqF3cMt61nGEV4g= github.com/aws/aws-sdk-go-v2/service/cloudwatch v1.32.2/go.mod h1:3ToKMEhVj+Q+HzZ8Hqin6LdAKtsi3zVXVNUPpQMd+Xk= -github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.10.4 h1:/b31bi3YVNlkzkBrm9LfpaKoaYZUxIAj4sHfOTmLfqw= -github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.10.4/go.mod h1:2aGXHFmbInwgP9ZfpmdIfOELL79zhdNYNmReK8qDfdQ= -github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.10.10 h1:DBYTXwIGQSGs9w4jKm60F5dmCQ3EEruxdc0MFh+3EY4= -github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.10.10/go.mod h1:wohMUQiFdzo0NtxbBg0mSRGZ4vL3n0dKjLTINdcIino= +github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.12.0 h1:TToQNkvGguu209puTojY/ozlqy2d/SFNcoLIqTFi42g= +github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.12.0/go.mod h1:0jp+ltwkf+SwG2fm/PKo8t4y8pJSgOCO4D8Lz3k0aHQ= +github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.4.4 h1:aaPpoG15S2qHkWm4KlEyF01zovK1nW4BBbyXuHNSE90= +github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.4.4/go.mod h1:eD9gS2EARTKgGr/W5xwgY/ik9z/zqpW+m/xOQbVxrMk= +github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.4 h1:tHxQi/XHPK0ctd/wdOw0t7Xrc2OxcRCnVzv8lwWPu0c= +github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.4/go.mod h1:4GQbF1vJzG60poZqWatZlhP31y8PGCCVTvIGPdaaYJ0= +github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.18.4 h1:E5ZAVOmI2apR8ADb72Q63KqwwwdW1XcMeXIlrZ1Psjg= +github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.18.4/go.mod h1:wezzqVUOVVdk+2Z/JzQT4NxAU0NbhRe5W8pIE72jsWI= +github.com/aws/aws-sdk-go-v2/service/s3 v1.66.3 h1:neNOYJl72bHrz9ikAEED4VqWyND/Po0DnEx64RW6YM4= +github.com/aws/aws-sdk-go-v2/service/s3 v1.66.3/go.mod h1:TMhLIyRIyoGVlaEMAt+ITMbwskSTpcGsCPDq91/ihY0= github.com/aws/aws-sdk-go-v2/service/sns v1.26.7 h1:DylmW2c1Z7qGxN3Y02k+voPbtM1mh7Rp+gV+7maG5io= github.com/aws/aws-sdk-go-v2/service/sns v1.26.7/go.mod h1:mLFiISZfiZAqZEfPWUsZBK8gD4dYCKuKAfapV+KrIVQ= github.com/aws/aws-sdk-go-v2/service/sqs v1.29.7 h1:tRNrFDGRm81e6nTX5Q4CFblea99eAfm0dxXazGpLceU= @@ -68,8 +78,8 @@ github.com/aws/aws-sdk-go-v2/service/ssooidc v1.21.7 h1:QPMJf+Jw8E1l7zqhZmMlFw6w github.com/aws/aws-sdk-go-v2/service/ssooidc v1.21.7/go.mod h1:ykf3COxYI0UJmxcfcxcVuz7b6uADi1FkiUz6Eb7AgM8= github.com/aws/aws-sdk-go-v2/service/sts v1.26.7 h1:NzO4Vrau795RkUdSHKEwiR01FaGzGOH1EETJ+5QHnm0= github.com/aws/aws-sdk-go-v2/service/sts v1.26.7/go.mod h1:6h2YuIoxaMSCFf5fi1EgZAwdfkGMgDY+DVfa61uLe4U= -github.com/aws/smithy-go v1.19.0 h1:KWFKQV80DpP3vJrrA9sVAHQ5gc2z8i4EzrLhLlWXcBM= -github.com/aws/smithy-go v1.19.0/go.mod h1:NukqUGpCZIILqqiV0NIjeFh24kd/FAa4beRb6nbIUPE= +github.com/aws/smithy-go v1.22.0 h1:uunKnWlcoL3zO7q+gG2Pk53joueEOsnNB28QdMsmiMM= +github.com/aws/smithy-go v1.22.0/go.mod h1:irrKGvNn1InZwb2d7fkIRNucdfwR8R+Ts3wxYa/cJHg= github.com/benbjohnson/clock v1.3.3 h1:g+rSsSaAzhHJYcIQE78hJ3AhyjjtQvleKDjlhdBnIhc= github.com/benbjohnson/clock v1.3.3/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= diff --git a/runtimes/go/storage/objects/internal/providers/gcp/bucket.go b/runtimes/go/storage/objects/internal/providers/gcs/bucket.go similarity index 98% rename from runtimes/go/storage/objects/internal/providers/gcp/bucket.go rename to runtimes/go/storage/objects/internal/providers/gcs/bucket.go index db45da8e7d..f44bf10310 100644 --- a/runtimes/go/storage/objects/internal/providers/gcp/bucket.go +++ b/runtimes/go/storage/objects/internal/providers/gcs/bucket.go @@ -1,4 +1,4 @@ -package gcp +package gcs import ( "context" @@ -35,7 +35,7 @@ type bucket struct { handle *storage.BucketHandle } -func (mgr *Manager) ProviderName() string { return "gcp" } +func (mgr *Manager) ProviderName() string { return "gcs" } func (mgr *Manager) Matches(cfg *config.BucketProvider) bool { return cfg.GCS != nil diff --git a/runtimes/go/storage/objects/internal/providers/providers.go b/runtimes/go/storage/objects/internal/providers/providers.go deleted file mode 100644 index cf2a3badeb..0000000000 --- a/runtimes/go/storage/objects/internal/providers/providers.go +++ /dev/null @@ -1 +0,0 @@ -package providers diff --git a/runtimes/go/storage/objects/provider_gcp.go b/runtimes/go/storage/objects/provider_gcs.go similarity index 73% rename from runtimes/go/storage/objects/provider_gcp.go rename to runtimes/go/storage/objects/provider_gcs.go index 5a451a5059..077688b96c 100644 --- a/runtimes/go/storage/objects/provider_gcp.go +++ b/runtimes/go/storage/objects/provider_gcs.go @@ -6,11 +6,11 @@ import ( "context" "encore.dev/appruntime/exported/config" - "encore.dev/storage/objects/internal/providers/gcp" + "encore.dev/storage/objects/internal/providers/s3" ) func init() { registerProvider(func(ctx context.Context, runtimeCfg *config.Runtime) provider { - return gcp.NewManager(ctx, runtimeCfg) + return s3.NewManager(ctx, runtimeCfg) }) } From bafd1f40d708f2956ee72cfe31e8921928b1fc27 Mon Sep 17 00:00:00 2001 From: Stefan Ekerfelt Date: Tue, 12 Nov 2024 13:32:59 +0100 Subject: [PATCH 26/37] Object config stuff (#8) --- cli/cmd/encore/build.go | 23 ++- cli/daemon/export/infra_config.go | 24 +++- docs/selfhost/configure-infra.md | 57 +++++++- runtimes/core/src/infracfg.rs | 77 +++++++++- .../exported/config/infra/config.go | 136 +++++++++++++++++- .../exported/config/infra/validation.go | 10 ++ .../js/encore.dev/internal/codegen/appinit.ts | 2 +- 7 files changed, 320 insertions(+), 9 deletions(-) diff --git a/cli/cmd/encore/build.go b/cli/cmd/encore/build.go index a8a6ce1502..12d2e059f3 100644 --- a/cli/cmd/encore/build.go +++ b/cli/cmd/encore/build.go @@ -14,6 +14,21 @@ import ( daemonpb "encr.dev/proto/encore/daemon" ) +var ( + targetOS = cmdutil.Oneof{ + Value: "linux", + Allowed: []string{"linux"}, + Flag: "os", + Desc: "the target operating system", + } + targetArch = cmdutil.Oneof{ + Value: "amd64", + Allowed: []string{"amd64", "arm64"}, + Flag: "arch", + Desc: "the target architecture", + } +) + func init() { buildCmd := &cobra.Command{ Use: "build", @@ -23,14 +38,14 @@ func init() { p := buildParams{ CgoEnabled: os.Getenv("CGO_ENABLED") == "1", - Goos: or(os.Getenv("GOOS"), "linux"), - Goarch: or(os.Getenv("GOARCH"), "amd64"), } dockerBuildCmd := &cobra.Command{ Use: "docker IMAGE_TAG", Short: "docker builds a portable docker image of your Encore application", Args: cobra.ExactArgs(1), Run: func(cmd *cobra.Command, args []string) { + p.Goarch = targetArch.Value + p.Goos = targetOS.Value p.AppRoot, _ = determineAppRoot() file, err := appfile.ParseFile(filepath.Join(p.AppRoot, appfile.Name)) if err == nil { @@ -48,13 +63,13 @@ func init() { dockerBuildCmd.Flags().BoolVarP(&p.Push, "push", "p", false, "push image to remote repository") dockerBuildCmd.Flags().StringVar(&p.BaseImg, "base", "scratch", "base image to build from") - dockerBuildCmd.Flags().StringVar(&p.Goos, "os", p.Goos, "target operating system. One of (linux, darwin, windows)") - dockerBuildCmd.Flags().StringVar(&p.Goarch, "arch", p.Goarch, "target architecture. One of (amd64, arm64)") dockerBuildCmd.Flags().BoolVar(&p.CgoEnabled, "cgo", false, "enable cgo") dockerBuildCmd.Flags().BoolVar(&p.SkipInfraConf, "skip-config", false, "do not read or generate a infra configuration file") dockerBuildCmd.Flags().StringVar(&p.InfraConfPath, "config", "", "infra configuration file path") p.Services = dockerBuildCmd.Flags().StringSlice("services", nil, "services to include in the image") p.Gateways = dockerBuildCmd.Flags().StringSlice("gateways", nil, "gateways to include in the image") + targetOS.AddFlag(dockerBuildCmd) + targetArch.AddFlag(dockerBuildCmd) rootCmd.AddCommand(buildCmd) buildCmd.AddCommand(dockerBuildCmd) } diff --git a/cli/daemon/export/infra_config.go b/cli/daemon/export/infra_config.go index aa7d5dc60f..a2ae5fb686 100644 --- a/cli/daemon/export/infra_config.go +++ b/cli/daemon/export/infra_config.go @@ -102,7 +102,10 @@ func buildAndValidateInfraConfig(params EmbeddedInfraConfigParams) (*infra.Infra // Add dependencies for all outbound RPCs for our hosted services. for _, p := range md.Pkgs { - if _, ok := hostedSvcs[p.ServiceName]; !ok { + if p.ServiceName == "" { + secrets = append(secrets, p.Secrets...) + continue + } else if _, ok := hostedSvcs[p.ServiceName]; !ok { continue } secrets = append(secrets, p.Secrets...) @@ -277,6 +280,25 @@ func buildAndValidateInfraConfig(params EmbeddedInfraConfigParams) (*infra.Infra missing["Topics"] = topics } + // Validate bucket config + buckets := fns.FlatMap(maps.Values(hostedSvcs), func(svc *meta.Service) []string { + return fns.Map(svc.Buckets, (*meta.BucketUsage).GetBucket) + }) + slices.Sort(buckets) + buckets = slices.Compact(buckets) + + for _, storage := range infraCfg.ObjectStorage { + for name, _ := range storage.GetBuckets() { + buckets, ok = fns.Delete(buckets, name) + if !ok { + storage.DeleteBucket(name) + } + } + } + if len(buckets) > 0 { + missing["Buckets"] = buckets + } + // Copy CORS config cors := infra.CORS(params.GlobalCORS) infraCfg.CORS = &cors diff --git a/docs/selfhost/configure-infra.md b/docs/selfhost/configure-infra.md index a8500756de..d84b8cab72 100644 --- a/docs/selfhost/configure-infra.md +++ b/docs/selfhost/configure-infra.md @@ -97,6 +97,16 @@ Here's an example configuration file you can use. } } } + ], + "object_storage": [ + { + "type": "gcs", + "buckets": { + "my-gcs-bucket": { + "name": "my-gcs-bucket", + } + } + } ] } ``` @@ -426,4 +436,49 @@ The configuration for each provider is different. Below are examples for each pr } ``` -This guide covers typical infrastructure configurations. Adjust according to your specific requirements to optimize your Encore app's infrastructure setup. \ No newline at end of file +### 10. Object Storage Configuration +Encore currently supports the following object storage providers: +- `gcs` for [Google Cloud Storage](https://cloud.google.com/storage) +- `s3` for [AWS S3](https://aws.amazon.com/s3/) + +#### 10.1. GCS Configuration + +```json +{ + "object_storage": [ + { + "type": "gcs", + "buckets": { + "my-gcs-bucket": { + "name": "my-gcs-bucket" + } + } + } + ] +} +``` + +- `name`: The full name of the GCS bucket. + +#### 10.2. S3 Configuration + +```json +{ + "object_storage": [ + { + "type": "s3", + "region": "us-east-1", + "buckets": { + "my-s3-bucket": { + "name": "my-s3-bucket" + } + } + } + ] +} +``` + +- `region`: The AWS region where the bucket is located. +- `name`: The full name of the S3 bucket. + +This guide covers typical infrastructure configurations. Adjust according to your specific requirements to optimize your Encore app's infrastructure setup. diff --git a/runtimes/core/src/infracfg.rs b/runtimes/core/src/infracfg.rs index 1212fdbf7b..d765a17d63 100644 --- a/runtimes/core/src/infracfg.rs +++ b/runtimes/core/src/infracfg.rs @@ -24,6 +24,35 @@ pub struct InfraConfig { pub hosted_services: Option>, pub hosted_gateways: Option>, pub cors: Option, + pub buckets: Option>, +} + +#[derive(Debug, Serialize, Deserialize)] +#[serde(tag = "type")] +pub enum ObjectStorage { + #[serde(rename = "gcs")] + GCS(GCS), + #[serde(rename = "s3")] + S3(S3), +} + +#[derive(Debug, Serialize, Deserialize)] +pub struct GCS { + pub endpoint: Option, + pub buckets: HashMap, +} + +#[derive(Debug, Serialize, Deserialize)] +pub struct S3 { + pub region: String, + pub endpoint: Option, + pub buckets: HashMap, +} + +#[derive(Debug, Serialize, Deserialize)] +pub struct Bucket { + pub name: String, + pub key_prefix: Option, } #[derive(Debug, Serialize, Deserialize, Default)] @@ -401,6 +430,52 @@ pub fn map_infra_to_runtime(infra: InfraConfig) -> RuntimeConfig { } }); + // Map Buckets + let buckets = infra.buckets.as_ref().map(|object_storages| { + object_storages + .iter() + .map(|os| match os { + ObjectStorage::GCS(gcs) => pbruntime::BucketCluster { + rid: get_next_rid(), + provider: Some(pbruntime::bucket_cluster::Provider::Gcs( + pbruntime::bucket_cluster::Gcs { + endpoint: gcs.endpoint.clone(), + }, + )), + buckets: gcs + .buckets + .iter() + .map(|(name, bucket)| pbruntime::Bucket { + encore_name: name.clone(), + cloud_name: bucket.name.clone(), + key_prefix: bucket.key_prefix.clone(), + rid: get_next_rid(), + }) + .collect(), + }, + ObjectStorage::S3(s3) => pbruntime::BucketCluster { + rid: get_next_rid(), + provider: Some(pbruntime::bucket_cluster::Provider::S3( + pbruntime::bucket_cluster::S3 { + region: s3.region.clone(), + endpoint: s3.endpoint.clone(), + }, + )), + buckets: s3 + .buckets + .iter() + .map(|(name, bucket)| pbruntime::Bucket { + encore_name: name.clone(), + cloud_name: bucket.name.clone(), + key_prefix: bucket.key_prefix.clone(), + rid: get_next_rid(), + }) + .collect(), + }, + }) + .collect() + }); + // Map Metrics let metrics = infra.metrics.as_ref().map(|metrics| { let (provider, interval) = match metrics { @@ -892,8 +967,8 @@ pub fn map_infra_to_runtime(infra: InfraConfig) -> RuntimeConfig { sql_clusters: sql_clusters.unwrap_or_default(), pubsub_clusters: pubsub_clusters.unwrap_or_default(), redis_clusters: redis_clusters.unwrap_or_default(), - bucket_clusters: vec![], // TODO app_secrets, + bucket_clusters: buckets.unwrap_or_default(), }); let infra_struct = Some(Infrastructure { diff --git a/runtimes/go/appruntime/exported/config/infra/config.go b/runtimes/go/appruntime/exported/config/infra/config.go index 8d743734eb..088e8c4aad 100644 --- a/runtimes/go/appruntime/exported/config/infra/config.go +++ b/runtimes/go/appruntime/exported/config/infra/config.go @@ -17,6 +17,7 @@ type InfraConfig struct { Redis map[string]*Redis `json:"redis,omitempty"` PubSub []*PubSub `json:"pubsub,omitempty"` Secrets Secrets `json:"secrets,omitempty"` + ObjectStorage []*ObjectStorage `json:"object_storage,omitempty"` // These fields are not defined in the json schema and should not be // set by the user. They're computed during the build/eject process. @@ -25,6 +26,138 @@ type InfraConfig struct { CORS *CORS `json:"cors,omitempty"` } +type ObjectStorage struct { + Type string `json:"type"` + GCS *GCS `json:"gcs,omitempty"` + S3 *S3 `json:"s3,omitempty"` +} + +func (o *ObjectStorage) GetBuckets() map[string]*Bucket { + switch o.Type { + case "gcs": + return o.GCS.Buckets + case "s3": + return o.S3.Buckets + default: + panic("unsupported object storage type") + } +} + +func (o *ObjectStorage) DeleteBucket(name string) { + switch o.Type { + case "gcs": + delete(o.GCS.Buckets, name) + case "s3": + delete(o.S3.Buckets, name) + default: + panic("unsupported object storage type") + } + +} + +func (a *ObjectStorage) Validate(v *validator) { + v.ValidateField("Type", OneOf(a.Type, "gcs", "s3")) + switch a.Type { + case "gcs": + a.GCS.Validate(v) + case "s3": + a.S3.Validate(v) + default: + v.ValidateField("type", Err("unsupported object storage type")) + } +} + +func (p *ObjectStorage) MarshalJSON() ([]byte, error) { + // Create a map to hold the JSON structure + m := make(map[string]interface{}) + + // Always add the type + m["type"] = p.Type + + // Add the specific pubsub configuration based on the type + switch p.Type { + case "gcs": + if p.GCS != nil { + for k, v := range structToMap(p.GCS) { + m[k] = v + } + } + case "s3": + if p.S3 != nil { + for k, v := range structToMap(p.S3) { + m[k] = v + } + } + default: + return nil, errors.New("unsupported object storage type") + } + + return json.Marshal(m) +} + +// UnmarshalJSON custom unmarshaller for PubSub. +func (p *ObjectStorage) UnmarshalJSON(data []byte) error { + // Anonymous struct to capture the "type" field first. + var aux struct { + Type string `json:"type,omitempty"` + } + if err := json.Unmarshal(data, &aux); err != nil { + return err + } + + // Set the Type field. + p.Type = aux.Type + + // Unmarshal based on the "type" field. + switch aux.Type { + case "gcs": + var g GCS + if err := json.Unmarshal(data, &g); err != nil { + return err + } + p.GCS = &g + case "s3": + var a S3 + if err := json.Unmarshal(data, &a); err != nil { + return err + } + p.S3 = &a + default: + return errors.New("unsupported object storage type") + } + + return nil +} + +type S3 struct { + Region string `json:"region"` + Endpoint string `json:"endpoint,omitempty"` + Buckets map[string]*Bucket `json:"buckets,omitempty"` +} + +func (a *S3) Validate(v *validator) { + v.ValidateField("region", NotZero(a.Region)) + ValidateChildMap(v, "buckets", a.Buckets) +} + +type GCS struct { + Endpoint string `json:"endpoint,omitempty"` + Buckets map[string]*Bucket `json:"buckets,omitempty"` +} + +func (a *GCS) Validate(v *validator) { + ValidateChildMap(v, "buckets", a.Buckets) +} + +type Bucket struct { + Name string `json:"name,omitempty"` + Prefix string `json:"prefix,omitempty"` +} + +func (a *Bucket) Validate(v *validator) { + v.ValidateField("name", NotZero(a.Name)) +} + type Metadata struct { AppID string `json:"app_id,omitempty"` EnvName string `json:"env_name,omitempty"` @@ -46,6 +179,7 @@ func (i *InfraConfig) Validate(v *validator) { v.ValidateChild("graceful_shutdown", i.GracefulShutdown) ValidateChildList(v, "auth", i.Auth) ValidateChildMap(v, "service_discovery", i.ServiceDiscovery) + ValidateChildList(v, "object_storage", i.ObjectStorage) v.ValidateChild("metrics", i.Metrics) ValidateChildList(v, "sql_servers", i.SQLServers) ValidateChildMap(v, "redis", i.Redis) @@ -127,7 +261,7 @@ type Auth struct { } func (a *Auth) Validate(v *validator) { - v.ValidateField("type", NotZero(a.Type)) + v.ValidateField("type", OneOf(a.Type, "auth")) v.ValidateEnvString("key", a.Key, "Service Authorization Key", NotZero[string]) } diff --git a/runtimes/go/appruntime/exported/config/infra/validation.go b/runtimes/go/appruntime/exported/config/infra/validation.go index 423762f3fc..4946ad6003 100644 --- a/runtimes/go/appruntime/exported/config/infra/validation.go +++ b/runtimes/go/appruntime/exported/config/infra/validation.go @@ -4,6 +4,7 @@ import ( "cmp" "errors" "fmt" + "slices" "github.com/modern-go/reflect2" ) @@ -49,6 +50,15 @@ func AnyNonZero[T comparable](v ...T) Predicate { } } +func OneOf[T comparable](v T, vals ...T) Predicate { + return func() error { + if !slices.Contains(vals, v) { + return errors.New("Must be one of: " + fmt.Sprint(vals)) + } + return nil + } +} + func NotZero[T comparable](v T) Predicate { return func() error { var zero T diff --git a/runtimes/js/encore.dev/internal/codegen/appinit.ts b/runtimes/js/encore.dev/internal/codegen/appinit.ts index 35f25da229..ce88c030c8 100644 --- a/runtimes/js/encore.dev/internal/codegen/appinit.ts +++ b/runtimes/js/encore.dev/internal/codegen/appinit.ts @@ -1 +1 @@ -export {registerGateways, registerHandlers, registerTestHandler, run, type Handler} from "../appinit/mod"; \ No newline at end of file +export {registerGateways, registerHandlers, registerTestHandler, run, type Handler} from "../appinit/mod"; \ No newline at end of file From 4596263f096039da084f339a22b2116215f3fc7d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Eriksson?= Date: Tue, 12 Nov 2024 14:10:13 +0100 Subject: [PATCH 27/37] Add s3 implementation --- .../objects/internal/providers/s3/bucket.go | 189 +++++++++++ .../internal/providers/s3/mock_client_test.go | 136 ++++++++ .../objects/internal/providers/s3/uploader.go | 299 ++++++++++++++++++ .../internal/providers/s3/uploader_test.go | 196 ++++++++++++ runtimes/go/storage/objects/provider_s3.go | 16 + 5 files changed, 836 insertions(+) create mode 100644 runtimes/go/storage/objects/internal/providers/s3/bucket.go create mode 100644 runtimes/go/storage/objects/internal/providers/s3/mock_client_test.go create mode 100644 runtimes/go/storage/objects/internal/providers/s3/uploader.go create mode 100644 runtimes/go/storage/objects/internal/providers/s3/uploader_test.go create mode 100644 runtimes/go/storage/objects/provider_s3.go diff --git a/runtimes/go/storage/objects/internal/providers/s3/bucket.go b/runtimes/go/storage/objects/internal/providers/s3/bucket.go new file mode 100644 index 0000000000..d6c37f31ad --- /dev/null +++ b/runtimes/go/storage/objects/internal/providers/s3/bucket.go @@ -0,0 +1,189 @@ +package s3 + +import ( + "context" + "errors" + "iter" + + "cloud.google.com/go/storage" + "github.com/aws/aws-sdk-go-v2/service/s3" + s3types "github.com/aws/aws-sdk-go-v2/service/s3/types" + "github.com/aws/smithy-go" + + "encore.dev/appruntime/exported/config" + "encore.dev/storage/objects/internal/types" +) + +type Manager struct { + ctx context.Context + runtime *config.Runtime + clients map[*config.BucketProvider]*s3.Client +} + +func NewManager(ctx context.Context, runtime *config.Runtime) *Manager { + return &Manager{ctx: ctx, runtime: runtime, clients: make(map[*config.BucketProvider]*s3.Client)} +} + +type bucket struct { + client *s3.Client + cfg *config.Bucket +} + +func (mgr *Manager) ProviderName() string { return "s3" } + +func (mgr *Manager) Matches(cfg *config.BucketProvider) bool { + return cfg.S3 != nil +} + +func (mgr *Manager) NewBucket(provider *config.BucketProvider, runtimeCfg *config.Bucket) types.BucketImpl { + client := mgr.clientForProvider(provider) + return &bucket{ + client: client, + cfg: runtimeCfg, + } +} + +func (b *bucket) Download(data types.DownloadData) (types.Downloader, error) { + object := string(data.Object) + resp, err := b.client.GetObject(data.Ctx, &s3.GetObjectInput{ + Bucket: &b.cfg.CloudName, + Key: &object, + VersionId: ptrOrNil(data.Version), + }) + if err != nil { + return nil, mapErr(err) + } + return resp.Body, nil +} + +func (b *bucket) Upload(data types.UploadData) (types.Uploader, error) { + return newUploader(b.client, b.cfg.CloudName, data), nil +} + +func mapListEntry(attrs *storage.ObjectAttrs) *types.ListEntry { + return &types.ListEntry{ + Object: types.CloudObject(attrs.Name), + Size: attrs.Size, + ETag: attrs.Etag, + } +} + +func (b *bucket) List(data types.ListData) iter.Seq2[*types.ListEntry, error] { + return func(yield func(*types.ListEntry, error) bool) { + var n int64 + var continuationToken string + for n < data.Limit { + // Abort early if the context is canceled. + if err := data.Ctx.Err(); err != nil { + yield(nil, err) + return + } + + maxKeys := int32(min(data.Limit-n, 1000)) + resp, err := b.client.ListObjectsV2(data.Ctx, &s3.ListObjectsV2Input{ + Bucket: &b.cfg.CloudName, + MaxKeys: &maxKeys, + ContinuationToken: ptrOrNil(continuationToken), + Prefix: ptrOrNil(data.Prefix), + }) + if err != nil { + yield(nil, mapErr(err)) + return + } + + for _, obj := range resp.Contents { + if !yield(&types.ListEntry{ + Object: types.CloudObject(*obj.Key), + Size: *obj.Size, + ETag: *obj.ETag, + }, nil) { + return + } + n++ + } + } + } +} + +func (b *bucket) Remove(data types.RemoveData) error { + object := string(data.Object) + _, err := b.client.DeleteObject(data.Ctx, &s3.DeleteObjectInput{ + Bucket: &b.cfg.CloudName, + Key: &object, + VersionId: ptrOrNil(data.Version), + }) + return mapErr(err) +} + +func (b *bucket) Attrs(data types.AttrsData) (*types.ObjectAttrs, error) { + object := string(data.Object) + resp, err := b.client.HeadObject(data.Ctx, &s3.HeadObjectInput{ + Bucket: &b.cfg.CloudName, + Key: &object, + VersionId: ptrOrNil(data.Version), + }) + if err != nil { + return nil, mapErr(err) + } + return &types.ObjectAttrs{ + Object: data.Object, + Version: valOrZero(resp.VersionId), + ContentType: valOrZero(resp.ContentType), + Size: valOrZero(resp.ContentLength), + ETag: valOrZero(resp.ETag), + }, nil +} + +func (mgr *Manager) clientForProvider(prov *config.BucketProvider) *s3.Client { + if client, ok := mgr.clients[prov]; ok { + return client + } + + client := s3.New(s3.Options{ + Region: prov.S3.Region, + BaseEndpoint: prov.S3.Endpoint, + }) + mgr.clients[prov] = client + return client +} + +func mapErr(err error) error { + var ( + noSuchKey *s3types.NoSuchKey + generic smithy.APIError + ) + switch { + case err == nil: + return nil + case errors.As(err, &noSuchKey): + return types.ErrObjectNotExist + case errors.As(err, &generic): + if generic.ErrorCode() == "PreconditionFailed" { + return types.ErrPreconditionFailed + } + return err + default: + return err + + } +} + +func ptrOrNil[T comparable](val T) *T { + var zero T + if val != zero { + return &val + } + return nil +} + +func valOrZero[T comparable](val *T) T { + if val != nil { + return *val + } + var zero T + return zero +} + +func ptr[T any](val T) *T { + return &val +} diff --git a/runtimes/go/storage/objects/internal/providers/s3/mock_client_test.go b/runtimes/go/storage/objects/internal/providers/s3/mock_client_test.go new file mode 100644 index 0000000000..ffe2538016 --- /dev/null +++ b/runtimes/go/storage/objects/internal/providers/s3/mock_client_test.go @@ -0,0 +1,136 @@ +// Code generated by MockGen. DO NOT EDIT. +// Source: ./uploader.go + +// Package s3 is a generated GoMock package. +package s3 + +import ( + context "context" + reflect "reflect" + + s3 "github.com/aws/aws-sdk-go-v2/service/s3" + gomock "github.com/golang/mock/gomock" +) + +// Mocks3Client is a mock of s3Client interface. +type Mocks3Client struct { + ctrl *gomock.Controller + recorder *Mocks3ClientMockRecorder +} + +// Mocks3ClientMockRecorder is the mock recorder for Mocks3Client. +type Mocks3ClientMockRecorder struct { + mock *Mocks3Client +} + +// NewMocks3Client creates a new mock instance. +func NewMocks3Client(ctrl *gomock.Controller) *Mocks3Client { + mock := &Mocks3Client{ctrl: ctrl} + mock.recorder = &Mocks3ClientMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *Mocks3Client) EXPECT() *Mocks3ClientMockRecorder { + return m.recorder +} + +// AbortMultipartUpload mocks base method. +func (m *Mocks3Client) AbortMultipartUpload(ctx context.Context, params *s3.AbortMultipartUploadInput, optFns ...func(*s3.Options)) (*s3.AbortMultipartUploadOutput, error) { + m.ctrl.T.Helper() + varargs := []interface{}{ctx, params} + for _, a := range optFns { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "AbortMultipartUpload", varargs...) + ret0, _ := ret[0].(*s3.AbortMultipartUploadOutput) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// AbortMultipartUpload indicates an expected call of AbortMultipartUpload. +func (mr *Mocks3ClientMockRecorder) AbortMultipartUpload(ctx, params interface{}, optFns ...interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]interface{}{ctx, params}, optFns...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AbortMultipartUpload", reflect.TypeOf((*Mocks3Client)(nil).AbortMultipartUpload), varargs...) +} + +// CompleteMultipartUpload mocks base method. +func (m *Mocks3Client) CompleteMultipartUpload(ctx context.Context, params *s3.CompleteMultipartUploadInput, optFns ...func(*s3.Options)) (*s3.CompleteMultipartUploadOutput, error) { + m.ctrl.T.Helper() + varargs := []interface{}{ctx, params} + for _, a := range optFns { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "CompleteMultipartUpload", varargs...) + ret0, _ := ret[0].(*s3.CompleteMultipartUploadOutput) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// CompleteMultipartUpload indicates an expected call of CompleteMultipartUpload. +func (mr *Mocks3ClientMockRecorder) CompleteMultipartUpload(ctx, params interface{}, optFns ...interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]interface{}{ctx, params}, optFns...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CompleteMultipartUpload", reflect.TypeOf((*Mocks3Client)(nil).CompleteMultipartUpload), varargs...) +} + +// CreateMultipartUpload mocks base method. +func (m *Mocks3Client) CreateMultipartUpload(ctx context.Context, params *s3.CreateMultipartUploadInput, optFns ...func(*s3.Options)) (*s3.CreateMultipartUploadOutput, error) { + m.ctrl.T.Helper() + varargs := []interface{}{ctx, params} + for _, a := range optFns { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "CreateMultipartUpload", varargs...) + ret0, _ := ret[0].(*s3.CreateMultipartUploadOutput) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// CreateMultipartUpload indicates an expected call of CreateMultipartUpload. +func (mr *Mocks3ClientMockRecorder) CreateMultipartUpload(ctx, params interface{}, optFns ...interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]interface{}{ctx, params}, optFns...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateMultipartUpload", reflect.TypeOf((*Mocks3Client)(nil).CreateMultipartUpload), varargs...) +} + +// PutObject mocks base method. +func (m *Mocks3Client) PutObject(ctx context.Context, params *s3.PutObjectInput, optFns ...func(*s3.Options)) (*s3.PutObjectOutput, error) { + m.ctrl.T.Helper() + varargs := []interface{}{ctx, params} + for _, a := range optFns { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "PutObject", varargs...) + ret0, _ := ret[0].(*s3.PutObjectOutput) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// PutObject indicates an expected call of PutObject. +func (mr *Mocks3ClientMockRecorder) PutObject(ctx, params interface{}, optFns ...interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]interface{}{ctx, params}, optFns...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "PutObject", reflect.TypeOf((*Mocks3Client)(nil).PutObject), varargs...) +} + +// UploadPart mocks base method. +func (m *Mocks3Client) UploadPart(ctx context.Context, params *s3.UploadPartInput, optFns ...func(*s3.Options)) (*s3.UploadPartOutput, error) { + m.ctrl.T.Helper() + varargs := []interface{}{ctx, params} + for _, a := range optFns { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "UploadPart", varargs...) + ret0, _ := ret[0].(*s3.UploadPartOutput) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// UploadPart indicates an expected call of UploadPart. +func (mr *Mocks3ClientMockRecorder) UploadPart(ctx, params interface{}, optFns ...interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]interface{}{ctx, params}, optFns...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UploadPart", reflect.TypeOf((*Mocks3Client)(nil).UploadPart), varargs...) +} diff --git a/runtimes/go/storage/objects/internal/providers/s3/uploader.go b/runtimes/go/storage/objects/internal/providers/s3/uploader.go new file mode 100644 index 0000000000..edd6263657 --- /dev/null +++ b/runtimes/go/storage/objects/internal/providers/s3/uploader.go @@ -0,0 +1,299 @@ +package s3 + +import ( + "bytes" + "context" + "crypto/md5" + "encoding/base64" + "errors" + "sync" + "time" + + "encore.dev/storage/objects/internal/types" + "github.com/aws/aws-sdk-go-v2/service/s3" + "golang.org/x/sync/errgroup" +) + +type uploader struct { + client s3Client + bucket string + data types.UploadData + ctx context.Context + out chan uploadEvent + + init sync.Once + done chan struct{} + attrs *types.ObjectAttrs + err error + + curr *buffer +} + +type uploadEvent struct { + data *buffer + abort error + done bool +} + +type buffer struct { + buf []byte + n int // number of bytes in buf +} + +func newUploader(client s3Client, bucket string, data types.UploadData) *uploader { + return &uploader{ + bucket: bucket, + client: client, + ctx: data.Ctx, + data: data, + out: make(chan uploadEvent, 10), + done: make(chan struct{}), + } +} + +func (u *uploader) Write(p []byte) (n int, err error) { + u.initUpload() + for len(p) > 0 { + curr := u.curr + if curr == nil { + curr = getBuf() + } + + copied := copy(curr.buf[curr.n:], p) + n += copied + curr.n += copied + + if copied < len(p) { + // Buffer is full, send it. + p = p[copied:] + select { + case u.out <- uploadEvent{data: curr}: + case <-u.done: + return n, u.err + } + + u.curr, curr = nil, nil + } else { + // We've written all the data. Keep track + // of the buffer for the next call. + u.curr = curr + return n, nil + } + } + + return n, nil +} + +func (u *uploader) Complete() (*types.ObjectAttrs, error) { + u.initUpload() + // If we have a current buffer, send it. + if curr := u.curr; curr != nil && curr.n > 0 { + select { + case u.out <- uploadEvent{data: curr, done: true}: + case <-u.done: + } + u.curr = nil + } else { + select { + case u.out <- uploadEvent{done: true}: + case <-u.done: + } + } + + // Wait for the upload to finish. + <-u.done + return u.attrs, u.err +} + +func (u *uploader) Abort(err error) { + u.initUpload() + // Ensure err is non-nil + if err == nil { + err = errors.New("upload aborted") + } + + select { + case u.out <- uploadEvent{abort: err}: + case <-u.done: + } +} + +func (u *uploader) initUpload() { + u.init.Do(func() { + go func() { + defer close(u.done) + attrs, err := u.doUpload() + u.attrs, u.err = attrs, mapErr(err) + }() + }) +} + +func (u *uploader) doUpload() (*types.ObjectAttrs, error) { + ev := <-u.out + if ev.abort != nil { + // Nothing to do. + return nil, ev.abort + } else if ev.done { + // First buffer is the final one; we can do a single-part upload. + var buf []byte + if ev.data != nil { + buf = ev.data.buf[:ev.data.n] + } + return u.singlePartUpload(buf) + } + + return u.multiPartUpload(ev.data) +} + +type s3Client interface { + PutObject(ctx context.Context, params *s3.PutObjectInput, optFns ...func(*s3.Options)) (*s3.PutObjectOutput, error) + CreateMultipartUpload(ctx context.Context, params *s3.CreateMultipartUploadInput, optFns ...func(*s3.Options)) (*s3.CreateMultipartUploadOutput, error) + UploadPart(ctx context.Context, params *s3.UploadPartInput, optFns ...func(*s3.Options)) (*s3.UploadPartOutput, error) + CompleteMultipartUpload(ctx context.Context, params *s3.CompleteMultipartUploadInput, optFns ...func(*s3.Options)) (*s3.CompleteMultipartUploadOutput, error) + AbortMultipartUpload(ctx context.Context, params *s3.AbortMultipartUploadInput, optFns ...func(*s3.Options)) (*s3.AbortMultipartUploadOutput, error) +} + +func (u *uploader) singlePartUpload(buf []byte) (*types.ObjectAttrs, error) { + key := ptr(u.data.Object.String()) + md5sum := md5.Sum(buf) + contentMD5 := base64.StdEncoding.EncodeToString(md5sum[:]) + resp, err := u.client.PutObject(u.ctx, &s3.PutObjectInput{ + Bucket: &u.bucket, + Key: key, + Body: bytes.NewReader(buf), + ContentType: ptrOrNil(u.data.Attrs.ContentType), + ContentMD5: &contentMD5, + ContentLength: ptr(int64(len(buf))), + }) + if err != nil { + return nil, err + } + + return &types.ObjectAttrs{ + Object: u.data.Object, + Version: valOrZero(resp.VersionId), + ContentType: u.data.Attrs.ContentType, + Size: int64(len(buf)), + ETag: valOrZero(resp.ETag), + }, nil +} + +func (u *uploader) multiPartUpload(initial *buffer) (attrs *types.ObjectAttrs, err error) { + key := ptr(u.data.Object.String()) + resp, err := u.client.CreateMultipartUpload(u.ctx, &s3.CreateMultipartUploadInput{ + Bucket: &u.bucket, + Key: key, + ContentType: ptrOrNil(u.data.Attrs.ContentType), + }) + if err != nil { + return nil, err + } + uploadID := valOrZero(resp.UploadId) + + defer func() { + if err != nil { + // The upload failed. Abort the multipart upload. + go func() { + ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second) + defer cancel() + _, _ = u.client.AbortMultipartUpload(ctx, &s3.AbortMultipartUploadInput{ + Bucket: &u.bucket, + Key: key, + UploadId: &uploadID, + }) + }() + } + }() + + g, groupCtx := errgroup.WithContext(u.ctx) + partNumber := int32(1) + var totalSize int64 + uploadPart := func(buf *buffer) { + if buf == nil { + // No data to upload. + return + } + + totalSize += int64(buf.n) + part := partNumber + partNumber++ + g.Go(func() error { + data := buf.buf[:buf.n] + defer putBuf(buf) + + md5sum := md5.Sum(data) + contentMD5 := base64.StdEncoding.EncodeToString(md5sum[:]) + _, err := u.client.UploadPart(groupCtx, &s3.UploadPartInput{ + Bucket: &u.bucket, + UploadId: &uploadID, + PartNumber: &part, + Body: bytes.NewReader(data), + ContentLength: ptr(int64(len(data))), + ContentMD5: ptr(contentMD5), + }) + return err + }) + } + + // Upload the first part, if given. + uploadPart(initial) + for { + ev := <-u.out + if ev.abort != nil { + return nil, ev.abort + } + + if ev.data != nil { + uploadPart(ev.data) + } + + if ev.done { + break + } + } + + // Wait for the uploads to complete. + if err := g.Wait(); err != nil { + return nil, err + } + + // Complete the multipart upload. + var completeResp *s3.CompleteMultipartUploadOutput + completeResp, err = u.client.CompleteMultipartUpload(u.ctx, &s3.CompleteMultipartUploadInput{ + Bucket: &u.bucket, + Key: key, + UploadId: &uploadID, + }) + if err != nil { + return nil, err + } + return &types.ObjectAttrs{ + Object: u.data.Object, + Version: valOrZero(completeResp.VersionId), + ContentType: u.data.Attrs.ContentType, + Size: totalSize, + ETag: valOrZero(completeResp.ETag), + }, nil +} + +// bufSize is the size of buffers allocated by bufPool. +// It's a variable for testing purposes. +var bufSize = 10 * 1024 * 1024 + +var bufPool = sync.Pool{ + New: func() any { + return &buffer{ + buf: make([]byte, bufSize), + } + }, +} + +func getBuf() *buffer { + buf := bufPool.Get().(*buffer) + buf.n = 0 + return buf +} + +func putBuf(buf *buffer) { + bufPool.Put(buf) +} diff --git a/runtimes/go/storage/objects/internal/providers/s3/uploader_test.go b/runtimes/go/storage/objects/internal/providers/s3/uploader_test.go new file mode 100644 index 0000000000..54c1181d4e --- /dev/null +++ b/runtimes/go/storage/objects/internal/providers/s3/uploader_test.go @@ -0,0 +1,196 @@ +package s3 + +import ( + "bytes" + "context" + "fmt" + "io" + "strings" + "testing" + + "encore.dev/storage/objects/internal/types" + "github.com/aws/aws-sdk-go-v2/service/s3" + qt "github.com/frankban/quicktest" + "github.com/golang/mock/gomock" +) + +//go:generate mockgen -source=./uploader.go -destination ./mock_client_test.go -package s3 s3Client + +func TestUploader_Sync(t *testing.T) { + c := qt.New(t) + + ctrl := gomock.NewController(c) + client := NewMocks3Client(ctrl) + + const ( + bucket = "bucket" + object = "object" + contentType = "text/plain" + ) + u := newUploader(client, bucket, types.UploadData{ + Ctx: context.Background(), + Object: object, + Attrs: types.UploadAttrs{ + ContentType: contentType, + }, + Pre: types.Preconditions{}, + }) + + const ( + version = "version" + etag = "etag" + ) + client.EXPECT().PutObject(gomock.Any(), gomock.Any()).Return(&s3.PutObjectOutput{ + VersionId: ptr(version), + ETag: ptr(etag), + }, nil) + + content := []byte("test") + n, err := u.Write(content) + c.Assert(n, qt.Equals, 4) + c.Assert(err, qt.Equals, nil) + + attrs, err := u.Complete() + c.Assert(err, qt.Equals, nil) + c.Assert(attrs, qt.DeepEquals, &types.ObjectAttrs{ + Object: types.CloudObject(object), + Version: version, + ContentType: contentType, + ETag: etag, + Size: int64(len(content)), + }) +} + +func TestUploader_MultipleWrites(t *testing.T) { + c := qt.New(t) + + ctrl := gomock.NewController(c) + client := NewMocks3Client(ctrl) + + const ( + bucket = "bucket" + object = "object" + contentType = "text/plain" + ) + u := newUploader(client, bucket, types.UploadData{ + Ctx: context.Background(), + Object: object, + Attrs: types.UploadAttrs{ + ContentType: contentType, + }, + Pre: types.Preconditions{}, + }) + + const ( + version = "version" + etag = "etag" + ) + client.EXPECT().PutObject(gomock.Any(), gomock.Any()).Return(&s3.PutObjectOutput{ + VersionId: ptr(version), + ETag: ptr(etag), + }, nil) + + baseContent := "test" + totalContent := strings.Repeat(baseContent, 10) + for i := 0; i < 10; i++ { + n, err := u.Write([]byte(baseContent)) + c.Assert(n, qt.Equals, len(baseContent)) + c.Assert(err, qt.Equals, nil) + } + + attrs, err := u.Complete() + c.Assert(err, qt.Equals, nil) + c.Assert(attrs, qt.DeepEquals, &types.ObjectAttrs{ + Object: types.CloudObject(object), + Version: version, + ContentType: contentType, + ETag: etag, + Size: int64(len(totalContent)), + }) +} + +func TestUploader_MultipartUpload(t *testing.T) { + c := qt.New(t) + + ctrl := gomock.NewController(c) + client := NewMocks3Client(ctrl) + + const ( + bucket = "bucket" + object = "object" + contentType = "text/plain" + ) + u := newUploader(client, bucket, types.UploadData{ + Ctx: context.Background(), + Object: object, + Attrs: types.UploadAttrs{ + ContentType: contentType, + }, + Pre: types.Preconditions{}, + }) + + withBufSize(c, 10) + const ( + version = "version" + etag = "etag" + uploadID = "uploadID" + ) + client.EXPECT().CreateMultipartUpload(gomock.Any(), gomock.Any()).Return(&s3.CreateMultipartUploadOutput{ + UploadId: ptr(uploadID), + }, nil) + client.EXPECT().UploadPart(gomock.Any(), &partMatcher{num: 1, data: "abcdefghij"}).Return(&s3.UploadPartOutput{}, nil) + client.EXPECT().UploadPart(gomock.Any(), &partMatcher{num: 2, data: "klmabcdefg"}).Return(&s3.UploadPartOutput{}, nil) + client.EXPECT().UploadPart(gomock.Any(), &partMatcher{num: 3, data: "hijklmabcd"}).Return(&s3.UploadPartOutput{}, nil) + client.EXPECT().UploadPart(gomock.Any(), &partMatcher{num: 4, data: "efghijklm"}).Return(&s3.UploadPartOutput{}, nil) + client.EXPECT().CompleteMultipartUpload(gomock.Any(), gomock.Any()).Return(&s3.CompleteMultipartUploadOutput{ + VersionId: ptr(version), + ETag: ptr(etag), + }, nil) + + baseContent := "abcdefghijklm" + totalContent := strings.Repeat(baseContent, 3) + for i := 0; i < 3; i++ { + n, err := u.Write([]byte(baseContent)) + c.Assert(n, qt.Equals, len(baseContent)) + c.Assert(err, qt.Equals, nil) + } + + attrs, err := u.Complete() + c.Assert(err, qt.Equals, nil) + c.Assert(attrs, qt.DeepEquals, &types.ObjectAttrs{ + Object: types.CloudObject(object), + Version: version, + ContentType: contentType, + ETag: etag, + Size: int64(len(totalContent)), + }) +} + +func withBufSize(c *qt.C, n int) { + orig := bufSize + bufSize = n + c.Cleanup(func() { bufSize = orig }) +} + +type partMatcher struct { + num int + data string +} + +func (m *partMatcher) Matches(x interface{}) bool { + part, ok := x.(*s3.UploadPartInput) + if !ok { + return false + } + data, err := io.ReadAll(part.Body) + if err != nil { + panic(err) + } + part.Body = bytes.NewReader(data) + fmt.Printf("part %d: %q\n", valOrZero(part.PartNumber), data) + return valOrZero(part.PartNumber) == int32(m.num) && string(data) == m.data +} + +func (m *partMatcher) String() string { + return fmt.Sprintf("is part %d with data %q", m.num, m.data) +} diff --git a/runtimes/go/storage/objects/provider_s3.go b/runtimes/go/storage/objects/provider_s3.go new file mode 100644 index 0000000000..b66388f0d6 --- /dev/null +++ b/runtimes/go/storage/objects/provider_s3.go @@ -0,0 +1,16 @@ +//go:build !encore_no_aws + +package objects + +import ( + "context" + + "encore.dev/appruntime/exported/config" + "encore.dev/storage/objects/internal/providers/gcs" +) + +func init() { + registerProvider(func(ctx context.Context, runtimeCfg *config.Runtime) provider { + return gcs.NewManager(ctx, runtimeCfg) + }) +} From 9d39a465628d35beb6e1d106b32d00e6b9775c07 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Eriksson?= Date: Tue, 12 Nov 2024 15:41:36 +0100 Subject: [PATCH 28/37] Make object errors type-safe --- pkg/traceparser/parser.go | 18 ++-- runtimes/core/src/infracfg.rs | 3 + runtimes/js/encore.dev/api/error.ts | 14 +-- .../js/encore.dev/storage/objects/bucket.ts | 20 +++-- .../js/encore.dev/storage/objects/error.ts | 74 ++++++++++++++++ runtimes/js/encore.dev/storage/objects/mod.ts | 1 + runtimes/js/src/objects.rs | 88 ++++++++++++------- 7 files changed, 161 insertions(+), 57 deletions(-) create mode 100644 runtimes/js/encore.dev/storage/objects/error.ts diff --git a/pkg/traceparser/parser.go b/pkg/traceparser/parser.go index 2effec3891..f4ac3a5cdd 100644 --- a/pkg/traceparser/parser.go +++ b/pkg/traceparser/parser.go @@ -591,10 +591,10 @@ func (tp *traceParser) bucketObjectUploadStart() *tracepb2.BucketObjectUploadSta func (tp *traceParser) bucketObjectAttrs() *tracepb2.BucketObjectAttributes { return &tracepb2.BucketObjectAttributes{ - Size: ptrOrNil(tp.UVarint()), - Version: ptrOrNil(tp.String()), - Etag: ptrOrNil(tp.String()), - ContentType: ptrOrNil(tp.String()), + Size: tp.OptUVarint(), + Version: tp.OptString(), + Etag: tp.OptString(), + ContentType: tp.OptString(), } } @@ -610,14 +610,14 @@ func (tp *traceParser) bucketObjectDownloadStart() *tracepb2.BucketObjectDownloa return &tracepb2.BucketObjectDownloadStart{ Bucket: tp.String(), Object: tp.String(), - Version: ptrOrNil(tp.String()), + Version: tp.OptString(), Stack: tp.stack(), } } func (tp *traceParser) bucketObjectDownloadEnd() *tracepb2.BucketObjectDownloadEnd { return &tracepb2.BucketObjectDownloadEnd{ - Size: ptrOrNil(tp.Uint64()), + Size: tp.OptUVarint(), Err: tp.errWithStack(), } } @@ -632,7 +632,7 @@ func (tp *traceParser) bucketDeleteObjectsStart() *tracepb2.BucketDeleteObjectsS for i := 0; i < int(num); i++ { ev.Entries = append(ev.Entries, &tracepb2.BucketDeleteObjectEntry{ Object: tp.String(), - Version: ptrOrNil(tp.String()), + Version: tp.OptString(), }) } @@ -648,7 +648,7 @@ func (tp *traceParser) bucketDeleteObjectsEnd() *tracepb2.BucketDeleteObjectsEnd func (tp *traceParser) bucketListObjectsStart() *tracepb2.BucketListObjectsStart { return &tracepb2.BucketListObjectsStart{ Bucket: tp.String(), - Prefix: ptrOrNil(tp.String()), + Prefix: tp.OptString(), Stack: tp.stack(), } } @@ -665,7 +665,7 @@ func (tp *traceParser) bucketObjectGetAttrsStart() *tracepb2.BucketObjectGetAttr return &tracepb2.BucketObjectGetAttrsStart{ Bucket: tp.String(), Object: tp.String(), - Version: ptrOrNil(tp.String()), + Version: tp.OptString(), Stack: tp.stack(), } } diff --git a/runtimes/core/src/infracfg.rs b/runtimes/core/src/infracfg.rs index d765a17d63..7e45db53d6 100644 --- a/runtimes/core/src/infracfg.rs +++ b/runtimes/core/src/infracfg.rs @@ -40,6 +40,8 @@ pub enum ObjectStorage { pub struct GCS { pub endpoint: Option, pub buckets: HashMap, + #[serde(default)] + pub anonymous: bool, } #[derive(Debug, Serialize, Deserialize)] @@ -440,6 +442,7 @@ pub fn map_infra_to_runtime(infra: InfraConfig) -> RuntimeConfig { provider: Some(pbruntime::bucket_cluster::Provider::Gcs( pbruntime::bucket_cluster::Gcs { endpoint: gcs.endpoint.clone(), + anonymous: gcs.anonymous, }, )), buckets: gcs diff --git a/runtimes/js/encore.dev/api/error.ts b/runtimes/js/encore.dev/api/error.ts index 289922c7ee..af61e474d2 100644 --- a/runtimes/js/encore.dev/api/error.ts +++ b/runtimes/js/encore.dev/api/error.ts @@ -105,17 +105,9 @@ export class APIError extends Error { configurable: true }); - // fix the prototype chain - if ((Object as any).setPrototypeOf == undefined) { - (this as any).__proto__ = APIError.prototype; - } else { - Object.setPrototypeOf(this, APIError.prototype); - } - - // capture a stack trace - if ((Error as any).captureStackTrace !== undefined) { - (Error as any).captureStackTrace(this, this.constructor); - } + // Fix the prototype chain, capture stack trace. + Object.setPrototypeOf(this, APIError.prototype); + Error.captureStackTrace(this, this.constructor); } } diff --git a/runtimes/js/encore.dev/storage/objects/bucket.ts b/runtimes/js/encore.dev/storage/objects/bucket.ts index 238f79012d..aef9cd44fd 100644 --- a/runtimes/js/encore.dev/storage/objects/bucket.ts +++ b/runtimes/js/encore.dev/storage/objects/bucket.ts @@ -1,6 +1,7 @@ import { getCurrentRequest } from "../../internal/reqtrack/mod"; import * as runtime from "../../internal/runtime/mod"; import { StringLiteral } from "../../internal/utils/constraints"; +import { unwrapErr } from "./error"; export interface BucketConfig { /** @@ -34,7 +35,7 @@ export class Bucket { async *list(options: ListOptions): AsyncGenerator { const source = getCurrentRequest(); - const iter = await this.impl.list(options, source); + const iter = unwrapErr(await this.impl.list(options, source)); while (true) { const entry = await iter.next(); if (entry === null) { @@ -52,7 +53,8 @@ export class Bucket { async exists(name: string, options?: ExistsOptions): Promise { const source = getCurrentRequest(); const impl = this.impl.object(name); - return impl.exists(options, source); + const res = await impl.exists(options, source); + return unwrapErr(res); } /** @@ -62,7 +64,8 @@ export class Bucket { async attrs(name: string, options?: AttrsOptions): Promise { const source = getCurrentRequest(); const impl = this.impl.object(name); - return impl.attrs(options, source); + const res = await impl.attrs(options, source); + return unwrapErr(res); } /** @@ -71,7 +74,8 @@ export class Bucket { async upload(name: string, data: Buffer, options?: UploadOptions): Promise { const source = getCurrentRequest(); const impl = this.impl.object(name); - return impl.upload(data, options, source); + const res = await impl.upload(data, options, source); + return unwrapErr(res); } /** @@ -80,7 +84,8 @@ export class Bucket { async download(name: string, options?: DownloadOptions): Promise { const source = getCurrentRequest(); const impl = this.impl.object(name); - return impl.downloadAll(options, source); + const res = await impl.downloadAll(options, source); + return unwrapErr(res); } /** @@ -90,7 +95,10 @@ export class Bucket { async remove(name: string, options?: DeleteOptions): Promise { const source = getCurrentRequest(); const impl = this.impl.object(name); - const _ = await impl.delete(options, source); + const err = await impl.delete(options, source); + if (err) { + unwrapErr(err); + } } } diff --git a/runtimes/js/encore.dev/storage/objects/error.ts b/runtimes/js/encore.dev/storage/objects/error.ts new file mode 100644 index 0000000000..8e1598b897 --- /dev/null +++ b/runtimes/js/encore.dev/storage/objects/error.ts @@ -0,0 +1,74 @@ +import * as runtime from "../../internal/runtime/mod"; +import log from "../../log/mod"; + +export class ObjectsError extends Error { + constructor(msg: string) { + // extending errors causes issues after you construct them, unless you apply the following fixes + super(msg); + + // set error name as constructor name, make it not enumerable to keep native Error behavior + // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/new.target#new.target_in_constructors + Object.defineProperty(this, "name", { + value: "ObjectsError", + enumerable: false, + configurable: true + }); + + // Fix the prototype chain, capture stack trace. + Object.setPrototypeOf(this, ObjectsError.prototype); + Error.captureStackTrace(this, this.constructor); + } +} + +export class ObjectNotFound extends ObjectsError { + constructor(msg: string) { + // extending errors causes issues after you construct them, unless you apply the following fixes + super(msg); + + // set error name as constructor name, make it not enumerable to keep native Error behavior + // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/new.target#new.target_in_constructors + Object.defineProperty(this, "name", { + value: "ObjectNotFound", + enumerable: false, + configurable: true + }); + + // Fix the prototype chain, capture stack trace. + Object.setPrototypeOf(this, ObjectNotFound.prototype); + Error.captureStackTrace(this, this.constructor); + } +} + +export class PreconditionFailed extends ObjectsError { + constructor(msg: string) { + // extending errors causes issues after you construct them, unless you apply the following fixes + super(msg); + + // set error name as constructor name, make it not enumerable to keep native Error behavior + // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/new.target#new.target_in_constructors + Object.defineProperty(this, "name", { + value: "PrecondionFailed", + enumerable: false, + configurable: true + }); + + // Fix the prototype chain, capture stack trace. + Object.setPrototypeOf(this, PreconditionFailed.prototype); + Error.captureStackTrace(this, this.constructor); + } +} + +export function unwrapErr(val: T | runtime.TypedObjectError): T { + if (val instanceof runtime.TypedObjectError) { + switch (val.kind) { + case runtime.ObjectErrorKind.NotFound: + throw new ObjectNotFound(val.message); + case runtime.ObjectErrorKind.PreconditionFailed: + throw new PreconditionFailed(val.message); + default: + throw new ObjectsError(val.message); + } + } + + return val; +} diff --git a/runtimes/js/encore.dev/storage/objects/mod.ts b/runtimes/js/encore.dev/storage/objects/mod.ts index 746f05933d..7b5c002230 100644 --- a/runtimes/js/encore.dev/storage/objects/mod.ts +++ b/runtimes/js/encore.dev/storage/objects/mod.ts @@ -1,2 +1,3 @@ export { Bucket } from "./bucket"; export type { BucketConfig, ObjectAttrs, UploadOptions } from "./bucket"; +export { ObjectsError, ObjectNotFound, PreconditionFailed } from "./error"; diff --git a/runtimes/js/src/objects.rs b/runtimes/js/src/objects.rs index bdaf4c35e4..0257b4ee59 100644 --- a/runtimes/js/src/objects.rs +++ b/runtimes/js/src/objects.rs @@ -26,14 +26,13 @@ impl Bucket { &self, options: Option, source: Option<&Request>, - ) -> napi::Result { + ) -> napi::Either { let options = options.unwrap_or_default().into(); let source = source.map(|s| s.inner.clone()); - self.bkt - .list(options, source) - .await - .map_err(map_objects_err) - .map(ListIterator::new) + match self.bkt.list(options, source).await { + Ok(iter) => napi::Either::A(ListIterator::new(iter)), + Err(err) => napi::Either::B(err.into()), + } } } @@ -53,14 +52,13 @@ impl BucketObject { &self, options: Option, source: Option<&Request>, - ) -> napi::Result { + ) -> napi::Either { let options = options.unwrap_or_default().into(); let source = source.map(|s| s.inner.clone()); - self.obj - .attrs(options, source) - .await - .map(ObjectAttrs::from) - .map_err(map_objects_err) + match self.obj.attrs(options, source).await { + Ok(attrs) => napi::Either::A(attrs.into()), + Err(err) => napi::Either::B(err.into()), + } } #[napi] @@ -68,13 +66,16 @@ impl BucketObject { &self, options: Option, source: Option<&Request>, - ) -> napi::Result { + ) -> napi::Either { let opts = options.unwrap_or_default().into(); let source = source.map(|s| s.inner.clone()); - self.obj.exists(opts, source).await.map_err(map_objects_err) + match self.obj.exists(opts, source).await { + Ok(val) => napi::Either::A(val), + Err(err) => napi::Either::B(err.into()), + } } - #[napi(ts_return_type = "Promise")] + #[napi(ts_return_type = "Promise")] pub fn upload( &self, env: Env, @@ -98,7 +99,10 @@ impl BucketObject { env.execute_tokio_future(fut, move |&mut _env, result| { // TODO: Decrement the ref count on the data buffer. - result.map(ObjectAttrs::from).map_err(map_objects_err) + match result { + Ok(attrs) => Ok(napi::Either::A(ObjectAttrs::from(attrs))), + Err(err) => Ok(napi::Either::B(TypedObjectError::from(err))), + } }) } @@ -107,15 +111,13 @@ impl BucketObject { &self, options: Option, source: Option<&Request>, - ) -> napi::Result { + ) -> napi::Either { let options = options.unwrap_or_default().into(); let source = source.map(|s| s.inner.clone()); - let buf = self - .obj - .download_all(options, source) - .await - .map_err(map_objects_err)?; - Ok(buf.into()) + match self.obj.download_all(options, source).await { + Ok(buf) => napi::Either::A(buf.into()), + Err(err) => napi::Either::B(err.into()), + } } #[napi] @@ -123,14 +125,13 @@ impl BucketObject { &self, options: Option, source: Option<&Request>, - ) -> napi::Result { + ) -> Option { let options = options.unwrap_or_default().into(); let source = source.map(|s| s.inner.clone()); - self.obj - .delete(options, source) - .await - .map_err(map_objects_err)?; - Ok(true) + match self.obj.delete(options, source).await { + Ok(()) => None, + Err(err) => Some(err.into()), + } } } @@ -202,8 +203,33 @@ impl From for core::UploadPreconditions { } } -fn map_objects_err(err: core::Error) -> napi::Error { - napi::Error::new(napi::Status::GenericFailure, err) +#[napi] +pub enum ObjectErrorKind { + NotFound, + PreconditionFailed, + Other, + Internal, +} + +#[napi] +pub struct TypedObjectError { + pub kind: ObjectErrorKind, + pub message: String, +} + +impl From for TypedObjectError { + fn from(value: core::Error) -> Self { + let kind = match &value { + core::Error::NotFound => ObjectErrorKind::NotFound, + core::Error::PreconditionFailed => ObjectErrorKind::PreconditionFailed, + core::Error::Internal(_) => ObjectErrorKind::Internal, + core::Error::Other(_) => ObjectErrorKind::Other, + }; + Self { + kind, + message: value.to_string(), + } + } } #[napi] From a30d716eb553c1b9468f8918cfcee56d7e9480f7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Eriksson?= Date: Tue, 12 Nov 2024 15:50:07 +0100 Subject: [PATCH 29/37] s3: handle preconditions --- .../objects/internal/providers/s3/uploader.go | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/runtimes/go/storage/objects/internal/providers/s3/uploader.go b/runtimes/go/storage/objects/internal/providers/s3/uploader.go index edd6263657..068d17d5ae 100644 --- a/runtimes/go/storage/objects/internal/providers/s3/uploader.go +++ b/runtimes/go/storage/objects/internal/providers/s3/uploader.go @@ -157,6 +157,12 @@ func (u *uploader) singlePartUpload(buf []byte) (*types.ObjectAttrs, error) { key := ptr(u.data.Object.String()) md5sum := md5.Sum(buf) contentMD5 := base64.StdEncoding.EncodeToString(md5sum[:]) + + var ifNoneMatch *string + if u.data.Pre.NotExists { + ifNoneMatch = ptr("*") + } + resp, err := u.client.PutObject(u.ctx, &s3.PutObjectInput{ Bucket: &u.bucket, Key: key, @@ -164,6 +170,7 @@ func (u *uploader) singlePartUpload(buf []byte) (*types.ObjectAttrs, error) { ContentType: ptrOrNil(u.data.Attrs.ContentType), ContentMD5: &contentMD5, ContentLength: ptr(int64(len(buf))), + IfNoneMatch: ifNoneMatch, }) if err != nil { return nil, err @@ -258,11 +265,17 @@ func (u *uploader) multiPartUpload(initial *buffer) (attrs *types.ObjectAttrs, e } // Complete the multipart upload. + var ifNoneMatch *string + if u.data.Pre.NotExists { + ifNoneMatch = ptr("*") + } + var completeResp *s3.CompleteMultipartUploadOutput completeResp, err = u.client.CompleteMultipartUpload(u.ctx, &s3.CompleteMultipartUploadInput{ - Bucket: &u.bucket, - Key: key, - UploadId: &uploadID, + Bucket: &u.bucket, + Key: key, + UploadId: &uploadID, + IfNoneMatch: ifNoneMatch, }) if err != nil { return nil, err From f803917aa3aa46ba80128b465d85fd7ccbb9f4cf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Eriksson?= Date: Thu, 14 Nov 2024 11:08:15 +0100 Subject: [PATCH 30/37] s3: resolve default credentials --- .../objects/internal/providers/s3/bucket.go | 23 +++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/runtimes/go/storage/objects/internal/providers/s3/bucket.go b/runtimes/go/storage/objects/internal/providers/s3/bucket.go index d6c37f31ad..febe9f2263 100644 --- a/runtimes/go/storage/objects/internal/providers/s3/bucket.go +++ b/runtimes/go/storage/objects/internal/providers/s3/bucket.go @@ -3,9 +3,13 @@ package s3 import ( "context" "errors" + "fmt" "iter" + "sync" "cloud.google.com/go/storage" + "github.com/aws/aws-sdk-go-v2/aws" + awsConfig "github.com/aws/aws-sdk-go-v2/config" "github.com/aws/aws-sdk-go-v2/service/s3" s3types "github.com/aws/aws-sdk-go-v2/service/s3/types" "github.com/aws/smithy-go" @@ -18,6 +22,9 @@ type Manager struct { ctx context.Context runtime *config.Runtime clients map[*config.BucketProvider]*s3.Client + + cfgOnce sync.Once + awsCfg aws.Config } func NewManager(ctx context.Context, runtime *config.Runtime) *Manager { @@ -139,14 +146,30 @@ func (mgr *Manager) clientForProvider(prov *config.BucketProvider) *s3.Client { return client } + cfg := mgr.getConfig() client := s3.New(s3.Options{ Region: prov.S3.Region, BaseEndpoint: prov.S3.Endpoint, + Credentials: cfg.Credentials, }) + mgr.clients[prov] = client return client } +// getConfig loads the required AWS config to connect to AWS +func (mgr *Manager) getConfig() aws.Config { + mgr.cfgOnce.Do(func() { + cfg, err := awsConfig.LoadDefaultConfig(context.Background()) + if err != nil { + panic(fmt.Sprintf("unable to load AWS config: %v", err)) + } + mgr.awsCfg = cfg + + }) + return mgr.awsCfg +} + func mapErr(err error) error { var ( noSuchKey *s3types.NoSuchKey From 735e367052426f11ab0c33834eccdec200621d6a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Eriksson?= Date: Thu, 14 Nov 2024 13:33:58 +0100 Subject: [PATCH 31/37] Fix s3 listing --- runtimes/core/src/infracfg.rs | 4 +--- runtimes/go/go.mod | 4 ++-- runtimes/go/storage/objects/bucket.go | 2 +- .../go/storage/objects/internal/providers/gcs/bucket.go | 2 +- .../go/storage/objects/internal/providers/s3/bucket.go | 7 +++++-- runtimes/go/storage/objects/internal/types/types.go | 2 +- 6 files changed, 11 insertions(+), 10 deletions(-) diff --git a/runtimes/core/src/infracfg.rs b/runtimes/core/src/infracfg.rs index 7e45db53d6..e72a8abf97 100644 --- a/runtimes/core/src/infracfg.rs +++ b/runtimes/core/src/infracfg.rs @@ -40,8 +40,6 @@ pub enum ObjectStorage { pub struct GCS { pub endpoint: Option, pub buckets: HashMap, - #[serde(default)] - pub anonymous: bool, } #[derive(Debug, Serialize, Deserialize)] @@ -442,7 +440,7 @@ pub fn map_infra_to_runtime(infra: InfraConfig) -> RuntimeConfig { provider: Some(pbruntime::bucket_cluster::Provider::Gcs( pbruntime::bucket_cluster::Gcs { endpoint: gcs.endpoint.clone(), - anonymous: gcs.anonymous, + anonymous: false, }, )), buckets: gcs diff --git a/runtimes/go/go.mod b/runtimes/go/go.mod index c525e33826..f69df96184 100644 --- a/runtimes/go/go.mod +++ b/runtimes/go/go.mod @@ -18,6 +18,7 @@ require ( github.com/aws/aws-sdk-go-v2/service/s3 v1.66.3 github.com/aws/aws-sdk-go-v2/service/sns v1.26.7 github.com/aws/aws-sdk-go-v2/service/sqs v1.29.7 + github.com/aws/smithy-go v1.22.0 github.com/benbjohnson/clock v1.3.3 github.com/felixge/httpsnoop v1.0.4 github.com/frankban/quicktest v1.14.5 @@ -38,6 +39,7 @@ require ( go.uber.org/automaxprocs v1.5.3 golang.org/x/crypto v0.25.0 golang.org/x/net v0.27.0 + golang.org/x/sync v0.8.0 golang.org/x/time v0.6.0 google.golang.org/api v0.191.0 google.golang.org/genproto/googleapis/api v0.0.0-20240725223205-93522f1f2a9f @@ -68,7 +70,6 @@ require ( github.com/aws/aws-sdk-go-v2/service/sso v1.18.7 // indirect github.com/aws/aws-sdk-go-v2/service/ssooidc v1.21.7 // indirect github.com/aws/aws-sdk-go-v2/service/sts v1.26.7 // indirect - github.com/aws/smithy-go v1.22.0 // indirect github.com/cespare/xxhash/v2 v2.2.0 // indirect github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect github.com/dnaeon/go-vcr v1.2.0 // indirect @@ -104,7 +105,6 @@ require ( go.opentelemetry.io/otel/metric v1.24.0 // indirect go.opentelemetry.io/otel/trace v1.24.0 // indirect golang.org/x/oauth2 v0.22.0 // indirect - golang.org/x/sync v0.8.0 // indirect golang.org/x/sys v0.22.0 // indirect golang.org/x/text v0.16.0 // indirect google.golang.org/genproto v0.0.0-20240730163845-b1a4ccb954bf // indirect diff --git a/runtimes/go/storage/objects/bucket.go b/runtimes/go/storage/objects/bucket.go index 12a294f088..804324f5e2 100644 --- a/runtimes/go/storage/objects/bucket.go +++ b/runtimes/go/storage/objects/bucket.go @@ -284,7 +284,7 @@ func (b *Bucket) mapQuery(ctx context.Context, q *Query) types.ListData { return types.ListData{ Ctx: ctx, Prefix: b.baseCloudPrefix + q.Prefix, - Limit: q.Limit, + Limit: ptrOrNil(q.Limit), } } diff --git a/runtimes/go/storage/objects/internal/providers/gcs/bucket.go b/runtimes/go/storage/objects/internal/providers/gcs/bucket.go index f44bf10310..f6bce45ce5 100644 --- a/runtimes/go/storage/objects/internal/providers/gcs/bucket.go +++ b/runtimes/go/storage/objects/internal/providers/gcs/bucket.go @@ -135,7 +135,7 @@ func (b *bucket) List(data types.ListData) iter.Seq2[*types.ListEntry, error] { } // Are we over the limit? - if data.Limit != 0 && n >= data.Limit { + if data.Limit != nil && n >= *data.Limit { return } n++ diff --git a/runtimes/go/storage/objects/internal/providers/s3/bucket.go b/runtimes/go/storage/objects/internal/providers/s3/bucket.go index febe9f2263..ad743e6c71 100644 --- a/runtimes/go/storage/objects/internal/providers/s3/bucket.go +++ b/runtimes/go/storage/objects/internal/providers/s3/bucket.go @@ -79,14 +79,17 @@ func (b *bucket) List(data types.ListData) iter.Seq2[*types.ListEntry, error] { return func(yield func(*types.ListEntry, error) bool) { var n int64 var continuationToken string - for n < data.Limit { + for data.Limit == nil || n < *data.Limit { // Abort early if the context is canceled. if err := data.Ctx.Err(); err != nil { yield(nil, err) return } - maxKeys := int32(min(data.Limit-n, 1000)) + maxKeys := int32(1000) + if data.Limit != nil { + maxKeys = min(int32(*data.Limit-n), 1000) + } resp, err := b.client.ListObjectsV2(data.Ctx, &s3.ListObjectsV2Input{ Bucket: &b.cfg.CloudName, MaxKeys: &maxKeys, diff --git a/runtimes/go/storage/objects/internal/types/types.go b/runtimes/go/storage/objects/internal/types/types.go index 2ea0f0bf2a..5860a3b8a4 100644 --- a/runtimes/go/storage/objects/internal/types/types.go +++ b/runtimes/go/storage/objects/internal/types/types.go @@ -67,7 +67,7 @@ type ObjectAttrs struct { type ListData struct { Ctx context.Context Prefix string - Limit int64 + Limit *int64 } type ListEntry struct { From 47320931b947f71708dd0a36cb150b4f5b928195 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Eriksson?= Date: Thu, 14 Nov 2024 14:17:33 +0100 Subject: [PATCH 32/37] s3: fix listing --- runtimes/go/storage/objects/internal/providers/s3/bucket.go | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/runtimes/go/storage/objects/internal/providers/s3/bucket.go b/runtimes/go/storage/objects/internal/providers/s3/bucket.go index ad743e6c71..a2b4ec5571 100644 --- a/runtimes/go/storage/objects/internal/providers/s3/bucket.go +++ b/runtimes/go/storage/objects/internal/providers/s3/bucket.go @@ -111,6 +111,12 @@ func (b *bucket) List(data types.ListData) iter.Seq2[*types.ListEntry, error] { } n++ } + + // Are we done? + if !valOrZero(resp.IsTruncated) { + return + } + continuationToken = valOrZero(resp.NextContinuationToken) } } } From 3e11a2b61a7ef4940291e8e67e6350a7909d6ffc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Eriksson?= Date: Thu, 14 Nov 2024 15:06:20 +0100 Subject: [PATCH 33/37] Add object storage docs --- docs/menu.cue | 5 + docs/primitives/object-storage.md | 260 +++++++++++++++++++++++++ runtimes/go/storage/objects/bucket.go | 8 + runtimes/go/storage/objects/objects.go | 2 +- runtimes/go/storage/objects/package.go | 2 +- 5 files changed, 275 insertions(+), 2 deletions(-) create mode 100644 docs/primitives/object-storage.md diff --git a/docs/menu.cue b/docs/menu.cue index 8aa35b933a..54356089a7 100644 --- a/docs/menu.cue +++ b/docs/menu.cue @@ -290,6 +290,11 @@ path: "/primitives/databases/troubleshooting" file: "primitives/database-troubleshooting" }] + }, { + kind: "basic" + text: "Object Storage" + path: "/primitives/object-storage" + file: "primitives/object-storage" }, { kind: "basic" text: "Cron Jobs" diff --git a/docs/primitives/object-storage.md b/docs/primitives/object-storage.md new file mode 100644 index 0000000000..2ba58a340e --- /dev/null +++ b/docs/primitives/object-storage.md @@ -0,0 +1,260 @@ +--- +seotitle: Using Object Storage in your backend application +seodesc: Learn how you can use Object Storage to store files and unstructured data in your backend application. +title: Object Storage +subtitle: Simple and scalable storage APIs for files and unstructured data +infobox: { + title: "Object Storage", + import: "encore.dev/storage/objects", +} +lang: go +--- + +Object Storage is a simple and scalable way to store files and unstructured data in your backend application. +The most well-known implementation is Amazon S3 ("Simple Storage Service"), but it's universally supported +by every major cloud provider. + +Encore provides a built-in, cloud-agnostic API for working with Object Storage, allowing you to store and retrieve files with ease. + +Additionally, when you use Encore's Object Storage APIs you also automatically get: + +* Automatic tracing and instrumentation of all Object Storage operations +* Built-in local development support, storing objects on the local filesystem +* Support for integration testing, using a local, in-memory storage backend + +And with Encore's Cloud Platform you also get: + +* Automatic infrastructure provisioning of Object Storage buckets +* Automatic fine-grained permission management of per-service storage operations (read, list, write, delete, etc.) +* Support for Object Storage in Preview Environments +* Native support for Object Storage in the Encore Platform's CI/CD pipeline + +## Creating a Bucket + +The core of Object Storage is the **Bucket**, which represents a collection of files. +In Encore, buckets must be declared as package level variables, and cannot be created inside functions. +Regardless of where you create a bucket, it can be accessed from any service by referencing the variable it's assigned to. + +When creating a bucket you can configure additional properties, like whether the objects in the bucket should be versioned. + +See the complete specification in the [package documentation](https://pkg.go.dev/encore.dev/storage/objects#NewBucket). + +For example, to create a bucket with profile pictures: + +```go +package user + +import "encore.dev/storage/objects" + +var ProfilePictures = objects.NewBucket("profile-pictures", objects.BucketConfig{}) +``` + +## Uploading files + +To upload a file to a bucket, use the `Upload` method on the bucket variable. +It returns a writer that you can use to write the contents of the file. + +To complete the upload, call the `Close` method on the writer. +To abort the upload, either cancel the context or call the `Abort` method on the writer. + +The `Upload` method additionally takes a set of options to configure the upload, +like setting attributes (`objects.WithUploadAttrs`) or to reject the upload if the +object already exists (`objects.WithPreconditions`). +See the [package documentation](https://pkg.go.dev/encore.dev/storage/objects#Bucket.Upload) for more details. + +```go +package user + +import ( + "context" + "io" + "net/http" + + "encore.dev/beta/auth" + "encore.dev/beta/errs" + "encore.dev/storage/objects" +) + +var ProfilePictures = objects.NewBucket("profile-pictures", objects.BucketConfig{}) + +//encore:api auth raw method=POST path=/upload-profile-picture +func UploadProfilePicture(w http.ResponseWriter, req *http.Request) { + // Store the user's profile picture with their user id as the key. + userID, _ := auth.UserID() + key := string(userID) // We store the profile + + w := ProfilePictures.Upload(c, key) + _, err := io.Copy(w, req.Body) + if err != nil { + // If something went wrong with copying data, abort the upload and return an error. + w.Abort() + errs.HTTPError(w, err) + return + } + + if err := w.Close(); err != nil { + errs.HTTPError(w, err) + return + } + + // All good! Return a 200 OK. + w.WriteHeader(http.StatusOK) +} +``` + +## Downloading files + +To download a file from a bucket, use the `Download` method on the bucket variable. +It returns a reader that you can use to read the contents of the file. + +The `Download` method additionally takes a set of options to configure the download, +like downloading a specific version if the bucket is versioned (`objects.WithVersion`). +See the [package documentation](https://pkg.go.dev/encore.dev/storage/objects#Bucket.Download) for more details. + +For example, to download the user's profile picture and serve it: + +```go +package user + +import ( + "context" + "io" + "net/http" + + "encore.dev" + "encore.dev/beta/auth" + "encore.dev/beta/errs" + "encore.dev/storage/objects" +) + +var ProfilePictures = objects.NewBucket("profile-pictures", objects.BucketConfig{}) + +//encore:api public raw method=GET path=/profile-picture/:userID +func ServeProfilePicture(w http.ResponseWriter, req *http.Request) { + userID := encore.CurrentRequest().PathParams.Get("userID") + reader := ProfilePictures.Download(c, userID) + + // Did we encounter an error? + if err := reader.Err(); err != nil { + errs.HTTPError(w, err) + return + } + + // Assuming all images are JPEGs. + w.Header().Set("Content-Type", "image/jpeg") + io.Copy(w, reader) +} +``` + +## Listing objects + +To list objects in a bucket, use the `List` method on the bucket variable. + +It returns an iterator of `(error, *objects.ListEntry)` pairs that you can use +to easily iterate over the objects in the bucket using a `range` loop. + +For example, to list all profile pictures: + +```go +for err, entry := range ProfilePictures.List(ctx, &objects.Query{}) { + if err != nil { + // Handle error + } + // Do something with entry +} +``` + +The `*objects.Query` type can be used to limit the number of objects returned, +or to filter them to a specific key prefix. + +See the [package documentation](https://pkg.go.dev/encore.dev/storage/objects#Bucket.List) for more details. + +## Deleting objects + +To delete an object from a bucket, use the `Remove` method on the bucket variable. + +For example, to delete a profile picture: + +```go +err := ProfilePictures.Remove(ctx, "my-user-id") +if err != nil && !errors.Is(err, objects.ErrObjectNotFound) { + // Handle error +} +``` + +## Retrieving object attributes + +You can retrieve information about an object using the `Attrs` method on the bucket variable. +It returns the attributes of the object, like its size, content type, and ETag. + +For example, to get the attributes of a profile picture: + +```go +attrs, err := ProfilePictures.Attrs(ctx, "my-user-id") +if errors.Is(err, objects.ErrObjectNotFound) { + // Object not found +} else if err != nil { + // Some other error +} +// Do something with attrs +``` + +For convenience there is also `Exists` which returns a boolean indicating whether the object exists. + +```go +exists, err := ProfilePictures.Exists(ctx, "my-user-id") +if err != nil { + // Handle error +} else if !exists { + // Object does not exist +} +``` + +### Using bucket references + +Encore uses static analysis to determine which services are accessing each bucket, +and what operations each service is performing. + +That information is used to provision infrastructure correctly, +render architecture diagrams, and configure IAM permissions. + +This means that `*objects.Bucket` variables can't be passed around however you'd like, +as it makes static analysis impossible in many cases. To work around these restrictions +Encore allows you to get a "reference" to a bucket that can be passed around any way you want +by calling `objects.BucketRef`. + +To ensure Encore still is aware of which permissions each service needs, the call to `objects.BucketRef` +must be made from within a service. Additionally, it must pre-declare the permissions it needs; +those permissions are then assumed to be used by the service. + +It looks like this (using the `ProfilePictures` topic above): + +```go +ref := objects.BucketRef[objects.Downloader](ProfilePictures) + +// ref is of type objects.Downloader, which allows downloading. +``` + +Encore provides permission interfaces for each operation that can be performed on a bucket: + +* `objects.Downloader` for downloading objects +* `objects.Uploader` for uploading objects +* `objects.Lister` for listing objects +* `objects.Attrser` for getting object attributes +* `objects.Remover` for removing objects + +If you need multiple permissions they can be combined by creating an interface +that embeds the permissions you need. + +```go +type myPerms interface { + objects.Downloader + objects.Uploader +} +ref := objects.BucketRef[myPerms](ProfilePictures) +``` + +For convenience Encore provides an `objects.ReadWriter` interface that gives complete read-write access +with all the permissions above. + +See the [package documentation](https://pkg.go.dev/encore.dev/storage/objects#BucketRef) for more details. diff --git a/runtimes/go/storage/objects/bucket.go b/runtimes/go/storage/objects/bucket.go index 804324f5e2..e29d13e1b8 100644 --- a/runtimes/go/storage/objects/bucket.go +++ b/runtimes/go/storage/objects/bucket.go @@ -126,6 +126,14 @@ func (w *Writer) Write(p []byte) (int, error) { return u.Write(p) } +func (w *Writer) Abort(err error) { + if err == nil { + err = errors.New("upload aborted") + } + u := w.initUpload() + u.Abort(err) +} + func (w *Writer) Close() error { u := w.initUpload() attrs, err := u.Complete() diff --git a/runtimes/go/storage/objects/objects.go b/runtimes/go/storage/objects/objects.go index 0ac7f7003a..f7037cdd83 100644 --- a/runtimes/go/storage/objects/objects.go +++ b/runtimes/go/storage/objects/objects.go @@ -4,7 +4,7 @@ package objects // NewBucket declares a new object storage bucket. // -// See https://encore.dev/docs/develop/object-storage for more information. +// See https://encore.dev/docs/primitives/object-storage for more information. func NewBucket(name string, cfg BucketConfig) *Bucket { return newBucket(Singleton, name) } diff --git a/runtimes/go/storage/objects/package.go b/runtimes/go/storage/objects/package.go index cd26c822d9..1056da6e9c 100644 --- a/runtimes/go/storage/objects/package.go +++ b/runtimes/go/storage/objects/package.go @@ -2,5 +2,5 @@ // to create and use Object Storage buckets (like for example Amazon S3) // for storing and retrieving files in a cloud-agnostic manner. // -// For more information see https://encore.dev/docs/develop/object-storage +// For more information see https://encore.dev/docs/primitives/object-storage package objects From 1179cce081153519354fa8a85bbbf6a64e732c07 Mon Sep 17 00:00:00 2001 From: Stefan Ekerfelt Date: Thu, 14 Nov 2024 12:46:19 +0100 Subject: [PATCH 34/37] Parse simple config for GO runtime --- runtimes/core/src/infracfg.rs | 4 +- .../exported/config/infra/config.go | 4 +- .../go/appruntime/exported/config/parse.go | 38 +++++++++++++++++++ 3 files changed, 42 insertions(+), 4 deletions(-) diff --git a/runtimes/core/src/infracfg.rs b/runtimes/core/src/infracfg.rs index e72a8abf97..e9461fc1f7 100644 --- a/runtimes/core/src/infracfg.rs +++ b/runtimes/core/src/infracfg.rs @@ -24,7 +24,7 @@ pub struct InfraConfig { pub hosted_services: Option>, pub hosted_gateways: Option>, pub cors: Option, - pub buckets: Option>, + pub object_storage: Option>, } #[derive(Debug, Serialize, Deserialize)] @@ -431,7 +431,7 @@ pub fn map_infra_to_runtime(infra: InfraConfig) -> RuntimeConfig { }); // Map Buckets - let buckets = infra.buckets.as_ref().map(|object_storages| { + let buckets = infra.object_storage.as_ref().map(|object_storages| { object_storages .iter() .map(|os| match os { diff --git a/runtimes/go/appruntime/exported/config/infra/config.go b/runtimes/go/appruntime/exported/config/infra/config.go index 088e8c4aad..41ddbcd510 100644 --- a/runtimes/go/appruntime/exported/config/infra/config.go +++ b/runtimes/go/appruntime/exported/config/infra/config.go @@ -150,8 +150,8 @@ func (a *GCS) Validate(v *validator) { } type Bucket struct { - Name string `json:"name,omitempty"` - Prefix string `json:"prefix,omitempty"` + Name string `json:"name,omitempty"` + KeyPrefix string `json:"key_prefix,omitempty"` } func (a *Bucket) Validate(v *validator) { diff --git a/runtimes/go/appruntime/exported/config/parse.go b/runtimes/go/appruntime/exported/config/parse.go index 796153a14d..7d36a0fda6 100644 --- a/runtimes/go/appruntime/exported/config/parse.go +++ b/runtimes/go/appruntime/exported/config/parse.go @@ -363,6 +363,35 @@ func parseInfraConfigEnv(infraCfgPath string) *Runtime { } } + // Map Buckets + cfg.BucketProviders = make([]*BucketProvider, len(infraCfg.ObjectStorage)) + for i, storage := range infraCfg.ObjectStorage { + switch storage.Type { + case "gcs": + cfg.BucketProviders[i] = &BucketProvider{ + GCS: &GCSBucketProvider{ + Endpoint: storage.GCS.Endpoint, + }, + } + case "s3": + cfg.BucketProviders[i] = &BucketProvider{ + S3: &S3BucketProvider{ + Region: storage.S3.Region, + Endpoint: nilOr(storage.S3.Endpoint), + }, + } + } + cfg.Buckets = map[string]*Bucket{} + for bucketName, bucket := range storage.GetBuckets() { + cfg.Buckets[bucketName] = &Bucket{ + ProviderID: i, + EncoreName: bucketName, + CloudName: bucket.Name, + KeyPrefix: "", + } + } + } + if infraCfg.CORS != nil { cfg.CORS = &CORS{ Debug: infraCfg.CORS.Debug, @@ -385,6 +414,15 @@ func parseInfraConfigEnv(infraCfgPath string) *Runtime { return &cfg } +func nilOr[T comparable](val T) *T { + var zero T + if val == zero { + return nil + } + return &val + +} + func orDefaultPtr[T any](val *T, def T) T { if val == nil { return def From eeb8d4fa66631c326a7d51e67701ef0c400cd143 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Eriksson?= Date: Thu, 14 Nov 2024 16:26:54 +0100 Subject: [PATCH 35/37] Improve docs --- docs/menu.cue | 5 + docs/primitives/object-storage.md | 6 +- docs/ts/primitives/object-storage.md | 131 ++++++++++++++++++ .../js/encore.dev/storage/objects/error.ts | 1 - 4 files changed, 140 insertions(+), 3 deletions(-) create mode 100644 docs/ts/primitives/object-storage.md diff --git a/docs/menu.cue b/docs/menu.cue index 54356089a7..ebe15975b3 100644 --- a/docs/menu.cue +++ b/docs/menu.cue @@ -517,6 +517,11 @@ text: "Databases" path: "/ts/primitives/databases" file: "ts/primitives/databases" + }, { + kind: "basic" + text: "Object Storage" + path: "/ts/primitives/object-storage" + file: "ts/primitives/object-storage" }, { kind: "basic" text: "Cron Jobs" diff --git a/docs/primitives/object-storage.md b/docs/primitives/object-storage.md index 2ba58a340e..d88b42ee3a 100644 --- a/docs/primitives/object-storage.md +++ b/docs/primitives/object-storage.md @@ -39,14 +39,16 @@ When creating a bucket you can configure additional properties, like whether the See the complete specification in the [package documentation](https://pkg.go.dev/encore.dev/storage/objects#NewBucket). -For example, to create a bucket with profile pictures: +For example, to create a bucket for storing profile pictures: ```go package user import "encore.dev/storage/objects" -var ProfilePictures = objects.NewBucket("profile-pictures", objects.BucketConfig{}) +var ProfilePictures = objects.NewBucket("profile-pictures", objects.BucketConfig{ + Versioned: false, +}) ``` ## Uploading files diff --git a/docs/ts/primitives/object-storage.md b/docs/ts/primitives/object-storage.md new file mode 100644 index 0000000000..0d1c07975d --- /dev/null +++ b/docs/ts/primitives/object-storage.md @@ -0,0 +1,131 @@ +--- +seotitle: Using Object Storage in your backend application +seodesc: Learn how you can use Object Storage to store files and unstructured data in your backend application. +title: Object Storage +subtitle: Simple and scalable storage APIs for files and unstructured data +infobox: { + title: "Object Storage", + import: "encore.dev/storage/objects", +} +lang: ts +--- + +Object Storage is a simple and scalable way to store files and unstructured data in your backend application. +The most well-known implementation is Amazon S3 ("Simple Storage Service"), but it's universally supported +by every major cloud provider. + +Encore provides a built-in, cloud-agnostic API for working with Object Storage, allowing you to store and retrieve files with ease. + +Additionally, when you use Encore's Object Storage APIs you also automatically get: + +* Automatic tracing and instrumentation of all Object Storage operations +* Built-in local development support, storing objects on the local filesystem +* Support for integration testing, using a local, in-memory storage backend + +And with Encore's Cloud Platform you also get: + +* Automatic infrastructure provisioning of Object Storage buckets +* Automatic fine-grained permission management of per-service storage operations (read, list, write, delete, etc.) +* Support for Object Storage in Preview Environments +* Native support for Object Storage in the Encore Platform's CI/CD pipeline + +## Creating a Bucket + +The core of Object Storage is the **Bucket**, which represents a collection of files. +In Encore, buckets must be declared as package level variables, and cannot be created inside functions. +Regardless of where you create a bucket, it can be accessed from any service by referencing the variable it's assigned to. + +When creating a bucket you can configure additional properties, like whether the objects in the bucket should be versioned. + +For example, to create a bucket for storing profile pictures: + +```ts +import { Bucket } from "encore.dev/storage/objects"; + +export const profilePictures = new Bucket("profile-pictures", { + versioned: false +}); +``` + +## Uploading files + +To upload a file to a bucket, use the `upload` method on the bucket variable. + +```ts +const data = Buffer.from(...); // image data +const attributes = await profilePictures.upload("my-image.jpeg", data, { + contentType: "image/jpeg", +}); +``` + +The `upload` method additionally takes an optional `UploadOptions` parameter +for configuring additinal options, like setting the content type (see above), +or to reject the upload if the object already exists. + + +## Downloading files + +To download a file from a bucket, use the `download` method on the bucket variable: + +```ts +const data = await profilePictures.download("my-image.jpeg"); +``` + +The `download` method additionally takes a set of options to configure the download, +like downloading a specific version if the bucket is versioned. + +## Listing objects + +To list objects in a bucket, use the `list` method on the bucket variable. + +It returns an async iterator of `ListEntry` objects that you can use to easily +iterate over the objects in the bucket using a `for await` loop. + +For example, to list all profile pictures: + +```ts +for await (const entry of profilePictures.list({})) { + // Do something with entry +} +``` + +The `ListOptions` type can be used to limit the number of objects returned, +or to filter them to a specific key prefix. + +## Deleting objects + +To delete an object from a bucket, use the `remove` method on the bucket variable. + +For example, to delete a profile picture: + +```ts +await profilePictures.remove("my-image.jpeg"); +``` + +## Retrieving object attributes + +You can retrieve information about an object using the `attrs` method on the bucket variable. +It returns the attributes of the object, like its size, content type, and ETag. + +For example, to get the attributes of a profile picture: + +```ts +const attrs = await profilePictures.attrs("my-image.jpeg"); +``` + +For convenience there is also `exists` which returns a boolean indicating whether the object exists. + +```ts +const exists = await profilePictures.exists("my-image.jpeg"); +``` + +## Error handling + +The methods throw exceptions if something goes wrong, like if the object doesn't exist or the operation fails. + +If an object does not exist, it throws an `ObjectNotFound` error. + +If an upload fails due to a precondition not being met (like if the object already exists +and the `notExists: true` option is set), it throws a `PreconditionFailed` error. + +Other errors are returned as `ObjectsError` errors (which the above errors also extend). diff --git a/runtimes/js/encore.dev/storage/objects/error.ts b/runtimes/js/encore.dev/storage/objects/error.ts index 8e1598b897..070534e73e 100644 --- a/runtimes/js/encore.dev/storage/objects/error.ts +++ b/runtimes/js/encore.dev/storage/objects/error.ts @@ -1,5 +1,4 @@ import * as runtime from "../../internal/runtime/mod"; -import log from "../../log/mod"; export class ObjectsError extends Error { constructor(msg: string) { From 5e3f0fee530825be10ba84cc30fbda9d87856a59 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Eriksson?= Date: Fri, 15 Nov 2024 14:40:05 +0100 Subject: [PATCH 36/37] Fix tests --- e2e-tests/app_test.go | 2 +- e2e-tests/echo_app_test.go | 2 +- runtimes/core/src/lib.rs | 2 +- .../exported/config/infra/testdata/runtime.json | 3 ++- runtimes/js/src/lib.rs | 2 +- tsparser/src/legacymeta/mod.rs | 9 +++------ tsparser/src/parser/resources/infra/mod.rs | 2 +- v2/parser/infra/objects/usage_test.go | 4 ++-- 8 files changed, 12 insertions(+), 14 deletions(-) diff --git a/e2e-tests/app_test.go b/e2e-tests/app_test.go index abbd4022c7..074e7ea90c 100644 --- a/e2e-tests/app_test.go +++ b/e2e-tests/app_test.go @@ -73,7 +73,7 @@ func RunApp(c testing.TB, appRoot string, logger RunLogger, env []string) *RunAp mgr := &Manager{} ns := &namespace.Namespace{ID: "some-id", Name: "default"} - rm := infra.NewResourceManager(app, mgr.ClusterMgr, ns, nil, 0, false) + rm := infra.NewResourceManager(app, mgr.ClusterMgr, mgr.ObjectsMgr, ns, nil, 0, false) run := &Run{ ID: GenID(), ListenAddr: ln.Addr().String(), diff --git a/e2e-tests/echo_app_test.go b/e2e-tests/echo_app_test.go index ad8f0bc847..44283b0edc 100644 --- a/e2e-tests/echo_app_test.go +++ b/e2e-tests/echo_app_test.go @@ -573,7 +573,7 @@ func TestProcClosedOnCtxCancel(t *testing.T) { mgr := &Manager{} ns := &namespace.Namespace{ID: "some-id", Name: "default"} - rm := infra.NewResourceManager(app, nil, ns, nil, 0, false) + rm := infra.NewResourceManager(app, nil, nil, ns, nil, 0, false) run := &Run{ ID: GenID(), App: app, diff --git a/runtimes/core/src/lib.rs b/runtimes/core/src/lib.rs index c98e794e95..4012d703f8 100644 --- a/runtimes/core/src/lib.rs +++ b/runtimes/core/src/lib.rs @@ -26,9 +26,9 @@ pub mod log; pub mod meta; pub mod model; mod names; +pub mod objects; pub mod proccfg; pub mod pubsub; -pub mod objects; pub mod secrets; pub mod sqldb; mod trace; diff --git a/runtimes/go/appruntime/exported/config/infra/testdata/runtime.json b/runtimes/go/appruntime/exported/config/infra/testdata/runtime.json index e87846a5a5..00d91d90d3 100644 --- a/runtimes/go/appruntime/exported/config/infra/testdata/runtime.json +++ b/runtimes/go/appruntime/exported/config/infra/testdata/runtime.json @@ -77,6 +77,7 @@ } } }, + "bucket_providers": [], "redis_servers": [ { "host": "my-redis-host", @@ -141,4 +142,4 @@ "shutdown_hooks": 10000000000, "handlers": 20000000000 } -} \ No newline at end of file +} diff --git a/runtimes/js/src/lib.rs b/runtimes/js/src/lib.rs index 7f7e4ccc76..43bde50cec 100644 --- a/runtimes/js/src/lib.rs +++ b/runtimes/js/src/lib.rs @@ -6,9 +6,9 @@ mod gateway; mod log; mod meta; mod napi_util; +pub mod objects; pub mod pubsub; mod pvalue; -pub mod objects; mod raw_api; mod request_meta; pub mod runtime; diff --git a/tsparser/src/legacymeta/mod.rs b/tsparser/src/legacymeta/mod.rs index ce9feb6339..bf6cfb2aa9 100644 --- a/tsparser/src/legacymeta/mod.rs +++ b/tsparser/src/legacymeta/mod.rs @@ -381,12 +381,9 @@ impl<'a> MetaBuilder<'a> { Usage::Bucket(access) => { let Some(svc) = self.service_for_range(&access.range) else { - HANDLER.with(|h| { - h.span_err( - access.range.to_span(), - "unable to determine which service is accessing this bucket", - ) - }); + access + .range + .err("cannot determine which service is accessing this bucket"); continue; }; diff --git a/tsparser/src/parser/resources/infra/mod.rs b/tsparser/src/parser/resources/infra/mod.rs index 854e6e1fd4..2babf8b0f6 100644 --- a/tsparser/src/parser/resources/infra/mod.rs +++ b/tsparser/src/parser/resources/infra/mod.rs @@ -1,6 +1,6 @@ pub mod cron; +pub mod objects; pub mod pubsub_subscription; pub mod pubsub_topic; pub mod secret; pub mod sqldb; -pub mod objects; diff --git a/v2/parser/infra/objects/usage_test.go b/v2/parser/infra/objects/usage_test.go index 6434583572..e534f00c4a 100644 --- a/v2/parser/infra/objects/usage_test.go +++ b/v2/parser/infra/objects/usage_test.go @@ -19,14 +19,14 @@ var bkt = objects.NewBucket("bucket", objects.BucketConfig{}) Want: []usage.Usage{}, }, { - Name: "publish", + Name: "upload", Code: ` var bkt = objects.NewBucket("bucket", objects.BucketConfig{}) func Foo() { bkt.Upload(context.Background(), "key") } `, - Want: []usage.Usage{&objects.MethodUsage{Perm: objects.WriteObject}}, + Want: []usage.Usage{&objects.MethodUsage{Method: "Upload", Perm: objects.WriteObject}}, }, { Name: "ref", From aed216efd1672e55613aeff2637d838d431b754e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Eriksson?= Date: Fri, 15 Nov 2024 15:34:09 +0100 Subject: [PATCH 37/37] Fix lints --- runtimes/core/src/objects/gcs/bucket.rs | 4 ++-- runtimes/core/src/objects/s3/bucket.rs | 1 + 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/runtimes/core/src/objects/gcs/bucket.rs b/runtimes/core/src/objects/gcs/bucket.rs index 56ca0bc511..65bef0e6c2 100644 --- a/runtimes/core/src/objects/gcs/bucket.rs +++ b/runtimes/core/src/objects/gcs/bucket.rs @@ -131,10 +131,10 @@ impl objects::BucketImpl for Bucket { // Are we close to being done? If so, adjust the max_results // to avoid over-fetching. if let Some(limit) = options.limit { - let remaining = (limit - total_seen).max(0); - if remaining == 0 { + if total_seen >= limit { break 'PageLoop; } + let remaining = limit - total_seen; req.max_results = Some(remaining.min(DEFAULT_MAX_RESULTS) as i32); } diff --git a/runtimes/core/src/objects/s3/bucket.rs b/runtimes/core/src/objects/s3/bucket.rs index a7082135cc..cefa205209 100644 --- a/runtimes/core/src/objects/s3/bucket.rs +++ b/runtimes/core/src/objects/s3/bucket.rs @@ -405,6 +405,7 @@ async fn read_chunk_async(reader: &mut R) -> std: Ok(Chunk::Part(buf)) } +#[allow(clippy::large_enum_variant)] enum UploadMultipartResult { CompleteSuccess { total_size: u64,