diff --git a/README.md b/README.md index 745f400..aa90aa7 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ Massa is a Massive Serverless Systems Architecture. Relax this is just an internal joke. The name Massa is due to a river near the Eigr mountain in Switzerland. -Now seriously Massa is just a [CloudState](https://github.com/cloudstateio/cloudstate) proxy implementation +Now seriously Massa is just [CloudState](https://github.com/cloudstateio/cloudstate) proxy implementation build on top of BEAM/Elixir ## Overview @@ -21,41 +21,29 @@ Massa takes advantage of Elixir's simplicity and elegance, mainly because we kno - [ ] KV - [x] Cluster transparent - [x] Discovery Support -- [ ] Eventing: - - [ ] Apache Kafka - - [ ] Amazon SQS - - [ ] Event Log - - [ ] Google Cloud Pub/Sub - - [ ] RabbitMQ - - [ ] Redis -- [ ] Interoperability with Cloudstate reference implementation +- [ ] Eventing +- [x] Interoperability with Cloudstate reference implementation - [ ] Metadata Support - [x] Observability: - [x] Health Checks - [x] Metrics - [x] Open Tracing - [ ] State Store: - - [ ] Cassandra - [ ] In Memory - [ ] Mnesia + - [ ] Cassandra - [ ] MongoDB - [ ] MSSQL - [ ] MySql - [ ] Postgres - - [ ] Redis - - [ ] Riak - [ ] TCK - [x] Transports and Protocols - [x] GRPC - [x] Client TCP - [x] Client Unix Domain Sockets - [ ] Reflection - - [ ] Server TCP - - [ ] HTTP - - [ ] Grpc-Web - - [ ] SSE - - [ ] Websockets -- [x] Very Fast + - [x] Server TCP + - [ ] HTTP ## Main Concepts diff --git a/massa_proxy/apps/massa_proxy/compile-pb.sh b/massa_proxy/apps/massa_proxy/compile-pb.sh index 8f28f26..1549edb 100755 --- a/massa_proxy/apps/massa_proxy/compile-pb.sh +++ b/massa_proxy/apps/massa_proxy/compile-pb.sh @@ -6,8 +6,13 @@ set -o pipefail # CloudState Protocol -protoc --elixir_out=gen_descriptors=true,plugins=grpc:./lib --proto_path=priv/protos/frontend/ priv/protos/frontend/google/api/annotations.proto +protoc --elixir_out=gen_descriptors=true,plugins=grpc:./lib --proto_path=priv/protos/proxy/ priv/protos/proxy/grpc/reflection/v1alpha/reflection.proto + protoc --elixir_out=gen_descriptors=true,plugins=grpc:./lib --proto_path=priv/protos/frontend/ priv/protos/frontend/google/api/http.proto +protoc --elixir_out=gen_descriptors=true,plugins=grpc:./lib --proto_path=priv/protos/frontend/ priv/protos/frontend/google/api/annotations.proto +protoc --elixir_out=gen_descriptors=true,plugins=grpc:./lib --proto_path=priv/protos/frontend/ priv/protos/frontend/google/api/httpbody.proto +protoc --elixir_out=gen_descriptors=true,plugins=grpc:./lib --proto_path=priv/protos/frontend/ priv/protos/frontend/google/api/auth.proto +protoc --elixir_out=gen_descriptors=true,plugins=grpc:./lib --proto_path=priv/protos/frontend/ priv/protos/frontend/google/api/source_info.proto protoc --elixir_out=gen_descriptors=true,plugins=grpc:./lib --proto_path=priv/protos/frontend/ priv/protos/frontend/cloudstate/entity_key.proto protoc --elixir_out=gen_descriptors=true,plugins=grpc:./lib --proto_path=priv/protos/protocol/ priv/protos/protocol/cloudstate/entity.proto diff --git a/massa_proxy/apps/massa_proxy/lib/google/api/auth.pb.ex b/massa_proxy/apps/massa_proxy/lib/google/api/auth.pb.ex new file mode 100644 index 0000000..d13e696 --- /dev/null +++ b/massa_proxy/apps/massa_proxy/lib/google/api/auth.pb.ex @@ -0,0 +1,181 @@ +defmodule Google.Api.Authentication do + @moduledoc false + use Protobuf, syntax: :proto3 + + @type t :: %__MODULE__{ + rules: [Google.Api.AuthenticationRule.t()], + providers: [Google.Api.AuthProvider.t()] + } + + defstruct [:rules, :providers] + + def descriptor do + # credo:disable-for-next-line + Elixir.Google.Protobuf.DescriptorProto.decode( + <<10, 14, 65, 117, 116, 104, 101, 110, 116, 105, 99, 97, 116, 105, 111, 110, 18, 52, 10, 5, + 114, 117, 108, 101, 115, 24, 3, 32, 3, 40, 11, 50, 30, 46, 103, 111, 111, 103, 108, 101, + 46, 97, 112, 105, 46, 65, 117, 116, 104, 101, 110, 116, 105, 99, 97, 116, 105, 111, 110, + 82, 117, 108, 101, 82, 5, 114, 117, 108, 101, 115, 18, 54, 10, 9, 112, 114, 111, 118, 105, + 100, 101, 114, 115, 24, 4, 32, 3, 40, 11, 50, 24, 46, 103, 111, 111, 103, 108, 101, 46, + 97, 112, 105, 46, 65, 117, 116, 104, 80, 114, 111, 118, 105, 100, 101, 114, 82, 9, 112, + 114, 111, 118, 105, 100, 101, 114, 115>> + ) + end + + field(:rules, 3, repeated: true, type: Google.Api.AuthenticationRule) + field(:providers, 4, repeated: true, type: Google.Api.AuthProvider) +end + +defmodule Google.Api.AuthenticationRule do + @moduledoc false + use Protobuf, syntax: :proto3 + + @type t :: %__MODULE__{ + selector: String.t(), + oauth: Google.Api.OAuthRequirements.t() | nil, + allow_without_credential: boolean, + requirements: [Google.Api.AuthRequirement.t()] + } + + defstruct [:selector, :oauth, :allow_without_credential, :requirements] + + def descriptor do + # credo:disable-for-next-line + Elixir.Google.Protobuf.DescriptorProto.decode( + <<10, 18, 65, 117, 116, 104, 101, 110, 116, 105, 99, 97, 116, 105, 111, 110, 82, 117, 108, + 101, 18, 26, 10, 8, 115, 101, 108, 101, 99, 116, 111, 114, 24, 1, 32, 1, 40, 9, 82, 8, + 115, 101, 108, 101, 99, 116, 111, 114, 18, 51, 10, 5, 111, 97, 117, 116, 104, 24, 2, 32, + 1, 40, 11, 50, 29, 46, 103, 111, 111, 103, 108, 101, 46, 97, 112, 105, 46, 79, 65, 117, + 116, 104, 82, 101, 113, 117, 105, 114, 101, 109, 101, 110, 116, 115, 82, 5, 111, 97, 117, + 116, 104, 18, 56, 10, 24, 97, 108, 108, 111, 119, 95, 119, 105, 116, 104, 111, 117, 116, + 95, 99, 114, 101, 100, 101, 110, 116, 105, 97, 108, 24, 5, 32, 1, 40, 8, 82, 22, 97, 108, + 108, 111, 119, 87, 105, 116, 104, 111, 117, 116, 67, 114, 101, 100, 101, 110, 116, 105, + 97, 108, 18, 63, 10, 12, 114, 101, 113, 117, 105, 114, 101, 109, 101, 110, 116, 115, 24, + 7, 32, 3, 40, 11, 50, 27, 46, 103, 111, 111, 103, 108, 101, 46, 97, 112, 105, 46, 65, 117, + 116, 104, 82, 101, 113, 117, 105, 114, 101, 109, 101, 110, 116, 82, 12, 114, 101, 113, + 117, 105, 114, 101, 109, 101, 110, 116, 115>> + ) + end + + field(:selector, 1, type: :string) + field(:oauth, 2, type: Google.Api.OAuthRequirements) + field(:allow_without_credential, 5, type: :bool) + field(:requirements, 7, repeated: true, type: Google.Api.AuthRequirement) +end + +defmodule Google.Api.JwtLocation do + @moduledoc false + use Protobuf, syntax: :proto3 + + @type t :: %__MODULE__{ + in: {atom, any}, + value_prefix: String.t() + } + + defstruct [:in, :value_prefix] + + def descriptor do + # credo:disable-for-next-line + Elixir.Google.Protobuf.DescriptorProto.decode( + <<10, 11, 74, 119, 116, 76, 111, 99, 97, 116, 105, 111, 110, 18, 24, 10, 6, 104, 101, 97, + 100, 101, 114, 24, 1, 32, 1, 40, 9, 72, 0, 82, 6, 104, 101, 97, 100, 101, 114, 18, 22, 10, + 5, 113, 117, 101, 114, 121, 24, 2, 32, 1, 40, 9, 72, 0, 82, 5, 113, 117, 101, 114, 121, + 18, 33, 10, 12, 118, 97, 108, 117, 101, 95, 112, 114, 101, 102, 105, 120, 24, 3, 32, 1, + 40, 9, 82, 11, 118, 97, 108, 117, 101, 80, 114, 101, 102, 105, 120, 66, 4, 10, 2, 105, + 110>> + ) + end + + oneof(:in, 0) + field(:header, 1, type: :string, oneof: 0) + field(:query, 2, type: :string, oneof: 0) + field(:value_prefix, 3, type: :string) +end + +defmodule Google.Api.AuthProvider do + @moduledoc false + use Protobuf, syntax: :proto3 + + @type t :: %__MODULE__{ + id: String.t(), + issuer: String.t(), + jwks_uri: String.t(), + audiences: String.t(), + authorization_url: String.t(), + jwt_locations: [Google.Api.JwtLocation.t()] + } + + defstruct [:id, :issuer, :jwks_uri, :audiences, :authorization_url, :jwt_locations] + + def descriptor do + # credo:disable-for-next-line + Elixir.Google.Protobuf.DescriptorProto.decode( + <<10, 12, 65, 117, 116, 104, 80, 114, 111, 118, 105, 100, 101, 114, 18, 14, 10, 2, 105, 100, + 24, 1, 32, 1, 40, 9, 82, 2, 105, 100, 18, 22, 10, 6, 105, 115, 115, 117, 101, 114, 24, 2, + 32, 1, 40, 9, 82, 6, 105, 115, 115, 117, 101, 114, 18, 25, 10, 8, 106, 119, 107, 115, 95, + 117, 114, 105, 24, 3, 32, 1, 40, 9, 82, 7, 106, 119, 107, 115, 85, 114, 105, 18, 28, 10, + 9, 97, 117, 100, 105, 101, 110, 99, 101, 115, 24, 4, 32, 1, 40, 9, 82, 9, 97, 117, 100, + 105, 101, 110, 99, 101, 115, 18, 43, 10, 17, 97, 117, 116, 104, 111, 114, 105, 122, 97, + 116, 105, 111, 110, 95, 117, 114, 108, 24, 5, 32, 1, 40, 9, 82, 16, 97, 117, 116, 104, + 111, 114, 105, 122, 97, 116, 105, 111, 110, 85, 114, 108, 18, 60, 10, 13, 106, 119, 116, + 95, 108, 111, 99, 97, 116, 105, 111, 110, 115, 24, 6, 32, 3, 40, 11, 50, 23, 46, 103, 111, + 111, 103, 108, 101, 46, 97, 112, 105, 46, 74, 119, 116, 76, 111, 99, 97, 116, 105, 111, + 110, 82, 12, 106, 119, 116, 76, 111, 99, 97, 116, 105, 111, 110, 115>> + ) + end + + field(:id, 1, type: :string) + field(:issuer, 2, type: :string) + field(:jwks_uri, 3, type: :string) + field(:audiences, 4, type: :string) + field(:authorization_url, 5, type: :string) + field(:jwt_locations, 6, repeated: true, type: Google.Api.JwtLocation) +end + +defmodule Google.Api.OAuthRequirements do + @moduledoc false + use Protobuf, syntax: :proto3 + + @type t :: %__MODULE__{ + canonical_scopes: String.t() + } + + defstruct [:canonical_scopes] + + def descriptor do + # credo:disable-for-next-line + Elixir.Google.Protobuf.DescriptorProto.decode( + <<10, 17, 79, 65, 117, 116, 104, 82, 101, 113, 117, 105, 114, 101, 109, 101, 110, 116, 115, + 18, 41, 10, 16, 99, 97, 110, 111, 110, 105, 99, 97, 108, 95, 115, 99, 111, 112, 101, 115, + 24, 1, 32, 1, 40, 9, 82, 15, 99, 97, 110, 111, 110, 105, 99, 97, 108, 83, 99, 111, 112, + 101, 115>> + ) + end + + field(:canonical_scopes, 1, type: :string) +end + +defmodule Google.Api.AuthRequirement do + @moduledoc false + use Protobuf, syntax: :proto3 + + @type t :: %__MODULE__{ + provider_id: String.t(), + audiences: String.t() + } + + defstruct [:provider_id, :audiences] + + def descriptor do + # credo:disable-for-next-line + Elixir.Google.Protobuf.DescriptorProto.decode( + <<10, 15, 65, 117, 116, 104, 82, 101, 113, 117, 105, 114, 101, 109, 101, 110, 116, 18, 31, + 10, 11, 112, 114, 111, 118, 105, 100, 101, 114, 95, 105, 100, 24, 1, 32, 1, 40, 9, 82, 10, + 112, 114, 111, 118, 105, 100, 101, 114, 73, 100, 18, 28, 10, 9, 97, 117, 100, 105, 101, + 110, 99, 101, 115, 24, 2, 32, 1, 40, 9, 82, 9, 97, 117, 100, 105, 101, 110, 99, 101, 115>> + ) + end + + field(:provider_id, 1, type: :string) + field(:audiences, 2, type: :string) +end diff --git a/massa_proxy/apps/massa_proxy/lib/google/api/httpbody.pb.ex b/massa_proxy/apps/massa_proxy/lib/google/api/httpbody.pb.ex new file mode 100644 index 0000000..2a435a7 --- /dev/null +++ b/massa_proxy/apps/massa_proxy/lib/google/api/httpbody.pb.ex @@ -0,0 +1,28 @@ +defmodule Google.Api.HttpBody do + @moduledoc false + use Protobuf, syntax: :proto3 + + @type t :: %__MODULE__{ + content_type: String.t(), + data: binary, + extensions: [Google.Protobuf.Any.t()] + } + + defstruct [:content_type, :data, :extensions] + + def descriptor do + # credo:disable-for-next-line + Elixir.Google.Protobuf.DescriptorProto.decode( + <<10, 8, 72, 116, 116, 112, 66, 111, 100, 121, 18, 33, 10, 12, 99, 111, 110, 116, 101, 110, + 116, 95, 116, 121, 112, 101, 24, 1, 32, 1, 40, 9, 82, 11, 99, 111, 110, 116, 101, 110, + 116, 84, 121, 112, 101, 18, 18, 10, 4, 100, 97, 116, 97, 24, 2, 32, 1, 40, 12, 82, 4, 100, + 97, 116, 97, 18, 52, 10, 10, 101, 120, 116, 101, 110, 115, 105, 111, 110, 115, 24, 3, 32, + 3, 40, 11, 50, 20, 46, 103, 111, 111, 103, 108, 101, 46, 112, 114, 111, 116, 111, 98, 117, + 102, 46, 65, 110, 121, 82, 10, 101, 120, 116, 101, 110, 115, 105, 111, 110, 115>> + ) + end + + field(:content_type, 1, type: :string) + field(:data, 2, type: :bytes) + field(:extensions, 3, repeated: true, type: Google.Protobuf.Any) +end diff --git a/massa_proxy/apps/massa_proxy/lib/google/api/source_info.pb.ex b/massa_proxy/apps/massa_proxy/lib/google/api/source_info.pb.ex new file mode 100644 index 0000000..9c64ca0 --- /dev/null +++ b/massa_proxy/apps/massa_proxy/lib/google/api/source_info.pb.ex @@ -0,0 +1,22 @@ +defmodule Google.Api.SourceInfo do + @moduledoc false + use Protobuf, syntax: :proto3 + + @type t :: %__MODULE__{ + source_files: [Google.Protobuf.Any.t()] + } + + defstruct [:source_files] + + def descriptor do + # credo:disable-for-next-line + Elixir.Google.Protobuf.DescriptorProto.decode( + <<10, 10, 83, 111, 117, 114, 99, 101, 73, 110, 102, 111, 18, 55, 10, 12, 115, 111, 117, 114, + 99, 101, 95, 102, 105, 108, 101, 115, 24, 1, 32, 3, 40, 11, 50, 20, 46, 103, 111, 111, + 103, 108, 101, 46, 112, 114, 111, 116, 111, 98, 117, 102, 46, 65, 110, 121, 82, 11, 115, + 111, 117, 114, 99, 101, 70, 105, 108, 101, 115>> + ) + end + + field(:source_files, 1, repeated: true, type: Google.Protobuf.Any) +end diff --git a/massa_proxy/apps/massa_proxy/lib/grpc/reflection/v1alpha/reflection.pb.ex b/massa_proxy/apps/massa_proxy/lib/grpc/reflection/v1alpha/reflection.pb.ex new file mode 100644 index 0000000..14a79c5 --- /dev/null +++ b/massa_proxy/apps/massa_proxy/lib/grpc/reflection/v1alpha/reflection.pb.ex @@ -0,0 +1,290 @@ +defmodule Grpc.Reflection.V1alpha.ServerReflectionRequest do + @moduledoc false + use Protobuf, syntax: :proto3 + + @type t :: %__MODULE__{ + message_request: {atom, any}, + host: String.t() + } + + defstruct [:message_request, :host] + + def descriptor do + # credo:disable-for-next-line + Elixir.Google.Protobuf.DescriptorProto.decode( + <<10, 23, 83, 101, 114, 118, 101, 114, 82, 101, 102, 108, 101, 99, 116, 105, 111, 110, 82, + 101, 113, 117, 101, 115, 116, 18, 18, 10, 4, 104, 111, 115, 116, 24, 1, 32, 1, 40, 9, 82, + 4, 104, 111, 115, 116, 18, 42, 10, 16, 102, 105, 108, 101, 95, 98, 121, 95, 102, 105, 108, + 101, 110, 97, 109, 101, 24, 3, 32, 1, 40, 9, 72, 0, 82, 14, 102, 105, 108, 101, 66, 121, + 70, 105, 108, 101, 110, 97, 109, 101, 18, 54, 10, 22, 102, 105, 108, 101, 95, 99, 111, + 110, 116, 97, 105, 110, 105, 110, 103, 95, 115, 121, 109, 98, 111, 108, 24, 4, 32, 1, 40, + 9, 72, 0, 82, 20, 102, 105, 108, 101, 67, 111, 110, 116, 97, 105, 110, 105, 110, 103, 83, + 121, 109, 98, 111, 108, 18, 103, 10, 25, 102, 105, 108, 101, 95, 99, 111, 110, 116, 97, + 105, 110, 105, 110, 103, 95, 101, 120, 116, 101, 110, 115, 105, 111, 110, 24, 5, 32, 1, + 40, 11, 50, 41, 46, 103, 114, 112, 99, 46, 114, 101, 102, 108, 101, 99, 116, 105, 111, + 110, 46, 118, 49, 97, 108, 112, 104, 97, 46, 69, 120, 116, 101, 110, 115, 105, 111, 110, + 82, 101, 113, 117, 101, 115, 116, 72, 0, 82, 23, 102, 105, 108, 101, 67, 111, 110, 116, + 97, 105, 110, 105, 110, 103, 69, 120, 116, 101, 110, 115, 105, 111, 110, 18, 66, 10, 29, + 97, 108, 108, 95, 101, 120, 116, 101, 110, 115, 105, 111, 110, 95, 110, 117, 109, 98, 101, + 114, 115, 95, 111, 102, 95, 116, 121, 112, 101, 24, 6, 32, 1, 40, 9, 72, 0, 82, 25, 97, + 108, 108, 69, 120, 116, 101, 110, 115, 105, 111, 110, 78, 117, 109, 98, 101, 114, 115, 79, + 102, 84, 121, 112, 101, 18, 37, 10, 13, 108, 105, 115, 116, 95, 115, 101, 114, 118, 105, + 99, 101, 115, 24, 7, 32, 1, 40, 9, 72, 0, 82, 12, 108, 105, 115, 116, 83, 101, 114, 118, + 105, 99, 101, 115, 66, 17, 10, 15, 109, 101, 115, 115, 97, 103, 101, 95, 114, 101, 113, + 117, 101, 115, 116>> + ) + end + + oneof(:message_request, 0) + field(:host, 1, type: :string) + field(:file_by_filename, 3, type: :string, oneof: 0) + field(:file_containing_symbol, 4, type: :string, oneof: 0) + field(:file_containing_extension, 5, type: Grpc.Reflection.V1alpha.ExtensionRequest, oneof: 0) + field(:all_extension_numbers_of_type, 6, type: :string, oneof: 0) + field(:list_services, 7, type: :string, oneof: 0) +end + +defmodule Grpc.Reflection.V1alpha.ExtensionRequest do + @moduledoc false + use Protobuf, syntax: :proto3 + + @type t :: %__MODULE__{ + containing_type: String.t(), + extension_number: integer + } + + defstruct [:containing_type, :extension_number] + + def descriptor do + # credo:disable-for-next-line + Elixir.Google.Protobuf.DescriptorProto.decode( + <<10, 16, 69, 120, 116, 101, 110, 115, 105, 111, 110, 82, 101, 113, 117, 101, 115, 116, 18, + 39, 10, 15, 99, 111, 110, 116, 97, 105, 110, 105, 110, 103, 95, 116, 121, 112, 101, 24, 1, + 32, 1, 40, 9, 82, 14, 99, 111, 110, 116, 97, 105, 110, 105, 110, 103, 84, 121, 112, 101, + 18, 41, 10, 16, 101, 120, 116, 101, 110, 115, 105, 111, 110, 95, 110, 117, 109, 98, 101, + 114, 24, 2, 32, 1, 40, 5, 82, 15, 101, 120, 116, 101, 110, 115, 105, 111, 110, 78, 117, + 109, 98, 101, 114>> + ) + end + + field(:containing_type, 1, type: :string) + field(:extension_number, 2, type: :int32) +end + +defmodule Grpc.Reflection.V1alpha.ServerReflectionResponse do + @moduledoc false + use Protobuf, syntax: :proto3 + + @type t :: %__MODULE__{ + message_response: {atom, any}, + valid_host: String.t(), + original_request: Grpc.Reflection.V1alpha.ServerReflectionRequest.t() | nil + } + + defstruct [:message_response, :valid_host, :original_request] + + def descriptor do + # credo:disable-for-next-line + Elixir.Google.Protobuf.DescriptorProto.decode( + <<10, 24, 83, 101, 114, 118, 101, 114, 82, 101, 102, 108, 101, 99, 116, 105, 111, 110, 82, + 101, 115, 112, 111, 110, 115, 101, 18, 29, 10, 10, 118, 97, 108, 105, 100, 95, 104, 111, + 115, 116, 24, 1, 32, 1, 40, 9, 82, 9, 118, 97, 108, 105, 100, 72, 111, 115, 116, 18, 91, + 10, 16, 111, 114, 105, 103, 105, 110, 97, 108, 95, 114, 101, 113, 117, 101, 115, 116, 24, + 2, 32, 1, 40, 11, 50, 48, 46, 103, 114, 112, 99, 46, 114, 101, 102, 108, 101, 99, 116, + 105, 111, 110, 46, 118, 49, 97, 108, 112, 104, 97, 46, 83, 101, 114, 118, 101, 114, 82, + 101, 102, 108, 101, 99, 116, 105, 111, 110, 82, 101, 113, 117, 101, 115, 116, 82, 15, 111, + 114, 105, 103, 105, 110, 97, 108, 82, 101, 113, 117, 101, 115, 116, 18, 107, 10, 24, 102, + 105, 108, 101, 95, 100, 101, 115, 99, 114, 105, 112, 116, 111, 114, 95, 114, 101, 115, + 112, 111, 110, 115, 101, 24, 4, 32, 1, 40, 11, 50, 47, 46, 103, 114, 112, 99, 46, 114, + 101, 102, 108, 101, 99, 116, 105, 111, 110, 46, 118, 49, 97, 108, 112, 104, 97, 46, 70, + 105, 108, 101, 68, 101, 115, 99, 114, 105, 112, 116, 111, 114, 82, 101, 115, 112, 111, + 110, 115, 101, 72, 0, 82, 22, 102, 105, 108, 101, 68, 101, 115, 99, 114, 105, 112, 116, + 111, 114, 82, 101, 115, 112, 111, 110, 115, 101, 18, 119, 10, 30, 97, 108, 108, 95, 101, + 120, 116, 101, 110, 115, 105, 111, 110, 95, 110, 117, 109, 98, 101, 114, 115, 95, 114, + 101, 115, 112, 111, 110, 115, 101, 24, 5, 32, 1, 40, 11, 50, 48, 46, 103, 114, 112, 99, + 46, 114, 101, 102, 108, 101, 99, 116, 105, 111, 110, 46, 118, 49, 97, 108, 112, 104, 97, + 46, 69, 120, 116, 101, 110, 115, 105, 111, 110, 78, 117, 109, 98, 101, 114, 82, 101, 115, + 112, 111, 110, 115, 101, 72, 0, 82, 27, 97, 108, 108, 69, 120, 116, 101, 110, 115, 105, + 111, 110, 78, 117, 109, 98, 101, 114, 115, 82, 101, 115, 112, 111, 110, 115, 101, 18, 100, + 10, 22, 108, 105, 115, 116, 95, 115, 101, 114, 118, 105, 99, 101, 115, 95, 114, 101, 115, + 112, 111, 110, 115, 101, 24, 6, 32, 1, 40, 11, 50, 44, 46, 103, 114, 112, 99, 46, 114, + 101, 102, 108, 101, 99, 116, 105, 111, 110, 46, 118, 49, 97, 108, 112, 104, 97, 46, 76, + 105, 115, 116, 83, 101, 114, 118, 105, 99, 101, 82, 101, 115, 112, 111, 110, 115, 101, 72, + 0, 82, 20, 108, 105, 115, 116, 83, 101, 114, 118, 105, 99, 101, 115, 82, 101, 115, 112, + 111, 110, 115, 101, 18, 79, 10, 14, 101, 114, 114, 111, 114, 95, 114, 101, 115, 112, 111, + 110, 115, 101, 24, 7, 32, 1, 40, 11, 50, 38, 46, 103, 114, 112, 99, 46, 114, 101, 102, + 108, 101, 99, 116, 105, 111, 110, 46, 118, 49, 97, 108, 112, 104, 97, 46, 69, 114, 114, + 111, 114, 82, 101, 115, 112, 111, 110, 115, 101, 72, 0, 82, 13, 101, 114, 114, 111, 114, + 82, 101, 115, 112, 111, 110, 115, 101, 66, 18, 10, 16, 109, 101, 115, 115, 97, 103, 101, + 95, 114, 101, 115, 112, 111, 110, 115, 101>> + ) + end + + oneof(:message_response, 0) + field(:valid_host, 1, type: :string) + field(:original_request, 2, type: Grpc.Reflection.V1alpha.ServerReflectionRequest) + + field(:file_descriptor_response, 4, + type: Grpc.Reflection.V1alpha.FileDescriptorResponse, + oneof: 0 + ) + + field(:all_extension_numbers_response, 5, + type: Grpc.Reflection.V1alpha.ExtensionNumberResponse, + oneof: 0 + ) + + field(:list_services_response, 6, type: Grpc.Reflection.V1alpha.ListServiceResponse, oneof: 0) + field(:error_response, 7, type: Grpc.Reflection.V1alpha.ErrorResponse, oneof: 0) +end + +defmodule Grpc.Reflection.V1alpha.FileDescriptorResponse do + @moduledoc false + use Protobuf, syntax: :proto3 + + @type t :: %__MODULE__{ + file_descriptor_proto: [binary] + } + + defstruct [:file_descriptor_proto] + + def descriptor do + # credo:disable-for-next-line + Elixir.Google.Protobuf.DescriptorProto.decode( + <<10, 22, 70, 105, 108, 101, 68, 101, 115, 99, 114, 105, 112, 116, 111, 114, 82, 101, 115, + 112, 111, 110, 115, 101, 18, 50, 10, 21, 102, 105, 108, 101, 95, 100, 101, 115, 99, 114, + 105, 112, 116, 111, 114, 95, 112, 114, 111, 116, 111, 24, 1, 32, 3, 40, 12, 82, 19, 102, + 105, 108, 101, 68, 101, 115, 99, 114, 105, 112, 116, 111, 114, 80, 114, 111, 116, 111>> + ) + end + + field(:file_descriptor_proto, 1, repeated: true, type: :bytes) +end + +defmodule Grpc.Reflection.V1alpha.ExtensionNumberResponse do + @moduledoc false + use Protobuf, syntax: :proto3 + + @type t :: %__MODULE__{ + base_type_name: String.t(), + extension_number: [integer] + } + + defstruct [:base_type_name, :extension_number] + + def descriptor do + # credo:disable-for-next-line + Elixir.Google.Protobuf.DescriptorProto.decode( + <<10, 23, 69, 120, 116, 101, 110, 115, 105, 111, 110, 78, 117, 109, 98, 101, 114, 82, 101, + 115, 112, 111, 110, 115, 101, 18, 36, 10, 14, 98, 97, 115, 101, 95, 116, 121, 112, 101, + 95, 110, 97, 109, 101, 24, 1, 32, 1, 40, 9, 82, 12, 98, 97, 115, 101, 84, 121, 112, 101, + 78, 97, 109, 101, 18, 41, 10, 16, 101, 120, 116, 101, 110, 115, 105, 111, 110, 95, 110, + 117, 109, 98, 101, 114, 24, 2, 32, 3, 40, 5, 82, 15, 101, 120, 116, 101, 110, 115, 105, + 111, 110, 78, 117, 109, 98, 101, 114>> + ) + end + + field(:base_type_name, 1, type: :string) + field(:extension_number, 2, repeated: true, type: :int32) +end + +defmodule Grpc.Reflection.V1alpha.ListServiceResponse do + @moduledoc false + use Protobuf, syntax: :proto3 + + @type t :: %__MODULE__{ + service: [Grpc.Reflection.V1alpha.ServiceResponse.t()] + } + + defstruct [:service] + + def descriptor do + # credo:disable-for-next-line + Elixir.Google.Protobuf.DescriptorProto.decode( + <<10, 19, 76, 105, 115, 116, 83, 101, 114, 118, 105, 99, 101, 82, 101, 115, 112, 111, 110, + 115, 101, 18, 66, 10, 7, 115, 101, 114, 118, 105, 99, 101, 24, 1, 32, 3, 40, 11, 50, 40, + 46, 103, 114, 112, 99, 46, 114, 101, 102, 108, 101, 99, 116, 105, 111, 110, 46, 118, 49, + 97, 108, 112, 104, 97, 46, 83, 101, 114, 118, 105, 99, 101, 82, 101, 115, 112, 111, 110, + 115, 101, 82, 7, 115, 101, 114, 118, 105, 99, 101>> + ) + end + + field(:service, 1, repeated: true, type: Grpc.Reflection.V1alpha.ServiceResponse) +end + +defmodule Grpc.Reflection.V1alpha.ServiceResponse do + @moduledoc false + use Protobuf, syntax: :proto3 + + @type t :: %__MODULE__{ + name: String.t() + } + + defstruct [:name] + + def descriptor do + # credo:disable-for-next-line + Elixir.Google.Protobuf.DescriptorProto.decode( + <<10, 15, 83, 101, 114, 118, 105, 99, 101, 82, 101, 115, 112, 111, 110, 115, 101, 18, 18, + 10, 4, 110, 97, 109, 101, 24, 1, 32, 1, 40, 9, 82, 4, 110, 97, 109, 101>> + ) + end + + field(:name, 1, type: :string) +end + +defmodule Grpc.Reflection.V1alpha.ErrorResponse do + @moduledoc false + use Protobuf, syntax: :proto3 + + @type t :: %__MODULE__{ + error_code: integer, + error_message: String.t() + } + + defstruct [:error_code, :error_message] + + def descriptor do + # credo:disable-for-next-line + Elixir.Google.Protobuf.DescriptorProto.decode( + <<10, 13, 69, 114, 114, 111, 114, 82, 101, 115, 112, 111, 110, 115, 101, 18, 29, 10, 10, + 101, 114, 114, 111, 114, 95, 99, 111, 100, 101, 24, 1, 32, 1, 40, 5, 82, 9, 101, 114, 114, + 111, 114, 67, 111, 100, 101, 18, 35, 10, 13, 101, 114, 114, 111, 114, 95, 109, 101, 115, + 115, 97, 103, 101, 24, 2, 32, 1, 40, 9, 82, 12, 101, 114, 114, 111, 114, 77, 101, 115, + 115, 97, 103, 101>> + ) + end + + field(:error_code, 1, type: :int32) + field(:error_message, 2, type: :string) +end + +defmodule Grpc.Reflection.V1alpha.ServerReflection.Service do + @moduledoc false + use GRPC.Service, name: "grpc.reflection.v1alpha.ServerReflection" + + def descriptor do + # credo:disable-for-next-line + Elixir.Google.Protobuf.ServiceDescriptorProto.decode( + <<10, 16, 83, 101, 114, 118, 101, 114, 82, 101, 102, 108, 101, 99, 116, 105, 111, 110, 18, + 127, 10, 20, 83, 101, 114, 118, 101, 114, 82, 101, 102, 108, 101, 99, 116, 105, 111, 110, + 73, 110, 102, 111, 18, 48, 46, 103, 114, 112, 99, 46, 114, 101, 102, 108, 101, 99, 116, + 105, 111, 110, 46, 118, 49, 97, 108, 112, 104, 97, 46, 83, 101, 114, 118, 101, 114, 82, + 101, 102, 108, 101, 99, 116, 105, 111, 110, 82, 101, 113, 117, 101, 115, 116, 26, 49, 46, + 103, 114, 112, 99, 46, 114, 101, 102, 108, 101, 99, 116, 105, 111, 110, 46, 118, 49, 97, + 108, 112, 104, 97, 46, 83, 101, 114, 118, 101, 114, 82, 101, 102, 108, 101, 99, 116, 105, + 111, 110, 82, 101, 115, 112, 111, 110, 115, 101, 40, 1, 48, 1>> + ) + end + + rpc( + :ServerReflectionInfo, + stream(Grpc.Reflection.V1alpha.ServerReflectionRequest), + stream(Grpc.Reflection.V1alpha.ServerReflectionResponse) + ) +end + +defmodule Grpc.Reflection.V1alpha.ServerReflection.Stub do + @moduledoc false + use GRPC.Stub, service: Grpc.Reflection.V1alpha.ServerReflection.Service +end diff --git a/massa_proxy/apps/massa_proxy/lib/http/endpoint.ex b/massa_proxy/apps/massa_proxy/lib/infra/http/endpoint.ex similarity index 100% rename from massa_proxy/apps/massa_proxy/lib/http/endpoint.ex rename to massa_proxy/apps/massa_proxy/lib/infra/http/endpoint.ex diff --git a/massa_proxy/apps/massa_proxy/lib/http/http.ex b/massa_proxy/apps/massa_proxy/lib/infra/http/http.ex similarity index 78% rename from massa_proxy/apps/massa_proxy/lib/http/http.ex rename to massa_proxy/apps/massa_proxy/lib/infra/http/http.ex index cc3b281..49f40af 100644 --- a/massa_proxy/apps/massa_proxy/lib/http/http.ex +++ b/massa_proxy/apps/massa_proxy/lib/infra/http/http.ex @@ -1,3 +1,4 @@ defmodule Http.MetricsExporter do + @moduledoc false use Prometheus.PlugExporter end diff --git a/massa_proxy/apps/massa_proxy/lib/metrics/metrics.ex b/massa_proxy/apps/massa_proxy/lib/infra/metrics/metrics.ex similarity index 65% rename from massa_proxy/apps/massa_proxy/lib/metrics/metrics.ex rename to massa_proxy/apps/massa_proxy/lib/infra/metrics/metrics.ex index 6b39afb..58392c8 100644 --- a/massa_proxy/apps/massa_proxy/lib/metrics/metrics.ex +++ b/massa_proxy/apps/massa_proxy/lib/infra/metrics/metrics.ex @@ -1,6 +1,9 @@ defmodule Metrics.Setup do + @moduledoc false + def setup do Metrics.ProxyInstrumenter.setup() Http.MetricsExporter.setup() + GRPCPrometheus.ServerInterceptor.setup() end end diff --git a/massa_proxy/apps/massa_proxy/lib/metrics/proxy_instrumenter.ex b/massa_proxy/apps/massa_proxy/lib/infra/metrics/proxy_instrumenter.ex similarity index 84% rename from massa_proxy/apps/massa_proxy/lib/metrics/proxy_instrumenter.ex rename to massa_proxy/apps/massa_proxy/lib/infra/metrics/proxy_instrumenter.ex index 4a1f671..8cb42ae 100644 --- a/massa_proxy/apps/massa_proxy/lib/metrics/proxy_instrumenter.ex +++ b/massa_proxy/apps/massa_proxy/lib/infra/metrics/proxy_instrumenter.ex @@ -1,4 +1,5 @@ defmodule Metrics.ProxyInstrumenter do + @moduledoc false use Prometheus.Metric def setup() do diff --git a/massa_proxy/apps/massa_proxy/lib/massa_proxy.ex b/massa_proxy/apps/massa_proxy/lib/massa_proxy.ex index ffc5fe1..e279da9 100644 --- a/massa_proxy/apps/massa_proxy/lib/massa_proxy.ex +++ b/massa_proxy/apps/massa_proxy/lib/massa_proxy.ex @@ -1,7 +1,9 @@ defmodule MassaProxy do + @moduledoc false end defmodule MassaProxy.CloudstateEntity do + @moduledoc false defstruct node: Node.self(), entity_type: "", service_name: "", diff --git a/massa_proxy/apps/massa_proxy/lib/massa_proxy/application.ex b/massa_proxy/apps/massa_proxy/lib/massa_proxy/application.ex index 9959ef1..52870dd 100644 --- a/massa_proxy/apps/massa_proxy/lib/massa_proxy/application.ex +++ b/massa_proxy/apps/massa_proxy/lib/massa_proxy/application.ex @@ -1,10 +1,48 @@ defmodule MassaProxy.Application do + @moduledoc false use Application + alias Vapor.Provider.{Env, Dotenv} @impl true def start(_type, _args) do + load_system_env() + ExRay.Store.create() Metrics.Setup.setup() MassaProxy.Supervisor.start_link([]) end + + defp load_system_env() do + providers = [ + %Dotenv{}, + %Env{ + bindings: [ + {:proxy_port, "PROXY_PORT", default: 9000, map: &String.to_integer/1, required: false}, + {:proxy_http_port, "PROXY_HTTP_PORT", + default: 9001, map: &String.to_integer/1, required: false}, + {:user_function_host, "USER_FUNCTION_HOST", default: "0.0.0.0", required: false}, + {:user_function_port, "USER_FUNCTION_PORT", + default: 8080, map: &String.to_integer/1, required: false}, + {:user_function_uds_enable, "PROXY_UDS_MODE", default: false, required: false}, + {:user_function_sock_addr, "PROXY_UDS_ADDRESS", + default: "/var/run/cloudstate.sock", required: false}, + {:heartbeat_interval, "PROXY_HEARTBEAT_INTERVAL", default: 240_000, required: false} + ] + } + ] + + config = Vapor.load!(providers) + + set_vars(config) + end + + defp set_vars(config) do + Application.put_env(:massa_proxy, :proxy_port, config.proxy_port) + Application.put_env(:massa_proxy, :proxy_http_port, config.proxy_http_port) + Application.put_env(:massa_proxy, :user_function_host, config.user_function_host) + Application.put_env(:massa_proxy, :user_function_port, config.user_function_port) + Application.put_env(:massa_proxy, :user_function_uds_enable, config.user_function_uds_enable) + Application.put_env(:massa_proxy, :user_function_sock_addr, config.user_function_sock_addr) + Application.put_env(:massa_proxy, :heartbeat_interval, config.heartbeat_interval) + end end diff --git a/massa_proxy/apps/massa_proxy/lib/massa_proxy/horde_connector.ex b/massa_proxy/apps/massa_proxy/lib/massa_proxy/cluster/horde_connector.ex similarity index 93% rename from massa_proxy/apps/massa_proxy/lib/massa_proxy/horde_connector.ex rename to massa_proxy/apps/massa_proxy/lib/massa_proxy/cluster/horde_connector.ex index 03605de..a72558f 100644 --- a/massa_proxy/apps/massa_proxy/lib/massa_proxy/horde_connector.ex +++ b/massa_proxy/apps/massa_proxy/lib/massa_proxy/cluster/horde_connector.ex @@ -1,4 +1,4 @@ -defmodule MassaProxy.HordeConnector do +defmodule MassaProxy.Cluster.HordeConnector do @moduledoc false require Logger @@ -15,7 +15,7 @@ defmodule MassaProxy.HordeConnector do Horde.DynamicSupervisor.start_child( MassaProxy.Supervisor, - {MassaProxy.StateHandoff, []} + {MassaProxy.Cluster.StateHandoff, []} ) Horde.DynamicSupervisor.start_child( diff --git a/massa_proxy/apps/massa_proxy/lib/massa_proxy/node_listener.ex b/massa_proxy/apps/massa_proxy/lib/massa_proxy/cluster/node_listener.ex similarity index 94% rename from massa_proxy/apps/massa_proxy/lib/massa_proxy/node_listener.ex rename to massa_proxy/apps/massa_proxy/lib/massa_proxy/cluster/node_listener.ex index b84f3f3..3d79a42 100644 --- a/massa_proxy/apps/massa_proxy/lib/massa_proxy/node_listener.ex +++ b/massa_proxy/apps/massa_proxy/lib/massa_proxy/cluster/node_listener.ex @@ -1,4 +1,5 @@ -defmodule MassaProxy.NodeListener do +defmodule MassaProxy.Cluster.NodeListener do + @moduledoc false use GenServer require Logger diff --git a/massa_proxy/apps/massa_proxy/lib/massa_proxy/state_handoff.ex b/massa_proxy/apps/massa_proxy/lib/massa_proxy/cluster/state_handoff.ex similarity index 96% rename from massa_proxy/apps/massa_proxy/lib/massa_proxy/state_handoff.ex rename to massa_proxy/apps/massa_proxy/lib/massa_proxy/cluster/state_handoff.ex index f08bf9b..29de838 100644 --- a/massa_proxy/apps/massa_proxy/lib/massa_proxy/state_handoff.ex +++ b/massa_proxy/apps/massa_proxy/lib/massa_proxy/cluster/state_handoff.ex @@ -1,4 +1,5 @@ -defmodule MassaProxy.StateHandoff do +defmodule MassaProxy.Cluster.StateHandoff do + @moduledoc false use GenServer require Logger @@ -46,7 +47,7 @@ defmodule MassaProxy.StateHandoff do # other_node is actuall a tuple { __MODULE__, other_node } passed from above, # by using that in GenServer.call we are sending a message to the process # named __MODULE__ on other_node - def handle_call({:set_neighbours, other_node}, from, this_crdt_pid) do + def handle_call({:set_neighbours, other_node}, _from, this_crdt_pid) do Logger.warn( "Sending :set_neighbours to #{inspect(other_node)} with #{inspect(this_crdt_pid)}" ) diff --git a/massa_proxy/apps/massa_proxy/lib/massa_proxy/entity_registry.ex b/massa_proxy/apps/massa_proxy/lib/massa_proxy/entity_registry.ex index 52a509b..a85c7ad 100644 --- a/massa_proxy/apps/massa_proxy/lib/massa_proxy/entity_registry.ex +++ b/massa_proxy/apps/massa_proxy/lib/massa_proxy/entity_registry.ex @@ -21,7 +21,7 @@ defmodule MassaProxy.EntityRegistry do def init(service) do Logger.info("[MassaProxy on #{inspect(Node.self())}][EntityRegistry]: Initializing...") Process.flag(:trap_exit, true) - entities = MassaProxy.StateHandoff.pickup(service) + entities = MassaProxy.Cluster.StateHandoff.pickup(service) {:ok, {service, entities}} end @@ -44,7 +44,7 @@ defmodule MassaProxy.EntityRegistry do end def terminate(reason, {service, entities}) do - MassaProxy.StateHandoff.handoff(service, entities) + MassaProxy.Cluster.StateHandoff.handoff(service, entities) :ok end diff --git a/massa_proxy/apps/massa_proxy/lib/massa_proxy/reflection/generator.ex b/massa_proxy/apps/massa_proxy/lib/massa_proxy/reflection/generator.ex new file mode 100644 index 0000000..20143e7 --- /dev/null +++ b/massa_proxy/apps/massa_proxy/lib/massa_proxy/reflection/generator.ex @@ -0,0 +1,71 @@ +defmodule MassaProxy.Generator do + @moduledoc false + require Logger + alias Protobuf.Protoc.Generator.Message, as: MessageGenerator + alias Protobuf.Protoc.Generator.Enum, as: EnumGenerator + alias Protobuf.Protoc.Generator.Service, as: ServiceGenerator + alias Protobuf.Protoc.Generator.Extension, as: ExtensionGenerator + + @locals_without_parens [field: 2, field: 3, oneof: 2, rpc: 3, extend: 4, extensions: 1] + + def generate_content(ctx, desc) do + ctx = %{ + ctx + | package: desc.package || "", + syntax: syntax(desc.syntax), + dep_type_mapping: get_dep_type_mapping(ctx, desc.dependency, desc.name) + } + + ctx = Map.put(ctx, :custom_file_options, %{}) + ctx = Map.put(ctx, :module_prefix, ctx.package || "") + + {enums, msgs} = MessageGenerator.generate_list(ctx, desc.message_type) + + list = + EnumGenerator.generate_list(ctx, desc.enum_type) ++ + enums ++ msgs ++ ServiceGenerator.generate_list(ctx, desc.service) + + # Logger.debug("List of Services #{inspect(list)}") + + list + |> List.flatten() + |> Enum.join("\n") + |> format_code() + end + + @doc false + def get_dep_pkgs(%{pkg_mapping: mapping, package: pkg}, deps) do + pkgs = deps |> Enum.map(fn dep -> mapping[dep] end) + pkgs = if pkg && String.length(pkg) > 0, do: [pkg | pkgs], else: pkgs + Enum.sort(pkgs, &(byte_size(&2) <= byte_size(&1))) + end + + def get_dep_type_mapping(%{global_type_mapping: global_mapping}, deps, file_name) do + mapping = + Enum.reduce(deps, %{}, fn dep, acc -> + Map.merge(acc, global_mapping[dep]) + end) + + Map.merge(mapping, global_mapping[file_name]) + end + + defp syntax("proto3"), do: :proto3 + defp syntax(_), do: :proto2 + + def format_code(code) do + formated = + if Code.ensure_loaded?(Code) && function_exported?(Code, :format_string!, 2) do + code + |> Code.format_string!(locals_without_parens: @locals_without_parens) + |> IO.iodata_to_binary() + else + code + end + + if formated == "" do + formated + else + formated <> "\n" + end + end +end diff --git a/massa_proxy/apps/massa_proxy/lib/massa_proxy/reflection/reflection.ex b/massa_proxy/apps/massa_proxy/lib/massa_proxy/reflection/reflection.ex new file mode 100644 index 0000000..fb0a69e --- /dev/null +++ b/massa_proxy/apps/massa_proxy/lib/massa_proxy/reflection/reflection.ex @@ -0,0 +1,82 @@ +defmodule MassaProxy.Reflection do + require Logger + + def compile(file_descriptor) do + ctx = + %Protobuf.Protoc.Context{gen_descriptors?: true, plugins: ["grpc"]} + |> find_types(file_descriptor) + + files = + file_descriptor + |> Enum.map(fn desc -> MassaProxy.Generator.generate_content(ctx, desc) end) + end + + @doc false + def find_types(ctx, descs) do + find_types(ctx, descs, %{}) + end + + @doc false + def find_types(ctx, [], acc) do + %{ctx | global_type_mapping: acc} + end + + def find_types(ctx, [desc | t], acc) do + types = find_types_in_proto(desc) + find_types(ctx, t, Map.put(acc, desc.name, types)) + end + + @doc false + def find_types_in_proto(%Google.Protobuf.FileDescriptorProto{} = desc) do + desc_ctx = %Protobuf.Protoc.Context{ + package: desc.package, + namespace: [] + } + + ctx = %{desc_ctx | module_prefix: desc_ctx.package || ""} + + %{} + |> find_types_in_proto(ctx, desc.message_type) + |> find_types_in_proto(ctx, desc.enum_type) + end + + defp find_types_in_proto(types, ctx, descs) when is_list(descs) do + Enum.reduce(descs, types, fn desc, acc -> + find_types_in_proto(acc, ctx, desc) + end) + end + + defp find_types_in_proto(types, ctx, %Google.Protobuf.DescriptorProto{name: name} = desc) do + new_ctx = append_ns(ctx, name) + + types + |> update_types(ctx, name) + |> find_types_in_proto(new_ctx, desc.enum_type) + |> find_types_in_proto(new_ctx, desc.nested_type) + end + + defp find_types_in_proto(types, ctx, %Google.Protobuf.EnumDescriptorProto{name: name}) do + update_types(types, ctx, name) + end + + defp append_ns(%{namespace: ns} = ctx, name) do + new_ns = ns ++ [name] + Map.put(ctx, :namespace, new_ns) + end + + defp update_types(types, %{namespace: ns, package: pkg, module_prefix: prefix}, name) do + type_name = + join_names(prefix || pkg, ns, name) + |> Protobuf.Protoc.Generator.Util.normalize_type_name() + + Map.put(types, "." <> join_names(pkg, ns, name), %{type_name: type_name}) + end + + defp join_names(pkg, ns, name) do + ns_str = Protobuf.Protoc.Generator.Util.join_name(ns) + + [pkg, ns_str, name] + |> Enum.filter(&(&1 && &1 != "")) + |> Enum.join(".") + end +end diff --git a/massa_proxy/apps/massa_proxy/lib/massa_proxy/server/dispatcher.ex b/massa_proxy/apps/massa_proxy/lib/massa_proxy/server/dispatcher.ex new file mode 100644 index 0000000..2314605 --- /dev/null +++ b/massa_proxy/apps/massa_proxy/lib/massa_proxy/server/dispatcher.ex @@ -0,0 +1,18 @@ +defmodule MassaProxy.Server.Dispatcher do + require Logger + + def dispatch(%{ + entity_type: entity_type, + persistence_id: persistence_id, + message: message, + stream: stream + }) do + Logger.info( + "Handle request for entity type #{inspect(entity_type)}. Message: #{inspect(message)}" + ) + + Logger.debug( + "Handle stream: #{inspect(stream)}. With persistence id: #{inspect(persistence_id)}" + ) + end +end diff --git a/massa_proxy/apps/massa_proxy/lib/massa_proxy/server/grpc_server.ex b/massa_proxy/apps/massa_proxy/lib/massa_proxy/server/grpc_server.ex new file mode 100644 index 0000000..f1bdbe1 --- /dev/null +++ b/massa_proxy/apps/massa_proxy/lib/massa_proxy/server/grpc_server.ex @@ -0,0 +1,112 @@ +defmodule MassaProxy.Server.GrpcServer do + @moduledoc false + require Logger + + @grpc_template_path Path.expand("./templates/grpc_service.ex.eex", :code.priv_dir(:massa_proxy)) + @grpc_endpoint_template_path Path.expand( + "./templates/grpc_endpoint.ex.eex", + :code.priv_dir(:massa_proxy) + ) + + def start(descriptors, entities), do: start_grpc(descriptors, entities) + + defp start_grpc(descriptors, entities) do + {uSecs, result} = + :timer.tc(fn -> + with {:ok, descriptors} <- descriptors |> compile, + {:ok, _} <- generate_services(entities), + {:ok, _} <- generate_endpoints(entities) do + start_proxy([]) + else + _ -> Logger.error("Error during gRPC Server initialization") + end + + :ok + end) + + Logger.info("Started gRPC Server in #{uSecs/1_000_000}ms") + end + + defp compile(descriptors) do + files = + descriptors + |> MassaProxy.Reflection.compile() + + for file <- files do + result = Code.eval_string(file) + Logger.debug("Compiled module: #{inspect(result)}") + end + + {:ok, descriptors} + end + + defp generate_services(entities) do + for entity <- entities do + name = Enum.join([normalize_service_name(entity.service_name), "Service"], ".") + services = Enum.at(entity.services, 0) |> Enum.at(0) + + methods = + services.methods + |> Flow.from_enumerable() + |> Flow.map(&normalize_mehod_name(&1.name)) + |> Enum.to_list() + + Logger.info("Generating Service #{name} with Methods: #{inspect(methods)}") + + mod = + EEx.eval_file( + @grpc_template_path, + mod_name: name, + name: name, + methods: methods, + handler: "MassaProxy.Server.Dispatcher", + entity_type: entity.entity_type, + persistence_id: entity.persistence_id + ) + + Logger.debug("Service defined: #{mod}") + mod_compiled = Code.eval_string(mod) + Logger.debug("Service compiled: #{inspect(mod_compiled)}") + end + + {:ok, entities} + end + + defp generate_endpoints(entities) do + services = + entities + |> Flow.from_enumerable() + |> Flow.map( + &Enum.join([normalize_service_name(&1.service_name), "Service.ProxyService"], ".") + ) + |> Enum.to_list() + + mod = + EEx.eval_file( + @grpc_endpoint_template_path, + service_names: services + ) + + Logger.debug("Endpoint defined: #{mod}") + mod_compiled = Code.eval_string(mod) + Logger.debug("Endpoint compiled: #{inspect(mod_compiled)}") + + {:ok, entities} + end + + defp start_proxy(args) do + Logger.info("Starting gRPC Server...") + Application.put_env(:grpc, :start_server, true, persistent: true) + spec = {GRPC.Server.Supervisor, {Massa.Server.Grpc.ProxyEndpoint, 9980}} + DynamicSupervisor.start_child(MassaProxy.LocalSupervisor, spec) + end + + defp normalize_service_name(name) do + name + |> String.split(".") + |> Enum.map(&Macro.camelize(&1)) + |> Enum.join(".") + end + + defp normalize_mehod_name(name), do: Macro.underscore(name) +end diff --git a/massa_proxy/apps/massa_proxy/lib/massa_proxy/supervisor.ex b/massa_proxy/apps/massa_proxy/lib/massa_proxy/supervisor.ex index 431724e..1e2e472 100644 --- a/massa_proxy/apps/massa_proxy/lib/massa_proxy/supervisor.ex +++ b/massa_proxy/apps/massa_proxy/lib/massa_proxy/supervisor.ex @@ -8,38 +8,33 @@ defmodule MassaProxy.Supervisor do end def init(_args) do - _port = get_http_port() - children = [ - Plug.Cowboy.child_spec( - scheme: :http, - plug: Http.Endpoint, - options: [port: _port] - ), + http_server(), cluster_supervisor(), + {DynamicSupervisor, [name: MassaProxy.LocalSupervisor, strategy: :one_for_one]}, {Horde.Registry, [name: MassaProxy.GlobalRegistry, keys: :unique]}, {Horde.DynamicSupervisor, [name: MassaProxy.GlobalSupervisor, strategy: :one_for_one]}, %{ - id: MassaProxy.HordeConnector, + id: MassaProxy.Cluster.HordeConnector, restart: :transient, start: { Task, :start_link, [ fn -> - MassaProxy.HordeConnector.connect() - MassaProxy.HordeConnector.start_children() + MassaProxy.Cluster.HordeConnector.connect() + MassaProxy.Cluster.HordeConnector.start_children() Node.list() |> Enum.each(fn node -> - :ok = MassaProxy.StateHandoff.join(node) + :ok = MassaProxy.Cluster.StateHandoff.join(node) end) end ] } }, - MassaProxy.NodeListener + MassaProxy.Cluster.NodeListener ] |> Enum.reject(&is_nil/1) @@ -50,10 +45,21 @@ defmodule MassaProxy.Supervisor do defp cluster_supervisor() do topologies = Application.get_env(:libcluster, :topologies) - if topologies && Code.ensure_compiled?(Cluster.Supervisor) do + if topologies && Code.ensure_compiled(Cluster.Supervisor) do {Cluster.Supervisor, [topologies, [name: MassaProxy.ClusterSupervisor]]} end end defp get_http_port(), do: Application.get_env(:massa_proxy, :proxy_http_port, 9001) + + defp http_server() do + port = get_http_port() + Logger.info("Starting HTTP Server on port #{port}") + + Plug.Cowboy.child_spec( + scheme: :http, + plug: Http.Endpoint, + options: [port: get_http_port()] + ) + end end diff --git a/massa_proxy/apps/massa_proxy/lib/discovery/manager.ex b/massa_proxy/apps/massa_proxy/lib/protocol/discovery/manager.ex similarity index 87% rename from massa_proxy/apps/massa_proxy/lib/discovery/manager.ex rename to massa_proxy/apps/massa_proxy/lib/protocol/discovery/manager.ex index 2a0d110..fdea01a 100644 --- a/massa_proxy/apps/massa_proxy/lib/discovery/manager.ex +++ b/massa_proxy/apps/massa_proxy/lib/protocol/discovery/manager.ex @@ -1,11 +1,12 @@ defmodule Discovery.Manager do - use ExRay, pre: :before_fun, post: :after_fun + @moduledoc false require Logger + use ExRay, pre: :before_fun, post: :after_fun alias ExRay.Span alias MassaProxy.CloudstateEntity + alias MassaProxy.Server.GrpcServer alias Google.Protobuf.FileDescriptorSet - alias Google.Protobuf.FieldDescriptorProto @protocol_minor_version 1 @protocol_major_version 0 @@ -15,6 +16,16 @@ defmodule Discovery.Manager do # Generates a request id @req_id :os.system_time(:milli_seconds) |> Integer.to_string() |> IO.inspect() + @trace kind: :critical + def report_error(channel, error) do + {_, response} = + channel + |> Cloudstate.EntityDiscovery.Stub.report_error(error) + + Logger.info("User function report error reply #{inspect(response)}") + response + end + @trace kind: :normal def discover(channel) do message = @@ -26,19 +37,11 @@ defmodule Discovery.Manager do supported_entity_types: @supported_entity_types ) - channel - |> Cloudstate.EntityDiscovery.Stub.discover(message) - |> handle_response - end - - @trace kind: :critical - def report_error(channel, error) do - {_, response} = - channel - |> Cloudstate.EntityDiscovery.Stub.report_error(error) - - Logger.info("User function report error reply #{inspect(response)}") - response + with {:ok, file_descriptors, user_entities} <- + channel + |> Cloudstate.EntityDiscovery.Stub.discover(message) + |> handle_response, + do: GrpcServer.start(file_descriptors, user_entities) end defp handle_response(response) do @@ -48,11 +51,9 @@ defmodule Discovery.Manager do end defp register_entities({:ok, message}) do - # TODO: Registry entities here entities = message.entities descriptor = FileDescriptorSet.decode(message.proto) file_descriptors = descriptor.file - # Logger.debug("file_descriptors -> #{inspect(file_descriptors)}") user_entities = entities @@ -61,13 +62,11 @@ defmodule Discovery.Manager do |> Flow.map(®ister_entity/1) |> Enum.to_list() - Logger.debug("Cloudstate Entities: #{inspect(user_entities)}.") - user_entities + Logger.debug("Found #{Enum.count(user_entities)} Entities to processing.") + {:ok, file_descriptors, user_entities} end defp register_entity(entity) do - Logger.debug("Registering Entity: #{inspect(entity)} ") - case entity.entity_type do "cloudstate.eventsourced.EventSourced" -> MassaProxy.EntityRegistry.register("EventSourced", [entity]) @@ -93,7 +92,7 @@ defmodule Discovery.Manager do |> Flow.map(&extract_services/1) |> Enum.to_list() - entity = %CloudstateEntity{ + %CloudstateEntity{ node: Node.self(), entity_type: entity.entity_type, service_name: entity.service_name, @@ -104,8 +103,6 @@ defmodule Discovery.Manager do end defp extract_messages(file) do - # Logger.info("Message -> #{inspect(file.message_type)}") - file.message_type |> Flow.from_enumerable() |> Flow.map(&to_message_item/1) @@ -121,28 +118,27 @@ defmodule Discovery.Manager do end defp to_message_item(message) do - _attributes = + attributes = message.field |> Flow.from_enumerable() |> Flow.map(&extract_method_attributes/1) |> Enum.to_list() - %{name: message.name, attributes: _attributes} + %{name: message.name, attributes: attributes} end defp to_service_item(service) do - _methods = + methods = service.method |> Flow.from_enumerable() |> Flow.map(&extract_service_method/1) |> Enum.to_list() - %{name: service.name, methods: _methods} + %{name: service.name, methods: methods} end defp extract_method_attributes(field) do - # Logger.info("Options -> #{inspect(field.options)}") - _field = + type_options = if field.options != nil && field.options.ctype != nil do field.options.ctype end @@ -152,7 +148,7 @@ defmodule Discovery.Manager do number: field.number, type: field.type, label: field.label, - options: %{type: _field} + options: %{type: type_options} } end @@ -214,10 +210,8 @@ defmodule Discovery.Manager do end defp extract_message(response) do - {:ok, message} = response - case response do - ok -> + {:ok, message} -> Logger.info("Received EntitySpec from user function with info: #{inspect(message)}") message diff --git a/massa_proxy/apps/massa_proxy/lib/discovery/worker.ex b/massa_proxy/apps/massa_proxy/lib/protocol/discovery/worker.ex similarity index 99% rename from massa_proxy/apps/massa_proxy/lib/discovery/worker.ex rename to massa_proxy/apps/massa_proxy/lib/protocol/discovery/worker.ex index 9b67ef7..bc3468a 100644 --- a/massa_proxy/apps/massa_proxy/lib/discovery/worker.ex +++ b/massa_proxy/apps/massa_proxy/lib/protocol/discovery/worker.ex @@ -1,4 +1,5 @@ defmodule Discovery.Worker do + @moduledoc false use GenServer require Logger diff --git a/massa_proxy/apps/massa_proxy/lib/eventsourced/router.ex b/massa_proxy/apps/massa_proxy/lib/protocol/eventsourced/router.ex similarity index 100% rename from massa_proxy/apps/massa_proxy/lib/eventsourced/router.ex rename to massa_proxy/apps/massa_proxy/lib/protocol/eventsourced/router.ex diff --git a/massa_proxy/apps/massa_proxy/mix.exs b/massa_proxy/apps/massa_proxy/mix.exs index 8619289..cd75ba9 100644 --- a/massa_proxy/apps/massa_proxy/mix.exs +++ b/massa_proxy/apps/massa_proxy/mix.exs @@ -37,13 +37,15 @@ defmodule MassaProxy.MixProject do [ # Base deps {:flow, "~> 1.0"}, + {:vapor, "~> 0.10.0"}, {:google_protos, "~> 0.1.0"}, # Grpc deps - {:grpc, github: "elixir-grpc/grpc"}, + {:grpc, github: "elixir-grpc/grpc", override: true}, # {:gun, "~> 2.0", hex: :grpc_gun, override: true}, # 2.9.0 fixes some important bugs, so it's better to use ~> 2.9.0 {:cowlib, "~> 2.9.0", override: true}, + {:grpc_prometheus, "~> 0.1.0"}, # Cluster deps # Node discovery for Kubernetes @@ -61,7 +63,10 @@ defmodule MassaProxy.MixProject do # Http facilities {:plug_cowboy, "~> 2.3"}, - {:poison, "~> 4.0"} + {:poison, "~> 4.0"}, + + # Best practices + {:credo, "~> 1.5", only: [:dev, :test], runtime: false} ] end end diff --git a/massa_proxy/apps/massa_proxy/priv/protos/frontend/google/api/auth.proto b/massa_proxy/apps/massa_proxy/priv/protos/frontend/google/api/auth.proto new file mode 100644 index 0000000..1a1cf26 --- /dev/null +++ b/massa_proxy/apps/massa_proxy/priv/protos/frontend/google/api/auth.proto @@ -0,0 +1,228 @@ +// Copyright 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +syntax = "proto3"; + +package google.api; + +option go_package = "google.golang.org/genproto/googleapis/api/serviceconfig;serviceconfig"; +option java_multiple_files = true; +option java_outer_classname = "AuthProto"; +option java_package = "com.google.api"; +option objc_class_prefix = "GAPI"; + +// `Authentication` defines the authentication configuration for an API. +// +// Example for an API targeted for external use: +// +// name: calendar.googleapis.com +// authentication: +// providers: +// - id: google_calendar_auth +// jwks_uri: https://www.googleapis.com/oauth2/v1/certs +// issuer: https://securetoken.google.com +// rules: +// - selector: "*" +// requirements: +// provider_id: google_calendar_auth +message Authentication { + // A list of authentication rules that apply to individual API methods. + // + // **NOTE:** All service configuration rules follow "last one wins" order. + repeated AuthenticationRule rules = 3; + + // Defines a set of authentication providers that a service supports. + repeated AuthProvider providers = 4; +} + +// Authentication rules for the service. +// +// By default, if a method has any authentication requirements, every request +// must include a valid credential matching one of the requirements. +// It's an error to include more than one kind of credential in a single +// request. +// +// If a method doesn't have any auth requirements, request credentials will be +// ignored. +message AuthenticationRule { + // Selects the methods to which this rule applies. + // + // Refer to [selector][google.api.DocumentationRule.selector] for syntax details. + string selector = 1; + + // The requirements for OAuth credentials. + OAuthRequirements oauth = 2; + + // If true, the service accepts API keys without any other credential. + // This flag only applies to HTTP and gRPC requests. + bool allow_without_credential = 5; + + // Requirements for additional authentication providers. + repeated AuthRequirement requirements = 7; +} + +// Specifies a location to extract JWT from an API request. +message JwtLocation { + oneof in { + // Specifies HTTP header name to extract JWT token. + string header = 1; + + // Specifies URL query parameter name to extract JWT token. + string query = 2; + } + + // The value prefix. The value format is "value_prefix{token}" + // Only applies to "in" header type. Must be empty for "in" query type. + // If not empty, the header value has to match (case sensitive) this prefix. + // If not matched, JWT will not be extracted. If matched, JWT will be + // extracted after the prefix is removed. + // + // For example, for "Authorization: Bearer {JWT}", + // value_prefix="Bearer " with a space at the end. + string value_prefix = 3; +} + +// Configuration for an authentication provider, including support for +// [JSON Web Token +// (JWT)](https://tools.ietf.org/html/draft-ietf-oauth-json-web-token-32). +message AuthProvider { + // The unique identifier of the auth provider. It will be referred to by + // `AuthRequirement.provider_id`. + // + // Example: "bookstore_auth". + string id = 1; + + // Identifies the principal that issued the JWT. See + // https://tools.ietf.org/html/draft-ietf-oauth-json-web-token-32#section-4.1.1 + // Usually a URL or an email address. + // + // Example: https://securetoken.google.com + // Example: 1234567-compute@developer.gserviceaccount.com + string issuer = 2; + + // URL of the provider's public key set to validate signature of the JWT. See + // [OpenID + // Discovery](https://openid.net/specs/openid-connect-discovery-1_0.html#ProviderMetadata). + // Optional if the key set document: + // - can be retrieved from + // [OpenID + // Discovery](https://openid.net/specs/openid-connect-discovery-1_0.html) + // of the issuer. + // - can be inferred from the email domain of the issuer (e.g. a Google + // service account). + // + // Example: https://www.googleapis.com/oauth2/v1/certs + string jwks_uri = 3; + + // The list of JWT + // [audiences](https://tools.ietf.org/html/draft-ietf-oauth-json-web-token-32#section-4.1.3). + // that are allowed to access. A JWT containing any of these audiences will + // be accepted. When this setting is absent, JWTs with audiences: + // - "https://[service.name]/[google.protobuf.Api.name]" + // - "https://[service.name]/" + // will be accepted. + // For example, if no audiences are in the setting, LibraryService API will + // accept JWTs with the following audiences: + // - + // https://library-example.googleapis.com/google.example.library.v1.LibraryService + // - https://library-example.googleapis.com/ + // + // Example: + // + // audiences: bookstore_android.apps.googleusercontent.com, + // bookstore_web.apps.googleusercontent.com + string audiences = 4; + + // Redirect URL if JWT token is required but not present or is expired. + // Implement authorizationUrl of securityDefinitions in OpenAPI spec. + string authorization_url = 5; + + // Defines the locations to extract the JWT. + // + // JWT locations can be either from HTTP headers or URL query parameters. + // The rule is that the first match wins. The checking order is: checking + // all headers first, then URL query parameters. + // + // If not specified, default to use following 3 locations: + // 1) Authorization: Bearer + // 2) x-goog-iap-jwt-assertion + // 3) access_token query parameter + // + // Default locations can be specified as followings: + // jwt_locations: + // - header: Authorization + // value_prefix: "Bearer " + // - header: x-goog-iap-jwt-assertion + // - query: access_token + repeated JwtLocation jwt_locations = 6; +} + +// OAuth scopes are a way to define data and permissions on data. For example, +// there are scopes defined for "Read-only access to Google Calendar" and +// "Access to Cloud Platform". Users can consent to a scope for an application, +// giving it permission to access that data on their behalf. +// +// OAuth scope specifications should be fairly coarse grained; a user will need +// to see and understand the text description of what your scope means. +// +// In most cases: use one or at most two OAuth scopes for an entire family of +// products. If your product has multiple APIs, you should probably be sharing +// the OAuth scope across all of those APIs. +// +// When you need finer grained OAuth consent screens: talk with your product +// management about how developers will use them in practice. +// +// Please note that even though each of the canonical scopes is enough for a +// request to be accepted and passed to the backend, a request can still fail +// due to the backend requiring additional scopes or permissions. +message OAuthRequirements { + // The list of publicly documented OAuth scopes that are allowed access. An + // OAuth token containing any of these scopes will be accepted. + // + // Example: + // + // canonical_scopes: https://www.googleapis.com/auth/calendar, + // https://www.googleapis.com/auth/calendar.read + string canonical_scopes = 1; +} + +// User-defined authentication requirements, including support for +// [JSON Web Token +// (JWT)](https://tools.ietf.org/html/draft-ietf-oauth-json-web-token-32). +message AuthRequirement { + // [id][google.api.AuthProvider.id] from authentication provider. + // + // Example: + // + // provider_id: bookstore_auth + string provider_id = 1; + + // NOTE: This will be deprecated soon, once AuthProvider.audiences is + // implemented and accepted in all the runtime components. + // + // The list of JWT + // [audiences](https://tools.ietf.org/html/draft-ietf-oauth-json-web-token-32#section-4.1.3). + // that are allowed to access. A JWT containing any of these audiences will + // be accepted. When this setting is absent, only JWTs with audience + // "https://[Service_name][google.api.Service.name]/[API_name][google.protobuf.Api.name]" + // will be accepted. For example, if no audiences are in the setting, + // LibraryService API will only accept JWTs with the following audience + // "https://library-example.googleapis.com/google.example.library.v1.LibraryService". + // + // Example: + // + // audiences: bookstore_android.apps.googleusercontent.com, + // bookstore_web.apps.googleusercontent.com + string audiences = 2; +} \ No newline at end of file diff --git a/massa_proxy/apps/massa_proxy/priv/protos/frontend/google/api/httpbody.proto b/massa_proxy/apps/massa_proxy/priv/protos/frontend/google/api/httpbody.proto new file mode 100644 index 0000000..bc5ea44 --- /dev/null +++ b/massa_proxy/apps/massa_proxy/priv/protos/frontend/google/api/httpbody.proto @@ -0,0 +1,77 @@ +// Copyright 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +syntax = "proto3"; + +package google.api; + +import "google/protobuf/any.proto"; + +option cc_enable_arenas = true; +option go_package = "google.golang.org/genproto/googleapis/api/httpbody;httpbody"; +option java_multiple_files = true; +option java_outer_classname = "HttpBodyProto"; +option java_package = "com.google.api"; +option objc_class_prefix = "GAPI"; + +// Message that represents an arbitrary HTTP body. It should only be used for +// payload formats that can't be represented as JSON, such as raw binary or +// an HTML page. +// +// +// This message can be used both in streaming and non-streaming API methods in +// the request as well as the response. +// +// It can be used as a top-level request field, which is convenient if one +// wants to extract parameters from either the URL or HTTP template into the +// request fields and also want access to the raw HTTP body. +// +// Example: +// +// message GetResourceRequest { +// // A unique request id. +// string request_id = 1; +// +// // The raw HTTP body is bound to this field. +// google.api.HttpBody http_body = 2; +// } +// +// service ResourceService { +// rpc GetResource(GetResourceRequest) returns (google.api.HttpBody); +// rpc UpdateResource(google.api.HttpBody) returns +// (google.protobuf.Empty); +// } +// +// Example with streaming methods: +// +// service CaldavService { +// rpc GetCalendar(stream google.api.HttpBody) +// returns (stream google.api.HttpBody); +// rpc UpdateCalendar(stream google.api.HttpBody) +// returns (stream google.api.HttpBody); +// } +// +// Use of this type only changes how the request and response bodies are +// handled, all other features will continue to work unchanged. +message HttpBody { + // The HTTP Content-Type header value specifying the content type of the body. + string content_type = 1; + + // The HTTP request/response body as raw binary. + bytes data = 2; + + // Application specific response metadata. Must be set in the first response + // for streaming APIs. + repeated google.protobuf.Any extensions = 3; +} \ No newline at end of file diff --git a/massa_proxy/apps/massa_proxy/priv/protos/frontend/google/api/source_info.proto b/massa_proxy/apps/massa_proxy/priv/protos/frontend/google/api/source_info.proto new file mode 100644 index 0000000..e49658b --- /dev/null +++ b/massa_proxy/apps/massa_proxy/priv/protos/frontend/google/api/source_info.proto @@ -0,0 +1,31 @@ +// Copyright 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +syntax = "proto3"; + +package google.api; + +import "google/protobuf/any.proto"; + +option go_package = "google.golang.org/genproto/googleapis/api/serviceconfig;serviceconfig"; +option java_multiple_files = true; +option java_outer_classname = "SourceInfoProto"; +option java_package = "com.google.api"; +option objc_class_prefix = "GAPI"; + +// Source information used to create a Service Config +message SourceInfo { + // All files used during config generation. + repeated google.protobuf.Any source_files = 1; +} \ No newline at end of file diff --git a/massa_proxy/apps/massa_proxy/priv/protos/proxy/grpc/reflection/v1alpha/reflection.proto b/massa_proxy/apps/massa_proxy/priv/protos/proxy/grpc/reflection/v1alpha/reflection.proto index 1becba2..462e85a 100644 --- a/massa_proxy/apps/massa_proxy/priv/protos/proxy/grpc/reflection/v1alpha/reflection.proto +++ b/massa_proxy/apps/massa_proxy/priv/protos/proxy/grpc/reflection/v1alpha/reflection.proto @@ -1 +1,136 @@ -404: Not Found \ No newline at end of file +// Copyright 2016 gRPC authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Service exported by server reflection + +syntax = "proto3"; + +package grpc.reflection.v1alpha; + +service ServerReflection { + // The reflection service is structured as a bidirectional stream, ensuring + // all related requests go to a single server. + rpc ServerReflectionInfo(stream ServerReflectionRequest) + returns (stream ServerReflectionResponse); +} + +// The message sent by the client when calling ServerReflectionInfo method. +message ServerReflectionRequest { + string host = 1; + // To use reflection service, the client should set one of the following + // fields in message_request. The server distinguishes requests by their + // defined field and then handles them using corresponding methods. + oneof message_request { + // Find a proto file by the file name. + string file_by_filename = 3; + + // Find the proto file that declares the given fully-qualified symbol name. + // This field should be a fully-qualified symbol name + // (e.g. .[.] or .). + string file_containing_symbol = 4; + + // Find the proto file which defines an extension extending the given + // message type with the given field number. + ExtensionRequest file_containing_extension = 5; + + // Finds the tag numbers used by all known extensions of the given message + // type, and appends them to ExtensionNumberResponse in an undefined order. + // Its corresponding method is best-effort: it's not guaranteed that the + // reflection service will implement this method, and it's not guaranteed + // that this method will provide all extensions. Returns + // StatusCode::UNIMPLEMENTED if it's not implemented. + // This field should be a fully-qualified type name. The format is + // . + string all_extension_numbers_of_type = 6; + + // List the full names of registered services. The content will not be + // checked. + string list_services = 7; + } +} + +// The type name and extension number sent by the client when requesting +// file_containing_extension. +message ExtensionRequest { + // Fully-qualified type name. The format should be . + string containing_type = 1; + int32 extension_number = 2; +} + +// The message sent by the server to answer ServerReflectionInfo method. +message ServerReflectionResponse { + string valid_host = 1; + ServerReflectionRequest original_request = 2; + // The server set one of the following fields accroding to the message_request + // in the request. + oneof message_response { + // This message is used to answer file_by_filename, file_containing_symbol, + // file_containing_extension requests with transitive dependencies. As + // the repeated label is not allowed in oneof fields, we use a + // FileDescriptorResponse message to encapsulate the repeated fields. + // The reflection service is allowed to avoid sending FileDescriptorProtos + // that were previously sent in response to earlier requests in the stream. + FileDescriptorResponse file_descriptor_response = 4; + + // This message is used to answer all_extension_numbers_of_type requst. + ExtensionNumberResponse all_extension_numbers_response = 5; + + // This message is used to answer list_services request. + ListServiceResponse list_services_response = 6; + + // This message is used when an error occurs. + ErrorResponse error_response = 7; + } +} + +// Serialized FileDescriptorProto messages sent by the server answering +// a file_by_filename, file_containing_symbol, or file_containing_extension +// request. +message FileDescriptorResponse { + // Serialized FileDescriptorProto messages. We avoid taking a dependency on + // descriptor.proto, which uses proto2 only features, by making them opaque + // bytes instead. + repeated bytes file_descriptor_proto = 1; +} + +// A list of extension numbers sent by the server answering +// all_extension_numbers_of_type request. +message ExtensionNumberResponse { + // Full name of the base type, including the package name. The format + // is . + string base_type_name = 1; + repeated int32 extension_number = 2; +} + +// A list of ServiceResponse sent by the server answering list_services request. +message ListServiceResponse { + // The information of each service may be expanded in the future, so we use + // ServiceResponse message to encapsulate it. + repeated ServiceResponse service = 1; +} + +// The information of a single service used by ListServiceResponse to answer +// list_services request. +message ServiceResponse { + // Full name of a registered service, including its package name. The format + // is . + string name = 1; +} + +// The error code and error message sent by the server when an error occurs. +message ErrorResponse { + // This field uses the error codes defined in grpc::StatusCode. + int32 error_code = 1; + string error_message = 2; +} \ No newline at end of file diff --git a/massa_proxy/apps/massa_proxy/priv/templates/grpc_endpoint.ex.eex b/massa_proxy/apps/massa_proxy/priv/templates/grpc_endpoint.ex.eex new file mode 100644 index 0000000..2d0a2c6 --- /dev/null +++ b/massa_proxy/apps/massa_proxy/priv/templates/grpc_endpoint.ex.eex @@ -0,0 +1,15 @@ +defmodule Massa.Server.Grpc.ProxyEndpoint do + @moduledoc false + use GRPC.Endpoint + + intercept(GRPC.Logger.Server) + intercept(GRPCPrometheus.ServerInterceptor) + + services = [ +<%= Enum.map service_names, fn(service_name) -> %> + <%= service_name %>, +<% end %> + ] + + run(services) +end \ No newline at end of file diff --git a/massa_proxy/apps/massa_proxy/priv/templates/grpc_service.ex.eex b/massa_proxy/apps/massa_proxy/priv/templates/grpc_service.ex.eex new file mode 100644 index 0000000..c720132 --- /dev/null +++ b/massa_proxy/apps/massa_proxy/priv/templates/grpc_service.ex.eex @@ -0,0 +1,23 @@ +defmodule <%= mod_name %>.ProxyService do + @moduledoc """ + This <%= mod_name %>.ProxyService module only passes + incoming gRPC requests to the respective Entity handlers. + + """ + @moduledoc since: "0.1.0" + + use GRPC.Server, service: <%= name %> + +<%= Enum.map methods, fn(method) -> %> + @spec <%= method %>(type :: any(), GRPC.Server.Stream.t()) :: type :: any() + def <%= method %>(message, stream), + do: + <%= handler %>.dispatch(%{ + entity_type: "<%= entity_type %>", + persistence_id: "<%= persistence_id %>", + message: message, + stream: stream + }) +<% end %> + +end \ No newline at end of file diff --git a/massa_proxy/apps/massa_proxy/start_with_envs.sh b/massa_proxy/apps/massa_proxy/start_with_envs.sh new file mode 100755 index 0000000..34f9989 --- /dev/null +++ b/massa_proxy/apps/massa_proxy/start_with_envs.sh @@ -0,0 +1,3 @@ +#!/bin/bash + +PROXY_HEARTBEAT_INTERVAL=240_000 PROXY_HTTP_PORT=9001 PROXY_PORT=9000 USER_FUNCTION_HOST=127.0.0.1 USER_FUNCTION_PORT=8080 PROXY_UDS_ADDRESS=false PROXY_UDS_MODE=/var/run/cloudstate.sock iex --name a@127.0.0.1 -S mix diff --git a/massa_proxy/config/config.exs b/massa_proxy/config/config.exs index f2d7ff1..b55079d 100644 --- a/massa_proxy/config/config.exs +++ b/massa_proxy/config/config.exs @@ -3,7 +3,9 @@ import Config # Our Logger general configuration config :logger, backends: [:console], - compile_time_purge_level: :debug + compile_time_purge_matching: [ + [level_lower_than: :debug] + ] config :protobuf, extensions: :enabled @@ -16,14 +18,7 @@ config :logger, :console, config :libcluster, topologies: [ dev: [ - strategy: Cluster.Strategy.Epmd, - config: [ - hosts: [ - :"a@127.0.0.1", - :"b@127.0.0.1", - :"c@127.0.0.1" - ] - ] + strategy: Cluster.Strategy.Gossip ] ] diff --git a/massa_proxy/config/prod.exs b/massa_proxy/config/prod.exs index ac3cfca..46ef105 100644 --- a/massa_proxy/config/prod.exs +++ b/massa_proxy/config/prod.exs @@ -3,7 +3,9 @@ import Config # Our Logger general configuration config :logger, backends: [:console], - compile_time_purge_level: :debug + compile_time_purge_matching: [ + [level_lower_than: :debug] + ] config :protobuf, extensions: :enabled diff --git a/massa_proxy/config/releases.exs b/massa_proxy/config/releases.exs index 73da84d..bc42924 100644 --- a/massa_proxy/config/releases.exs +++ b/massa_proxy/config/releases.exs @@ -3,7 +3,9 @@ import Config # Our Logger general configuration config :logger, backends: [:console], - compile_time_purge_level: :debug + compile_time_purge_matching: [ + [level_lower_than: :debug] + ] config :protobuf, extensions: :enabled diff --git a/massa_proxy/mix.lock b/massa_proxy/mix.lock index 1d34998..180dbd8 100644 --- a/massa_proxy/mix.lock +++ b/massa_proxy/mix.lock @@ -7,11 +7,13 @@ "broadway_cloud_pub_sub": {:hex, :broadway_cloud_pub_sub, "0.6.0", "178569dfd76f11b237b28f23c9e80f73dd618524c79d13377d83a66f9fb1f1af", [:mix], [{:broadway, "~> 0.6.0", [hex: :broadway, repo: "hexpm", optional: false]}, {:google_api_pub_sub, "~> 0.11", [hex: :google_api_pub_sub, repo: "hexpm", optional: false]}, {:goth, "~> 1.0", [hex: :goth, repo: "hexpm", optional: true]}, {:hackney, "~> 1.6", [hex: :hackney, repo: "hexpm", optional: false]}], "hexpm", "64b123d965a2edbeb01427a42c9d3c35c151977cee971553007bc96454788731"}, "broadway_rabbitmq": {:hex, :broadway_rabbitmq, "0.6.1", "ddafc4f0e2ee73fe4f3b66597e2a8abe79c294794b72521f5610cc924fb5310c", [:mix], [{:amqp, "~> 1.3", [hex: :amqp, repo: "hexpm", optional: false]}, {:broadway, "~> 0.6.0", [hex: :broadway, repo: "hexpm", optional: false]}], "hexpm", "faaf4525407979dd856b37e9e0d5299934ba1714253799048ac1060f9341e792"}, "broadway_sqs": {:hex, :broadway_sqs, "0.6.1", "be9717c2e1b6356145304c4c6900076c712a2e1b74bfed681fd88fd102018752", [:mix], [{:broadway, "~> 0.6.0", [hex: :broadway, repo: "hexpm", optional: false]}, {:ex_aws_sqs, "~> 3.2.1 or ~> 3.3", [hex: :ex_aws_sqs, repo: "hexpm", optional: false]}, {:saxy, "~> 1.1", [hex: :saxy, repo: "hexpm", optional: false]}], "hexpm", "a17981e2f76535db8dc9b77e94b047ee397f06bcda575257908acd7d5a6407df"}, + "bunt": {:hex, :bunt, "0.2.0", "951c6e801e8b1d2cbe58ebbd3e616a869061ddadcc4863d0a2182541acae9a38", [:mix], [], "hexpm", "7af5c7e09fe1d40f76c8e4f9dd2be7cebd83909f31fee7cd0e9eadc567da8353"}, "certifi": {:hex, :certifi, "2.5.2", "b7cfeae9d2ed395695dd8201c57a2d019c0c43ecaf8b8bcb9320b40d6662f340", [:rebar3], [{:parse_trans, "~>3.3", [hex: :parse_trans, repo: "hexpm", optional: false]}], "hexpm", "3b3b5f36493004ac3455966991eaf6e768ce9884693d9968055aeeeb1e575040"}, "connection": {:hex, :connection, "1.0.4", "a1cae72211f0eef17705aaededacac3eb30e6625b04a6117c1b2db6ace7d5976", [:mix], [], "hexpm", "4a0850c9be22a43af9920a71ab17c051f5f7d45c209e40269a1938832510e4d9"}, "cowboy": {:hex, :cowboy, "2.7.0", "91ed100138a764355f43316b1d23d7ff6bdb0de4ea618cb5d8677c93a7a2f115", [:rebar3], [{:cowlib, "~> 2.8.0", [hex: :cowlib, repo: "hexpm", optional: false]}, {:ranch, "~> 1.7.1", [hex: :ranch, repo: "hexpm", optional: false]}], "hexpm", "04fd8c6a39edc6aaa9c26123009200fc61f92a3a94f3178c527b70b767c6e605"}, "cowlib": {:hex, :cowlib, "2.9.1", "61a6c7c50cf07fdd24b2f45b89500bb93b6686579b069a89f88cb211e1125c78", [:rebar3], [], "hexpm", "e4175dc240a70d996156160891e1c62238ede1729e45740bdd38064dad476170"}, "credentials_obfuscation": {:hex, :credentials_obfuscation, "2.1.1", "0876201f8c50d7583d47c1372d7acbcea668ee9343e9ff22dc815515edf2403a", [:rebar3], [], "hexpm", "838859ad420ebf9c8cae599ce6244122535117a4ad724a018bd1752c3a3620a5"}, + "credo": {:hex, :credo, "1.5.5", "e8f422026f553bc3bebb81c8e8bf1932f498ca03339856c7fec63d3faac8424b", [:mix], [{:bunt, "~> 0.2.0", [hex: :bunt, repo: "hexpm", optional: false]}, {:file_system, "~> 0.2.8", [hex: :file_system, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "dd8623ab7091956a855dc9f3062486add9c52d310dfd62748779c4315d8247de"}, "db_connection": {:hex, :db_connection, "2.2.2", "3bbca41b199e1598245b716248964926303b5d4609ff065125ce98bcd368939e", [:mix], [{:connection, "~> 1.0.2", [hex: :connection, repo: "hexpm", optional: false]}], "hexpm", "642af240d8a8affb93b4ba5a6fcd2bbcbdc327e1a524b825d383711536f8070c"}, "decimal": {:hex, :decimal, "1.8.1", "a4ef3f5f3428bdbc0d35374029ffcf4ede8533536fa79896dd450168d9acdf3c", [:mix], [], "hexpm", "3cb154b00225ac687f6cbd4acc4b7960027c757a5152b369923ead9ddbca7aec"}, "delta_crdt": {:hex, :delta_crdt, "0.5.10", "e866f8d1b89bee497a98b9793e9ba0ea514112a1c41a0c30dcde3463d4984d14", [:mix], [{:merkle_map, "~> 0.2.0", [hex: :merkle_map, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "ed5c685df9528788d7c056762c23f75358f3cadd4779698188a55ccae24d087a"}, @@ -22,6 +24,7 @@ "ex_aws_sqs": {:hex, :ex_aws_sqs, "3.2.1", "fc6772b1cd894a73494498f73820f4171e88f48dadcb64c632d1413fb4592cdb", [:mix], [{:ex_aws, "~> 2.0", [hex: :ex_aws, repo: "hexpm", optional: false]}, {:saxy, "~> 1.1", [hex: :saxy, repo: "hexpm", optional: true]}, {:sweet_xml, ">= 0.0.0", [hex: :sweet_xml, repo: "hexpm", optional: true]}], "hexpm", "ae77e296dffc0608221f14287cea5621b4419b94794804fd20bbf6cf8c71561e"}, "ex_ray": {:hex, :ex_ray, "0.1.4", "672c70e97ce5ba9700d25ae5f292e337f6261196e8f22976e5ce86d9c0519f67", [:mix], [{:otter, "~> 0.4.0", [hex: :otter, repo: "hexpm", optional: false]}], "hexpm", "eab2f4656e4f682b4400735583a5801b1ecbcf2d63971afe58ec7a7cbc5ec94c"}, "exquisite": {:hex, :exquisite, "0.1.10", "e3ca4f8b812696a40a6da3bcd4e9861cef879ee2eb239d5485b0f96885fc9fba", [:mix], [], "hexpm", "0af9319851f1e21dd4adab812c82247f05e11cc19820fa17cc74afd696e2313c"}, + "file_system": {:hex, :file_system, "0.2.10", "fb082005a9cd1711c05b5248710f8826b02d7d1784e7c3451f9c1231d4fc162d", [:mix], [], "hexpm", "41195edbfb562a593726eda3b3e8b103a309b733ad25f3d642ba49696bf715dc"}, "flow": {:hex, :flow, "1.0.0", "3df0b6687ba01dae248c615d50e1f8b36631e3eb9b9a663adf38262035d9fc08", [:mix], [{:gen_stage, "~> 1.0", [hex: :gen_stage, repo: "hexpm", optional: false]}], "hexpm", "103e0d30539db120a432d6433123a6ed885b10bde244e9f4194e3beee080ac29"}, "gen_stage": {:hex, :gen_stage, "1.0.0", "51c8ae56ff54f9a2a604ca583798c210ad245f415115453b773b621c49776df5", [:mix], [], "hexpm", "1d9fc978db5305ac54e6f5fec7adf80cd893b1000cf78271564c516aa2af7706"}, "goldrush": {:hex, :goldrush, "0.1.9", "f06e5d5f1277da5c413e84d5a2924174182fb108dabb39d5ec548b27424cd106", [:rebar3], [], "hexpm", "99cb4128cffcb3227581e5d4d803d5413fa643f4eb96523f77d9e6937d994ceb"}, @@ -29,6 +32,7 @@ "google_gax": {:hex, :google_gax, "0.4.0", "83651f8561c02a295826cb96b4bddde030e2369747bbddc592c4569526bafe94", [:mix], [{:poison, ">= 3.0.0 and < 5.0.0", [hex: :poison, repo: "hexpm", optional: false]}, {:tesla, "~> 1.2", [hex: :tesla, repo: "hexpm", optional: false]}], "hexpm", "a95d36f1dd753ab31268dd8bb6de9911243c911cfda9080f64778f6297b9ac57"}, "google_protos": {:hex, :google_protos, "0.1.0", "c6b9e12092d17571b093d4156d004494ca143b65dbbcbfc3ffff463ea03467c0", [:mix], [{:protobuf, "~> 0.5", [hex: :protobuf, repo: "hexpm", optional: false]}], "hexpm", "ff5564525f89d2638a4cfa9fb4d31e9ee9d9d7cb937b3e8a95f558440c039e1b"}, "grpc": {:git, "https://github.com/elixir-grpc/grpc.git", "6493159b763530f7d04a904885ab315d2cafc05f", []}, + "grpc_prometheus": {:hex, :grpc_prometheus, "0.1.0", "a2f45ca83018c4ae59e4c293b7455634ac09e38c36cba7cc1fb8affdf462a6d5", [:mix], [{:grpc, ">= 0.0.0", [hex: :grpc, repo: "hexpm", optional: true]}, {:prometheus, "~> 4.0", [hex: :prometheus, repo: "hexpm", optional: false]}, {:prometheus_ex, "~> 3.0", [hex: :prometheus_ex, repo: "hexpm", optional: false]}], "hexpm", "8b9ab3098657e7daec0b3edc78e1d02418bc0871618d8ca89b51b74a8086bb71"}, "gun": {:hex, :grpc_gun, "2.0.0", "f99678a2ab975e74372a756c86ec30a8384d3ac8a8b86c7ed6243ef4e61d2729", [:rebar3], [{:cowlib, "~> 2.8.0", [hex: :cowlib, repo: "hexpm", optional: false]}], "hexpm", "03dbbca1a9c604a0267a40ea1d69986225091acb822de0b2dbea21d5815e410b"}, "hackney": {:hex, :hackney, "1.16.0", "5096ac8e823e3a441477b2d187e30dd3fff1a82991a806b2003845ce72ce2d84", [:rebar3], [{:certifi, "2.5.2", [hex: :certifi, repo: "hexpm", optional: false]}, {:idna, "6.0.1", [hex: :idna, repo: "hexpm", optional: false]}, {:metrics, "1.0.1", [hex: :metrics, repo: "hexpm", optional: false]}, {:mimerl, "~>1.1", [hex: :mimerl, repo: "hexpm", optional: false]}, {:parse_trans, "3.3.0", [hex: :parse_trans, repo: "hexpm", optional: false]}, {:ssl_verify_fun, "1.1.6", [hex: :ssl_verify_fun, repo: "hexpm", optional: false]}], "hexpm", "3bf0bebbd5d3092a3543b783bf065165fa5d3ad4b899b836810e513064134e18"}, "horde": {:hex, :horde, "0.8.3", "82c5276109edf5e76c802aa51b5196a1624d57cfb92b64e4bcab1e95f9aab45f", [:mix], [{:delta_crdt, "~> 0.5.10", [hex: :delta_crdt, repo: "hexpm", optional: false]}, {:libring, "~> 1.4", [hex: :libring, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4.0", [hex: :telemetry, repo: "hexpm", optional: false]}, {:telemetry_poller, "~> 0.5.0", [hex: :telemetry_poller, repo: "hexpm", optional: false]}], "hexpm", "cd9d3079711bc18dceb0bc3b207868df800fed59e29dea05aa3c1874496cc1d2"}, @@ -42,6 +46,7 @@ "metrics": {:hex, :metrics, "1.0.1", "25f094dea2cda98213cecc3aeff09e940299d950904393b2a29d191c346a8486", [:rebar3], [], "hexpm", "69b09adddc4f74a40716ae54d140f93beb0fb8978d8636eaded0c31b6f099f16"}, "mime": {:hex, :mime, "1.3.1", "30ce04ab3175b6ad0bdce0035cba77bba68b813d523d1aac73d9781b4d193cf8", [:mix], [], "hexpm", "6cbe761d6a0ca5a31a0931bf4c63204bceb64538e664a8ecf784a9a6f3b875f1"}, "mimerl": {:hex, :mimerl, "1.2.0", "67e2d3f571088d5cfd3e550c383094b47159f3eee8ffa08e64106cdf5e981be3", [:rebar3], [], "hexpm", "f278585650aa581986264638ebf698f8bb19df297f66ad91b18910dfc6e19323"}, + "norm": {:hex, :norm, "0.12.0", "b27a629fea9aaf7757604e9d4ed06cd815c2c5fb335f38b33c1bf17db9b217b0", [:mix], [], "hexpm", "aecd53df72219966d9493e3426a32d83490c822ef618ef442721b1ae68cb6f0c"}, "otter": {:hex, :otter, "0.4.0", "f12d1d5493104507a649d30962d76de5db0858cdb3f0de26e9712c8ef247719f", [:rebar3], [{:otter_lib, "~>0.1.1", [hex: :otter_lib, repo: "hexpm", optional: false]}], "hexpm", "7c9b65d019c0431c9d762e22e70bced04691fc75184f13a791fa7aef429e1aa0"}, "otter_lib": {:hex, :otter_lib, "0.1.1", "9ef4401013c52f581278d7beb15ae536dc629cdac72dfeebac2384069f9461fd", [:rebar3], [], "hexpm", "b467b2d0be600bc374a6fea4dbf679b60727152b39b4659ac067fc895cb4d236"}, "parse_trans": {:hex, :parse_trans, "3.3.0", "09765507a3c7590a784615cfd421d101aec25098d50b89d7aa1d66646bc571c1", [:rebar3], [], "hexpm", "17ef63abde837ad30680ea7f857dd9e7ced9476cdd7b0394432af4bfc241b960"}, @@ -63,6 +68,10 @@ "telemetry": {:hex, :telemetry, "0.4.2", "2808c992455e08d6177322f14d3bdb6b625fbcfd233a73505870d8738a2f4599", [:rebar3], [], "hexpm", "2d1419bd9dda6a206d7b5852179511722e2b18812310d304620c7bd92a13fcef"}, "telemetry_poller": {:hex, :telemetry_poller, "0.5.1", "21071cc2e536810bac5628b935521ff3e28f0303e770951158c73eaaa01e962a", [:rebar3], [{:telemetry, "~> 0.4", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "4cab72069210bc6e7a080cec9afffad1b33370149ed5d379b81c7c5f0c663fd4"}, "tesla": {:hex, :tesla, "1.3.3", "26ae98627af5c406584aa6755ab5fc96315d70d69a24dd7f8369cfcb75094a45", [:mix], [{:castore, "~> 0.1", [hex: :castore, repo: "hexpm", optional: true]}, {:exjsx, ">= 3.0.0", [hex: :exjsx, repo: "hexpm", optional: true]}, {:fuse, "~> 2.4", [hex: :fuse, repo: "hexpm", optional: true]}, {:gun, "~> 1.3", [hex: :gun, repo: "hexpm", optional: true]}, {:hackney, "~> 1.6", [hex: :hackney, repo: "hexpm", optional: true]}, {:ibrowse, "~> 4.4.0", [hex: :ibrowse, repo: "hexpm", optional: true]}, {:jason, ">= 1.0.0", [hex: :jason, repo: "hexpm", optional: true]}, {:mime, "~> 1.0", [hex: :mime, repo: "hexpm", optional: false]}, {:mint, "~> 1.0", [hex: :mint, repo: "hexpm", optional: true]}, {:poison, ">= 1.0.0", [hex: :poison, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4", [hex: :telemetry, repo: "hexpm", optional: true]}], "hexpm", "2648f1c276102f9250299e0b7b57f3071c67827349d9173f34c281756a1b124c"}, + "toml": {:hex, :toml, "0.6.2", "38f445df384a17e5d382befe30e3489112a48d3ba4c459e543f748c2f25dd4d1", [:mix], [], "hexpm", "d013e45126d74c0c26a38d31f5e8e9b83ea19fc752470feb9a86071ca5a672fa"}, "unicode_util_compat": {:hex, :unicode_util_compat, "0.5.0", "8516502659002cec19e244ebd90d312183064be95025a319a6c7e89f4bccd65b", [:rebar3], [], "hexpm", "d48d002e15f5cc105a696cf2f1bbb3fc72b4b770a184d8420c8db20da2674b38"}, + "vapor": {:hex, :vapor, "0.10.0", "547a94b381093dea61a4ca2200109385b6e44b86d72d1ebf93e5ac1a8873bc3c", [:mix], [{:jason, "~> 1.1", [hex: :jason, repo: "hexpm", optional: false]}, {:norm, "~> 0.9", [hex: :norm, repo: "hexpm", optional: false]}, {:toml, "~> 0.3", [hex: :toml, repo: "hexpm", optional: false]}, {:yaml_elixir, "~> 2.1", [hex: :yaml_elixir, repo: "hexpm", optional: false]}], "hexpm", "ee6d089a71309647a0a2a2ae6cf3bea61739a983e8c1310d53ff04b1493afbc1"}, "xandra": {:hex, :xandra, "0.13.1", "f82866e6c47527f74f35dd3007b5311121852dd861a29ed1613e27ccfaba0102", [:mix], [{:db_connection, "~> 2.0", [hex: :db_connection, repo: "hexpm", optional: false]}, {:decimal, "~> 1.7", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm", "a2efdb8921e3b694bf3505e40c5ec9d353d8fa3755cec946be7c18b8236d7230"}, + "yamerl": {:hex, :yamerl, "0.8.1", "07da13ffa1d8e13948943789665c62ccd679dfa7b324a4a2ed3149df17f453a4", [:rebar3], [], "hexpm", "96cb30f9d64344fed0ef8a92e9f16f207de6c04dfff4f366752ca79f5bceb23f"}, + "yaml_elixir": {:hex, :yaml_elixir, "2.6.0", "1f469e4e7b006df44539685feb0a9d0d58c98230cef4ecd264ea116eb18e5d09", [:mix], [{:yamerl, "~> 0.8", [hex: :yamerl, repo: "hexpm", optional: false]}], "hexpm", "e40abb99a65e98020929d3e9b310c9f0f62aa76c3be7c0ada2aa440e12b258d9"}, }