From 3d7696f41227380e6db57ec550d681353610209b Mon Sep 17 00:00:00 2001 From: Erik Carlsson Date: Thu, 19 Dec 2024 10:04:30 +0100 Subject: [PATCH] Add support for signed upload urls. --- cli/daemon/run/runtime_config2.go | 34 + pkg/rtconfgen/convert.go | 7 + proto/encore/runtime/v1/infra.pb.go | 1154 ++++++----------- proto/encore/runtime/v1/infra.proto | 15 + runtimes/core/src/infracfg.rs | 1 + runtimes/core/src/objects/gcs/bucket.rs | 103 +- runtimes/core/src/objects/mod.rs | 18 + runtimes/core/src/objects/noop/mod.rs | 10 + runtimes/core/src/objects/s3/bucket.rs | 34 +- .../go/appruntime/exported/config/config.go | 10 + runtimes/go/storage/objects/bucket.go | 16 + .../objects/internal/providers/gcs/bucket.go | 81 +- .../objects/internal/providers/noop/noop.go | 4 + .../objects/internal/providers/s3/bucket.go | 31 +- .../storage/objects/internal/types/types.go | 8 + runtimes/go/storage/objects/options.go | 27 + runtimes/go/storage/objects/refs.go | 4 + .../js/encore.dev/storage/objects/bucket.ts | 21 +- runtimes/js/src/objects.rs | 28 + .../src/parser/resources/infra/objects.rs | 1 + 20 files changed, 824 insertions(+), 783 deletions(-) diff --git a/cli/daemon/run/runtime_config2.go b/cli/daemon/run/runtime_config2.go index 1f3dd580d6..85b96c3b0c 100644 --- a/cli/daemon/run/runtime_config2.go +++ b/cli/daemon/run/runtime_config2.go @@ -378,6 +378,11 @@ func (g *RuntimeConfigGenerator) initialize() error { Gcs: &runtimev1.BucketCluster_GCS{ Endpoint: &bktProviderConfig.GCS.Endpoint, Anonymous: true, + LocalSign: &runtimev1.BucketCluster_GCS_LocalSignOptions{ + BaseUrl: publicBaseURL, + AccessId: "dummy-sa@encore.local", + PrivateKey: dummyPrivateKey, + }, }, }, }) @@ -833,3 +838,32 @@ func gzipBytes(data []byte) []byte { _ = w.Close() return buf.Bytes() } + +const dummyPrivateKey = `-----BEGIN PRIVATE KEY----- +MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCuqGuf20fyTPGw +tkUjBRZMh9T5eyGxkw9aZkQWJAENiZE/NkpHaErvrhRhgI5q59nxDDaPoomGuOdV +weFRoq9tb+WDxA0gwDi+3eY4D99kepfGjrnovh4Pmt3PoCTAUd+ODIxJCZ9o6YNe +Qp3fk1XYoLYldCONRCbq00frnZG+IGQFRH7VXjOcbgxkKUvZhFyX/W+tOLCBfkuB +mRFVw0sxxvdhMbKaDknq0c4AoF6MDFb8mfEqxF0eFK/VQ9dD53v4VLRREOb7CMAv +KHPf5ikz94eiiEEug24FboXXtntxH/W0wU5pUkflyx22Onk4rbPpv5f/NbzetdkF +B2OiUluvAgMBAAECggEALYAAsZ9didjTqdaCAlKD8aH9MJUMPQdzm3hCyoXMpGsv +JImPJjUcOH5gHtpvv5fw5ePpnteX/jnTQjsE6NB55Qeeggoj5WFOJyMFo5s29iUd +vwNVmTVV/Xi5yioNCPELTSUlsq1IEvuqVncCS8lFNu7/JJix3k5f2RL7jHz7B81X +KNZsLTIeij/NG3tFM5lEca+u6y4IFaONNbAd0PSWbfDA4wD5KzluSmHXW3/U4BeG +zyRqpCVtCTRIw+C5gu+VRCa/op0CjwUT5yJiPlr4nyU4xAoD0FhWTwI06TkBMk5M +Xv0v6dmtvp9WVvUh220Xpf1wxkjYPSJS/ICDP/O8QQKBgQDysHUWBEzuIKbmiIGl +1UdNr8I6HTDB65mT5gEXiJIW9iOSYJO/94sk/VbfUCjFM1oKFiKwBCP8/LBsyiRM +V5jmvKg2IG+Q5OuakY/RFx0DUKSsCfGr3ouUWI6mz1DMESD4yNpaZeR6w0BLWEPD +5N+gb/YwXi+V0j1QJR2XJYIm4QKBgQC4PMAZk6krRIiGmQ8VuiEEm07T75ddYmSb +ZlBDzElW3LiCYVF1ZfiDUgfp+djcv2MpR0gR/CVu81LtxIWmIMk8AVSJ7Nh/i6ki +2nv9p44KT7xcJ4iQzwOFapdjqsnIGJ14EQUfmgYuS1scKk0BCnfMBqh7BVuF44rm +qt7XcQckjwKBgQCl66pBITOPYld5KT6qKASVwmIh5S8ehXr8OLXqZv6qICH1w32A +Mze4VFP+XQliuVcHqlaQzGPmZMQhvJnQb9sjdTvztX1RLJE/neEbbJfzWkEbNbk6 +be4zv8/Xj8mHmvZV4MwYHa11mOPuHyxFU8boI2PHcb1KyvAMSTPP0F8JQQKBgB4Y +BkTnQr3Hjwl1XOpuodAP0lt6Cl59oPNlTf0VFHG00gqx/M1RX7uLnbFRV2QPexIW +C6asaizqYARoknAlcNl1WirBXkfPN0xzJce0I9Z5WcovxvXoaqnTVHE6R4WAx9AB +77VOwm2zb2l1W2itHg5clA6sPFvtZBXzmTzVwJXvAoGAcJAh7xzDi63YUcruyRPp +MNs5c5zFu0phm++8L8IHBZNLXcwtvYEvwHcyDM8YJWVOwWa8p0+5z33CkZRhkKg+ +5D5h1o9FhnRNzOZyhRbFSdrecYSmNhDNTTU0S0uw13PeBp6RnQoCrSppkhCCBXF4 +WkNQs3xfT17/eAx5MEe3zOA= +-----END PRIVATE KEY-----` diff --git a/pkg/rtconfgen/convert.go b/pkg/rtconfgen/convert.go index e01969da8e..8c18f5e42c 100644 --- a/pkg/rtconfgen/convert.go +++ b/pkg/rtconfgen/convert.go @@ -411,6 +411,13 @@ func (c *legacyConverter) Convert() (*config.Runtime, error) { Endpoint: prov.Gcs.GetEndpoint(), Anonymous: prov.Gcs.Anonymous, } + if opt := prov.Gcs.LocalSign; opt != nil { + p.GCS.LocalSign = &config.GCSLocalSignOptions{ + BaseUrl: opt.BaseUrl, + AccessId: opt.AccessId, + PrivateKey: opt.PrivateKey, + } + } default: c.setErrf("unknown object storage provider type %T", prov) continue diff --git a/proto/encore/runtime/v1/infra.pb.go b/proto/encore/runtime/v1/infra.pb.go index d069965434..719c47078b 100644 --- a/proto/encore/runtime/v1/infra.pb.go +++ b/proto/encore/runtime/v1/infra.pb.go @@ -1,7 +1,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.31.0 -// protoc v4.23.4 +// protoc-gen-go v1.35.2 +// protoc v5.29.1 // source: encore/runtime/v1/infra.proto package runtimev1 @@ -135,11 +135,9 @@ type Infrastructure struct { func (x *Infrastructure) Reset() { *x = Infrastructure{} - if protoimpl.UnsafeEnabled { - mi := &file_encore_runtime_v1_infra_proto_msgTypes[0] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } + mi := &file_encore_runtime_v1_infra_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) } func (x *Infrastructure) String() string { @@ -150,7 +148,7 @@ func (*Infrastructure) ProtoMessage() {} func (x *Infrastructure) ProtoReflect() protoreflect.Message { mi := &file_encore_runtime_v1_infra_proto_msgTypes[0] - if protoimpl.UnsafeEnabled && x != nil { + if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -192,11 +190,9 @@ type SQLCluster struct { func (x *SQLCluster) Reset() { *x = SQLCluster{} - if protoimpl.UnsafeEnabled { - mi := &file_encore_runtime_v1_infra_proto_msgTypes[1] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } + mi := &file_encore_runtime_v1_infra_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) } func (x *SQLCluster) String() string { @@ -207,7 +203,7 @@ func (*SQLCluster) ProtoMessage() {} func (x *SQLCluster) ProtoReflect() protoreflect.Message { mi := &file_encore_runtime_v1_infra_proto_msgTypes[1] - if protoimpl.UnsafeEnabled && x != nil { + if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -258,11 +254,9 @@ type TLSConfig struct { func (x *TLSConfig) Reset() { *x = TLSConfig{} - if protoimpl.UnsafeEnabled { - mi := &file_encore_runtime_v1_infra_proto_msgTypes[2] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } + mi := &file_encore_runtime_v1_infra_proto_msgTypes[2] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) } func (x *TLSConfig) String() string { @@ -273,7 +267,7 @@ func (*TLSConfig) ProtoMessage() {} func (x *TLSConfig) ProtoReflect() protoreflect.Message { mi := &file_encore_runtime_v1_infra_proto_msgTypes[2] - if protoimpl.UnsafeEnabled && x != nil { + if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -319,11 +313,9 @@ type SQLServer struct { func (x *SQLServer) Reset() { *x = SQLServer{} - if protoimpl.UnsafeEnabled { - mi := &file_encore_runtime_v1_infra_proto_msgTypes[3] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } + mi := &file_encore_runtime_v1_infra_proto_msgTypes[3] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) } func (x *SQLServer) String() string { @@ -334,7 +326,7 @@ func (*SQLServer) ProtoMessage() {} func (x *SQLServer) ProtoReflect() protoreflect.Message { mi := &file_encore_runtime_v1_infra_proto_msgTypes[3] - if protoimpl.UnsafeEnabled && x != nil { + if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -390,11 +382,9 @@ type ClientCert struct { func (x *ClientCert) Reset() { *x = ClientCert{} - if protoimpl.UnsafeEnabled { - mi := &file_encore_runtime_v1_infra_proto_msgTypes[4] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } + mi := &file_encore_runtime_v1_infra_proto_msgTypes[4] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) } func (x *ClientCert) String() string { @@ -405,7 +395,7 @@ func (*ClientCert) ProtoMessage() {} func (x *ClientCert) ProtoReflect() protoreflect.Message { mi := &file_encore_runtime_v1_infra_proto_msgTypes[4] - if protoimpl.UnsafeEnabled && x != nil { + if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -456,11 +446,9 @@ type SQLRole struct { func (x *SQLRole) Reset() { *x = SQLRole{} - if protoimpl.UnsafeEnabled { - mi := &file_encore_runtime_v1_infra_proto_msgTypes[5] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } + mi := &file_encore_runtime_v1_infra_proto_msgTypes[5] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) } func (x *SQLRole) String() string { @@ -471,7 +459,7 @@ func (*SQLRole) ProtoMessage() {} func (x *SQLRole) ProtoReflect() protoreflect.Message { mi := &file_encore_runtime_v1_infra_proto_msgTypes[5] - if protoimpl.UnsafeEnabled && x != nil { + if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -530,11 +518,9 @@ type SQLDatabase struct { func (x *SQLDatabase) Reset() { *x = SQLDatabase{} - if protoimpl.UnsafeEnabled { - mi := &file_encore_runtime_v1_infra_proto_msgTypes[6] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } + mi := &file_encore_runtime_v1_infra_proto_msgTypes[6] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) } func (x *SQLDatabase) String() string { @@ -545,7 +531,7 @@ func (*SQLDatabase) ProtoMessage() {} func (x *SQLDatabase) ProtoReflect() protoreflect.Message { mi := &file_encore_runtime_v1_infra_proto_msgTypes[6] - if protoimpl.UnsafeEnabled && x != nil { + if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -604,11 +590,9 @@ type SQLConnectionPool struct { func (x *SQLConnectionPool) Reset() { *x = SQLConnectionPool{} - if protoimpl.UnsafeEnabled { - mi := &file_encore_runtime_v1_infra_proto_msgTypes[7] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } + mi := &file_encore_runtime_v1_infra_proto_msgTypes[7] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) } func (x *SQLConnectionPool) String() string { @@ -619,7 +603,7 @@ func (*SQLConnectionPool) ProtoMessage() {} func (x *SQLConnectionPool) ProtoReflect() protoreflect.Message { mi := &file_encore_runtime_v1_infra_proto_msgTypes[7] - if protoimpl.UnsafeEnabled && x != nil { + if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -675,11 +659,9 @@ type RedisCluster struct { func (x *RedisCluster) Reset() { *x = RedisCluster{} - if protoimpl.UnsafeEnabled { - mi := &file_encore_runtime_v1_infra_proto_msgTypes[8] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } + mi := &file_encore_runtime_v1_infra_proto_msgTypes[8] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) } func (x *RedisCluster) String() string { @@ -690,7 +672,7 @@ func (*RedisCluster) ProtoMessage() {} func (x *RedisCluster) ProtoReflect() protoreflect.Message { mi := &file_encore_runtime_v1_infra_proto_msgTypes[8] - if protoimpl.UnsafeEnabled && x != nil { + if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -744,11 +726,9 @@ type RedisServer struct { func (x *RedisServer) Reset() { *x = RedisServer{} - if protoimpl.UnsafeEnabled { - mi := &file_encore_runtime_v1_infra_proto_msgTypes[9] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } + mi := &file_encore_runtime_v1_infra_proto_msgTypes[9] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) } func (x *RedisServer) String() string { @@ -759,7 +739,7 @@ func (*RedisServer) ProtoMessage() {} func (x *RedisServer) ProtoReflect() protoreflect.Message { mi := &file_encore_runtime_v1_infra_proto_msgTypes[9] - if protoimpl.UnsafeEnabled && x != nil { + if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -818,11 +798,9 @@ type RedisConnectionPool struct { func (x *RedisConnectionPool) Reset() { *x = RedisConnectionPool{} - if protoimpl.UnsafeEnabled { - mi := &file_encore_runtime_v1_infra_proto_msgTypes[10] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } + mi := &file_encore_runtime_v1_infra_proto_msgTypes[10] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) } func (x *RedisConnectionPool) String() string { @@ -833,7 +811,7 @@ func (*RedisConnectionPool) ProtoMessage() {} func (x *RedisConnectionPool) ProtoReflect() protoreflect.Message { mi := &file_encore_runtime_v1_infra_proto_msgTypes[10] - if protoimpl.UnsafeEnabled && x != nil { + if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -897,11 +875,9 @@ type RedisRole struct { func (x *RedisRole) Reset() { *x = RedisRole{} - if protoimpl.UnsafeEnabled { - mi := &file_encore_runtime_v1_infra_proto_msgTypes[11] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } + mi := &file_encore_runtime_v1_infra_proto_msgTypes[11] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) } func (x *RedisRole) String() string { @@ -912,7 +888,7 @@ func (*RedisRole) ProtoMessage() {} func (x *RedisRole) ProtoReflect() protoreflect.Message { mi := &file_encore_runtime_v1_infra_proto_msgTypes[11] - if protoimpl.UnsafeEnabled && x != nil { + if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -1000,11 +976,9 @@ type RedisDatabase struct { func (x *RedisDatabase) Reset() { *x = RedisDatabase{} - if protoimpl.UnsafeEnabled { - mi := &file_encore_runtime_v1_infra_proto_msgTypes[12] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } + mi := &file_encore_runtime_v1_infra_proto_msgTypes[12] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) } func (x *RedisDatabase) String() string { @@ -1015,7 +989,7 @@ func (*RedisDatabase) ProtoMessage() {} func (x *RedisDatabase) ProtoReflect() protoreflect.Message { mi := &file_encore_runtime_v1_infra_proto_msgTypes[12] - if protoimpl.UnsafeEnabled && x != nil { + if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -1080,11 +1054,9 @@ type AppSecret struct { func (x *AppSecret) Reset() { *x = AppSecret{} - if protoimpl.UnsafeEnabled { - mi := &file_encore_runtime_v1_infra_proto_msgTypes[13] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } + mi := &file_encore_runtime_v1_infra_proto_msgTypes[13] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) } func (x *AppSecret) String() string { @@ -1095,7 +1067,7 @@ func (*AppSecret) ProtoMessage() {} func (x *AppSecret) ProtoReflect() protoreflect.Message { mi := &file_encore_runtime_v1_infra_proto_msgTypes[13] - if protoimpl.UnsafeEnabled && x != nil { + if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -1152,11 +1124,9 @@ type PubSubCluster struct { func (x *PubSubCluster) Reset() { *x = PubSubCluster{} - if protoimpl.UnsafeEnabled { - mi := &file_encore_runtime_v1_infra_proto_msgTypes[14] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } + mi := &file_encore_runtime_v1_infra_proto_msgTypes[14] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) } func (x *PubSubCluster) String() string { @@ -1167,7 +1137,7 @@ func (*PubSubCluster) ProtoMessage() {} func (x *PubSubCluster) ProtoReflect() protoreflect.Message { mi := &file_encore_runtime_v1_infra_proto_msgTypes[14] - if protoimpl.UnsafeEnabled && x != nil { + if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -1307,11 +1277,9 @@ type PubSubTopic struct { func (x *PubSubTopic) Reset() { *x = PubSubTopic{} - if protoimpl.UnsafeEnabled { - mi := &file_encore_runtime_v1_infra_proto_msgTypes[15] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } + mi := &file_encore_runtime_v1_infra_proto_msgTypes[15] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) } func (x *PubSubTopic) String() string { @@ -1322,7 +1290,7 @@ func (*PubSubTopic) ProtoMessage() {} func (x *PubSubTopic) ProtoReflect() protoreflect.Message { mi := &file_encore_runtime_v1_infra_proto_msgTypes[15] - if protoimpl.UnsafeEnabled && x != nil { + if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -1426,11 +1394,9 @@ type PubSubSubscription struct { func (x *PubSubSubscription) Reset() { *x = PubSubSubscription{} - if protoimpl.UnsafeEnabled { - mi := &file_encore_runtime_v1_infra_proto_msgTypes[16] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } + mi := &file_encore_runtime_v1_infra_proto_msgTypes[16] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) } func (x *PubSubSubscription) String() string { @@ -1441,7 +1407,7 @@ func (*PubSubSubscription) ProtoMessage() {} func (x *PubSubSubscription) ProtoReflect() protoreflect.Message { mi := &file_encore_runtime_v1_infra_proto_msgTypes[16] - if protoimpl.UnsafeEnabled && x != nil { + if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -1539,11 +1505,9 @@ type BucketCluster struct { 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) - } + mi := &file_encore_runtime_v1_infra_proto_msgTypes[17] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) } func (x *BucketCluster) String() string { @@ -1554,7 +1518,7 @@ func (*BucketCluster) ProtoMessage() {} func (x *BucketCluster) ProtoReflect() protoreflect.Message { mi := &file_encore_runtime_v1_infra_proto_msgTypes[17] - if protoimpl.UnsafeEnabled && x != nil { + if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -1643,11 +1607,9 @@ type Bucket struct { 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) - } + mi := &file_encore_runtime_v1_infra_proto_msgTypes[18] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) } func (x *Bucket) String() string { @@ -1658,7 +1620,7 @@ func (*Bucket) ProtoMessage() {} func (x *Bucket) ProtoReflect() protoreflect.Message { mi := &file_encore_runtime_v1_infra_proto_msgTypes[18] - if protoimpl.UnsafeEnabled && x != nil { + if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -1728,11 +1690,9 @@ type Gateway struct { func (x *Gateway) Reset() { *x = Gateway{} - if protoimpl.UnsafeEnabled { - mi := &file_encore_runtime_v1_infra_proto_msgTypes[19] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } + mi := &file_encore_runtime_v1_infra_proto_msgTypes[19] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) } func (x *Gateway) String() string { @@ -1743,7 +1703,7 @@ func (*Gateway) ProtoMessage() {} func (x *Gateway) ProtoReflect() protoreflect.Message { mi := &file_encore_runtime_v1_infra_proto_msgTypes[19] - if protoimpl.UnsafeEnabled && x != nil { + if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -1805,11 +1765,9 @@ type Infrastructure_Credentials struct { func (x *Infrastructure_Credentials) Reset() { *x = Infrastructure_Credentials{} - if protoimpl.UnsafeEnabled { - mi := &file_encore_runtime_v1_infra_proto_msgTypes[20] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } + mi := &file_encore_runtime_v1_infra_proto_msgTypes[20] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) } func (x *Infrastructure_Credentials) String() string { @@ -1820,7 +1778,7 @@ func (*Infrastructure_Credentials) ProtoMessage() {} func (x *Infrastructure_Credentials) ProtoReflect() protoreflect.Message { mi := &file_encore_runtime_v1_infra_proto_msgTypes[20] - if protoimpl.UnsafeEnabled && x != nil { + if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -1871,11 +1829,9 @@ type Infrastructure_Resources struct { func (x *Infrastructure_Resources) Reset() { *x = Infrastructure_Resources{} - if protoimpl.UnsafeEnabled { - mi := &file_encore_runtime_v1_infra_proto_msgTypes[21] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } + mi := &file_encore_runtime_v1_infra_proto_msgTypes[21] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) } func (x *Infrastructure_Resources) String() string { @@ -1886,7 +1842,7 @@ func (*Infrastructure_Resources) ProtoMessage() {} func (x *Infrastructure_Resources) ProtoReflect() protoreflect.Message { mi := &file_encore_runtime_v1_infra_proto_msgTypes[21] - if protoimpl.UnsafeEnabled && x != nil { + if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -1954,11 +1910,9 @@ type RedisRole_AuthACL struct { func (x *RedisRole_AuthACL) Reset() { *x = RedisRole_AuthACL{} - if protoimpl.UnsafeEnabled { - mi := &file_encore_runtime_v1_infra_proto_msgTypes[22] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } + mi := &file_encore_runtime_v1_infra_proto_msgTypes[22] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) } func (x *RedisRole_AuthACL) String() string { @@ -1969,7 +1923,7 @@ func (*RedisRole_AuthACL) ProtoMessage() {} func (x *RedisRole_AuthACL) ProtoReflect() protoreflect.Message { mi := &file_encore_runtime_v1_infra_proto_msgTypes[22] - if protoimpl.UnsafeEnabled && x != nil { + if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -2006,11 +1960,9 @@ type PubSubCluster_EncoreCloud struct { func (x *PubSubCluster_EncoreCloud) Reset() { *x = PubSubCluster_EncoreCloud{} - if protoimpl.UnsafeEnabled { - mi := &file_encore_runtime_v1_infra_proto_msgTypes[23] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } + mi := &file_encore_runtime_v1_infra_proto_msgTypes[23] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) } func (x *PubSubCluster_EncoreCloud) String() string { @@ -2021,7 +1973,7 @@ func (*PubSubCluster_EncoreCloud) ProtoMessage() {} func (x *PubSubCluster_EncoreCloud) ProtoReflect() protoreflect.Message { mi := &file_encore_runtime_v1_infra_proto_msgTypes[23] - if protoimpl.UnsafeEnabled && x != nil { + if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -2044,11 +1996,9 @@ type PubSubCluster_AWSSqsSns struct { func (x *PubSubCluster_AWSSqsSns) Reset() { *x = PubSubCluster_AWSSqsSns{} - if protoimpl.UnsafeEnabled { - mi := &file_encore_runtime_v1_infra_proto_msgTypes[24] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } + mi := &file_encore_runtime_v1_infra_proto_msgTypes[24] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) } func (x *PubSubCluster_AWSSqsSns) String() string { @@ -2059,7 +2009,7 @@ func (*PubSubCluster_AWSSqsSns) ProtoMessage() {} func (x *PubSubCluster_AWSSqsSns) ProtoReflect() protoreflect.Message { mi := &file_encore_runtime_v1_infra_proto_msgTypes[24] - if protoimpl.UnsafeEnabled && x != nil { + if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -2082,11 +2032,9 @@ type PubSubCluster_GCPPubSub struct { func (x *PubSubCluster_GCPPubSub) Reset() { *x = PubSubCluster_GCPPubSub{} - if protoimpl.UnsafeEnabled { - mi := &file_encore_runtime_v1_infra_proto_msgTypes[25] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } + mi := &file_encore_runtime_v1_infra_proto_msgTypes[25] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) } func (x *PubSubCluster_GCPPubSub) String() string { @@ -2097,7 +2045,7 @@ func (*PubSubCluster_GCPPubSub) ProtoMessage() {} func (x *PubSubCluster_GCPPubSub) ProtoReflect() protoreflect.Message { mi := &file_encore_runtime_v1_infra_proto_msgTypes[25] - if protoimpl.UnsafeEnabled && x != nil { + if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -2123,11 +2071,9 @@ type PubSubCluster_NSQ struct { func (x *PubSubCluster_NSQ) Reset() { *x = PubSubCluster_NSQ{} - if protoimpl.UnsafeEnabled { - mi := &file_encore_runtime_v1_infra_proto_msgTypes[26] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } + mi := &file_encore_runtime_v1_infra_proto_msgTypes[26] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) } func (x *PubSubCluster_NSQ) String() string { @@ -2138,7 +2084,7 @@ func (*PubSubCluster_NSQ) ProtoMessage() {} func (x *PubSubCluster_NSQ) ProtoReflect() protoreflect.Message { mi := &file_encore_runtime_v1_infra_proto_msgTypes[26] - if protoimpl.UnsafeEnabled && x != nil { + if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -2170,11 +2116,9 @@ type PubSubCluster_AzureServiceBus struct { func (x *PubSubCluster_AzureServiceBus) Reset() { *x = PubSubCluster_AzureServiceBus{} - if protoimpl.UnsafeEnabled { - mi := &file_encore_runtime_v1_infra_proto_msgTypes[27] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } + mi := &file_encore_runtime_v1_infra_proto_msgTypes[27] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) } func (x *PubSubCluster_AzureServiceBus) String() string { @@ -2185,7 +2129,7 @@ func (*PubSubCluster_AzureServiceBus) ProtoMessage() {} func (x *PubSubCluster_AzureServiceBus) ProtoReflect() protoreflect.Message { mi := &file_encore_runtime_v1_infra_proto_msgTypes[27] - if protoimpl.UnsafeEnabled && x != nil { + if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -2218,11 +2162,9 @@ type PubSubTopic_GCPConfig struct { func (x *PubSubTopic_GCPConfig) Reset() { *x = PubSubTopic_GCPConfig{} - if protoimpl.UnsafeEnabled { - mi := &file_encore_runtime_v1_infra_proto_msgTypes[28] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } + mi := &file_encore_runtime_v1_infra_proto_msgTypes[28] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) } func (x *PubSubTopic_GCPConfig) String() string { @@ -2233,7 +2175,7 @@ func (*PubSubTopic_GCPConfig) ProtoMessage() {} func (x *PubSubTopic_GCPConfig) ProtoReflect() protoreflect.Message { mi := &file_encore_runtime_v1_infra_proto_msgTypes[28] - if protoimpl.UnsafeEnabled && x != nil { + if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -2272,11 +2214,9 @@ type PubSubSubscription_GCPConfig struct { func (x *PubSubSubscription_GCPConfig) Reset() { *x = PubSubSubscription_GCPConfig{} - if protoimpl.UnsafeEnabled { - mi := &file_encore_runtime_v1_infra_proto_msgTypes[29] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } + mi := &file_encore_runtime_v1_infra_proto_msgTypes[29] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) } func (x *PubSubSubscription_GCPConfig) String() string { @@ -2287,7 +2227,7 @@ func (*PubSubSubscription_GCPConfig) ProtoMessage() {} func (x *PubSubSubscription_GCPConfig) ProtoReflect() protoreflect.Message { mi := &file_encore_runtime_v1_infra_proto_msgTypes[29] - if protoimpl.UnsafeEnabled && x != nil { + if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -2340,11 +2280,9 @@ type BucketCluster_S3 struct { 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) - } + 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 { @@ -2355,7 +2293,7 @@ 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 { + if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -2407,15 +2345,16 @@ type BucketCluster_GCS struct { 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"` + // Additional options for signed URLs when running in local dev mode. + // Only use with anonymous mode. + LocalSign *BucketCluster_GCS_LocalSignOptions `protobuf:"bytes,3,opt,name=local_sign,json=localSign,proto3,oneof" json:"local_sign,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) - } + 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 { @@ -2426,7 +2365,7 @@ 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 { + if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -2455,6 +2394,77 @@ func (x *BucketCluster_GCS) GetAnonymous() bool { return false } +func (x *BucketCluster_GCS) GetLocalSign() *BucketCluster_GCS_LocalSignOptions { + if x != nil { + return x.LocalSign + } + return nil +} + +type BucketCluster_GCS_LocalSignOptions struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // Base prefix to use for presigned URLs. + BaseUrl string `protobuf:"bytes,1,opt,name=base_url,json=baseUrl,proto3" json:"base_url,omitempty"` + // Use these credentials to sign local URLs. Only pass dummy credentials + // here, no actual secrets. + AccessId string `protobuf:"bytes,2,opt,name=access_id,json=accessId,proto3" json:"access_id,omitempty"` + PrivateKey string `protobuf:"bytes,3,opt,name=private_key,json=privateKey,proto3" json:"private_key,omitempty"` +} + +func (x *BucketCluster_GCS_LocalSignOptions) Reset() { + *x = BucketCluster_GCS_LocalSignOptions{} + mi := &file_encore_runtime_v1_infra_proto_msgTypes[32] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *BucketCluster_GCS_LocalSignOptions) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*BucketCluster_GCS_LocalSignOptions) ProtoMessage() {} + +func (x *BucketCluster_GCS_LocalSignOptions) ProtoReflect() protoreflect.Message { + mi := &file_encore_runtime_v1_infra_proto_msgTypes[32] + if 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_LocalSignOptions.ProtoReflect.Descriptor instead. +func (*BucketCluster_GCS_LocalSignOptions) Descriptor() ([]byte, []int) { + return file_encore_runtime_v1_infra_proto_rawDescGZIP(), []int{17, 1, 0} +} + +func (x *BucketCluster_GCS_LocalSignOptions) GetBaseUrl() string { + if x != nil { + return x.BaseUrl + } + return "" +} + +func (x *BucketCluster_GCS_LocalSignOptions) GetAccessId() string { + if x != nil { + return x.AccessId + } + return "" +} + +func (x *BucketCluster_GCS_LocalSignOptions) GetPrivateKey() string { + if x != nil { + return x.PrivateKey + } + return "" +} + // CORS describes the CORS configuration for a gateway. type Gateway_CORS struct { state protoimpl.MessageState @@ -2498,11 +2508,9 @@ type Gateway_CORS struct { func (x *Gateway_CORS) Reset() { *x = Gateway_CORS{} - if protoimpl.UnsafeEnabled { - mi := &file_encore_runtime_v1_infra_proto_msgTypes[32] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } + mi := &file_encore_runtime_v1_infra_proto_msgTypes[33] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) } func (x *Gateway_CORS) String() string { @@ -2512,8 +2520,8 @@ 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[32] - if protoimpl.UnsafeEnabled && x != nil { + mi := &file_encore_runtime_v1_infra_proto_msgTypes[33] + if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -2624,11 +2632,9 @@ type Gateway_CORSAllowedOrigins struct { func (x *Gateway_CORSAllowedOrigins) Reset() { *x = Gateway_CORSAllowedOrigins{} - if protoimpl.UnsafeEnabled { - mi := &file_encore_runtime_v1_infra_proto_msgTypes[33] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } + mi := &file_encore_runtime_v1_infra_proto_msgTypes[34] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) } func (x *Gateway_CORSAllowedOrigins) String() string { @@ -2638,8 +2644,8 @@ 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[33] - if protoimpl.UnsafeEnabled && x != nil { + mi := &file_encore_runtime_v1_infra_proto_msgTypes[34] + if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -2969,7 +2975,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, 0x94, 0x04, 0x0a, 0x0d, 0x42, 0x75, 0x63, 0x6b, 0x65, + 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x22, 0xec, 0x05, 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, @@ -2996,89 +3002,103 @@ var file_encore_runtime_v1_infra_proto_rawDesc = []byte{ 0x65, 0x73, 0x73, 0x4b, 0x65, 0x79, 0x88, 0x01, 0x01, 0x42, 0x0b, 0x0a, 0x09, 0x5f, 0x65, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x42, 0x10, 0x0a, 0x0e, 0x5f, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x5f, 0x6b, 0x65, 0x79, 0x5f, 0x69, 0x64, 0x42, 0x14, 0x0a, 0x12, 0x5f, 0x73, 0x65, 0x63, - 0x72, 0x65, 0x74, 0x5f, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x5f, 0x6b, 0x65, 0x79, 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, 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, 0xce, 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, 0x12, 0x2b, - 0x0a, 0x0f, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x5f, 0x62, 0x61, 0x73, 0x65, 0x5f, 0x75, 0x72, - 0x6c, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x48, 0x01, 0x52, 0x0d, 0x70, 0x75, 0x62, 0x6c, 0x69, - 0x63, 0x42, 0x61, 0x73, 0x65, 0x55, 0x72, 0x6c, 0x88, 0x01, 0x01, 0x42, 0x0d, 0x0a, 0x0b, 0x5f, - 0x6b, 0x65, 0x79, 0x5f, 0x70, 0x72, 0x65, 0x66, 0x69, 0x78, 0x42, 0x12, 0x0a, 0x10, 0x5f, 0x70, - 0x75, 0x62, 0x6c, 0x69, 0x63, 0x5f, 0x62, 0x61, 0x73, 0x65, 0x5f, 0x75, 0x72, 0x6c, 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, + 0x72, 0x65, 0x74, 0x5f, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x5f, 0x6b, 0x65, 0x79, 0x1a, 0xa8, + 0x02, 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, 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, 0x12, 0x59, 0x0a, 0x0a, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x5f, 0x73, + 0x69, 0x67, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x35, 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, 0x2e, + 0x4c, 0x6f, 0x63, 0x61, 0x6c, 0x53, 0x69, 0x67, 0x6e, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, + 0x48, 0x01, 0x52, 0x09, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x53, 0x69, 0x67, 0x6e, 0x88, 0x01, 0x01, + 0x1a, 0x6b, 0x0a, 0x10, 0x4c, 0x6f, 0x63, 0x61, 0x6c, 0x53, 0x69, 0x67, 0x6e, 0x4f, 0x70, 0x74, + 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x19, 0x0a, 0x08, 0x62, 0x61, 0x73, 0x65, 0x5f, 0x75, 0x72, 0x6c, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x62, 0x61, 0x73, 0x65, 0x55, 0x72, 0x6c, 0x12, + 0x1b, 0x0a, 0x09, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x08, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x49, 0x64, 0x12, 0x1f, 0x0a, 0x0b, + 0x70, 0x72, 0x69, 0x76, 0x61, 0x74, 0x65, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x03, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x0a, 0x70, 0x72, 0x69, 0x76, 0x61, 0x74, 0x65, 0x4b, 0x65, 0x79, 0x42, 0x0b, 0x0a, + 0x09, 0x5f, 0x65, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x42, 0x0d, 0x0a, 0x0b, 0x5f, 0x6c, + 0x6f, 0x63, 0x61, 0x6c, 0x5f, 0x73, 0x69, 0x67, 0x6e, 0x42, 0x0a, 0x0a, 0x08, 0x70, 0x72, 0x6f, + 0x76, 0x69, 0x64, 0x65, 0x72, 0x22, 0xce, 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, 0x12, 0x2b, 0x0a, 0x0f, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, + 0x5f, 0x62, 0x61, 0x73, 0x65, 0x5f, 0x75, 0x72, 0x6c, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x48, + 0x01, 0x52, 0x0d, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x42, 0x61, 0x73, 0x65, 0x55, 0x72, 0x6c, + 0x88, 0x01, 0x01, 0x42, 0x0d, 0x0a, 0x0b, 0x5f, 0x6b, 0x65, 0x79, 0x5f, 0x70, 0x72, 0x65, 0x66, + 0x69, 0x78, 0x42, 0x12, 0x0a, 0x10, 0x5f, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x5f, 0x62, 0x61, + 0x73, 0x65, 0x5f, 0x75, 0x72, 0x6c, 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 ( @@ -3094,45 +3114,46 @@ 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, 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 - (*Infrastructure)(nil), // 2: encore.runtime.v1.Infrastructure - (*SQLCluster)(nil), // 3: encore.runtime.v1.SQLCluster - (*TLSConfig)(nil), // 4: encore.runtime.v1.TLSConfig - (*SQLServer)(nil), // 5: encore.runtime.v1.SQLServer - (*ClientCert)(nil), // 6: encore.runtime.v1.ClientCert - (*SQLRole)(nil), // 7: encore.runtime.v1.SQLRole - (*SQLDatabase)(nil), // 8: encore.runtime.v1.SQLDatabase - (*SQLConnectionPool)(nil), // 9: encore.runtime.v1.SQLConnectionPool - (*RedisCluster)(nil), // 10: encore.runtime.v1.RedisCluster - (*RedisServer)(nil), // 11: encore.runtime.v1.RedisServer - (*RedisConnectionPool)(nil), // 12: encore.runtime.v1.RedisConnectionPool - (*RedisRole)(nil), // 13: encore.runtime.v1.RedisRole - (*RedisDatabase)(nil), // 14: encore.runtime.v1.RedisDatabase - (*AppSecret)(nil), // 15: encore.runtime.v1.AppSecret - (*PubSubCluster)(nil), // 16: encore.runtime.v1.PubSubCluster - (*PubSubTopic)(nil), // 17: encore.runtime.v1.PubSubTopic - (*PubSubSubscription)(nil), // 18: encore.runtime.v1.PubSubSubscription - (*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 - (*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_msgTypes = make([]protoimpl.MessageInfo, 35) +var file_encore_runtime_v1_infra_proto_goTypes = []any{ + (ServerKind)(0), // 0: encore.runtime.v1.ServerKind + (PubSubTopic_DeliveryGuarantee)(0), // 1: encore.runtime.v1.PubSubTopic.DeliveryGuarantee + (*Infrastructure)(nil), // 2: encore.runtime.v1.Infrastructure + (*SQLCluster)(nil), // 3: encore.runtime.v1.SQLCluster + (*TLSConfig)(nil), // 4: encore.runtime.v1.TLSConfig + (*SQLServer)(nil), // 5: encore.runtime.v1.SQLServer + (*ClientCert)(nil), // 6: encore.runtime.v1.ClientCert + (*SQLRole)(nil), // 7: encore.runtime.v1.SQLRole + (*SQLDatabase)(nil), // 8: encore.runtime.v1.SQLDatabase + (*SQLConnectionPool)(nil), // 9: encore.runtime.v1.SQLConnectionPool + (*RedisCluster)(nil), // 10: encore.runtime.v1.RedisCluster + (*RedisServer)(nil), // 11: encore.runtime.v1.RedisServer + (*RedisConnectionPool)(nil), // 12: encore.runtime.v1.RedisConnectionPool + (*RedisRole)(nil), // 13: encore.runtime.v1.RedisRole + (*RedisDatabase)(nil), // 14: encore.runtime.v1.RedisDatabase + (*AppSecret)(nil), // 15: encore.runtime.v1.AppSecret + (*PubSubCluster)(nil), // 16: encore.runtime.v1.PubSubCluster + (*PubSubTopic)(nil), // 17: encore.runtime.v1.PubSubTopic + (*PubSubSubscription)(nil), // 18: encore.runtime.v1.PubSubSubscription + (*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 + (*BucketCluster_S3)(nil), // 32: encore.runtime.v1.BucketCluster.S3 + (*BucketCluster_GCS)(nil), // 33: encore.runtime.v1.BucketCluster.GCS + (*BucketCluster_GCS_LocalSignOptions)(nil), // 34: encore.runtime.v1.BucketCluster.GCS.LocalSignOptions + (*Gateway_CORS)(nil), // 35: encore.runtime.v1.Gateway.CORS + (*Gateway_CORSAllowedOrigins)(nil), // 36: encore.runtime.v1.Gateway.CORSAllowedOrigins + (*SecretData)(nil), // 37: 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 @@ -3141,17 +3162,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 - 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 + 37, // 6: encore.runtime.v1.ClientCert.key:type_name -> encore.runtime.v1.SecretData + 37, // 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 - 36, // 14: encore.runtime.v1.RedisRole.auth_string:type_name -> encore.runtime.v1.SecretData + 37, // 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 - 36, // 16: encore.runtime.v1.AppSecret.data:type_name -> encore.runtime.v1.SecretData + 37, // 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 @@ -3165,7 +3186,7 @@ var file_encore_runtime_v1_infra_proto_depIdxs = []int32{ 20, // 27: encore.runtime.v1.BucketCluster.buckets:type_name -> encore.runtime.v1.Bucket 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 + 35, // 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 @@ -3175,15 +3196,16 @@ var file_encore_runtime_v1_infra_proto_depIdxs = []int32{ 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 - 36, // 41: encore.runtime.v1.BucketCluster.S3.secret_access_key:type_name -> encore.runtime.v1.SecretData - 35, // 42: encore.runtime.v1.Gateway.CORS.allowed_origins:type_name -> encore.runtime.v1.Gateway.CORSAllowedOrigins - 35, // 43: encore.runtime.v1.Gateway.CORS.allowed_origins_without_credentials:type_name -> encore.runtime.v1.Gateway.CORSAllowedOrigins - 44, // [44:44] is the sub-list for method output_type - 44, // [44:44] is the sub-list for method input_type - 44, // [44:44] is the sub-list for extension type_name - 44, // [44:44] is the sub-list for extension extendee - 0, // [0:44] is the sub-list for field type_name + 37, // 40: encore.runtime.v1.RedisRole.AuthACL.password:type_name -> encore.runtime.v1.SecretData + 37, // 41: encore.runtime.v1.BucketCluster.S3.secret_access_key:type_name -> encore.runtime.v1.SecretData + 34, // 42: encore.runtime.v1.BucketCluster.GCS.local_sign:type_name -> encore.runtime.v1.BucketCluster.GCS.LocalSignOptions + 36, // 43: encore.runtime.v1.Gateway.CORS.allowed_origins:type_name -> encore.runtime.v1.Gateway.CORSAllowedOrigins + 36, // 44: encore.runtime.v1.Gateway.CORS.allowed_origins_without_credentials:type_name -> encore.runtime.v1.Gateway.CORSAllowedOrigins + 45, // [45:45] is the sub-list for method output_type + 45, // [45:45] is the sub-list for method input_type + 45, // [45:45] is the sub-list for extension type_name + 45, // [45:45] is the sub-list for extension extendee + 0, // [0:45] is the sub-list for field type_name } func init() { file_encore_runtime_v1_infra_proto_init() } @@ -3192,447 +3214,37 @@ func file_encore_runtime_v1_infra_proto_init() { return } file_encore_runtime_v1_secretdata_proto_init() - if !protoimpl.UnsafeEnabled { - file_encore_runtime_v1_infra_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*Infrastructure); 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[1].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*SQLCluster); 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[2].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*TLSConfig); 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[3].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*SQLServer); 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[4].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*ClientCert); 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[5].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*SQLRole); 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[6].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*SQLDatabase); 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[7].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*SQLConnectionPool); 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[8].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*RedisCluster); 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[9].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*RedisServer); 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[10].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*RedisConnectionPool); 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[11].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*RedisRole); 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[12].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*RedisDatabase); 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[13].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*AppSecret); 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[14].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*PubSubCluster); 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[15].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*PubSubTopic); 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[16].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*PubSubSubscription); 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[17].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*BucketCluster); 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[18].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*Bucket); 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[19].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*Gateway); 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[20].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*Infrastructure_Credentials); 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[21].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*Infrastructure_Resources); 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[22].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*RedisRole_AuthACL); 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[23].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*PubSubCluster_EncoreCloud); 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[24].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*PubSubCluster_AWSSqsSns); 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[25].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*PubSubCluster_GCPPubSub); 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[26].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*PubSubCluster_NSQ); 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[27].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*PubSubCluster_AzureServiceBus); 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[28].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*PubSubTopic_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[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.(*BucketCluster_S3); 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.(*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 - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - } - file_encore_runtime_v1_infra_proto_msgTypes[2].OneofWrappers = []interface{}{} - file_encore_runtime_v1_infra_proto_msgTypes[3].OneofWrappers = []interface{}{} - file_encore_runtime_v1_infra_proto_msgTypes[5].OneofWrappers = []interface{}{} - file_encore_runtime_v1_infra_proto_msgTypes[9].OneofWrappers = []interface{}{} - file_encore_runtime_v1_infra_proto_msgTypes[11].OneofWrappers = []interface{}{ + file_encore_runtime_v1_infra_proto_msgTypes[2].OneofWrappers = []any{} + file_encore_runtime_v1_infra_proto_msgTypes[3].OneofWrappers = []any{} + file_encore_runtime_v1_infra_proto_msgTypes[5].OneofWrappers = []any{} + file_encore_runtime_v1_infra_proto_msgTypes[9].OneofWrappers = []any{} + file_encore_runtime_v1_infra_proto_msgTypes[11].OneofWrappers = []any{ (*RedisRole_Acl)(nil), (*RedisRole_AuthString)(nil), } - file_encore_runtime_v1_infra_proto_msgTypes[12].OneofWrappers = []interface{}{} - file_encore_runtime_v1_infra_proto_msgTypes[14].OneofWrappers = []interface{}{ + file_encore_runtime_v1_infra_proto_msgTypes[12].OneofWrappers = []any{} + file_encore_runtime_v1_infra_proto_msgTypes[14].OneofWrappers = []any{ (*PubSubCluster_Encore)(nil), (*PubSubCluster_Aws)(nil), (*PubSubCluster_Gcp)(nil), (*PubSubCluster_Azure)(nil), (*PubSubCluster_Nsq)(nil), } - file_encore_runtime_v1_infra_proto_msgTypes[15].OneofWrappers = []interface{}{ + file_encore_runtime_v1_infra_proto_msgTypes[15].OneofWrappers = []any{ (*PubSubTopic_GcpConfig)(nil), } - file_encore_runtime_v1_infra_proto_msgTypes[16].OneofWrappers = []interface{}{ + file_encore_runtime_v1_infra_proto_msgTypes[16].OneofWrappers = []any{ (*PubSubSubscription_GcpConfig)(nil), } - file_encore_runtime_v1_infra_proto_msgTypes[17].OneofWrappers = []interface{}{ + file_encore_runtime_v1_infra_proto_msgTypes[17].OneofWrappers = []any{ (*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{}{} - file_encore_runtime_v1_infra_proto_msgTypes[32].OneofWrappers = []interface{}{ + file_encore_runtime_v1_infra_proto_msgTypes[18].OneofWrappers = []any{} + file_encore_runtime_v1_infra_proto_msgTypes[29].OneofWrappers = []any{} + file_encore_runtime_v1_infra_proto_msgTypes[30].OneofWrappers = []any{} + file_encore_runtime_v1_infra_proto_msgTypes[31].OneofWrappers = []any{} + file_encore_runtime_v1_infra_proto_msgTypes[33].OneofWrappers = []any{ (*Gateway_CORS_AllowedOrigins)(nil), (*Gateway_CORS_UnsafeAllowAllOriginsWithCredentials)(nil), } @@ -3642,7 +3254,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: 34, + NumMessages: 35, NumExtensions: 0, NumServices: 0, }, diff --git a/proto/encore/runtime/v1/infra.proto b/proto/encore/runtime/v1/infra.proto index 1e7016e293..c50039c732 100644 --- a/proto/encore/runtime/v1/infra.proto +++ b/proto/encore/runtime/v1/infra.proto @@ -336,6 +336,21 @@ message BucketCluster { // Whether to connect anonymously or if a service account should be resolved. bool anonymous = 2; + + // Additional options for signed URLs when running in local dev mode. + // Only use with anonymous mode. + optional LocalSignOptions local_sign = 3; + + message LocalSignOptions { + // Base prefix to use for presigned URLs. + string base_url = 1; + + // Use these credentials to sign local URLs. Only pass dummy credentials + // here, no actual secrets. + string access_id = 2; + string private_key = 3; + + } } } diff --git a/runtimes/core/src/infracfg.rs b/runtimes/core/src/infracfg.rs index 0e9c8d50ed..e96f943378 100644 --- a/runtimes/core/src/infracfg.rs +++ b/runtimes/core/src/infracfg.rs @@ -445,6 +445,7 @@ pub fn map_infra_to_runtime(infra: InfraConfig) -> RuntimeConfig { pbruntime::bucket_cluster::Gcs { endpoint: gcs.endpoint, anonymous: false, + local_sign: None, }, )), buckets: gcs diff --git a/runtimes/core/src/objects/gcs/bucket.rs b/runtimes/core/src/objects/gcs/bucket.rs index ba8c3a8fce..f8c7c0d403 100644 --- a/runtimes/core/src/objects/gcs/bucket.rs +++ b/runtimes/core/src/objects/gcs/bucket.rs @@ -3,16 +3,20 @@ 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 google_cloud_storage::sign::SignBy; +use google_cloud_storage::sign::SignedURLOptions; use std::borrow::Cow; use std::future::Future; use std::pin::Pin; use std::sync::Arc; +use std::time::{Duration, SystemTime}; use tokio::io::AsyncRead; +use url::Url; use crate::encore::runtime::v1 as pb; use crate::objects::{ AttrsOptions, DeleteOptions, DownloadOptions, DownloadStream, Error, ExistsOptions, ListEntry, - ListOptions, ObjectAttrs, PublicUrlError, UploadOptions, + ListOptions, ObjectAttrs, PublicUrlError, UploadOptions, UploadUrlOptions, }; use crate::{objects, CloudName, EncoreName}; use google_cloud_storage as gcs; @@ -26,16 +30,33 @@ pub struct Bucket { cloud_name: CloudName, public_base_url: Option, key_prefix: Option, + local_sign: Option, +} + +#[derive(Debug)] +pub struct LocalSignOptions { + base_url: String, + access_id: String, + private_key: String, +} + +fn local_sign_config_from_client(client: Arc) -> Option { + client.cfg.local_sign.as_ref().map(|cfg| LocalSignOptions { + base_url: cfg.base_url.clone(), + access_id: cfg.access_id.clone(), + private_key: cfg.private_key.clone(), + }) } impl Bucket { pub(super) fn new(client: Arc, cfg: &pb::Bucket) -> Self { Self { - client, + client: client.clone(), encore_name: cfg.encore_name.clone().into(), cloud_name: cfg.cloud_name.clone().into(), public_base_url: cfg.public_base_url.clone(), key_prefix: cfg.key_prefix.clone(), + local_sign: local_sign_config_from_client(client.clone()), } } @@ -207,6 +228,56 @@ impl objects::ObjectImpl for Object { }) } + fn get_upload_url( + self: Arc, + options: UploadUrlOptions, + ) -> Pin> + Send>> { + Box::pin(async move { + match self.bkt.client.get().await { + Ok(client) => { + let gcs_opts = SignedURLOptions { + method: gcs::sign::SignedURLMethod::PUT, + expires: Duration::from_secs(options.ttl), + start_time: Some(SystemTime::now()), + ..Default::default() + }; + + // We use a fake GCS service for local development. Ideally, the runtime + // code would be oblivious to this once the GCS client is set up. But that + // turns out to be difficult for URL signing, so we add a special case + // here. + let local_sign = &self.bkt.local_sign; + let access_id: Option; + let sign_by: Option; + if let Some(opt) = local_sign { + access_id = Some(opt.access_id.clone()); + sign_by = Some(SignBy::PrivateKey(opt.private_key.as_bytes().to_vec())); + } else { + access_id = None; + sign_by = None; + } + + let name = self.bkt.obj_name(Cow::Borrowed(&self.key)).into_owned(); + let mut url = client + .signed_url(&self.bkt.cloud_name, &name, access_id, sign_by, gcs_opts) + .await + .map_err(map_sign_err)?; + + // More special handling for the local dev case. + if let Some(cfg) = local_sign { + url = replace_url_prefix(url, cfg.base_url.clone()); + } + + Ok(url) + } + Err(err) => Err(Error::Internal(anyhow::anyhow!( + "unable to resolve client: {}", + err + ))), + } + }) + } + fn exists( self: Arc, options: ExistsOptions, @@ -368,6 +439,27 @@ impl objects::ObjectImpl for Object { } } +fn replace_url_prefix(orig_url: String, base: String) -> String { + match Url::parse(&orig_url) { + Ok(url) => { + let mut out = format!( + "{}/{}", + base.trim_end_matches('/'), + url.path().trim_start_matches("/") + ); + if let Some(query) = url.query() { + out.push('?'); + out.push_str(query); + } + out + } + Err(_) => { + // If the input URL fails parsing, just don't do the replace + orig_url + } + } +} + 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); @@ -408,3 +500,10 @@ fn map_err(err: gcs::http::Error) -> Error { err => Error::Other(err.into()), } } + +fn map_sign_err(err: gcs::sign::SignedURLError) -> Error { + match err { + gcs::sign::SignedURLError::InvalidOption(_e) => Error::PreconditionFailed, + err => Error::Internal(anyhow::anyhow!(err)), + } +} diff --git a/runtimes/core/src/objects/mod.rs b/runtimes/core/src/objects/mod.rs index b6d65282b4..052b7f8767 100644 --- a/runtimes/core/src/objects/mod.rs +++ b/runtimes/core/src/objects/mod.rs @@ -51,6 +51,11 @@ trait ObjectImpl: Debug + Send + Sync { options: UploadOptions, ) -> Pin> + Send>>; + fn get_upload_url( + self: Arc, + options: UploadUrlOptions, + ) -> Pin> + Send>>; + fn download( self: Arc, options: DownloadOptions, @@ -223,6 +228,14 @@ impl Object { } } + pub async fn get_upload_url( + &self, + options: UploadUrlOptions, + _source: Option>, + ) -> Result { + self.imp.clone().get_upload_url(options).await + } + pub fn download_stream( &self, options: DownloadOptions, @@ -419,6 +432,11 @@ pub struct AttrsOptions { pub version: Option, } +#[derive(Debug, Default)] +pub struct UploadUrlOptions { + pub ttl: u64, +} + #[derive(Debug, Default)] pub struct DeleteOptions { pub version: Option, diff --git a/runtimes/core/src/objects/noop/mod.rs b/runtimes/core/src/objects/noop/mod.rs index 43302713d9..976ccebedb 100644 --- a/runtimes/core/src/objects/noop/mod.rs +++ b/runtimes/core/src/objects/noop/mod.rs @@ -10,6 +10,7 @@ use crate::{encore::runtime::v1 as pb, EncoreName}; use super::{ AttrsOptions, DeleteOptions, DownloadOptions, ExistsOptions, ListOptions, PublicUrlError, + UploadUrlOptions, }; #[derive(Debug)] @@ -83,6 +84,15 @@ impl objects::ObjectImpl for Object { )))) } + fn get_upload_url( + self: Arc, + _options: UploadUrlOptions, + ) -> Pin> + Send>> { + Box::pin(future::ready(Err(objects::Error::Internal( + anyhow::anyhow!("noop bucket does not support getting upload URL"), + )))) + } + fn exists( self: Arc, _options: ExistsOptions, diff --git a/runtimes/core/src/objects/s3/bucket.rs b/runtimes/core/src/objects/s3/bucket.rs index 9030c8db82..09ba01b937 100644 --- a/runtimes/core/src/objects/s3/bucket.rs +++ b/runtimes/core/src/objects/s3/bucket.rs @@ -1,6 +1,8 @@ use async_stream::{stream, try_stream}; use aws_sdk_s3 as s3; use aws_sdk_s3::error::SdkError; +use aws_sdk_s3::presigning::PresigningConfig; +use aws_sdk_s3::presigning::PresigningConfigError; use aws_smithy_types::byte_stream::ByteStream; use base64::Engine; use bytes::{Bytes, BytesMut}; @@ -10,12 +12,13 @@ use std::future::Future; use std::pin::Pin; use std::sync::Arc; use std::task::Poll; +use std::time::Duration; use tokio::io::{AsyncRead, AsyncReadExt}; use crate::encore::runtime::v1 as pb; use crate::objects::{ self, AttrsOptions, DeleteOptions, DownloadOptions, Error, ExistsOptions, ListEntry, - ListOptions, ObjectAttrs, PublicUrlError, + ListOptions, ObjectAttrs, PublicUrlError, UploadUrlOptions, }; use crate::{CloudName, EncoreName}; @@ -178,6 +181,29 @@ impl objects::ObjectImpl for Object { }) } + fn get_upload_url( + self: Arc, + options: UploadUrlOptions, + ) -> Pin> + Send>> { + Box::pin(async move { + let client = self.bkt.client.get().await.clone(); + let obj_name = self.bkt.obj_name(Cow::Borrowed(&self.name)); + + let ttl = Duration::from_secs(options.ttl); + + let res = client + .put_object() + .bucket(&self.bkt.cloud_name) + .key(obj_name) + .presigned(PresigningConfig::expires_in(ttl).map_err(map_sign_config_err)?) + .await; + match res { + Ok(req) => Ok(String::from(req.uri())), + Err(err) => Err(Error::Other(err.into())), + } + }) + } + fn exists( self: Arc, options: ExistsOptions, @@ -549,3 +575,9 @@ where Error::Other(anyhow::anyhow!("failed to upload: {:?}", err)) } } + +fn map_sign_config_err(_err: PresigningConfigError) -> objects::Error { + // We can't access the kind of error, unfortunately, but currently all + // possible error kinds are related to expiration time. + Error::PreconditionFailed +} diff --git a/runtimes/go/appruntime/exported/config/config.go b/runtimes/go/appruntime/exported/config/config.go index 158c16c874..d26282ad0e 100644 --- a/runtimes/go/appruntime/exported/config/config.go +++ b/runtimes/go/appruntime/exported/config/config.go @@ -420,6 +420,16 @@ type S3BucketProvider struct { type GCSBucketProvider struct { Endpoint string `json:"endpoint"` Anonymous bool `json:"anonymous"` + + // Additional options for signed URLs when running in local dev mode. + // Only use with anonymous mode. + LocalSign *GCSLocalSignOptions `json:"local_sign"` +} + +type GCSLocalSignOptions struct { + BaseUrl string `json:"base_url"` + AccessId string `json:"access_id"` + PrivateKey string `json:"private_key"` } type Bucket struct { diff --git a/runtimes/go/storage/objects/bucket.go b/runtimes/go/storage/objects/bucket.go index 6fa44d8196..b76cec37a3 100644 --- a/runtimes/go/storage/objects/bucket.go +++ b/runtimes/go/storage/objects/bucket.go @@ -572,6 +572,22 @@ func (b *Bucket) Attrs(ctx context.Context, object string, options ...AttrsOptio return b.mapAttrs(attrs), nil } +func (b *Bucket) GetUploadUrl(ctx context.Context, object string, options ...UploadUrlOption) (string, error) { + var opt uploadUrlOptions + for _, o := range options { + o.applyUploadUrl(&opt) + } + url, urlErr := b.impl.GetUploadUrl(types.UploadUrlData{ + Ctx: ctx, + Object: b.toCloudObject(object), + Ttl: opt.ttl, + }) + if urlErr != nil { + return "", urlErr + } + return url, 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 diff --git a/runtimes/go/storage/objects/internal/providers/gcs/bucket.go b/runtimes/go/storage/objects/internal/providers/gcs/bucket.go index f6bce45ce5..a64aa2426f 100644 --- a/runtimes/go/storage/objects/internal/providers/gcs/bucket.go +++ b/runtimes/go/storage/objects/internal/providers/gcs/bucket.go @@ -6,7 +6,10 @@ import ( "fmt" "iter" "net/http" + "net/url" "strconv" + "strings" + "time" "cloud.google.com/go/storage" "google.golang.org/api/googleapi" @@ -29,10 +32,17 @@ func NewManager(ctx context.Context, runtime *config.Runtime) *Manager { return &Manager{ctx: ctx, runtime: runtime, clients: make(map[*config.BucketProvider]*storage.Client)} } +type localSignOptions struct { + baseUrl string + accessId string + privateKey string +} + type bucket struct { - client *storage.Client - cfg *config.Bucket - handle *storage.BucketHandle + client *storage.Client + cfg *config.Bucket + handle *storage.BucketHandle + localSign *localSignOptions } func (mgr *Manager) ProviderName() string { return "gcs" } @@ -43,8 +53,10 @@ func (mgr *Manager) Matches(cfg *config.BucketProvider) bool { func (mgr *Manager) NewBucket(provider *config.BucketProvider, runtimeCfg *config.Bucket) types.BucketImpl { client := mgr.clientForProvider(provider) + + local_sign := localSignOptionsForProvider(provider) handle := client.Bucket(runtimeCfg.CloudName) - return &bucket{client, runtimeCfg, handle} + return &bucket{client, runtimeCfg, handle, local_sign} } func (b *bucket) Download(data types.DownloadData) (types.Downloader, error) { @@ -178,6 +190,55 @@ func (b *bucket) Attrs(data types.AttrsData) (*types.ObjectAttrs, error) { return mapAttrs(resp), mapErr(err) } +func (b *bucket) GetUploadUrl(data types.UploadUrlData) (string, error) { + // GCS allows a max of seven days TTL. We catch that here to return the + // right error type. The GCS client returns a generic error for this that + // is hard to distinguish from others. + if data.Ttl >= 7*24*3600 { + return "", types.ErrPreconditionFailed + // TODO: would be nice to return a specific error message with this one. + } + + opts := &storage.SignedURLOptions{ + Scheme: storage.SigningSchemeV4, + Method: "PUT", + Expires: time.Now().Add(time.Duration(data.Ttl) * time.Second), + } + + // We use a fake GCS service for local development. Ideally, the runtime + // code would be oblivious to this once the GCS client is set up. But that + // turns out to be difficult for URL signing, so we add a special case + // here. + if b.localSign != nil { + opts.GoogleAccessID = b.localSign.accessId + opts.PrivateKey = []byte(b.localSign.privateKey) + } + + url, err := b.handle.SignedURL(data.Object.String(), opts) + if err != nil { + return "", mapErr(err) + } + + // More special handling for the local dev case. + if b.localSign != nil { + url = replaceURLPrefix(url, b.localSign.baseUrl) + } + + return url, nil +} + +func replaceURLPrefix(orig_url string, base string) string { + u, err := url.Parse(orig_url) + if err != nil { + return orig_url // If the input URL is not valid, just return it as-is + } + out := strings.TrimRight(base, "/") + "/" + strings.TrimLeft(u.Path, "/") + if u.RawQuery != "" { + out += "?" + u.RawQuery + } + return out +} + func (mgr *Manager) clientForProvider(prov *config.BucketProvider) *storage.Client { if client, ok := mgr.clients[prov]; ok { return client @@ -200,6 +261,18 @@ func (mgr *Manager) clientForProvider(prov *config.BucketProvider) *storage.Clie return client } +func localSignOptionsForProvider(prov *config.BucketProvider) *localSignOptions { + if opts := prov.GCS.LocalSign; opts == nil { + return nil + } else { + return &localSignOptions{ + baseUrl: opts.BaseUrl, + accessId: opts.AccessId, + privateKey: opts.PrivateKey, + } + } +} + func mapErr(err error) error { switch { case err == nil: diff --git a/runtimes/go/storage/objects/internal/providers/noop/noop.go b/runtimes/go/storage/objects/internal/providers/noop/noop.go index 2e5951ef6e..6d04ec3a63 100644 --- a/runtimes/go/storage/objects/internal/providers/noop/noop.go +++ b/runtimes/go/storage/objects/internal/providers/noop/noop.go @@ -32,3 +32,7 @@ func (b *BucketImpl) Remove(data types.RemoveData) error { func (b *BucketImpl) Attrs(data types.AttrsData) (*types.ObjectAttrs, error) { return nil, fmt.Errorf("cannot get attributes from noop bucket") } + +func (b *BucketImpl) GetUploadUrl(data types.UploadUrlData) (string, error) { + return "", fmt.Errorf("cannot get upload url from noop bucket") +} diff --git a/runtimes/go/storage/objects/internal/providers/s3/bucket.go b/runtimes/go/storage/objects/internal/providers/s3/bucket.go index 5a1b5b87c8..50e05f872f 100644 --- a/runtimes/go/storage/objects/internal/providers/s3/bucket.go +++ b/runtimes/go/storage/objects/internal/providers/s3/bucket.go @@ -6,6 +6,7 @@ import ( "fmt" "iter" "sync" + "time" "cloud.google.com/go/storage" "github.com/aws/aws-sdk-go-v2/aws" @@ -33,8 +34,9 @@ func NewManager(ctx context.Context, runtime *config.Runtime) *Manager { } type bucket struct { - client *s3.Client - cfg *config.Bucket + client *s3.Client + presign_client *s3.PresignClient + cfg *config.Bucket } func (mgr *Manager) ProviderName() string { return "s3" } @@ -46,8 +48,9 @@ func (mgr *Manager) Matches(cfg *config.BucketProvider) bool { func (mgr *Manager) NewBucket(provider *config.BucketProvider, runtimeCfg *config.Bucket) types.BucketImpl { client := mgr.clientForProvider(provider) return &bucket{ - client: client, - cfg: runtimeCfg, + client: client, + presign_client: s3.NewPresignClient(client), + cfg: runtimeCfg, } } @@ -151,6 +154,26 @@ func (b *bucket) Attrs(data types.AttrsData) (*types.ObjectAttrs, error) { }, nil } +func (b *bucket) GetUploadUrl(data types.UploadUrlData) (string, error) { + object := string(data.Object) + params := s3.PutObjectInput{ + Bucket: &b.cfg.CloudName, + Key: &object, + } + sign_opts := func(opts *s3.PresignOptions) { + opts.Expires = time.Duration(data.Ttl) * time.Second + } + req, err := b.presign_client.PresignPutObject(data.Ctx, ¶ms, sign_opts) + + url := "" + if req != nil { + url = req.URL + // TODO: add check/warn against unexpected method and headers + // (we expect PUT and host:<> but nothing else.) + } + return url, mapErr(err) +} + func (mgr *Manager) clientForProvider(prov *config.BucketProvider) *s3.Client { if client, ok := mgr.clients[prov]; ok { return client diff --git a/runtimes/go/storage/objects/internal/types/types.go b/runtimes/go/storage/objects/internal/types/types.go index 4b0508f0a1..6c19d166e8 100644 --- a/runtimes/go/storage/objects/internal/types/types.go +++ b/runtimes/go/storage/objects/internal/types/types.go @@ -13,6 +13,7 @@ type BucketImpl interface { List(data ListData) iter.Seq2[*ListEntry, error] Remove(data RemoveData) error Attrs(data AttrsData) (*ObjectAttrs, error) + GetUploadUrl(data UploadUrlData) (string, error) } // CloudObject is the cloud name for an object. @@ -90,6 +91,13 @@ type AttrsData struct { Version string // non-zero means specific version } +type UploadUrlData struct { + Ctx context.Context + Object CloudObject + + Ttl uint64 +} + //publicapigen:keep var ( //publicapigen:keep diff --git a/runtimes/go/storage/objects/options.go b/runtimes/go/storage/objects/options.go index 370eaed3f8..8a415de78b 100644 --- a/runtimes/go/storage/objects/options.go +++ b/runtimes/go/storage/objects/options.go @@ -33,10 +33,25 @@ func (o withVersionOption) attrsOption() {} //publicapigen:keep func (o withVersionOption) existsOption() {} +//publicapigen:keep +func (o withTtlOption) uploadUrlOption() {} + func (o withVersionOption) applyDownload(opts *downloadOptions) { opts.version = o.version } func (o withVersionOption) applyRemove(opts *removeOptions) { opts.version = o.version } func (o withVersionOption) applyAttrs(opts *attrsOptions) { opts.version = o.version } func (o withVersionOption) applyExists(opts *existsOptions) { opts.version = o.version } +func (o withTtlOption) applyUploadUrl(opts *uploadUrlOptions) { opts.ttl = o.ttl } + +// WithTtl is used for signed URLs, to specify the lifetime of the generated +// URL, in seconds. The max value is seven days. +func WithTtl(ttl uint64) withTtlOption { + return withTtlOption{ttl: ttl} +} + +//publicapigen:keep +type withTtlOption struct { + ttl uint64 +} //publicapigen:keep type downloadOptions struct { @@ -139,6 +154,18 @@ type attrsOptions struct { version string } +// UploadUrlOption describes available options for the GetUploadUrl operation. +type UploadUrlOption interface { + //publicapigen:keep + uploadUrlOption() + + applyUploadUrl(*uploadUrlOptions) +} + +type uploadUrlOptions struct { + ttl uint64 +} + // ExistsOption describes available options for the Exists operation. type ExistsOption interface { //publicapigen:keep diff --git a/runtimes/go/storage/objects/refs.go b/runtimes/go/storage/objects/refs.go index 0e1403bcca..20cf7ff0f5 100644 --- a/runtimes/go/storage/objects/refs.go +++ b/runtimes/go/storage/objects/refs.go @@ -38,6 +38,10 @@ type Uploader interface { // Upload begins uploading an object to the bucket. Upload(ctx context.Context, object string, options ...UploadOption) *Writer + // GetUploadUrl returns a signed URL that can be used to upload directly to + // storage, without any other authentication. + GetUploadUrl(ctx context.Context, object string, options ...UploadUrlOption) (string, error) + perms() } diff --git a/runtimes/js/encore.dev/storage/objects/bucket.ts b/runtimes/js/encore.dev/storage/objects/bucket.ts index 1a5e530f06..3521a2bd27 100644 --- a/runtimes/js/encore.dev/storage/objects/bucket.ts +++ b/runtimes/js/encore.dev/storage/objects/bucket.ts @@ -86,6 +86,19 @@ export class Bucket extends BucketPerms implements Uploader, Downloader, Attrser return unwrapErr(res); } + /** + * Generate an external URL to allow uploading an object to the bucket. + * + * Anyone with possession of the URL can write to the given object name + * without any additional auth. + */ + async getUploadUrl(name: string, options?: UploadUrlOptions): Promise { + const source = getCurrentRequest(); + const impl = this.impl.object(name); + const res = await impl.getUploadUrl(options, source); + return unwrapErr(res); + } + /** * Downloads an object from the bucket and returns its contents. */ @@ -193,5 +206,11 @@ export interface UploadOptions { contentType?: string; preconditions?: { notExists?: boolean; - }, + } +} + +export interface UploadUrlOptions { + /** The expiration time of the url, in seconds from signing. The maximum + * value is seven days */ + ttl: number; } diff --git a/runtimes/js/src/objects.rs b/runtimes/js/src/objects.rs index 094ecb1d10..be821a34dd 100644 --- a/runtimes/js/src/objects.rs +++ b/runtimes/js/src/objects.rs @@ -106,6 +106,20 @@ impl BucketObject { }) } + #[napi] + pub async fn get_upload_url( + &self, + options: Option, // TODO: can/should this be made non-optional, since ttl is required? + source: Option<&Request>, + ) -> napi::Either { + let options = options.unwrap_or_default().into(); + let source = source.map(|s| s.inner.clone()); + match self.obj.get_upload_url(options, source).await { + Ok(attrs) => napi::Either::A(attrs.into()), + Err(err) => napi::Either::B(err.into()), + } + } + #[napi] pub async fn download_all( &self, @@ -293,6 +307,12 @@ pub struct AttrsOptions { pub version: Option, } +#[napi(object)] +#[derive(Debug, Default)] +pub struct UploadUrlOptions { + pub ttl: i64, +} + #[napi(object)] #[derive(Debug, Default)] pub struct DeleteOptions { @@ -344,6 +364,14 @@ impl From for core::AttrsOptions { } } +impl From for core::UploadUrlOptions { + fn from(value: UploadUrlOptions) -> Self { + Self { + ttl: value.ttl as u64, + } + } +} + impl From for core::ListOptions { fn from(value: ListOptions) -> Self { Self { diff --git a/tsparser/src/parser/resources/infra/objects.rs b/tsparser/src/parser/resources/infra/objects.rs index f5337cef39..50165e10c5 100644 --- a/tsparser/src/parser/resources/infra/objects.rs +++ b/tsparser/src/parser/resources/infra/objects.rs @@ -120,6 +120,7 @@ pub fn resolve_bucket_usage(data: &ResolveUsageData, bucket: Lrc) -> Opt "list" => Operation::ListObjects, "exists" | "attrs" => Operation::GetObjectMetadata, "upload" => Operation::WriteObject, + "getUploadUrl" => Operation::WriteObject, "download" => Operation::ReadObjectContents, "remove" => Operation::DeleteObject,