├── .circleci └── config.yml ├── .github └── CODEOWNERS ├── .gitignore ├── ADOPTERS.md ├── BUILD.md ├── CHANGES.md ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── DCO ├── GOVERNANCE.md ├── ISSUE_TEMPLATE.md ├── LICENSE ├── MAINTAINERS.md ├── PLUGINS.md ├── README.md ├── admin ├── names │ └── src │ │ └── main │ │ └── scala │ │ └── io │ │ └── buoyant │ │ └── admin │ │ └── names │ │ ├── BoundNamesHandler.scala │ │ ├── DelegateApiHandler.scala │ │ └── DelegateHandler.scala └── src │ ├── main │ ├── resources │ │ └── io │ │ │ └── buoyant │ │ │ └── admin │ │ │ ├── .eslintignore │ │ │ ├── .eslintrc.json │ │ │ ├── .gitignore │ │ │ ├── README.md │ │ │ ├── css │ │ │ ├── dashboard.css │ │ │ ├── delegator.css │ │ │ ├── fonts.css │ │ │ ├── fonts │ │ │ │ ├── OFL.txt │ │ │ │ ├── SourceSansPro-300.woff2 │ │ │ │ ├── SourceSansPro-400.woff2 │ │ │ │ ├── SourceSansPro-600.woff2 │ │ │ │ └── glyphicons-halflings-regular.woff │ │ │ ├── lib │ │ │ │ └── bootstrap.min.css │ │ │ └── logger.css │ │ │ ├── images │ │ │ ├── favicon.png │ │ │ └── linkerd-horizontal-white-transbg-vectorized.svg │ │ │ ├── js │ │ │ ├── lib │ │ │ │ ├── bootstrap.min.js │ │ │ │ ├── handlebars.runtime.js │ │ │ │ ├── jquery-3.1.1.min.js │ │ │ │ ├── lodash.min.js │ │ │ │ ├── require.js │ │ │ │ ├── smoothie.js │ │ │ │ └── text.js │ │ │ ├── main-linkerd.js │ │ │ ├── main-namerd.js │ │ │ ├── spec │ │ │ │ ├── BarChartSpec.js │ │ │ │ ├── MetricsCollectorSpec.js │ │ │ │ ├── RouterClientsSpec.js │ │ │ │ ├── RouterServicesSpec.js │ │ │ │ ├── RouterSummarySpec.js │ │ │ │ ├── UtilsSpec.js │ │ │ │ ├── fixtures │ │ │ │ │ └── metrics.js │ │ │ │ └── test-main.js │ │ │ ├── src │ │ │ │ ├── admin.js │ │ │ │ ├── bar_chart.js │ │ │ │ ├── colors.js │ │ │ │ ├── combined_client_graph.js │ │ │ │ ├── dashboard.js │ │ │ │ ├── dashboard_delegate.js │ │ │ │ ├── delegate.js │ │ │ │ ├── delegator.js │ │ │ │ ├── dtab_viewer.js │ │ │ │ ├── latency_color_util.js │ │ │ │ ├── logging.js │ │ │ │ ├── metrics_collector.js │ │ │ │ ├── namerd.js │ │ │ │ ├── process_info.js │ │ │ │ ├── request_totals.js │ │ │ │ ├── router_client.js │ │ │ │ ├── router_client_dashboard.js │ │ │ │ ├── router_clients.js │ │ │ │ ├── router_server.js │ │ │ │ ├── router_servers.js │ │ │ │ ├── router_service.js │ │ │ │ ├── router_service_dashboard.js │ │ │ │ ├── router_summary.js │ │ │ │ ├── success_rate_graph.js │ │ │ │ └── utils.js │ │ │ └── template │ │ │ │ ├── barchart.handlebars │ │ │ │ ├── compiled_templates.js │ │ │ │ ├── delegatenode.handlebars │ │ │ │ ├── delegator.handlebars │ │ │ │ ├── dentry.handlebars │ │ │ │ ├── error_modal.handlebars │ │ │ │ ├── latencies.partial.handlebars │ │ │ │ ├── logging_row.handlebars │ │ │ │ ├── metric.partial.handlebars │ │ │ │ ├── namerd_namespace.handlebars │ │ │ │ ├── namerd_stats.handlebars │ │ │ │ ├── process_info.handlebars │ │ │ │ ├── rate_metric.partial.handlebars │ │ │ │ ├── request_stats.handlebars │ │ │ │ ├── request_totals.handlebars │ │ │ │ ├── router_client.handlebars │ │ │ │ ├── router_client_container.handlebars │ │ │ │ ├── router_container.handlebars │ │ │ │ ├── router_option.handlebars │ │ │ │ ├── router_server.handlebars │ │ │ │ ├── router_server_container.handlebars │ │ │ │ ├── router_service_container.handlebars │ │ │ │ ├── router_service_metrics.handlebars │ │ │ │ ├── router_services_container.handlebars │ │ │ │ └── router_summary.handlebars │ │ │ ├── karma.conf.js │ │ │ ├── openssl.cnf │ │ │ ├── package-lock.json │ │ │ ├── package.json │ │ │ └── twitter-server │ │ │ ├── images │ │ │ └── favicon.ico │ │ │ ├── jquery-ui.min.js │ │ │ └── js │ │ │ └── threads.js │ └── scala │ │ └── io │ │ └── buoyant │ │ └── admin │ │ ├── Admin.scala │ │ ├── AdminAssetsFilter.scala │ │ ├── AdminConfig.scala │ │ ├── App.scala │ │ ├── Build.scala │ │ ├── ConfigHandler.scala │ │ ├── HtmlView.scala │ │ ├── IndexTxtHandler.scala │ │ ├── LoggingHandler.scala │ │ ├── SecurityFilter.scala │ │ ├── StaticFilter.scala │ │ └── TimeZoneLogFormat.scala │ └── test │ └── scala │ └── io │ └── buoyant │ └── admin │ ├── LogFormatterTest.scala │ └── SecurityFilterTest.scala ├── ci ├── coverage-publish.sh ├── docker-publish.sh ├── h2.sh ├── test.sh ├── twitter-develop.sh └── update.sh ├── config └── src │ ├── main │ ├── resources │ │ └── META-INF │ │ │ └── services │ │ │ ├── io.buoyant.config.ConfigDeserializer │ │ │ └── io.buoyant.config.ConfigSerializer │ └── scala │ │ └── io │ │ └── buoyant │ │ └── config │ │ ├── ConfigError.scala │ │ ├── ConfigInitializer.scala │ │ ├── JsonStreamParser.scala │ │ ├── Parser.scala │ │ ├── PolymorphicConfig.scala │ │ └── types │ │ ├── ByteBufferSerializer.scala │ │ ├── DirectoryDeserializer.scala │ │ ├── DtabDeserializer.scala │ │ ├── FileDeserializer.scala │ │ ├── HostAndPort.scala │ │ ├── InetAddressDeserializer.scala │ │ ├── LogLevelDeserializer.scala │ │ ├── PathDeserializer.scala │ │ ├── PathMatcherDeserializer.scala │ │ └── PortDeserializer.scala │ └── test │ └── scala │ └── io │ └── buoyant │ └── config │ └── ParserTest.scala ├── consul └── src │ ├── main │ ├── resources │ │ └── META-INF │ │ │ └── services │ │ │ ├── io.buoyant.config.ConfigDeserializer │ │ │ └── io.buoyant.config.ConfigSerializer │ └── scala │ │ └── io │ │ └── buoyant │ │ └── consul │ │ ├── SetAuthTokenFilter.scala │ │ ├── SetHostFilter.scala │ │ ├── package.scala │ │ ├── utils.scala │ │ └── v1 │ │ ├── AgentApi.scala │ │ ├── BaseApi.scala │ │ ├── ConsistencyMode.scala │ │ ├── ConsulApi.scala │ │ ├── HealthStatus.scala │ │ ├── Instrumentation.scala │ │ ├── KvApi.scala │ │ └── package.scala │ └── test │ └── scala │ └── io │ └── buoyant │ └── consul │ └── v1 │ ├── AgentApiTest.scala │ ├── BaseApiTest.scala │ ├── CatalogApiTest.scala │ ├── ConsistencyModeTest.scala │ ├── HealthApiTest.scala │ ├── HealthStatusTest.scala │ └── KvApiTest.scala ├── etcd ├── README.md └── src │ ├── integration │ └── scala │ │ └── io │ │ └── buoyant │ │ └── etcd │ │ ├── EtcdFixture.scala │ │ └── EtcdIntegrationTest.scala │ ├── main │ └── scala │ │ └── io │ │ └── buoyant │ │ └── etcd │ │ ├── ApiError.scala │ │ ├── Etcd.scala │ │ ├── Key.scala │ │ ├── Node.scala │ │ └── NodeOp.scala │ └── test │ └── scala │ └── io │ └── buoyant │ └── etcd │ └── KeyTest.scala ├── finagle ├── README.md ├── benchmark │ └── src │ │ └── main │ │ └── scala │ │ └── com │ │ └── twitter │ │ └── finagle │ │ └── buoyant │ │ └── h2 │ │ └── BufferedStreamBenchmark.scala ├── buoyant │ └── src │ │ ├── main │ │ └── scala │ │ │ └── com │ │ │ └── twitter │ │ │ └── finagle │ │ │ ├── buoyant │ │ │ ├── ExistentialStability.scala │ │ │ ├── PathMatcher.scala │ │ │ ├── RetryFilter.scala │ │ │ ├── SocketOptionsConfig.scala │ │ │ ├── TlsClientConfig.scala │ │ │ ├── TlsServerConfig.scala │ │ │ └── package.scala │ │ │ └── netty4 │ │ │ └── buoyant │ │ │ └── BufferingConnectDelay.scala │ │ └── test │ │ └── scala │ │ └── com │ │ └── twitter │ │ └── finagle │ │ └── buoyant │ │ ├── ExistentialStabilityTest.scala │ │ └── PathMatcherTest.scala └── h2 │ ├── README.md │ └── src │ ├── e2e │ ├── resources │ │ ├── cacert.pem │ │ ├── linkerd-tls-e2e-cert.pem │ │ └── linkerd-tls-e2e-key.pem │ └── scala │ │ └── com │ │ └── twitter │ │ └── finagle │ │ └── buoyant │ │ └── h2 │ │ └── TlsEndToEndTest.scala │ ├── main │ └── scala │ │ ├── com │ │ └── twitter │ │ │ └── finagle │ │ │ ├── buoyant │ │ │ ├── H2.scala │ │ │ └── h2 │ │ │ │ ├── AccessLogger.scala │ │ │ │ ├── BufferedStream.scala │ │ │ │ ├── ConnectionHeaders.scala │ │ │ │ ├── DelayedReleaseService.scala │ │ │ │ ├── Error.scala │ │ │ │ ├── H2ApplicationProtocol.scala │ │ │ │ ├── H2Transport.scala │ │ │ │ ├── Message.scala │ │ │ │ ├── Method.scala │ │ │ │ ├── Status.scala │ │ │ │ ├── Stream.scala │ │ │ │ ├── StreamProxy.scala │ │ │ │ ├── TracingFilter.scala │ │ │ │ ├── netty4 │ │ │ │ ├── DebugHandler.scala │ │ │ │ ├── Netty4ClientDispatcher.scala │ │ │ │ ├── Netty4DispatcherBase.scala │ │ │ │ ├── Netty4H2Listener.scala │ │ │ │ ├── Netty4H2Settings.scala │ │ │ │ ├── Netty4H2Transporter.scala │ │ │ │ ├── Netty4H2Writer.scala │ │ │ │ ├── Netty4Message.scala │ │ │ │ ├── Netty4ServerDispatcher.scala │ │ │ │ ├── Netty4StreamTransport.scala │ │ │ │ └── ServerUpgradeHandler.scala │ │ │ │ ├── param.scala │ │ │ │ └── service │ │ │ │ ├── H2Classifier.scala │ │ │ │ ├── H2Classifiers.scala │ │ │ │ └── H2ReqRepFrame.scala │ │ │ └── netty4 │ │ │ └── transport │ │ │ └── buoyant │ │ │ └── BufferingChannelTransport.scala │ │ └── io │ │ └── netty │ │ └── handler │ │ └── codec │ │ └── http2 │ │ ├── H2FrameCodec.scala │ │ ├── H2FrameStream.scala │ │ └── Http2FrameCodecServerUpgrader.scala │ └── test │ └── scala │ ├── com │ └── twitter │ │ └── finagle │ │ └── buoyant │ │ └── h2 │ │ ├── BufferedStreamTest.scala │ │ ├── TracingFilterTest.scala │ │ └── netty4 │ │ ├── Netty4ClientDispatcherTest.scala │ │ ├── Netty4ServerDispatcherTest.scala │ │ └── Netty4StreamTransportTest.scala │ └── io │ └── buoyant │ └── test │ └── h2 │ └── StreamTestUtils.scala ├── grpc ├── README.md ├── eg │ └── src │ │ ├── main │ │ └── protobuf │ │ │ ├── base.proto │ │ │ └── eg.proto │ │ └── test │ │ └── scala │ │ └── io │ │ └── buoyant │ │ └── grpc │ │ └── EgTest.scala ├── gen │ └── src │ │ └── main │ │ └── scala │ │ └── io │ │ └── buoyant │ │ └── grpc │ │ └── gen │ │ ├── Generator.scala │ │ ├── Main.scala │ │ └── ProtoFile.scala ├── interop │ ├── README.md │ └── src │ │ ├── main │ │ ├── protobuf │ │ │ ├── empty.proto │ │ │ ├── messages.proto │ │ │ └── test.proto │ │ └── scala │ │ │ └── io │ │ │ └── buoyant │ │ │ └── grpc │ │ │ └── interop │ │ │ ├── Client.scala │ │ │ └── Server.scala │ │ └── test │ │ └── scala │ │ └── io │ │ └── buoyant │ │ └── grpc │ │ └── interop │ │ ├── InteropTestBase.scala │ │ ├── LocalInteropTest.scala │ │ └── NetworkedEndToEndTest.scala └── runtime │ └── src │ ├── main │ └── scala │ │ └── io │ │ └── buoyant │ │ └── grpc │ │ └── runtime │ │ ├── ClientDispatcher.scala │ │ ├── Codec.scala │ │ ├── DecodingStream.scala │ │ ├── GrpcStatus.scala │ │ ├── H2Headers.scala │ │ ├── ServerDispatcher.scala │ │ ├── Stream.scala │ │ └── VarEventStream.scala │ └── test │ └── scala │ └── io │ └── buoyant │ └── grpc │ └── runtime │ ├── DecodingStreamTest.scala │ ├── GrpcStatusTest.scala │ ├── UnaryToUnaryTest.scala │ └── VarEventStreamTest.scala ├── interpreter ├── consul │ └── src │ │ ├── main │ │ ├── resources │ │ │ └── META-INF │ │ │ │ └── services │ │ │ │ └── io.buoyant.namer.InterpreterInitializer │ │ └── scala │ │ │ └── io │ │ │ └── buoyant │ │ │ └── interpreter │ │ │ └── consul │ │ │ └── ConsulInterpreterInitializer.scala │ │ └── test │ │ └── scala │ │ └── io │ │ └── buoyant │ │ └── interpreter │ │ └── consul │ │ └── ConsulInterpreterTest.scala ├── fs │ └── src │ │ ├── main │ │ ├── resources │ │ │ └── META-INF │ │ │ │ └── services │ │ │ │ └── io.buoyant.namer.InterpreterInitializer │ │ └── scala │ │ │ └── io │ │ │ └── buoyant │ │ │ └── interpreter │ │ │ └── fs │ │ │ ├── FsInterpreterConfig.scala │ │ │ └── FsInterpreterInitializer.scala │ │ └── test │ │ └── scala │ │ └── io │ │ └── buoyant │ │ └── interpreter │ │ └── fs │ │ └── FsInterpreterTest.scala ├── istio │ └── src │ │ ├── main │ │ ├── resources │ │ │ └── META-INF │ │ │ │ └── services │ │ │ │ └── io.buoyant.namer.InterpreterInitializer │ │ └── scala │ │ │ └── io │ │ │ └── buoyant │ │ │ └── interpreter │ │ │ └── k8s │ │ │ └── istio │ │ │ ├── IstioInterpreter.scala │ │ │ └── IstioInterpreterInitializer.scala │ │ └── test │ │ └── scala │ │ └── IstioInterpreterTest.scala ├── k8s │ └── src │ │ ├── main │ │ ├── resources │ │ │ └── META-INF │ │ │ │ └── services │ │ │ │ ├── io.buoyant.namer.InterpreterInitializer │ │ │ │ └── io.buoyant.namer.TransformerInitializer │ │ └── scala │ │ │ └── io │ │ │ └── buoyant │ │ │ ├── interpreter │ │ │ └── k8s │ │ │ │ └── ConfigMapInterpreterInitializer.scala │ │ │ └── transformer │ │ │ └── k8s │ │ │ ├── DaemonSetTransformerInitializer.scala │ │ │ └── LocalNodeTransformerInitializer.scala │ │ └── test │ │ └── scala │ │ └── io │ │ └── buoyant │ │ ├── interpreter │ │ └── k8s │ │ │ └── ConfigMapInterpreterTest.scala │ │ └── transformer │ │ └── k8s │ │ ├── DaemonSetTransformerTest.scala │ │ └── LocalnodeTransformerTest.scala ├── mesh │ └── src │ │ ├── main │ │ ├── resources │ │ │ └── META-INF │ │ │ │ └── services │ │ │ │ ├── io.buoyant.config.ConfigSerializer │ │ │ │ └── io.buoyant.namer.InterpreterInitializer │ │ └── scala │ │ │ └── io │ │ │ └── buoyant │ │ │ └── interpreter │ │ │ ├── MeshInterpreterInitializer.scala │ │ │ └── mesh │ │ │ ├── BoundNameTreeSerializer.scala │ │ │ ├── BufSerializers.scala │ │ │ ├── Client.scala │ │ │ ├── EndpointSerializer.scala │ │ │ ├── PathSerializer.scala │ │ │ └── StreamState.scala │ │ └── test │ │ └── scala │ │ └── io │ │ └── buoyant │ │ └── interpreter │ │ ├── MeshInterpreterInitializerTest.scala │ │ └── mesh │ │ ├── BufSerializersTest.scala │ │ └── ClientTest.scala ├── namerd │ └── src │ │ ├── main │ │ ├── resources │ │ │ └── META-INF │ │ │ │ └── services │ │ │ │ └── io.buoyant.namer.InterpreterInitializer │ │ └── scala │ │ │ └── io │ │ │ └── buoyant │ │ │ └── namerd │ │ │ └── iface │ │ │ ├── NamerdHandler.scala │ │ │ ├── NamerdHttpInterpreterInitializer.scala │ │ │ └── NamerdInterpreterInitializer.scala │ │ └── test │ │ └── scala │ │ └── io │ │ └── buoyant │ │ └── namerd │ │ └── iface │ │ ├── NamerdHttpTest.scala │ │ └── NamerdTest.scala ├── per-host │ └── src │ │ └── main │ │ ├── resources │ │ └── META-INF │ │ │ └── services │ │ │ └── io.buoyant.namer.TransformerInitializer │ │ └── scala │ │ └── io │ │ └── buoyant │ │ └── transformer │ │ └── perHost │ │ ├── LocalhostTransformerInitializer.scala │ │ ├── PortTransformer.scala │ │ ├── PortTransformerInitializer.scala │ │ └── SpecificHostTransformerInitializer.scala └── subnet │ └── src │ ├── main │ └── scala │ │ └── io │ │ └── buoyant │ │ └── transformer │ │ ├── Netmask.scala │ │ ├── SubnetGatewayTransformer.scala │ │ └── SubnetLocalTransformer.scala │ └── test │ └── scala │ └── io │ └── buoyant │ └── transformer │ └── NetmaskTest.scala ├── istio-proto └── src │ └── main │ ├── README.md │ ├── protobuf │ ├── gogoproto │ │ └── gogo.proto │ ├── google │ │ ├── protobuf │ │ │ ├── any.proto │ │ │ ├── descriptor.proto │ │ │ ├── duration.proto │ │ │ └── timestamp.proto │ │ └── rpc │ │ │ ├── README.md │ │ │ ├── code.proto │ │ │ ├── error_details.proto │ │ │ └── status.proto │ ├── mixer │ │ └── v1 │ │ │ ├── attributes.proto │ │ │ ├── check.proto │ │ │ ├── quota.proto │ │ │ ├── report.proto │ │ │ └── service.proto │ └── proxy │ │ └── v1 │ │ └── config │ │ ├── dest_policy.proto │ │ ├── http_fault.proto │ │ ├── l4_fault.proto │ │ ├── proxy_mesh.proto │ │ └── route_rule.proto │ └── resources │ └── mixer │ └── v1 │ └── global_dictionary.yaml ├── istio └── src │ ├── main │ └── scala │ │ └── io │ │ └── buoyant │ │ └── k8s │ │ └── istio │ │ ├── ApiserverClient.scala │ │ ├── ClusterCache.scala │ │ ├── CurrentIstioPath.scala │ │ ├── DiscoveryClient.scala │ │ ├── IstioAttribute.scala │ │ ├── IstioNamer.scala │ │ ├── IstioRequest.scala │ │ ├── IstioRequestAuthorizerFilter.scala │ │ ├── IstioResponse.scala │ │ ├── IstioServices.scala │ │ ├── PollingApiClient.scala │ │ ├── RouteCache.scala │ │ ├── identifiers │ │ ├── IngresssTrafficIdentifier.scala │ │ ├── InternalTrafficIdentifier.scala │ │ ├── IstioIdentifierBase.scala │ │ └── IstioProtocolSpecificRequestHandler.scala │ │ ├── mixer │ │ ├── MixerApiRequests.scala │ │ └── MixerClient.scala │ │ └── package.scala │ └── test │ └── scala │ └── io │ └── buoyant │ └── k8s │ └── istio │ ├── ApiserverClientTest.scala │ ├── CurrentIstioPathTest.scala │ ├── IstioRequestAuthorizerFilterTest.scala │ ├── identifiers │ ├── IngressTrafficIdentifierTest.scala │ └── InternalTrafficIdentifierTest.scala │ └── mixer │ ├── MixerApiRequestsTest.scala │ └── MixerClientTest.scala ├── k8s └── src │ ├── main │ └── scala │ │ └── io │ │ └── buoyant │ │ └── k8s │ │ ├── Api.scala │ │ ├── AuthFilter.scala │ │ ├── ClientConfig.scala │ │ ├── EndpointsNamer.scala │ │ ├── IngressCache.scala │ │ ├── InstrumentedWatch.scala │ │ ├── PortMapLogger.scala │ │ ├── SerializationModule.scala │ │ ├── ServiceNamer.scala │ │ ├── SetHostFilter.scala │ │ ├── Watchable.scala │ │ ├── package.scala │ │ ├── resources.scala │ │ ├── v1.scala │ │ └── v1beta1.scala │ └── test │ └── scala │ └── io │ └── buoyant │ └── k8s │ ├── CustomResourceTest.scala │ ├── EndpointsNamerTest.scala │ ├── IngressCacheTest.scala │ ├── JsonTest.scala │ ├── ServiceNamerTest.scala │ ├── v1 │ └── ApiTest.scala │ └── v1beta1 │ └── ApiTest.scala ├── linkerd ├── README.md ├── admin │ └── src │ │ ├── main │ │ └── scala │ │ │ ├── com │ │ │ └── twitter │ │ │ │ └── finagle │ │ │ │ └── client │ │ │ │ └── buoyant │ │ │ │ └── ClientStateHandler.scala │ │ │ └── io │ │ │ └── buoyant │ │ │ └── linkerd │ │ │ └── admin │ │ │ ├── AdminFilter.scala │ │ │ ├── AdminHandler.scala │ │ │ ├── DashboardHandler.scala │ │ │ ├── HelpPageHandler.scala │ │ │ ├── HttpIdentifierHandler.scala │ │ │ └── LinkerdAdmin.scala │ │ └── test │ │ └── scala │ │ └── io │ │ └── buoyant │ │ └── linkerd │ │ └── admin │ │ ├── AdminHandlerTest.scala │ │ ├── ConfigHandlerTest.scala │ │ ├── DashboardHandlerTest.scala │ │ ├── LinkerdAdminTest.scala │ │ └── names │ │ ├── DelegateApiHandlerTest.scala │ │ └── DelegatorTest.scala ├── announcer │ └── serversets │ │ └── src │ │ └── main │ │ ├── resources │ │ └── META-INF │ │ │ └── services │ │ │ └── io.buoyant.linkerd.AnnouncerInitializer │ │ └── scala │ │ ├── com │ │ └── twitter │ │ │ └── finagle │ │ │ └── zookeeper │ │ │ └── buoyant │ │ │ └── ZkAnnouncer.scala │ │ └── io │ │ └── buoyant │ │ └── linkerd │ │ └── announcer │ │ └── serversets │ │ └── ServersetsAnnouncerInitializer.scala ├── core │ └── src │ │ ├── e2e │ │ └── scala │ │ │ └── io │ │ │ └── buoyant │ │ │ └── linkerd │ │ │ └── telemeter │ │ │ └── UsageDataTelemeterEndToEndTest.scala │ │ ├── main │ │ ├── protobuf │ │ │ └── usage.proto │ │ └── scala │ │ │ └── io │ │ │ └── buoyant │ │ │ └── linkerd │ │ │ ├── AddForwardedHeaderConfig.scala │ │ │ ├── Announcer.scala │ │ │ ├── AnnouncerInitializer.scala │ │ │ ├── ClearContext.scala │ │ │ ├── Client.scala │ │ │ ├── ClientConfig.scala │ │ │ ├── FailureAccrualInitializer.scala │ │ │ ├── IdentifierInitializer.scala │ │ │ ├── LabelerConfig.scala │ │ │ ├── Linker.scala │ │ │ ├── LoadBalancerConfig.scala │ │ │ ├── MetricsPruningModule.scala │ │ │ ├── ProtocolException.scala │ │ │ ├── ProtocolInitializer.scala │ │ │ ├── RequestAuthorizerInitializer.scala │ │ │ ├── ResponseClassifierInitializer.scala │ │ │ ├── Router.scala │ │ │ ├── RouterContextFormatter.scala │ │ │ ├── Server.scala │ │ │ ├── SessionConfig.scala │ │ │ ├── Svc.scala │ │ │ ├── SvcConfig.scala │ │ │ ├── TracePropagator.scala │ │ │ ├── TracePropagatorInitializer.scala │ │ │ ├── package.scala │ │ │ └── telemeter │ │ │ └── UsageDataTelemeter.scala │ │ └── test │ │ └── scala │ │ └── io │ │ └── buoyant │ │ └── linkerd │ │ ├── AddForwardedHeaderConfigTest.scala │ │ ├── ClientConfigTest.scala │ │ ├── ClientSessionConfigTest.scala │ │ ├── ClientTest.scala │ │ ├── HostConnectionPoolTest.scala │ │ ├── LinkerTest.scala │ │ ├── LoadBalancerTest.scala │ │ ├── RetriesConfigTest.scala │ │ ├── RouterTest.scala │ │ ├── ServerTest.scala │ │ ├── SocketOptionsTest.scala │ │ ├── SvcTest.scala │ │ ├── TestAnnouncer.scala │ │ ├── TestProtocol.scala │ │ └── telemeter │ │ └── UsageDataTelemeterTest.scala ├── docs │ ├── announcer.md │ ├── client_tls.md │ ├── config.md │ ├── failure_accrual.md │ ├── interpreter.md │ ├── load_balancer.md │ ├── namer.md │ ├── protocol-h2.md │ ├── protocol-http.md │ ├── protocol-mux.md │ ├── protocol-thrift.md │ ├── protocol-thriftmux.md │ ├── response_classifier.md │ ├── retries.md │ ├── routers.md │ ├── telemetry.md │ └── transformer.md ├── examples │ ├── access-logs.yaml │ ├── admin-tls.yaml │ ├── admin.yaml │ ├── client-options.yaml │ ├── consul-interpreter.yaml │ ├── consul.yaml │ ├── curator.yaml │ ├── disable-tls.yaml │ ├── example.dtab │ ├── fs.yaml │ ├── h2.yaml │ ├── h2spec.yaml │ ├── http.yaml │ ├── influxdb.yaml │ ├── io.l5d.fs │ │ ├── cat │ │ ├── default │ │ ├── dog │ │ └── thrift │ ├── k8s-servicemesh.yaml │ ├── k8s.yaml │ ├── loadbalancer.yaml │ ├── marathon-leader.yaml │ ├── marathon.yaml │ ├── mutualTls.yaml │ ├── namerd-http.yaml │ ├── namerd-mesh.yaml │ ├── namerd-tls.yaml │ ├── namerd.yaml │ ├── prometheus.yaml │ ├── proxy.yaml │ ├── recent-requests.yaml │ ├── retries.yaml │ ├── rewriting.yaml │ ├── serversets.yaml │ ├── service-options.yaml │ ├── servicemesh.yaml │ ├── socket-options.yaml │ ├── src │ │ └── test │ │ │ └── scala │ │ │ └── io │ │ │ └── buoyant │ │ │ └── linkerd │ │ │ └── examples │ │ │ └── ExamplesTest.scala │ ├── static_namer.yaml │ ├── statsd.yaml │ ├── thrift.yaml │ ├── thriftmux.yaml │ ├── tls.yaml │ ├── tracelog.yaml │ └── zipkin.yaml ├── failure-accrual │ └── src │ │ ├── main │ │ ├── resources │ │ │ └── META-INF │ │ │ │ └── services │ │ │ │ └── io.buoyant.linkerd.FailureAccrualInitializer │ │ └── scala │ │ │ └── io │ │ │ └── buoyant │ │ │ └── linkerd │ │ │ └── failureAccrual │ │ │ ├── ConsecutiveFailuresInitializer.scala │ │ │ ├── NoneInitializer.scala │ │ │ ├── SuccessRateInitializer.scala │ │ │ └── SuccessRateWindowedInitializer.scala │ │ └── test │ │ └── scala │ │ └── io │ │ └── buoyant │ │ └── linkerd │ │ └── failureAccrual │ │ ├── ConfigTest.scala │ │ ├── ConsecutiveFailuresTest.scala │ │ ├── NoneTest.scala │ │ ├── SuccessRateTest.scala │ │ └── SuccessRateWindowedTest.scala ├── main │ └── src │ │ ├── e2e │ │ └── scala │ │ │ └── io │ │ │ └── buoyant │ │ │ └── linkerd │ │ │ └── EndToEndTest.scala │ │ └── main │ │ ├── resources │ │ └── log4j.properties │ │ └── scala │ │ └── io │ │ └── buoyant │ │ └── linkerd │ │ └── Main.scala ├── protocol │ ├── benchmark │ │ └── src │ │ │ └── main │ │ │ └── scala │ │ │ └── io │ │ │ └── buoyant │ │ │ └── protocol │ │ │ └── AccessLoggerBenchmark.scala │ ├── h2 │ │ └── src │ │ │ ├── e2e │ │ │ └── scala │ │ │ │ └── io │ │ │ │ └── buoyant │ │ │ │ └── linkerd │ │ │ │ └── protocol │ │ │ │ └── h2 │ │ │ │ ├── H2EndToEndTest.scala │ │ │ │ └── StreamClassificationEndToEndTest.scala │ │ │ ├── integration │ │ │ └── scala │ │ │ │ └── io │ │ │ │ └── buoyant │ │ │ │ └── linkerd │ │ │ │ └── protocol │ │ │ │ └── h2 │ │ │ │ └── ForwardClientCertTest.scala │ │ │ ├── main │ │ │ ├── resources │ │ │ │ └── META-INF │ │ │ │ │ └── services │ │ │ │ │ ├── io.buoyant.linkerd.IdentifierInitializer │ │ │ │ │ ├── io.buoyant.linkerd.ProtocolInitializer │ │ │ │ │ ├── io.buoyant.linkerd.RequestAuthorizerInitializer │ │ │ │ │ ├── io.buoyant.linkerd.ResponseClassifierInitializer │ │ │ │ │ └── io.buoyant.linkerd.TracePropagatorInitializer │ │ │ └── scala │ │ │ │ ├── com │ │ │ │ └── twitter │ │ │ │ │ └── finagle │ │ │ │ │ └── buoyant │ │ │ │ │ └── h2 │ │ │ │ │ └── LinkerdHeaders.scala │ │ │ │ └── io │ │ │ │ └── buoyant │ │ │ │ └── linkerd │ │ │ │ └── protocol │ │ │ │ ├── H2Config.scala │ │ │ │ └── h2 │ │ │ │ ├── ClientConfig.scala │ │ │ │ ├── ErrorReseter.scala │ │ │ │ ├── H2AccessLogger.scala │ │ │ │ ├── H2ClassifierConfig.scala │ │ │ │ ├── H2Classifiers.scala │ │ │ │ ├── H2DiagnosticTracer.scala │ │ │ │ ├── H2RequestAuthorizerConfig.scala │ │ │ │ ├── H2TracePropagatorConfig.scala │ │ │ │ ├── HeaderPathIdentifier.scala │ │ │ │ ├── HeaderTokenIdentifier.scala │ │ │ │ ├── IngressIdentifier.scala │ │ │ │ ├── LinkerdTracePropagator.scala │ │ │ │ ├── ZipkinTracePropagator.scala │ │ │ │ ├── grpc │ │ │ │ ├── GrpcClassifier.scala │ │ │ │ └── GrpcClassifierConfig.scala │ │ │ │ └── istio │ │ │ │ ├── H2IstioRequest.scala │ │ │ │ ├── H2IstioRequestHandler.scala │ │ │ │ ├── H2IstioResponse.scala │ │ │ │ ├── IstioIdentifier.scala │ │ │ │ ├── IstioIngressIdentifier.scala │ │ │ │ └── IstioRequestAuthorizer.scala │ │ │ └── test │ │ │ └── scala │ │ │ └── io │ │ │ └── buoyant │ │ │ └── linkerd │ │ │ └── protocol │ │ │ └── h2 │ │ │ ├── CompliantGrpcClassifierTest.scala │ │ │ ├── Downstream.scala │ │ │ ├── ErrorReseterTest.scala │ │ │ ├── GrpcClassifierTest.scala │ │ │ ├── H2ClassifiersTest.scala │ │ │ ├── H2ConfigTest.scala │ │ │ ├── H2DiagnosticTracerTest.scala │ │ │ ├── H2InitializerTest.scala │ │ │ ├── HeaderPathIdentifierTest.scala │ │ │ ├── HeaderTokenIdentifierTest.scala │ │ │ ├── IngressIdentifierConfigTest.scala │ │ │ ├── IngressIdentifierTest.scala │ │ │ ├── Upstream.scala │ │ │ └── istio │ │ │ ├── H2IstioRequestTest.scala │ │ │ ├── H2IstioResponseTest.scala │ │ │ ├── IstioIdentifierTest.scala │ │ │ └── IstioRequestAuthorizerConfigTest.scala │ ├── http │ │ └── src │ │ │ ├── e2e │ │ │ ├── resources │ │ │ │ ├── cacert.pem │ │ │ │ ├── linkerd-tls-e2e-cert.pem │ │ │ │ └── linkerd-tls-e2e-key.pem │ │ │ └── scala │ │ │ │ └── io │ │ │ │ └── buoyant │ │ │ │ └── linkerd │ │ │ │ └── protocol │ │ │ │ ├── HttpEndToEndTest.scala │ │ │ │ ├── HttpStreamingTest.scala │ │ │ │ ├── HttpTlsEndToEndTest.scala │ │ │ │ ├── ResponseClassificationEndToEndTest.scala │ │ │ │ └── RetriesEndToEndTest.scala │ │ │ ├── integration │ │ │ └── scala │ │ │ │ └── io │ │ │ │ └── buoyant │ │ │ │ └── linkerd │ │ │ │ └── protocol │ │ │ │ ├── ClientAuthTest.scala │ │ │ │ ├── Downstream.scala │ │ │ │ ├── ForwardClientCertTest.scala │ │ │ │ ├── TlsBoundPathTest.scala │ │ │ │ ├── TlsCertReloadingTest.scala │ │ │ │ ├── TlsNoValidationTest.scala │ │ │ │ ├── TlsStaticValidationTest.scala │ │ │ │ ├── TlsTerminationTest.scala │ │ │ │ └── Upstream.scala │ │ │ ├── main │ │ │ ├── resources │ │ │ │ └── META-INF │ │ │ │ │ └── services │ │ │ │ │ ├── io.buoyant.linkerd.IdentifierInitializer │ │ │ │ │ ├── io.buoyant.linkerd.ProtocolInitializer │ │ │ │ │ ├── io.buoyant.linkerd.RequestAuthorizerInitializer │ │ │ │ │ ├── io.buoyant.linkerd.ResponseClassifierInitializer │ │ │ │ │ └── io.buoyant.linkerd.TracePropagatorInitializer │ │ │ └── scala │ │ │ │ ├── com │ │ │ │ └── twitter │ │ │ │ │ └── finagle │ │ │ │ │ └── buoyant │ │ │ │ │ └── linkerd │ │ │ │ │ ├── DelayedRelease.scala │ │ │ │ │ └── LinkerdHeaders.scala │ │ │ │ └── io │ │ │ │ └── buoyant │ │ │ │ └── linkerd │ │ │ │ └── protocol │ │ │ │ ├── HttpConfig.scala │ │ │ │ ├── HttpIdentifierConfig.scala │ │ │ │ ├── HttpRequestAuthorizerConfig.scala │ │ │ │ ├── HttpTracePropagatorConfig.scala │ │ │ │ ├── LinkerdTracePropagator.scala │ │ │ │ ├── ZipkinTracePropagator.scala │ │ │ │ └── http │ │ │ │ ├── AccessLogger.scala │ │ │ │ ├── DiagnosticTracer.scala │ │ │ │ ├── ErrorResponder.scala │ │ │ │ ├── FramingFilter.scala │ │ │ │ ├── HeaderIdentifierConfig.scala │ │ │ │ ├── HeaderTokenIdentifierConfig.scala │ │ │ │ ├── IngressIdentifier.scala │ │ │ │ ├── MethodAndHostIdentifierConfig.scala │ │ │ │ ├── PathIdentifierConfig.scala │ │ │ │ ├── ResponseClassifiers.scala │ │ │ │ ├── RewriteHostHeader.scala │ │ │ │ ├── StaticIdentifierConfig.scala │ │ │ │ ├── StatusCodeStatsFilter.scala │ │ │ │ └── istio │ │ │ │ ├── HttpIstioRequest.scala │ │ │ │ ├── HttpIstioRequestHandler.scala │ │ │ │ ├── HttpIstioResponse.scala │ │ │ │ ├── IstioIdentifier.scala │ │ │ │ ├── IstioIngressIdentifier.scala │ │ │ │ └── IstioRequestAuthorizer.scala │ │ │ └── test │ │ │ └── scala │ │ │ ├── com │ │ │ └── twitter │ │ │ │ └── finagle │ │ │ │ └── buoyant │ │ │ │ └── linkerd │ │ │ │ └── HeadersTest.scala │ │ │ └── io │ │ │ └── buoyant │ │ │ └── linkerd │ │ │ └── protocol │ │ │ ├── HttpInitializerTest.scala │ │ │ └── http │ │ │ ├── AccessLoggerTest.scala │ │ │ ├── DiagnosticTracerTest.scala │ │ │ ├── ErrorResponderTest.scala │ │ │ ├── HeaderIdentifierConfigTest.scala │ │ │ ├── HeaderTokenIdentifierConfigTest.scala │ │ │ ├── HttpConfigTest.scala │ │ │ ├── IngressIdentifierConfigTest.scala │ │ │ ├── IngressIdentifierTest.scala │ │ │ ├── MethodAndHostIdentifierConfigTest.scala │ │ │ ├── PathIdentifierConfigTest.scala │ │ │ ├── ResponseClassifiersTest.scala │ │ │ ├── RewriteHostHeaderTest.scala │ │ │ ├── StaticIdentifierConfigTest.scala │ │ │ ├── StatusCodeStatsFilterTest.scala │ │ │ └── istio │ │ │ ├── HttpIstioRequestTest.scala │ │ │ ├── HttpIstioResponseTest.scala │ │ │ ├── IstioIdentifierTest.scala │ │ │ └── IstioRequestAuthorizerInitializerConfigTest.scala │ ├── mux │ │ └── src │ │ │ └── main │ │ │ ├── resources │ │ │ └── META-INF │ │ │ │ └── services │ │ │ │ └── io.buoyant.linkerd.ProtocolInitializer │ │ │ └── scala │ │ │ └── io │ │ │ └── buoyant │ │ │ └── linkerd │ │ │ └── protocol │ │ │ └── MuxInitializer.scala │ ├── thrift │ │ └── src │ │ │ ├── e2e │ │ │ └── scala │ │ │ │ └── io │ │ │ │ └── buoyant │ │ │ │ └── linkerd │ │ │ │ └── protocol │ │ │ │ └── ThriftEndToEndTest.scala │ │ │ ├── main │ │ │ ├── resources │ │ │ │ └── META-INF │ │ │ │ │ └── services │ │ │ │ │ ├── io.buoyant.config.ConfigDeserializer │ │ │ │ │ ├── io.buoyant.config.ConfigSerializer │ │ │ │ │ └── io.buoyant.linkerd.ProtocolInitializer │ │ │ └── scala │ │ │ │ ├── com │ │ │ │ └── twitter │ │ │ │ │ └── finagle │ │ │ │ │ └── buoyant │ │ │ │ │ └── linkerd │ │ │ │ │ ├── TTwitterClientFilter.scala │ │ │ │ │ ├── TTwitterServerFilter.scala │ │ │ │ │ ├── ThriftClientPrep.scala │ │ │ │ │ ├── ThriftServerPrep.scala │ │ │ │ │ └── ThriftTraceInitializer.scala │ │ │ │ └── io │ │ │ │ └── buoyant │ │ │ │ ├── config │ │ │ │ └── types │ │ │ │ │ └── ThriftProtocolDeserializer.scala │ │ │ │ └── linkerd │ │ │ │ └── protocol │ │ │ │ └── ThriftInitializer.scala │ │ │ └── test │ │ │ └── scala │ │ │ └── io │ │ │ └── buoyant │ │ │ └── linkerd │ │ │ └── protocol │ │ │ └── ThriftInitializerTest.scala │ └── thriftmux │ │ └── src │ │ ├── main │ │ ├── resources │ │ │ └── META-INF │ │ │ │ └── services │ │ │ │ └── io.buoyant.linkerd.ProtocolInitializer │ │ └── scala │ │ │ ├── com │ │ │ └── twitter │ │ │ │ └── finagle │ │ │ │ └── buoyant │ │ │ │ └── linkerd │ │ │ │ └── ThriftMuxServerPrep.scala │ │ │ └── io │ │ │ └── buoyant │ │ │ └── linkerd │ │ │ └── protocol │ │ │ └── ThriftMuxInitializer.scala │ │ └── test │ │ └── scala │ │ └── io │ │ └── buoyant │ │ └── linkerd │ │ └── protocol │ │ └── ThriftMuxInitializerTest.scala ├── telemeter │ └── usage │ │ └── src │ │ └── main │ │ └── resources │ │ └── META-INF │ │ └── services │ │ └── io.buoyant.telemetry.TelemeterInitializer └── tls │ └── src │ └── main │ └── scala │ └── io │ └── buoyant │ └── linkerd │ └── tls │ └── TlsUtils.scala ├── marathon └── src │ ├── main │ └── scala │ │ └── io │ │ └── buoyant │ │ └── marathon │ │ └── v2 │ │ ├── Api.scala │ │ └── WatchState.scala │ └── test │ └── scala │ └── io │ └── buoyant │ └── marathon │ └── v2 │ └── ApiTest.scala ├── mesh ├── README.md └── core │ └── src │ └── main │ ├── protobuf │ ├── codec.proto │ ├── delegator.proto │ ├── dtab.proto │ ├── interpreter.proto │ └── resolver.proto │ └── scala │ └── io │ └── linkerd │ └── mesh │ └── Converters.scala ├── namer ├── consul │ └── src │ │ ├── main │ │ ├── resources │ │ │ └── META-INF │ │ │ │ └── services │ │ │ │ └── io.buoyant.namer.NamerInitializer │ │ └── scala │ │ │ └── io │ │ │ └── buoyant │ │ │ └── namer │ │ │ └── consul │ │ │ ├── ConsulInitializer.scala │ │ │ ├── ConsulNamer.scala │ │ │ ├── LookupCache.scala │ │ │ ├── SvcAddr.scala │ │ │ └── package.scala │ │ └── test │ │ └── scala │ │ └── io │ │ └── buoyant │ │ └── namer │ │ └── consul │ │ ├── ConsulNamerTest.scala │ │ ├── ConsulTest.scala │ │ └── SvcAddrTest.scala ├── core │ └── src │ │ ├── main │ │ ├── resources │ │ │ └── META-INF │ │ │ │ └── services │ │ │ │ ├── io.buoyant.namer.NamerInitializer │ │ │ │ └── io.buoyant.namer.TransformerInitializer │ │ └── scala │ │ │ └── io │ │ │ └── buoyant │ │ │ ├── RewritingNamer.scala │ │ │ ├── hostport.scala │ │ │ ├── http.scala │ │ │ ├── namer │ │ │ ├── BackoffConfig.scala │ │ │ ├── ConfiguredDtabNamer.scala │ │ │ ├── ConstTransformer.scala │ │ │ ├── ConstTransformerInitializer.scala │ │ │ ├── DefaultInterpreterInitializer.scala │ │ │ ├── DelegateTree.scala │ │ │ ├── Delegator.scala │ │ │ ├── EnumeratingNamer.scala │ │ │ ├── InstrumentedVar.scala │ │ │ ├── InterpreterInitializer.scala │ │ │ ├── JNamer.scala │ │ │ ├── Metadata.scala │ │ │ ├── NameTreeTransformer.scala │ │ │ ├── NamerInitializer.scala │ │ │ ├── Param.scala │ │ │ ├── Paths.scala │ │ │ ├── ReplaceTransformer.scala │ │ │ ├── ReplaceTransformerInitializer.scala │ │ │ ├── RewritingNamer.scala │ │ │ ├── RewritingNamerInitializer.scala │ │ │ ├── TransformerConfig.scala │ │ │ └── package.scala │ │ │ └── rinet.scala │ │ └── test │ │ └── scala │ │ └── io │ │ └── buoyant │ │ ├── HostPortTest.scala │ │ ├── HttpTest.scala │ │ ├── RinetTest.scala │ │ └── namer │ │ ├── DelegateTreeTest.scala │ │ ├── InstrumentedVarTest.scala │ │ ├── InterpreterInitializerTest.scala │ │ ├── NamerInitializerTest.scala │ │ ├── NamerInitializersTest.scala │ │ ├── NamerTestUtil.scala │ │ ├── RewritingNamerTest.scala │ │ ├── TestInterpreter.scala │ │ ├── TestNamer.scala │ │ ├── TestTransformer.scala │ │ ├── booNamer.scala │ │ ├── booNamerInitializer.scala │ │ ├── booUrnsNamer.scala │ │ └── booUrnsNamerInitializer.scala ├── curator │ └── src │ │ ├── main │ │ ├── resources │ │ │ └── META-INF │ │ │ │ └── services │ │ │ │ └── io.buoyant.namer.NamerInitializer │ │ └── scala │ │ │ └── io │ │ │ └── buoyant │ │ │ └── namer │ │ │ └── curator │ │ │ ├── CuratorInitializer.scala │ │ │ └── CuratorNamer.scala │ │ └── test │ │ └── scala │ │ └── io │ │ └── buoyant │ │ └── namer │ │ └── curator │ │ └── CuratorTest.scala ├── dnssrv │ └── src │ │ ├── integration │ │ └── scala │ │ │ └── io │ │ │ └── buoyant │ │ │ └── namer │ │ │ └── dnssrv │ │ │ └── DnsSrvNamerIntegrationTest.scala │ │ ├── main │ │ ├── resources │ │ │ └── META-INF │ │ │ │ └── services │ │ │ │ └── io.buoyant.namer.NamerInitializer │ │ └── scala │ │ │ └── io │ │ │ └── buoyant │ │ │ └── namer │ │ │ └── dnssrv │ │ │ ├── DnsSrvNamer.scala │ │ │ └── DnsSrvNamerInitializer.scala │ │ └── test │ │ └── scala │ │ └── io │ │ └── buoyant │ │ └── namer │ │ └── dnssrv │ │ └── DnsSrvNamerTest.scala ├── fs │ └── src │ │ ├── main │ │ ├── resources │ │ │ └── META-INF │ │ │ │ └── services │ │ │ │ ├── io.buoyant.config.ConfigSerializer │ │ │ │ └── io.buoyant.namer.NamerInitializer │ │ └── scala │ │ │ └── io │ │ │ └── buoyant │ │ │ └── namer │ │ │ └── fs │ │ │ ├── FsInitializer.scala │ │ │ ├── UpRegSerializer.scala │ │ │ ├── WatchState.scala │ │ │ ├── Watcher.scala │ │ │ └── WatchingNamer.scala │ │ └── test │ │ └── scala │ │ └── io │ │ └── buoyant │ │ └── namer │ │ └── fs │ │ └── FsTest.scala ├── istio │ └── src │ │ └── main │ │ ├── resources │ │ └── services │ │ │ └── io.buoyant.namer.NamerInitializer │ │ └── scala │ │ └── io │ │ └── buoyant │ │ └── namer │ │ └── k8s │ │ └── istio │ │ └── IstioInitializer.scala ├── k8s │ └── src │ │ ├── main │ │ ├── resources │ │ │ └── META-INF │ │ │ │ └── services │ │ │ │ └── io.buoyant.namer.NamerInitializer │ │ └── scala │ │ │ └── io │ │ │ └── buoyant │ │ │ └── namer │ │ │ └── k8s │ │ │ ├── K8sExternalInitializer.scala │ │ │ ├── K8sInitializer.scala │ │ │ └── K8sNamespacedInitializer.scala │ │ └── test │ │ └── scala │ │ └── io │ │ └── buoyant │ │ └── namer │ │ └── k8s │ │ ├── K8sExternalTest.scala │ │ └── K8sTest.scala ├── marathon │ └── src │ │ ├── main │ │ ├── resources │ │ │ └── META-INF │ │ │ │ └── services │ │ │ │ └── io.buoyant.namer.NamerInitializer │ │ └── scala │ │ │ └── io │ │ │ └── buoyant │ │ │ └── namer │ │ │ └── marathon │ │ │ ├── AppIdNamer.scala │ │ │ ├── Authenticator.scala │ │ │ ├── BasicAuthenticatorFilter.scala │ │ │ └── MarathonInitializer.scala │ │ └── test │ │ └── scala │ │ └── io │ │ └── buoyant │ │ └── namer │ │ └── marathon │ │ ├── AppIdNamerTest.scala │ │ ├── AuthenticatorTest.scala │ │ └── MarathonTest.scala ├── rancher │ └── src │ │ ├── main │ │ ├── resources │ │ │ └── META-INF │ │ │ │ └── services │ │ │ │ └── io.buoyant.namer.NamerInitializer │ │ └── scala │ │ │ └── io │ │ │ └── buoyant │ │ │ └── namer │ │ │ └── rancher │ │ │ ├── RancherClient.scala │ │ │ ├── RancherInitializer.scala │ │ │ └── RancherNamer.scala │ │ └── test │ │ └── scala │ │ └── io │ │ └── buoyant │ │ └── namer │ │ └── rancher │ │ └── RancherTest.scala ├── serversets │ └── src │ │ ├── main │ │ ├── resources │ │ │ └── META-INF │ │ │ │ └── services │ │ │ │ └── io.buoyant.namer.NamerInitializer │ │ └── scala │ │ │ ├── com │ │ │ └── twitter │ │ │ │ └── finagle │ │ │ │ └── serversets2 │ │ │ │ └── BouyantZkResolver.scala │ │ │ └── io │ │ │ └── buoyant │ │ │ └── namer │ │ │ └── serversets │ │ │ ├── ServersetNamer.scala │ │ │ └── ServersetsInitializer.scala │ │ └── test │ │ └── scala │ │ └── io │ │ └── buoyant │ │ └── namer │ │ └── serversets │ │ ├── ServersetNamerTest.scala │ │ └── ServersetsTest.scala └── zk-leader │ └── src │ ├── main │ ├── java │ │ └── com │ │ │ └── twitter │ │ │ └── common │ │ │ └── zookeeper │ │ │ ├── Candidate.java │ │ │ ├── CandidateImpl.java │ │ │ └── ExceptionalCommand.java │ ├── resources │ │ └── META-INF │ │ │ └── services │ │ │ └── io.buoyant.namer.NamerInitializer │ └── scala │ │ └── io │ │ └── buoyant │ │ └── namer │ │ └── zk │ │ ├── ZkLeaderNamer.scala │ │ ├── ZkLeaderNamerInitializer.scala │ │ └── leader.scala │ └── test │ └── scala │ └── io │ └── buoyant │ └── namer │ └── zk │ └── ZkLeaderTest.scala ├── namerd ├── README.md ├── core │ └── src │ │ ├── main │ │ └── scala │ │ │ └── io │ │ │ └── buoyant │ │ │ └── namerd │ │ │ ├── DtabCodec.scala │ │ │ ├── DtabStore.scala │ │ │ ├── DtabStoreInitializer.scala │ │ │ ├── InterfaceConfig.scala │ │ │ ├── InterpreterInterfaceConfig.scala │ │ │ ├── Namerd.scala │ │ │ ├── NamerdConfig.scala │ │ │ └── package.scala │ │ └── test │ │ └── scala │ │ └── io │ │ └── buoyant │ │ └── namerd │ │ ├── ConfiguredDtabNamerTest.scala │ │ ├── DtabStoreTest.scala │ │ ├── NamerdConfigTest.scala │ │ ├── NullDtabStore.scala │ │ └── TestNamerInterface.scala ├── dcos-bootstrap │ └── src │ │ └── main │ │ └── scala │ │ ├── com │ │ └── twitter │ │ │ └── finagle │ │ │ └── serverset2 │ │ │ └── ZkClient.scala │ │ └── io │ │ └── buoyant │ │ └── namerd │ │ └── DcosBootstrap.scala ├── docs │ ├── config.md │ ├── interface.md │ └── storage.md ├── examples │ ├── all.yaml │ ├── basic.yaml │ ├── certs │ │ ├── namerd-cacert.pem │ │ ├── namerd-cakey.pk8 │ │ ├── namerd-cert.pem │ │ └── namerd-key.pk8 │ ├── consul-storage.yaml │ ├── consul.yaml │ ├── destination.yaml │ ├── disco │ │ └── default │ ├── etcd.yaml │ ├── k8s-mesh.yaml │ ├── k8s.yaml │ ├── k8s │ │ └── 3rdparty.yaml │ ├── mesh.yaml │ ├── minimal.yaml │ ├── src │ │ └── test │ │ │ └── scala │ │ │ └── io │ │ │ └── buoyant │ │ │ └── namerd │ │ │ └── examples │ │ │ └── ExamplesTest.scala │ ├── static.yaml │ ├── tls.yaml │ └── zk.yaml ├── iface │ ├── control-http │ │ └── src │ │ │ ├── main │ │ │ ├── resources │ │ │ │ └── META-INF │ │ │ │ │ └── services │ │ │ │ │ └── io.buoyant.namerd.InterfaceInitializer │ │ │ └── scala │ │ │ │ └── io │ │ │ │ └── buoyant │ │ │ │ └── namerd │ │ │ │ └── iface │ │ │ │ ├── AddrHandler.scala │ │ │ │ ├── BindHandler.scala │ │ │ │ ├── BoundNamesHandler.scala │ │ │ │ ├── DelegateHandler.scala │ │ │ │ ├── DtabHandler.scala │ │ │ │ ├── HttpControlService.scala │ │ │ │ ├── HttpControlServiceConfig.scala │ │ │ │ ├── Json.scala │ │ │ │ ├── NameInterpreterCache.scala │ │ │ │ ├── ResolveHandler.scala │ │ │ │ └── StreamingNamerClient.scala │ │ │ └── test │ │ │ └── scala │ │ │ └── io │ │ │ └── buoyant │ │ │ └── namerd │ │ │ └── iface │ │ │ ├── HttpControlServiceConfigTest.scala │ │ │ ├── HttpControlServiceTest.scala │ │ │ └── HttpNamerEndToEndTest.scala │ ├── destination │ │ └── src │ │ │ ├── main │ │ │ ├── protobuf │ │ │ │ ├── destination.proto │ │ │ │ └── net.proto │ │ │ ├── resources │ │ │ │ ├── META-INF │ │ │ │ │ └── services │ │ │ │ │ │ └── io.buoyant.namerd.InterfaceInitializer │ │ │ │ └── pull-destination-proto.sh │ │ │ └── scala │ │ │ │ └── io │ │ │ │ └── buoyant │ │ │ │ └── namerd │ │ │ │ └── iface │ │ │ │ ├── DestinationIfaceInitializer.scala │ │ │ │ └── destination │ │ │ │ └── DestinationService.scala │ │ │ └── test │ │ │ └── scala │ │ │ └── io │ │ │ └── buoyant │ │ │ └── namerd │ │ │ └── iface │ │ │ ├── DestinationIfaceInitializerTest.scala │ │ │ └── destination │ │ │ └── DestinationServiceTest.scala │ ├── interpreter-thrift-idl │ │ └── src │ │ │ └── main │ │ │ └── thrift │ │ │ └── namer.thrift │ ├── interpreter-thrift │ │ └── src │ │ │ ├── main │ │ │ ├── resources │ │ │ │ └── META-INF │ │ │ │ │ └── services │ │ │ │ │ ├── io.buoyant.config.ConfigSerializer │ │ │ │ │ └── io.buoyant.namerd.InterfaceInitializer │ │ │ └── scala │ │ │ │ └── io │ │ │ │ └── buoyant │ │ │ │ └── namerd │ │ │ │ └── iface │ │ │ │ ├── AddrReqSerializer.scala │ │ │ │ ├── AddrSerializer.scala │ │ │ │ ├── BindReqSerializer.scala │ │ │ │ ├── BoundSerializer.scala │ │ │ │ ├── ByteBufferSerializers.scala │ │ │ │ ├── ObserverCache.scala │ │ │ │ ├── PollState.scala │ │ │ │ ├── ThriftInterpreterInterfaceConfig.scala │ │ │ │ ├── ThriftNamerClient.scala │ │ │ │ └── ThriftNamerInterface.scala │ │ │ └── test │ │ │ └── scala │ │ │ └── io │ │ │ └── buoyant │ │ │ └── namerd │ │ │ └── iface │ │ │ ├── ByteBufferSerializersTest.scala │ │ │ ├── ObserverCacheTest.scala │ │ │ ├── ThriftInterpreterInterfaceConfigTest.scala │ │ │ ├── ThriftNamerClientTest.scala │ │ │ ├── ThriftNamerEndToEndTest.scala │ │ │ └── ThriftNamerInterfaceTest.scala │ └── mesh │ │ └── src │ │ ├── main │ │ ├── resources │ │ │ └── META-INF │ │ │ │ └── services │ │ │ │ └── io.buoyant.namerd.InterfaceInitializer │ │ └── scala │ │ │ └── io │ │ │ └── buoyant │ │ │ └── namerd │ │ │ └── iface │ │ │ ├── MeshIfaceInitializer.scala │ │ │ └── mesh │ │ │ ├── CodecService.scala │ │ │ ├── DelegatorService.scala │ │ │ ├── Errors.scala │ │ │ ├── InterpreterService.scala │ │ │ └── ResolverService.scala │ │ └── test │ │ └── scala │ │ └── io │ │ └── buoyant │ │ └── namerd │ │ └── iface │ │ ├── MeshIfaceInitializerTest.scala │ │ └── mesh │ │ ├── DelegatorServiceTest.scala │ │ └── ResolverServiceTest.scala ├── main │ └── src │ │ └── main │ │ ├── resources │ │ └── log4j.properties │ │ └── scala │ │ └── io │ │ └── buoyant │ │ └── namerd │ │ ├── DtabHandler.scala │ │ ├── DtabListHandler.scala │ │ ├── Main.scala │ │ └── NamerdAdmin.scala └── storage │ ├── consul │ └── src │ │ ├── main │ │ ├── resources │ │ │ └── META-INF │ │ │ │ └── services │ │ │ │ └── io.buoyant.namerd.DtabStoreInitializer │ │ └── scala │ │ │ └── io │ │ │ └── buoyant │ │ │ └── namerd │ │ │ └── storage │ │ │ └── consul │ │ │ ├── ConsulDtabStore.scala │ │ │ └── ConsulDtabStoreInitializer.scala │ │ └── test │ │ └── scala │ │ └── io │ │ └── buoyant │ │ └── namerd │ │ └── storage │ │ └── consul │ │ ├── ConsulConfigTest.scala │ │ └── ConsulDtabStoreTest.scala │ ├── etcd │ └── src │ │ ├── integration │ │ └── scala │ │ │ └── io │ │ │ └── buoyant │ │ │ └── namerd │ │ │ └── storage │ │ │ └── etcd │ │ │ └── EtcdDtabStoreIntegrationTest.scala │ │ ├── main │ │ ├── resources │ │ │ └── META-INF │ │ │ │ └── services │ │ │ │ └── io.buoyant.namerd.DtabStoreInitializer │ │ └── scala │ │ │ └── io │ │ │ └── buoyant │ │ │ └── namerd │ │ │ └── storage │ │ │ └── etcd │ │ │ ├── EtcdDtabStore.scala │ │ │ └── EtcdDtabStoreInitializer.scala │ │ └── test │ │ └── scala │ │ └── io │ │ └── buoyant │ │ └── namerd │ │ └── storage │ │ └── etcd │ │ └── EtcdConfigTest.scala │ ├── in-memory │ └── src │ │ ├── main │ │ ├── resources │ │ │ └── META-INF │ │ │ │ └── services │ │ │ │ └── io.buoyant.namerd.DtabStoreInitializer │ │ └── scala │ │ │ └── io │ │ │ └── buoyant │ │ │ └── namerd │ │ │ └── storage │ │ │ ├── InMemoryDtabStore.scala │ │ │ └── InMemoryDtabStoreInitializer.scala │ │ └── test │ │ └── scala │ │ └── io │ │ └── buoyant │ │ └── namerd │ │ └── storage │ │ └── InMemoryDtabStoreTest.scala │ ├── k8s │ └── src │ │ ├── main │ │ ├── resources │ │ │ └── META-INF │ │ │ │ └── services │ │ │ │ ├── io.buoyant.k8s.SerializationModule │ │ │ │ └── io.buoyant.namerd.DtabStoreInitializer │ │ └── scala │ │ │ └── io │ │ │ └── buoyant │ │ │ └── namerd │ │ │ └── storage │ │ │ ├── K8sDtabStore.scala │ │ │ ├── K8sDtabStoreInitializer.scala │ │ │ └── kubernetes │ │ │ ├── Dtab.scala │ │ │ ├── DtabDescriptor.scala │ │ │ ├── DtabList.scala │ │ │ ├── DtabSerializationModule.scala │ │ │ ├── DtabWatch.scala │ │ │ └── resources.scala │ │ └── test │ │ └── scala │ │ └── io │ │ └── buoyant │ │ └── namerd │ │ └── storage │ │ └── K8sConfigTest.scala │ └── zk │ └── src │ ├── main │ ├── resources │ │ └── META-INF │ │ │ └── services │ │ │ └── io.buoyant.namerd.DtabStoreInitializer │ └── scala │ │ ├── com │ │ └── twitter │ │ │ └── finagle │ │ │ └── serverset2 │ │ │ └── client │ │ │ ├── apache │ │ │ └── buoyant │ │ │ │ └── ApacheZooKeeper.scala │ │ │ └── buoyant │ │ │ ├── ZkDtabStore.scala │ │ │ └── ZkSession.scala │ │ └── io │ │ └── buoyant │ │ └── namerd │ │ └── storage │ │ └── ZkDtabStoreInitializer.scala │ └── test │ └── scala │ └── io │ └── buoyant │ └── namerd │ └── storage │ └── AclTest.scala ├── project ├── Base.scala ├── Deps.scala ├── Finagle.scala ├── Grpc.scala ├── LinkerdBuild.scala ├── build.properties └── plugins.sbt ├── protoc ├── router ├── base-http │ └── src │ │ ├── main │ │ └── scala │ │ │ └── io │ │ │ └── buoyant │ │ │ └── router │ │ │ └── http │ │ │ ├── ForwardClientCertFilter.scala │ │ │ ├── HeadersLike.scala │ │ │ ├── MaxCallDepthFilter.scala │ │ │ └── RequestLike.scala │ │ └── test │ │ └── scala │ │ └── io │ │ └── buoyant │ │ └── router │ │ └── http │ │ └── MaxCallDepthFilterTest.scala ├── core │ └── src │ │ ├── e2e │ │ └── scala │ │ │ ├── com │ │ │ └── twitter │ │ │ │ └── finagle │ │ │ │ └── buoyant │ │ │ │ └── Echo.scala │ │ │ └── io │ │ │ └── buoyant │ │ │ └── router │ │ │ └── EchoEndToEndTest.scala │ │ ├── main │ │ └── scala │ │ │ ├── com │ │ │ └── twitter │ │ │ │ └── finagle │ │ │ │ ├── buoyant │ │ │ │ ├── Dst.scala │ │ │ │ ├── DstTracing.scala │ │ │ │ ├── EncodeResidual.scala │ │ │ │ ├── FailureAccrualFactory.scala │ │ │ │ ├── Sampler.scala │ │ │ │ ├── TotalTimeout.scala │ │ │ │ ├── TraceInitializer.scala │ │ │ │ └── package.scala │ │ │ │ ├── loadbalancer │ │ │ │ └── buoyant │ │ │ │ │ └── DeregisterLoadBalancerFactory.scala │ │ │ │ └── naming │ │ │ │ └── buoyant │ │ │ │ ├── DstBindingFactory.scala │ │ │ │ ├── DynBoundFactory.scala │ │ │ │ ├── RichConnectionFailedException.scala │ │ │ │ ├── RichConnectionFailedModule.scala │ │ │ │ └── RichNoBrokersAvailableException.scala │ │ │ └── io │ │ │ └── buoyant │ │ │ └── router │ │ │ ├── ClassifiedRetries.scala │ │ │ ├── ClassifiedTracing.scala │ │ │ ├── DiscardingFactoryToService.scala │ │ │ ├── ForwardedHeaderLabeler.scala │ │ │ ├── LocalClassifierStatsFilter.scala │ │ │ ├── Originator.scala │ │ │ ├── PathRegistry.scala │ │ │ ├── PerDstPathFilter.scala │ │ │ ├── PerDstPathStatsFilter.scala │ │ │ ├── RetryBudgetModule.scala │ │ │ ├── Router.scala │ │ │ ├── RouterLabel.scala │ │ │ ├── RoutingFactory.scala │ │ │ └── context │ │ │ ├── LocalKey.scala │ │ │ ├── dst.scala │ │ │ └── responseClassifier.scala │ │ └── test │ │ └── scala │ │ ├── com │ │ └── twitter │ │ │ └── finagle │ │ │ ├── buoyant │ │ │ └── TraceInitializerTest.scala │ │ │ └── naming │ │ │ └── buoyant │ │ │ └── DstBindingFactoryTest.scala │ │ └── io │ │ └── buoyant │ │ └── router │ │ ├── ClassifiedRetriesTest.scala │ │ ├── ClassifiedTracingTest.scala │ │ ├── DiscardingFactoryToServiceTest.scala │ │ ├── PerDstPathFilterTest.scala │ │ ├── PerDstPathStatsFilterTest.scala │ │ ├── RouterTest.scala │ │ └── RoutingFactoryTest.scala ├── h2 │ └── src │ │ ├── e2e │ │ └── scala │ │ │ └── io │ │ │ └── buoyant │ │ │ └── router │ │ │ └── h2 │ │ │ ├── ClientServerHelpers.scala │ │ │ ├── ConcurrentStreamsEndToEndTest.scala │ │ │ ├── FlowControlEndToEndTest.scala │ │ │ ├── LargeStreamEndToEndTest.scala │ │ │ └── RouterEndToEndTest.scala │ │ ├── main │ │ └── scala │ │ │ ├── com │ │ │ └── twitter │ │ │ │ └── finagle │ │ │ │ └── buoyant │ │ │ │ └── h2 │ │ │ │ └── H2FailureAccrualFactory.scala │ │ │ └── io │ │ │ └── buoyant │ │ │ └── router │ │ │ ├── H2.scala │ │ │ ├── H2Instances.scala │ │ │ ├── context │ │ │ └── h2 │ │ │ │ └── H2ClassifierCtx.scala │ │ │ └── h2 │ │ │ ├── ClassifiedRetries.scala │ │ │ ├── ClassifiedRetryFilter.scala │ │ │ ├── ClassifierFilter.scala │ │ │ ├── DupRequest.scala │ │ │ ├── H2AddForwardedHeader.scala │ │ │ ├── LocalClassifierStreamStatsFilter.scala │ │ │ ├── PerDstPathStreamStatsFilter.scala │ │ │ ├── ProxyRewriteFilter.scala │ │ │ ├── StreamStatsFilter.scala │ │ │ └── ViaHeaderFilter.scala │ │ └── test │ │ └── scala │ │ ├── com │ │ └── twitter │ │ │ └── finagle │ │ │ └── buoyant │ │ │ └── h2 │ │ │ └── H2FailureAccrualFactoryTest.scala │ │ └── io │ │ └── buoyant │ │ └── router │ │ ├── H2Test.scala │ │ └── h2 │ │ ├── ClassifiedRetryFilterTest.scala │ │ ├── DupRequestTest.scala │ │ ├── H2AddForwardedHeaderTest.scala │ │ ├── PerDstPathStreamStatsFilterTest.scala │ │ └── StreamTestStatsFilterTest.scala ├── http │ └── src │ │ ├── e2e │ │ └── scala │ │ │ └── io │ │ │ └── buoyant │ │ │ └── router │ │ │ └── HttpEndToEndTest.scala │ │ ├── main │ │ └── scala │ │ │ └── io │ │ │ └── buoyant │ │ │ ├── http │ │ │ └── status.scala │ │ │ └── router │ │ │ ├── Http.scala │ │ │ ├── HttpInstances.scala │ │ │ └── http │ │ │ ├── AddForwardedHeader.scala │ │ │ ├── ClassifierFilter.scala │ │ │ ├── ContentLengthFilter.scala │ │ │ ├── HeaderIdentifier.scala │ │ │ ├── MethodAndHostIdentifier.scala │ │ │ ├── PathIdentifier.scala │ │ │ ├── ProxyRewriteFilter.scala │ │ │ ├── StaticIdentifier.scala │ │ │ ├── StripHopByHopHeadersFilter.scala │ │ │ ├── TimestampHeaderFilter.scala │ │ │ ├── TracingFilter.scala │ │ │ └── ViaHeaderAppenderFilter.scala │ │ └── test │ │ └── scala │ │ └── io │ │ └── buoyant │ │ ├── http │ │ └── StatusTest.scala │ │ └── router │ │ └── http │ │ ├── AddForwardedHeaderTest.scala │ │ ├── HeaderIdentifierTest.scala │ │ ├── MethodAndHostIdentifierTest.scala │ │ ├── PathIdentifierTest.scala │ │ ├── ProxyRewriteFilterTest.scala │ │ ├── StripHopByHopHeadersFilterTest.scala │ │ ├── TimestampHeaderFilterTest.scala │ │ ├── TracingFilterTest.scala │ │ └── ViaHeaderAppenderFilterTest.scala ├── mux │ └── src │ │ ├── e2e │ │ └── scala │ │ │ └── io │ │ │ └── buoyant │ │ │ └── router │ │ │ └── MuxEndToEndTest.scala │ │ └── main │ │ └── scala │ │ └── io │ │ └── buoyant │ │ └── router │ │ ├── Mux.scala │ │ └── MuxEncodeResidual.scala ├── thrift-idl │ └── src │ │ └── main │ │ └── thrift │ │ └── ping.thrift ├── thrift │ └── src │ │ ├── e2e │ │ └── scala │ │ │ └── io │ │ │ └── buoyant │ │ │ └── router │ │ │ └── ThriftEndToEndTest.scala │ │ ├── main │ │ └── scala │ │ │ └── io │ │ │ └── buoyant │ │ │ └── router │ │ │ ├── Thrift.scala │ │ │ └── thrift │ │ │ ├── Dest.scala │ │ │ ├── Identifier.scala │ │ │ └── TracingFilter.scala │ │ └── test │ │ └── scala │ │ └── io │ │ └── buoyant │ │ └── router │ │ └── thrift │ │ └── IdentifierTest.scala └── thriftmux │ └── src │ ├── e2e │ └── scala │ │ └── io │ │ └── buoyant │ │ └── router │ │ └── ThriftMuxEndToEndTest.scala │ └── main │ └── scala │ └── io │ └── buoyant │ └── router │ └── ThriftMux.scala ├── sbt ├── telemetry ├── admin-metrics-export │ └── src │ │ ├── main │ │ └── scala │ │ │ └── io │ │ │ └── buoyant │ │ │ └── telemetry │ │ │ └── admin │ │ │ └── AdminMetricsExportTelemeter.scala │ │ └── test │ │ └── scala │ │ └── io │ │ └── buoyant │ │ └── telemetry │ │ └── admin │ │ └── AdminMetricsExportTelemeterTest.scala ├── core │ └── src │ │ ├── main │ │ └── scala │ │ │ ├── com │ │ │ └── twitter │ │ │ │ └── finagle │ │ │ │ └── stats │ │ │ │ └── buoyant │ │ │ │ └── Metric.scala │ │ │ └── io │ │ │ └── buoyant │ │ │ └── telemetry │ │ │ ├── MetricsTree.scala │ │ │ ├── MetricsTreeStatsReceiver.scala │ │ │ ├── Telemeter.scala │ │ │ └── TelemeterInitializer.scala │ │ └── test │ │ ├── resources │ │ └── META-INF │ │ │ └── services │ │ │ └── io.buoyant.telemetry.TelemeterInitializer │ │ └── scala │ │ └── io │ │ └── buoyant │ │ └── telemetry │ │ └── TelemeterInitializerTest.scala ├── influxdb │ └── src │ │ ├── main │ │ ├── resources │ │ │ └── META-INF │ │ │ │ └── services │ │ │ │ └── io.buoyant.telemetry.TelemeterInitializer │ │ └── scala │ │ │ └── io │ │ │ └── buoyant │ │ │ └── telemetry │ │ │ └── influxdb │ │ │ ├── InfluxDbTelemeter.scala │ │ │ └── InfluxDbTelemeterInitializer.scala │ │ └── test │ │ └── scala │ │ └── io │ │ └── buoyant │ │ └── telemetry │ │ └── influxdb │ │ ├── InfluxDbTelemeterInitializerTest.scala │ │ └── InfluxDbTelemeterTest.scala ├── prometheus │ └── src │ │ ├── main │ │ ├── resources │ │ │ └── META-INF │ │ │ │ └── services │ │ │ │ └── io.buoyant.telemetry.TelemeterInitializer │ │ └── scala │ │ │ └── io │ │ │ └── buoyant │ │ │ └── telemetry │ │ │ └── prometheus │ │ │ ├── PrometheusTelemeter.scala │ │ │ └── PrometheusTelemeterInitializer.scala │ │ └── test │ │ └── scala │ │ └── io │ │ └── buoyant │ │ └── telemetry │ │ └── prometheus │ │ ├── PrometheusTelemeterInitializerTest.scala │ │ └── PrometheusTelemeterTest.scala ├── recent-requests │ └── src │ │ └── main │ │ ├── resources │ │ └── META-INF │ │ │ └── services │ │ │ └── io.buoyant.telemetry.TelemeterInitializer │ │ └── scala │ │ └── io │ │ └── buoyant │ │ └── telemetry │ │ └── recentRequests │ │ ├── RecentRequestsAdminHandler.scala │ │ ├── RecentRequestsInitializer.scala │ │ ├── RecentRequestsTelemeter.scala │ │ └── RecentRequetsTracer.scala ├── statsd │ └── src │ │ ├── main │ │ ├── resources │ │ │ └── META-INF │ │ │ │ └── services │ │ │ │ └── io.buoyant.telemetry.TelemeterInitializer │ │ └── scala │ │ │ └── io │ │ │ └── buoyant │ │ │ └── telemetry │ │ │ ├── StatsDInitializer.scala │ │ │ └── statsd │ │ │ ├── Metric.scala │ │ │ ├── StatsDStatsReceiver.scala │ │ │ └── StatsDTelemeter.scala │ │ └── test │ │ └── scala │ │ └── io │ │ └── buoyant │ │ └── telemetry │ │ ├── StatsDInitializerTest.scala │ │ └── statsd │ │ ├── MetricTest.scala │ │ ├── MockStatsDClient.scala │ │ ├── StatsDStatsReceiverTest.scala │ │ └── StatsDTelemeterTest.scala ├── tracelog │ └── src │ │ ├── main │ │ ├── resources │ │ │ └── META-INF │ │ │ │ └── services │ │ │ │ └── io.buoyant.telemetry.TelemeterInitializer │ │ └── scala │ │ │ └── io │ │ │ └── buoyant │ │ │ └── telemetry │ │ │ └── TracelogInitializer.scala │ │ └── test │ │ └── scala │ │ └── io │ │ └── buoyant │ │ └── telemetry │ │ └── TracelogInitializerTest.scala └── zipkin │ └── src │ ├── main │ ├── resources │ │ └── META-INF │ │ │ └── services │ │ │ └── io.buoyant.telemetry.TelemeterInitializer │ └── scala │ │ ├── com │ │ └── twitter │ │ │ └── finagle │ │ │ └── buoyant │ │ │ └── zipkin │ │ │ └── ZipkinTracer.scala │ │ └── io │ │ └── buoyant │ │ └── telemetry │ │ └── ZipkinInitializer.scala │ └── test │ └── scala │ └── io │ └── buoyant │ └── telemetry │ └── ZipkinInitializerTest.scala ├── test-util └── src │ └── main │ └── scala │ └── io │ └── buoyant │ └── test │ ├── ActivityValues.scala │ ├── Awaits.scala │ ├── BudgetedRetries.scala │ ├── Events.scala │ ├── Exceptions.scala │ ├── FunSuite.scala │ ├── Json.scala │ └── Logging.scala └── validator └── src └── main └── scala └── io └── buoyant └── namerd └── Validator.scala /.github/CODEOWNERS: -------------------------------------------------------------------------------- 1 | * @adleong @olix0r @zaharidichev 2 | 3 | # William and Oliver should approve all changelog entries. 4 | CHANGES.md @wmorgan @olix0r @admc 5 | 6 | # The DNS SRV namer 7 | /namer/dnssrv/ @ccmtaylor 8 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | target/ 2 | dist/ 3 | logs/ 4 | project/boot/ 5 | project/plugins/project/ 6 | project/plugins/src_managed/ 7 | http.pid 8 | npm-debug.log 9 | .sbt-launch.jar 10 | .protoc 11 | .idea 12 | .iml 13 | .DS_Store 14 | 15 | **/*.iml 16 | -------------------------------------------------------------------------------- /ISSUE_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | # Thanks for your help improving the project! # 2 | 3 | ## Getting Help ## 4 | 5 | Github issues are for bug reports and feature requests. For questions about 6 | Linkerd, how to use it, or debugging assistance, start by 7 | [asking a question in the forums](https://discourse.linkerd.io/) or join us on 8 | [Slack](https://slack.linkerd.io/). 9 | 10 | Full details at [CONTRIBUTING.md](https://github.com/linkerd/linkerd/blob/main/CONTRIBUTING.md). 11 | 12 | ## Filing a Linkerd issue ## 13 | 14 | Issue Type: 15 | 16 | - [ ] Bug report 17 | - [ ] Feature request 18 | 19 | **What happened**: 20 | 21 | **What you expected to happen**: 22 | 23 | **How to reproduce it (as minimally and precisely as possible)**: 24 | 25 | **Anything else we need to know?**: 26 | 27 | **Environment**: 28 | - linkerd/namerd version, config files: 29 | - Platform, version, and config files (Kubernetes, DC/OS, etc): 30 | - Cloud provider or hardware configuration: 31 | -------------------------------------------------------------------------------- /MAINTAINERS.md: -------------------------------------------------------------------------------- 1 | The Linkerd maintainers are: 2 | 3 | * Oliver Gould <ver@buoyant.io> @olix0r (super-maintainer) 4 | * Alex Leong <alex@buoyant.io> @adleong (super-maintainer) 5 | * Zarahi Dichev <zahari@buoyant.io> @zaharidichev (super-maintainer) 6 | * Risha Mars <mars@buoyant.io> @rmars: Admin UI 7 | * William Morgan <william@buoyant.io> @wmorgan: docs and governance 8 | * Adam Christian <adam@buoyant.io> @admc: docs and governance 9 | * Borys Pierov <pierovbg@ncbi.nlm.nih.gov> @Ashald: Consul integration 10 | * Zack Angelo <zack.angelo@bigcommerce.com> @zackangelo: H2 codec 11 | * Christopher Taylor <ccmtaylor+linkerd@gmail.com> @ccmtaylor: DNS SRV namer 12 | 13 | 14 | <!-- 15 | # Adding a new maintainer 16 | 17 | * Submit a PR modifying this file 18 | * Add maintainer to .github/CODEOWNERS 19 | * Obtain approvals per GOVERNANCE.md 20 | * Invite maintainer to https://github.com/orgs/linkerd/teams/linkerd-maintainers/members 21 | * Invite maintainer to https://github.com/orgs/linkerd/people 22 | --> 23 | -------------------------------------------------------------------------------- /PLUGINS.md: -------------------------------------------------------------------------------- 1 | # External Linkerd Plugins 2 | 3 | A list of Linkerd plugins that exist outside of the Linkerd repo. If you have 4 | developed a Linkerd plugin, we'd love a pull request that adds it to this list. 5 | 6 | * [linkerd-zipkin telemeter](https://github.com/linkerd/linkerd-zipkin) 7 | * [Open Policy Agent identifier](https://github.com/open-policy-agent/contrib/tree/master/linkerd_authz) 8 | * [Path to Host identifier](https://github.com/Attest/linkerd-plugins) 9 | * [Static Namer Plugin](https://github.com/megogo/linkerd-static-namer) 10 | -------------------------------------------------------------------------------- /admin/names/src/main/scala/io/buoyant/admin/names/BoundNamesHandler.scala: -------------------------------------------------------------------------------- 1 | package io.buoyant.admin.names 2 | 3 | import com.twitter.finagle.Service 4 | import com.twitter.finagle.http.{MediaType, Request, Response} 5 | import com.twitter.util.{Activity, Future} 6 | import io.buoyant.namer.{EnumeratingNamer, RichActivity} 7 | 8 | class BoundNamesHandler(namers: Seq[EnumeratingNamer]) extends Service[Request, Response] { 9 | override def apply(req: Request): Future[Response] = { 10 | Activity.collect(namers.map(_.getAllNames)) 11 | .toFuture 12 | .map { names => 13 | val json = names.toSet.flatten.map(_.show).mkString("[\"", "\",\"", "\"]") 14 | val rsp = Response() 15 | rsp.contentString = json 16 | rsp.contentType = MediaType.Json 17 | rsp 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /admin/src/main/resources/io/buoyant/admin/.eslintignore: -------------------------------------------------------------------------------- 1 | js/lib 2 | js/template/compiled_templates.js 3 | -------------------------------------------------------------------------------- /admin/src/main/resources/io/buoyant/admin/.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "env": { 3 | "browser": true, 4 | "jasmine": true, 5 | "jquery": true 6 | }, 7 | "extends": [ 8 | "eslint:recommended", 9 | "plugin:jasmine/recommended", 10 | "plugin:requirejs/recommended" 11 | ], 12 | "globals": { 13 | "_": true, 14 | "Handlebars": true 15 | }, 16 | "plugins": [ 17 | "eslint-plugin-requirejs", 18 | "jasmine" 19 | ], 20 | "rules": { 21 | "no-console": ["warn", { "allow": ["warn", "error"] }], 22 | "no-unused-vars": ["error", { "argsIgnorePattern": "^_", "varsIgnorePattern": "^_" }] 23 | }, 24 | "parserOptions": { 25 | "ecmaVersion": 6 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /admin/src/main/resources/io/buoyant/admin/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | -------------------------------------------------------------------------------- /admin/src/main/resources/io/buoyant/admin/css/fonts.css: -------------------------------------------------------------------------------- 1 | @font-face { 2 | font-family: 'Source Sans Pro'; 3 | font-style: normal; 4 | font-weight: 300; 5 | src: local('Source Sans Pro Light'), local('SourceSansPro-Light'), url(fonts/SourceSansPro-300.woff2) format('woff2'); 6 | } 7 | @font-face { 8 | font-family: 'Source Sans Pro'; 9 | font-style: normal; 10 | font-weight: 400; 11 | src: local('Source Sans Pro'), local('SourceSansPro'), url(fonts/SourceSansPro-400.woff2) format('woff2'); 12 | } 13 | @font-face { 14 | font-family: 'Source Sans Pro'; 15 | font-style: normal; 16 | font-weight: 600; 17 | src: local('Source Sans Pro Semibold'), local('SourceSansPro-Semibold'), url(fonts/SourceSansPro-600.woff2) format('woff2'); 18 | } 19 | -------------------------------------------------------------------------------- /admin/src/main/resources/io/buoyant/admin/css/fonts/SourceSansPro-300.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/linkerd/linkerd/ea82499d386e44e8958be58e0386f593e639645b/admin/src/main/resources/io/buoyant/admin/css/fonts/SourceSansPro-300.woff2 -------------------------------------------------------------------------------- /admin/src/main/resources/io/buoyant/admin/css/fonts/SourceSansPro-400.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/linkerd/linkerd/ea82499d386e44e8958be58e0386f593e639645b/admin/src/main/resources/io/buoyant/admin/css/fonts/SourceSansPro-400.woff2 -------------------------------------------------------------------------------- /admin/src/main/resources/io/buoyant/admin/css/fonts/SourceSansPro-600.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/linkerd/linkerd/ea82499d386e44e8958be58e0386f593e639645b/admin/src/main/resources/io/buoyant/admin/css/fonts/SourceSansPro-600.woff2 -------------------------------------------------------------------------------- /admin/src/main/resources/io/buoyant/admin/css/fonts/glyphicons-halflings-regular.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/linkerd/linkerd/ea82499d386e44e8958be58e0386f593e639645b/admin/src/main/resources/io/buoyant/admin/css/fonts/glyphicons-halflings-regular.woff -------------------------------------------------------------------------------- /admin/src/main/resources/io/buoyant/admin/css/logger.css: -------------------------------------------------------------------------------- 1 | .table-striped>tbody>tr:nth-child(odd)>td, .table-striped>tbody>tr:nth-child(odd)>th { 2 | background-color: #161963; 3 | } 4 | 5 | .table-striped>tbody>tr>td { 6 | border-top: 1px solid dimgray 7 | } 8 | 9 | .table-striped>thead>tr { 10 | border-bottom: 1px solid dimgray 11 | } 12 | 13 | .table>thead>tr>th { 14 | border-bottom: none; 15 | } 16 | 17 | .table h5 { 18 | color: white; 19 | } 20 | 21 | table>caption{ 22 | color: red; 23 | } 24 | -------------------------------------------------------------------------------- /admin/src/main/resources/io/buoyant/admin/images/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/linkerd/linkerd/ea82499d386e44e8958be58e0386f593e639645b/admin/src/main/resources/io/buoyant/admin/images/favicon.png -------------------------------------------------------------------------------- /admin/src/main/resources/io/buoyant/admin/js/main-namerd.js: -------------------------------------------------------------------------------- 1 | require.config({ 2 | paths: { 3 | 'jQuery': 'lib/jquery-3.1.1.min', 4 | 'lodash': 'lib/lodash.min', 5 | 'handlebars.runtime': 'lib/handlebars.runtime', 6 | 'bootstrap': 'lib/bootstrap.min', 7 | 'text': 'lib/text' 8 | }, 9 | shim: { 10 | 'jQuery': { 11 | exports: '#39; 12 | }, 13 | 'lodash': { 14 | exports: '_' 15 | }, 16 | 'bootstrap': { 17 | deps : ['jQuery'], 18 | exports: 'Bootstrap' 19 | } 20 | } 21 | }); 22 | 23 | require([ 24 | 'jQuery', 25 | 'bootstrap', 26 | 'src/dashboard_delegate', 27 | 'src/logging' 28 | ], function ($, bootstrap, namerdDtabPlayground, loggingConfig) { 29 | if(window.location.pathname.indexOf("/dtab") >= 0){ 30 | new namerdDtabPlayground(); 31 | } else if(window.location.pathname.endsWith("/logging")){ 32 | new loggingConfig(); 33 | } 34 | }); 35 | -------------------------------------------------------------------------------- /admin/src/main/resources/io/buoyant/admin/js/src/dashboard_delegate.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | define([ 4 | 'jQuery', 5 | 'src/delegator' 6 | ], function( 7 | $, 8 | Delegator 9 | ) { 10 | return function() { 11 | var dtab = JSON.parse($("#data").html()); 12 | Delegator($(".delegator"), dtab.namespace, [], dtab.dtab); 13 | } 14 | }); 15 | -------------------------------------------------------------------------------- /admin/src/main/resources/io/buoyant/admin/js/src/delegate.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | define([ 4 | 'jQuery', 5 | 'src/admin', 6 | 'src/delegator' 7 | ], function( 8 | $, 9 | AdminHelpers, 10 | Delegator 11 | ) { 12 | return function() { 13 | var dtabMap = JSON.parse($("#dtab-data").html()); 14 | var dtabBaseMap = JSON.parse($("#dtab-base-data").html()); 15 | 16 | var selectedRouter = AdminHelpers.getSelectedRouter(); 17 | var dtab = dtabMap[selectedRouter]; 18 | var dtabBase = dtabBaseMap[selectedRouter]; 19 | 20 | if (!dtab) { 21 | var defaultRouter = $(".router-menu-option:first").text(); 22 | if (dtabMap[defaultRouter]) { 23 | AdminHelpers.selectRouter(defaultRouter); 24 | } else { 25 | console.warn("undefined router:", selectedRouter); 26 | } 27 | } else { 28 | $(".router-label-title").text("Router \"" + selectedRouter + "\""); 29 | 30 | Delegator($(".delegator"), selectedRouter, dtab, dtabBase); 31 | } 32 | } 33 | }); 34 | -------------------------------------------------------------------------------- /admin/src/main/resources/io/buoyant/admin/js/src/latency_color_util.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | define(['lodash'], function(_) { 4 | var latencyMetricToColorShade = { 5 | "max": "light", 6 | "p9990": "tint", 7 | "p99": "neutral", 8 | "p95": "shade", 9 | "p50": "dark" 10 | } 11 | var latencyKeys = _.keys(latencyMetricToColorShade); 12 | 13 | function createLatencyLegend(colorLookup) { 14 | return _.mapValues(latencyMetricToColorShade, function(shade) { 15 | return colorLookup[shade]; 16 | }); 17 | } 18 | 19 | function getLatencyData(latencyData, latencyLegend) { 20 | return _.map(latencyKeys, function(key) { 21 | return { 22 | latencyLabel: key, 23 | latencyValue: _.get(latencyData, "stat." + key), 24 | latencyColor: latencyLegend[key] 25 | }; 26 | }); 27 | } 28 | 29 | return { 30 | createLatencyLegend: createLatencyLegend, 31 | getLatencyData: getLatencyData 32 | }; 33 | }); 34 | -------------------------------------------------------------------------------- /admin/src/main/resources/io/buoyant/admin/js/src/router_servers.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | define([ 4 | 'jQuery', 5 | 'lodash', 6 | 'handlebars.runtime', 7 | 'src/router_server', 8 | 'template/compiled_templates' 9 | ], function($, _, Handlebars, RouterServer, templates) { 10 | var serverContainerTemplate = templates.router_server_container; 11 | var rateMetricPartial = templates["rate_metric.partial"]; 12 | 13 | var RouterServers = (function() { 14 | return function (metricsCollector, initialData, $serverEl, routerName) { 15 | var servers = initialData[routerName].servers; 16 | Handlebars.registerPartial('rateMetricPartial', rateMetricPartial); 17 | 18 | _.map(servers, function(server) { 19 | var $el = $(serverContainerTemplate({server: server})); 20 | $serverEl.append($el); 21 | RouterServer(metricsCollector, server, $el, routerName); 22 | }); 23 | } 24 | })(); 25 | return RouterServers; 26 | }); 27 | -------------------------------------------------------------------------------- /admin/src/main/resources/io/buoyant/admin/js/src/router_service.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | define([ 4 | 'jQuery', 5 | 'template/compiled_templates', 6 | 'bootstrap' 7 | ], function($, templates) { 8 | 9 | function render(svcClientMetrics, $container) { 10 | var clientsHtml = templates.router_service_metrics({ 11 | clients: svcClientMetrics 12 | }); 13 | 14 | $container.html(clientsHtml); 15 | } 16 | 17 | return function(metricsCollector, $routerContainer, service) { 18 | var $svcContainer = $(templates.router_service_container({ 19 | service: service 20 | })); 21 | $routerContainer.append($svcContainer); 22 | 23 | var $metricsContainer = $($svcContainer.find(".svc-metrics")[0]); 24 | 25 | function onMetricsUpdate(metrics) { 26 | render(metrics, $metricsContainer); 27 | } 28 | 29 | return { 30 | onMetricsUpdate: onMetricsUpdate 31 | } 32 | }; 33 | }); 34 | -------------------------------------------------------------------------------- /admin/src/main/resources/io/buoyant/admin/js/template/barchart.handlebars: -------------------------------------------------------------------------------- 1 | <div class="metrics-bar-chart-container"> 2 | <div class="bar-chart-label metric-header pull-left"> 3 | {{label.description}}: {{label.value}} 4 | </div> 5 | 6 | {{#if warningLabel}} 7 | <div class="bar-chart-label metric-header warning pull-left"> 8 | {{warningLabel}} 9 | </div> 10 | {{/if}} 11 | 12 | <div class="clearfix"></div> 13 | <div class="overlay-bars bar-container graph-gradient {{color}}" style="width:{{barContainerWidth}}px;"></div> 14 | <div class="overlay-bars bar graph-gradient {{color}}" style="width:{{barWidth}}px;"></div> 15 | </div> 16 | -------------------------------------------------------------------------------- /admin/src/main/resources/io/buoyant/admin/js/template/dentry.handlebars: -------------------------------------------------------------------------------- 1 | <div class='dentry'> 2 | <div class='dentry-part dentry-prefix' data-dentry-prefix='{{prefix}}'> 3 | <span class='prefix-content'>{{prefix}}</span> 4 | </div> 5 | <div class='fake-column text-center'>=></div> 6 | <div class='dentry-part dentry-dst' data-dentry-dst='{{dst}}'> 7 | <span class='dst-content'>{{dst}}</span> 8 | </div> 9 | <div class='fake-column'>;</div> 10 | </div> 11 | <div class='clearfix'></div> 12 | -------------------------------------------------------------------------------- /admin/src/main/resources/io/buoyant/admin/js/template/error_modal.handlebars: -------------------------------------------------------------------------------- 1 | <div class="modal-dialog modal-sm"> 2 | <div class="modal-content"> 3 | <div class="modal-header"> 4 | <button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">×</span></button> 5 | <h4 class="modal-title">Whoops!</h4> 6 | </div> 7 | <div class="modal-body"> 8 | <p>Looks like there was an issue completing your request.</p> 9 | <pre>{{.}}</pre> 10 | </div> 11 | </div> 12 | </div> 13 | -------------------------------------------------------------------------------- /admin/src/main/resources/io/buoyant/admin/js/template/latencies.partial.handlebars: -------------------------------------------------------------------------------- 1 | <div class="col-md-2 router-latencies-container"> 2 | <div class="metric-header">Latencies</div> 3 | <div class="router-latencies"> 4 | {{#each this}} 5 | <div> 6 | <span class="latency-label"> 7 | <span class="latency-legend" style="background-color:{{latencyColor}};"> </span>{{latencyLabel}} 8 | </span> 9 | <span class="pull-right latency-value">{{latencyValue}} ms</span> 10 | </div> 11 | {{/each}} 12 | </div> 13 | </div> 14 | -------------------------------------------------------------------------------- /admin/src/main/resources/io/buoyant/admin/js/template/logging_row.handlebars: -------------------------------------------------------------------------------- 1 | <tr> 2 | <td><h5>{{logger}}</h5></td> 3 | <td> 4 | <div class="btn-group pull-right" role="group"> 5 | {{#each logLevels}} 6 | <a class="btn btn-sm 7 | {{#if this.isActive}} 8 | btn-primary active disabled" {{else}} btn-default" {{/if}} 9 | data-level={{this.level}} data-logger={{@root.logger}} href="#"> 10 | {{this.level}} 11 | </a> 12 | {{/each}} 13 | </div> 14 | </td> 15 | </tr> 16 | -------------------------------------------------------------------------------- /admin/src/main/resources/io/buoyant/admin/js/template/metric.partial.handlebars: -------------------------------------------------------------------------------- 1 | <div class="{{containerClass}}"> 2 | <div class="metric-header"> 3 | {{#if description}} 4 | {{description}} 5 | {{else}} 6 | metric 7 | {{/if}} 8 | </div> 9 | <div class="{{metricClass}} {{style}}"> 10 | {{#if value}} 11 | {{value}} 12 | {{else}} 13 | 0 14 | {{/if}} 15 | </div> 16 | </div> 17 | -------------------------------------------------------------------------------- /admin/src/main/resources/io/buoyant/admin/js/template/namerd_namespace.handlebars: -------------------------------------------------------------------------------- 1 | <div class='namespace-container container-fluid'> 2 | <h3>{{namespace}} 3 | <small>used by 4 | {{#each routers}} 5 | <a class="router-list-item" href="/delegator?router={{.}}">{{.}}</a> 6 | {{/each}} 7 | </small> 8 | </h3> 9 | </div> 10 | -------------------------------------------------------------------------------- /admin/src/main/resources/io/buoyant/admin/js/template/namerd_stats.handlebars: -------------------------------------------------------------------------------- 1 | <h2>Namerd stats</h2> 2 | <div class="namerd-metrics-container"> 3 | {{> metricPartial connections containerClass="metric-container" metricClass="metric-large"}} 4 | {{> metricPartial bindcache containerClass="metric-container" metricClass="metric-large"}} 5 | {{> metricPartial addrcache containerClass="metric-container" metricClass="metric-large"}} 6 | </div> 7 | -------------------------------------------------------------------------------- /admin/src/main/resources/io/buoyant/admin/js/template/process_info.handlebars: -------------------------------------------------------------------------------- 1 | <div id="process-info"> 2 | <ul class="list-inline topline-stats"> 3 | {{#each stats}} 4 | <li class="col-md-2" data-key="{{dataKey}}"> 5 | <strong class="stat-label">{{description}}</strong> 6 | <span id="{{elemId}}" class="stat">{{value}}</span> 7 | </li> 8 | {{/each}} 9 | </ul> 10 | </div> 11 | -------------------------------------------------------------------------------- /admin/src/main/resources/io/buoyant/admin/js/template/rate_metric.partial.handlebars: -------------------------------------------------------------------------------- 1 | <div class="{{containerClass}}"> 2 | <div class="metric-header"> 3 | {{description}} 4 | </div> 5 | <div> 6 | <span class="{{metricClass}}"> 7 | {{#if value}} 8 | {{value}} 9 | {{else}} 10 | 0 11 | {{/if}} 12 | </span> 13 | 14 | <span class="metric-small"> 15 | {{#if rate}} 16 | {{rate}} 17 | {{/if}} 18 | </span> 19 | </div> 20 | </div> 21 | -------------------------------------------------------------------------------- /admin/src/main/resources/io/buoyant/admin/js/template/request_stats.handlebars: -------------------------------------------------------------------------------- 1 | {{#each keys}} 2 | <dt>{{@this}}</dt> 3 | <dd data-key="{{@this}}">...</dd> 4 | {{/each}} 5 | -------------------------------------------------------------------------------- /admin/src/main/resources/io/buoyant/admin/js/template/request_totals.handlebars: -------------------------------------------------------------------------------- 1 | <div class="row"> 2 | {{#each metrics}} 3 | <div class="col-md-2"> 4 | <div class="metric-header"> 5 | {{description}} 6 | </div> 7 | <div class="request-total"> 8 | {{#if value}} 9 | {{value}} 10 | {{else}} 11 | 0 12 | {{/if}} 13 | </div> 14 | </div> 15 | {{/each}} 16 | </div> 17 | -------------------------------------------------------------------------------- /admin/src/main/resources/io/buoyant/admin/js/template/router_client.handlebars: -------------------------------------------------------------------------------- 1 | <div class="client-metrics row"> 2 | <div class="col-md-2"> 3 | {{> rateMetricPartial data.successRate containerClass="metric-container" metricClass="metric-large success-metric"}} 4 | {{> metricPartial data.requests containerClass="metric-container" metricClass="metric-large"}} 5 | </div> 6 | 7 | <div class="col-md-2"> 8 | {{> metricPartial data.failures containerClass="metric-container" metricClass="failure-metric metric-large"}} 9 | {{> metricPartial data.connections containerClass="metric-container" metricClass="metric-large"}} 10 | </div> 11 | 12 | {{> latencyPartial latencies}} 13 | </div> 14 | -------------------------------------------------------------------------------- /admin/src/main/resources/io/buoyant/admin/js/template/router_client_container.handlebars: -------------------------------------------------------------------------------- 1 | <div class="client-container clearfix"> 2 | <div class="header-line"> </div> 3 | <div class="router-header-large"> 4 | <div class="client-id"> 5 | <div class="pull-left transformer-prefix" style="display: none">{{prefix}}</div> 6 | <div class="pull-left client-suffix is-first">{{client}}</div> 7 | </div> 8 | <div class="client-toggle pull-right"> 9 | <a class="client-expand" target="blank">expand</a> 10 | <a class="client-collapse" target="blank">collapse</a> 11 | </div> 12 | </div> 13 | <div class="client-content-container"> 14 | <div class="metrics-container col-md-6"></div> 15 | <div class="chart-container col-md-6"> 16 | <div class="router-graph-header">Client success rate</div> 17 | <div class="client-success-rate"></div> 18 | </div> 19 | <div class="clearfix"></div> 20 | <div class="bar-chart-container row"> 21 | <div class="col-md-6 lb-bar-chart"></div> 22 | </div> 23 | </div> 24 | </div> 25 | -------------------------------------------------------------------------------- /admin/src/main/resources/io/buoyant/admin/js/template/router_container.handlebars: -------------------------------------------------------------------------------- 1 | {{#each routers}} 2 | <div class="router router-{{this}} row" data-router="{{this}}"> 3 | <div class="summary row"></div> 4 | 5 | <div class="combined-client-graph"> 6 | <div class="router-graph-header">Requests per client</div> 7 | <canvas class="router-graph" height="181"></canvas> 8 | </div> 9 | 10 | <div class="router-stats row"> 11 | <div class="retries-bar-chart col-md-6"></div> 12 | <div class="retries-stats col-md-6"></div> 13 | <div class="clearfix"></div> 14 | </div> 15 | 16 | <div class="clients router-clients"> 17 | <div class="pull-left router-subsection-title">Clients</div> 18 | <div class="pull-right client-toggle"> 19 | <a target="blank" class="expand-all">expand all</a> · 20 | <a target="blank" class="collapse-all">collapse all</a> 21 | </div> 22 | </div> 23 | <div class="servers router-servers row"> 24 | <div class="router-subsection-title">Servers</div> 25 | </div> 26 | </div> 27 | {{/each}} 28 | -------------------------------------------------------------------------------- /admin/src/main/resources/io/buoyant/admin/js/template/router_option.handlebars: -------------------------------------------------------------------------------- 1 | <li><a href='#' class='router-menu-option'>{{#if label}}{{label}}{{else}}{{protocol}}{{/if}}</a></li> 2 | -------------------------------------------------------------------------------- /admin/src/main/resources/io/buoyant/admin/js/template/router_server.handlebars: -------------------------------------------------------------------------------- 1 | <div class="client-metrics row"> 2 | <div class="col-md-2"> 3 | {{> rateMetricPartial metrics.success containerClass="success-metric-container metric-container col-md-2" metricClass="metric-large success-metric"}} 4 | {{> rateMetricPartial metrics.requests containerClass="neutral-metric-container metric-container col-md-2" metricClass="metric-large"}} 5 | </div> 6 | 7 | <div class="col-md-2"> 8 | {{> rateMetricPartial metrics.failures containerClass="failure-metric-container metric-container col-md-2" metricClass="metric-large failure-metric"}} 9 | {{> rateMetricPartial metrics.connections containerClass="neutral-metric-container metric-container col-md-2" metricClass="metric-large"}} 10 | </div> 11 | 12 | {{> latencyPartial latencies}} 13 | </div> 14 | -------------------------------------------------------------------------------- /admin/src/main/resources/io/buoyant/admin/js/template/router_server_container.handlebars: -------------------------------------------------------------------------------- 1 | <div class="server-header router-header-large"> 2 | {{server}} 3 | </div> 4 | 5 | <div class="router-server clearfix"> 6 | <div class="server-metrics metrics-container col-md-6"></div> 7 | <div class="server-success-chart col-md-6"> 8 | <div class="router-graph-header">Server success rate</div> 9 | </div> 10 | </div> 11 | -------------------------------------------------------------------------------- /admin/src/main/resources/io/buoyant/admin/js/template/router_service_container.handlebars: -------------------------------------------------------------------------------- 1 | <div class="svc-container" data-service="{{service}}"> 2 | <div class="metric-large">{{service}}</div> 3 | <div class="svc-metrics"></div> 4 | </div> 5 | -------------------------------------------------------------------------------- /admin/src/main/resources/io/buoyant/admin/js/template/router_service_metrics.handlebars: -------------------------------------------------------------------------------- 1 | <div class="service-client-metrics row"> 2 | <div class="service-subsection-header">Requests per client</div> 3 | {{#each clients}} 4 | <div class="client-metrics row"> 5 | <div class="col-md-1 trail-in"> 6 | {{#if requests}} 7 | {{requests}} 8 | {{else}} 9 | 0 10 | {{/if}} 11 | </div> 12 | <div class="col-md-2">{{client}}</div> 13 | </div> 14 | {{/each}} 15 | </div> 16 | -------------------------------------------------------------------------------- /admin/src/main/resources/io/buoyant/admin/js/template/router_services_container.handlebars: -------------------------------------------------------------------------------- 1 | <div class="router row" data-router="{{router}}"> 2 | <div class="router-subsection-title">Router</div> 3 | <div class="metric-large">{{router}}</div> 4 | 5 | <div class="router-subsection-title svc-subsection">Services</div> 6 | </div> 7 | -------------------------------------------------------------------------------- /admin/src/main/resources/io/buoyant/admin/openssl.cnf: -------------------------------------------------------------------------------- 1 | # Empty openssl.cnf to satisfy phantomjs -------------------------------------------------------------------------------- /admin/src/main/resources/io/buoyant/admin/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "devDependencies": { 3 | "eslint": "^6.8.0", 4 | "eslint-plugin-jasmine": "^4.1.1", 5 | "eslint-plugin-requirejs": "^4.0.0", 6 | "handlebars": "^4.7.7", 7 | "jasmine": "^3.5.0", 8 | "karma": "^5.0.4", 9 | "karma-cli": "^2.0.0", 10 | "karma-jasmine": "^3.1.1", 11 | "karma-mocha-reporter": "^2.2.5", 12 | "karma-phantomjs-launcher": "^1.0.4", 13 | "karma-requirejs": "^1.1.0", 14 | "requirejs": "^2.3.6", 15 | "text": "git+https://git@github.com/requirejs/text.git" 16 | }, 17 | "scripts": { 18 | "eslint": "eslint js", 19 | "test": "karma start --single-run" 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /admin/src/main/resources/io/buoyant/admin/twitter-server/images/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/linkerd/linkerd/ea82499d386e44e8958be58e0386f593e639645b/admin/src/main/resources/io/buoyant/admin/twitter-server/images/favicon.ico -------------------------------------------------------------------------------- /admin/src/main/scala/io/buoyant/admin/AdminAssetsFilter.scala: -------------------------------------------------------------------------------- 1 | package io.buoyant.admin 2 | 3 | import com.twitter.finagle.{Service, SimpleFilter} 4 | import com.twitter.finagle.http.{Request, Response} 5 | 6 | class AdminAssetsFilter(additionalElements: String = "") extends SimpleFilter[Request, Response] { 7 | def apply(req: Request, svc: Service[Request, Response]) = { 8 | val serviced = svc(req) 9 | serviced.map { res => 10 | val wrappedHtml = s""" 11 | <script src="files/js/lib/jquery.min.js"></script> 12 | <script src="files/jquery-ui.min.js"></script> 13 | <script src='files/js/lib/bootstrap.min.js'></script> 14 | <link href="files/css/lib/bootstrap.min.css" rel="stylesheet"> 15 | ${additionalElements} 16 | <div class="container">${res.contentString}</div> 17 | """.stripMargin 18 | 19 | res.contentString = wrappedHtml 20 | res 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /admin/src/main/scala/io/buoyant/admin/App.scala: -------------------------------------------------------------------------------- 1 | package io.buoyant.admin 2 | 3 | import com.twitter.app.{App => TApp} 4 | import com.twitter.logging.Logging 5 | import com.twitter.server._ 6 | 7 | trait App extends TApp 8 | with Linters 9 | with Logging 10 | with TimeZoneLogFormat 11 | with Hooks 12 | with Stats 13 | -------------------------------------------------------------------------------- /admin/src/main/scala/io/buoyant/admin/Build.scala: -------------------------------------------------------------------------------- 1 | package io.buoyant.admin 2 | 3 | import java.io.InputStream 4 | import java.util.Properties 5 | 6 | /** Build metadata for a linker */ 7 | case class Build(version: String, revision: String, name: String) 8 | 9 | object Build { 10 | val unknown = Build("?", "?", "?") 11 | 12 | def load(resource: String): Build = 13 | load(getClass.getResourceAsStream(resource)) 14 | 15 | def load(stream: InputStream): Build = 16 | Option(stream) match { 17 | case None => unknown 18 | case Some(resource) => 19 | val props = new Properties 20 | try props.load(resource) finally resource.close() 21 | Build( 22 | props.getProperty("version", "?"), 23 | props.getProperty("build_revision", "?"), 24 | props.getProperty("build_name", "?") 25 | ) 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /admin/src/main/scala/io/buoyant/admin/ConfigHandler.scala: -------------------------------------------------------------------------------- 1 | package io.buoyant.admin 2 | 3 | import com.twitter.finagle.Service 4 | import com.twitter.finagle.http.{MediaType, Request, Response} 5 | import com.twitter.util.Future 6 | import io.buoyant.config.{ConfigInitializer, Parser} 7 | 8 | class ConfigHandler(config: Any, configInitializers: Iterable[Seq[ConfigInitializer]]) 9 | extends Service[Request, Response] { 10 | 11 | val mapper = Parser.jsonObjectMapper(configInitializers) 12 | 13 | override def apply(request: Request): Future[Response] = { 14 | val response = Response() 15 | response.contentType = MediaType.Json + ";charset=UTF-8" 16 | response.contentString = mapper.writeValueAsString(config) 17 | Future.value(response) 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /admin/src/main/scala/io/buoyant/admin/HtmlView.scala: -------------------------------------------------------------------------------- 1 | package io.buoyant.admin 2 | 3 | import com.twitter.finagle.http.{Response, MediaType} 4 | import com.twitter.util.Future 5 | 6 | trait HtmlView { 7 | def html( 8 | content: String, 9 | tailContent: String = "", 10 | csses: Seq[String] = Nil, 11 | navHighlight: String = "", 12 | showRouterDropdown: Boolean = false 13 | ): String 14 | 15 | def mkResponse( 16 | content: String, 17 | mediaType: String = MediaType.Html 18 | ): Future[Response] 19 | } 20 | -------------------------------------------------------------------------------- /admin/src/main/scala/io/buoyant/admin/IndexTxtHandler.scala: -------------------------------------------------------------------------------- 1 | package io.buoyant.admin 2 | 3 | import com.twitter.finagle.Service 4 | import com.twitter.finagle.http.{MediaType, Request, Response} 5 | import com.twitter.io.Buf 6 | import com.twitter.util.Future 7 | 8 | class IndexTxtHandler(paths: Seq[String]) 9 | extends Service[Request, Response] { 10 | 11 | def apply(req: Request): Future[Response] = { 12 | val rsp = Response() 13 | rsp.version = req.version 14 | rsp.contentType = MediaType.Txt 15 | rsp.content = Buf.Utf8(paths.mkString("", "\n", "\n")) 16 | Future.value(rsp) 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /admin/src/main/scala/io/buoyant/admin/StaticFilter.scala: -------------------------------------------------------------------------------- 1 | package io.buoyant.admin 2 | 3 | import com.twitter.finagle.{Service, SimpleFilter} 4 | import com.twitter.finagle.http.{MediaType, Response, Request} 5 | import com.twitter.util.Future 6 | 7 | object StaticFilter extends SimpleFilter[Request, Response] { 8 | 9 | private[this] val contentTypes: Map[String, String] = Map( 10 | "css" -> "text/css", 11 | "gif" -> MediaType.Gif, 12 | "html" -> MediaType.Html, 13 | "jpeg" -> MediaType.Jpeg, 14 | "jpg" -> MediaType.Jpeg, 15 | "js" -> MediaType.Javascript, 16 | "json" -> MediaType.Json, 17 | "png" -> MediaType.Png, 18 | "svg" -> "image/svg+xml", 19 | "txt" -> MediaType.Txt 20 | ) 21 | 22 | def apply(req: Request, svc: Service[Request, Response]) = { 23 | svc(req).flatMap { res => 24 | val contentType = contentTypes.getOrElse(req.fileExtension, "application/octet-stream") 25 | res.contentType = contentType + ";charset=UTF-8" 26 | Future.value(res) 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /admin/src/test/scala/io/buoyant/admin/LogFormatterTest.scala: -------------------------------------------------------------------------------- 1 | package io.buoyant.admin 2 | 3 | import java.util.TimeZone 4 | import java.util.logging.{Level, LogRecord} 5 | 6 | import com.twitter.util.TimeFormat 7 | import io.buoyant.test.FunSuite 8 | 9 | class LogFormatterTest extends FunSuite { 10 | 11 | val record = new LogRecord(Level.INFO, "Logging useful info") 12 | record.setMillis(1519468460239L) 13 | record.setThreadID(10) 14 | 15 | test("uses UTC timezone") { 16 | val utcFormatter = new LogFormatter( 17 | new TimeFormat(" MMdd HH:mm:ss.SSS z", TimeZone.getTimeZone("UTC")) 18 | ) 19 | assert( 20 | utcFormatter 21 | .format(record) == "I 0224 10:34:20.239 UTC THREAD10: Logging useful info\n" 22 | ) 23 | } 24 | 25 | test("uses non UTC timezone") { 26 | val gmtFormatter = new LogFormatter( 27 | new TimeFormat(" MMdd HH:mm:ss.SSS z", TimeZone.getTimeZone("GMT+8")) 28 | ) 29 | assert( 30 | gmtFormatter 31 | .format(record) == "I 0224 18:34:20.239 GMT+08:00 THREAD10: Logging useful info\n" 32 | ) 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /ci/coverage-publish.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | set -eu 4 | 5 | if [ -z "${COVERALLS_REPO_TOKEN:-}" ]; then 6 | echo "COVERALLS_REPO_TOKEN must be set" >&2 7 | exit 1 8 | fi 9 | 10 | ./sbt coverageAggregate 11 | ./sbt coveralls 12 | -------------------------------------------------------------------------------- /ci/docker-publish.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | set -eu 4 | 5 | # usage: docker-publish.sh [tag] 6 | 7 | tag="" 8 | if [ -n "${1:-}" ]; then 9 | tag="$1" 10 | fi 11 | 12 | # if DOCKER_CREDENTIALS is set, save it locally. 13 | if [ -n "${DOCKER_CREDENTIALS:-}" ]; then 14 | mkdir -p ~/.docker 15 | echo "$DOCKER_CREDENTIALS" > ~/.docker/config.json 16 | fi 17 | 18 | # For debugging, allow this to be run without pushing. 19 | docker_target="dockerBuildAndPush" 20 | if [ "${NO_PUSH:-}" = "1" ]; then 21 | docker_target="docker" 22 | fi 23 | 24 | if [ -n "$tag" ]; then 25 | ./sbt "set Base.dockerTag in (linkerd, Bundle) := \"${tag}\"" "linkerd/bundle:${docker_target}" \ 26 | "set Base.dockerTag in (namerd, Bundle) := \"${tag}\"" "namerd/bundle:${docker_target}" \ 27 | "set Base.dockerTag in (namerd, Dcos) := \"dcos-${tag}\"" "namerd/dcos:${docker_target}" 28 | else 29 | ./sbt "linkerd/bundle:${docker_target}" \ 30 | "namerd/bundle:${docker_target}" \ 31 | "namerd/dcos:${docker_target}" 32 | fi 33 | -------------------------------------------------------------------------------- /ci/update.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | set -eu 4 | 5 | if [ "${TWITTER_DEVELOP:-}" = "1" ]; then 6 | export GIT_SHA_DIR=$HOME/.gitshas 7 | mkdir -p "$GIT_SHA_DIR" 8 | ./ci/twitter-develop.sh 9 | fi 10 | 11 | ./sbt update 12 | -------------------------------------------------------------------------------- /config/src/main/resources/META-INF/services/io.buoyant.config.ConfigDeserializer: -------------------------------------------------------------------------------- 1 | io.buoyant.config.types.DirectoryDeserializer 2 | io.buoyant.config.types.DtabDeserializer 3 | io.buoyant.config.types.FileDeserializer 4 | io.buoyant.config.types.InetAddressDeserializer 5 | io.buoyant.config.types.LogLevelDeserializer 6 | io.buoyant.config.types.PathDeserializer 7 | io.buoyant.config.types.PathMatcherDeserializer 8 | io.buoyant.config.types.PortDeserializer -------------------------------------------------------------------------------- /config/src/main/resources/META-INF/services/io.buoyant.config.ConfigSerializer: -------------------------------------------------------------------------------- 1 | io.buoyant.config.types.ByteBufferSerializer 2 | io.buoyant.config.types.DirectorySerializer 3 | io.buoyant.config.types.DtabSerializer 4 | io.buoyant.config.types.FileSerializer 5 | io.buoyant.config.types.InetAddressSerializer 6 | io.buoyant.config.types.LogLevelSerializer 7 | io.buoyant.config.types.PathSerializer 8 | io.buoyant.config.types.PortSerializer 9 | io.buoyant.config.types.PathMatcherSerializer 10 | -------------------------------------------------------------------------------- /config/src/main/scala/io/buoyant/config/ConfigError.scala: -------------------------------------------------------------------------------- 1 | package io.buoyant.config 2 | 3 | import com.fasterxml.jackson.databind.jsontype.NamedType 4 | import java.net.InetSocketAddress 5 | import scala.util.control.NoStackTrace 6 | 7 | trait ConfigError extends NoStackTrace 8 | 9 | object NoRoutersSpecified extends ConfigError { 10 | def message = "At least one router must be specified in the configuration." 11 | } 12 | 13 | case class ConflictingSubtypes(t0: NamedType, t1: NamedType) extends ConfigError { 14 | def message = s"Conflicting subtypes: $t0, $t1" 15 | } 16 | 17 | case class ConflictingLabels(name: String) extends ConfigError { 18 | def message = s"Multiple routers with the label $name" 19 | } 20 | case class ConflictingStreamingOptions(name: String) extends ConfigError { 21 | def message = s"Conflicting streaming options set. Can't disable streaming and set streamAfterContentLengthKB in $name" 22 | } 23 | 24 | case class ConflictingPorts( 25 | addr0: InetSocketAddress, 26 | addr1: InetSocketAddress 27 | ) extends ConfigError { 28 | def message = s"Server conflict on port ${addr0.getPort}" 29 | } 30 | -------------------------------------------------------------------------------- /config/src/main/scala/io/buoyant/config/ConfigInitializer.scala: -------------------------------------------------------------------------------- 1 | package io.buoyant.config 2 | 3 | import com.fasterxml.jackson.databind.ObjectMapper 4 | import com.fasterxml.jackson.databind.jsontype.NamedType 5 | 6 | trait ConfigInitializer { 7 | 8 | def configClass: Class[_] 9 | def configId: String = configClass.getName 10 | 11 | lazy val namedType = new NamedType(configClass, configId) 12 | 13 | def registerSubtypes(mapper: ObjectMapper): Unit = 14 | mapper.registerSubtypes(namedType) 15 | } 16 | -------------------------------------------------------------------------------- /config/src/main/scala/io/buoyant/config/PolymorphicConfig.scala: -------------------------------------------------------------------------------- 1 | package io.buoyant.config 2 | 3 | import com.fasterxml.jackson.annotation.{JsonProperty, JsonTypeInfo} 4 | 5 | /** 6 | * An abstract class that defines the property "kind" as both a var on 7 | * the object and as type information for JSON de/serialization. 8 | * 9 | * Config objects that expect to be subclassed by multiple other configs 10 | * with different "kind" values should extend this trait. 11 | */ 12 | @JsonTypeInfo( 13 | use = JsonTypeInfo.Id.NAME, 14 | include = JsonTypeInfo.As.EXISTING_PROPERTY, 15 | property = "kind", 16 | visible = true 17 | ) 18 | abstract class PolymorphicConfig { 19 | @JsonProperty("kind") 20 | var kind: String = "" 21 | } 22 | -------------------------------------------------------------------------------- /config/src/main/scala/io/buoyant/config/types/ByteBufferSerializer.scala: -------------------------------------------------------------------------------- 1 | package io.buoyant.config.types 2 | 3 | import com.fasterxml.jackson.core.JsonGenerator 4 | import com.fasterxml.jackson.databind.SerializerProvider 5 | import com.twitter.io.Buf 6 | import io.buoyant.config.ConfigSerializer 7 | import java.nio.ByteBuffer 8 | 9 | class ByteBufferSerializer extends ConfigSerializer[ByteBuffer] { 10 | override def serialize( 11 | value: ByteBuffer, 12 | jgen: JsonGenerator, 13 | provider: SerializerProvider 14 | ): Unit = { 15 | val Buf.Utf8(s) = Buf.ByteBuffer.Shared(value) 16 | jgen.writeString(s) 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /config/src/main/scala/io/buoyant/config/types/DirectoryDeserializer.scala: -------------------------------------------------------------------------------- 1 | package io.buoyant.config.types 2 | 3 | import com.fasterxml.jackson.core.{JsonGenerator, JsonParser} 4 | import com.fasterxml.jackson.databind.{SerializerProvider, DeserializationContext} 5 | import io.buoyant.config.{ConfigSerializer, ConfigDeserializer} 6 | import java.nio.file.Paths 7 | 8 | case class Directory(path: java.nio.file.Path) { 9 | require(path.toFile.isDirectory, s"$path is not a directory") 10 | } 11 | 12 | class DirectoryDeserializer extends ConfigDeserializer[Directory] { 13 | override def deserialize(jp: JsonParser, ctxt: DeserializationContext): Directory = 14 | Directory(Paths.get(_parseString(jp, ctxt))) 15 | } 16 | 17 | class DirectorySerializer extends ConfigSerializer[Directory] { 18 | override def serialize( 19 | value: Directory, 20 | jgen: JsonGenerator, 21 | provider: SerializerProvider 22 | ): Unit = jgen.writeString(value.path.toString) 23 | } 24 | -------------------------------------------------------------------------------- /config/src/main/scala/io/buoyant/config/types/DtabDeserializer.scala: -------------------------------------------------------------------------------- 1 | package io.buoyant.config.types 2 | 3 | import com.fasterxml.jackson.core.{JsonGenerator, JsonParser} 4 | import com.fasterxml.jackson.databind.{SerializerProvider, DeserializationContext} 5 | import com.twitter.finagle.Dtab 6 | import io.buoyant.config.{ConfigSerializer, ConfigDeserializer} 7 | 8 | class DtabDeserializer extends ConfigDeserializer[Dtab] { 9 | override def deserialize(parser: JsonParser, ctx: DeserializationContext): Dtab = 10 | Dtab.read(_parseString(parser, ctx)) 11 | } 12 | 13 | class DtabSerializer extends ConfigSerializer[Dtab] { 14 | override def serialize( 15 | value: Dtab, 16 | jgen: JsonGenerator, 17 | provider: SerializerProvider 18 | ): Unit = jgen.writeString(value.show) 19 | } 20 | -------------------------------------------------------------------------------- /config/src/main/scala/io/buoyant/config/types/FileDeserializer.scala: -------------------------------------------------------------------------------- 1 | package io.buoyant.config.types 2 | 3 | import com.fasterxml.jackson.core.{JsonGenerator, JsonParser} 4 | import com.fasterxml.jackson.databind.{SerializerProvider, DeserializationContext} 5 | import io.buoyant.config.{ConfigSerializer, ConfigDeserializer} 6 | import java.nio.file.Paths 7 | 8 | case class File(path: java.nio.file.Path) { 9 | require(path.toFile.isFile, s"$path is not a file") 10 | } 11 | 12 | class FileDeserializer extends ConfigDeserializer[File] { 13 | override def deserialize(jp: JsonParser, ctxt: DeserializationContext): File = 14 | File(Paths.get(_parseString(jp, ctxt))) 15 | } 16 | 17 | class FileSerializer extends ConfigSerializer[File] { 18 | override def serialize( 19 | value: File, 20 | jgen: JsonGenerator, 21 | provider: SerializerProvider 22 | ): Unit = jgen.writeString(value.path.toString) 23 | } 24 | -------------------------------------------------------------------------------- /config/src/main/scala/io/buoyant/config/types/InetAddressDeserializer.scala: -------------------------------------------------------------------------------- 1 | package io.buoyant.config.types 2 | 3 | import com.fasterxml.jackson.core.{JsonGenerator, JsonParser} 4 | import com.fasterxml.jackson.databind.{DeserializationContext, SerializerProvider} 5 | import com.google.common.net.InetAddresses 6 | import io.buoyant.config.{ConfigDeserializer, ConfigSerializer} 7 | import java.net.InetAddress 8 | 9 | class InetAddressDeserializer extends ConfigDeserializer[InetAddress] { 10 | override def deserialize(jp: JsonParser, ctxt: DeserializationContext): InetAddress = 11 | InetAddresses.forString(_parseString(jp, ctxt)) 12 | } 13 | 14 | class InetAddressSerializer extends ConfigSerializer[InetAddress] { 15 | override def serialize( 16 | value: InetAddress, 17 | jgen: JsonGenerator, 18 | provider: SerializerProvider 19 | ): Unit = jgen.writeString(value.getHostName) 20 | } 21 | -------------------------------------------------------------------------------- /config/src/main/scala/io/buoyant/config/types/LogLevelDeserializer.scala: -------------------------------------------------------------------------------- 1 | 2 | package io.buoyant.config.types 3 | 4 | import com.fasterxml.jackson.core.{JsonGenerator, JsonParser} 5 | import com.fasterxml.jackson.databind.{SerializerProvider, DeserializationContext} 6 | import com.twitter.logging.Level 7 | import io.buoyant.config.{ConfigSerializer, ConfigDeserializer} 8 | 9 | class LogLevelDeserializer extends ConfigDeserializer[Level] { 10 | 11 | override def deserialize(jp: JsonParser, ctxt: DeserializationContext): Level = { 12 | val lname = _parseString(jp, ctxt) 13 | Level.parse(lname.toUpperCase).getOrElse { 14 | throw new IllegalArgumentException(s"Illegal log level: $lname") 15 | } 16 | } 17 | } 18 | 19 | class LogLevelSerializer extends ConfigSerializer[Level] { 20 | override def serialize( 21 | level: Level, 22 | jgen: JsonGenerator, 23 | provider: SerializerProvider 24 | ): Unit = jgen.writeString(level.name) 25 | } 26 | -------------------------------------------------------------------------------- /config/src/main/scala/io/buoyant/config/types/PathDeserializer.scala: -------------------------------------------------------------------------------- 1 | package io.buoyant.config.types 2 | 3 | import com.fasterxml.jackson.core.{JsonGenerator, JsonParser} 4 | import com.fasterxml.jackson.databind.{SerializerProvider, DeserializationContext} 5 | import com.twitter.finagle.Path 6 | import io.buoyant.config.{ConfigSerializer, ConfigDeserializer} 7 | 8 | class PathDeserializer extends ConfigDeserializer[Path] { 9 | override def deserialize(jp: JsonParser, ctxt: DeserializationContext): Path = 10 | Path.read(_parseString(jp, ctxt)) 11 | } 12 | 13 | class PathSerializer extends ConfigSerializer[Path] { 14 | override def serialize( 15 | value: Path, 16 | jgen: JsonGenerator, 17 | provider: SerializerProvider 18 | ): Unit = jgen.writeString(value.show) 19 | } 20 | -------------------------------------------------------------------------------- /config/src/main/scala/io/buoyant/config/types/PathMatcherDeserializer.scala: -------------------------------------------------------------------------------- 1 | package io.buoyant.config.types 2 | 3 | import com.fasterxml.jackson.core.{JsonGenerator, JsonParser} 4 | import com.fasterxml.jackson.databind.{SerializerProvider, DeserializationContext} 5 | import com.twitter.finagle.buoyant.PathMatcher 6 | import io.buoyant.config.{ConfigSerializer, ConfigDeserializer} 7 | 8 | class PathMatcherDeserializer extends ConfigDeserializer[PathMatcher] { 9 | override def deserialize(jp: JsonParser, ctxt: DeserializationContext): PathMatcher = 10 | PathMatcher(_parseString(jp, ctxt)) 11 | } 12 | 13 | class PathMatcherSerializer extends ConfigSerializer[PathMatcher] { 14 | override def serialize( 15 | value: PathMatcher, 16 | jgen: JsonGenerator, 17 | provider: SerializerProvider 18 | ): Unit = jgen.writeString(value.toString) 19 | } 20 | -------------------------------------------------------------------------------- /config/src/main/scala/io/buoyant/config/types/PortDeserializer.scala: -------------------------------------------------------------------------------- 1 | package io.buoyant.config.types 2 | 3 | import com.fasterxml.jackson.core.{JsonGenerator, JsonParser} 4 | import com.fasterxml.jackson.databind.{SerializerProvider, DeserializationContext} 5 | import io.buoyant.config.{ConfigSerializer, ConfigDeserializer} 6 | 7 | case class Port(port: Int) { 8 | val MinValue = 0 9 | val MaxValue = math.pow(2, 16) - 1 10 | require((MinValue <= port) && (port <= MaxValue), s"$port outside valid range for ports") 11 | } 12 | 13 | class PortDeserializer extends ConfigDeserializer[Port] { 14 | override def deserialize(jp: JsonParser, ctxt: DeserializationContext): Port = 15 | Port(jp.getIntValue) 16 | } 17 | 18 | class PortSerializer extends ConfigSerializer[Port] { 19 | override def serialize( 20 | value: Port, 21 | jgen: JsonGenerator, 22 | provider: SerializerProvider 23 | ): Unit = jgen.writeNumber(value.port) 24 | } 25 | -------------------------------------------------------------------------------- /consul/src/main/resources/META-INF/services/io.buoyant.config.ConfigDeserializer: -------------------------------------------------------------------------------- 1 | io.buoyant.consul.v1.ConsistencyModeDeserializer 2 | io.buoyant.consul.v1.HealthStatusDeserializer 3 | -------------------------------------------------------------------------------- /consul/src/main/resources/META-INF/services/io.buoyant.config.ConfigSerializer: -------------------------------------------------------------------------------- 1 | io.buoyant.consul.v1.ConsistencyModeSerializer 2 | io.buoyant.consul.v1.HealthStatusSerializer 3 | -------------------------------------------------------------------------------- /consul/src/main/scala/io/buoyant/consul/SetAuthTokenFilter.scala: -------------------------------------------------------------------------------- 1 | package io.buoyant.consul 2 | 3 | import com.twitter.finagle.http.{Request, Response} 4 | import com.twitter.finagle.{Service, SimpleFilter} 5 | 6 | class SetAuthTokenFilter(token: String) extends SimpleFilter[Request, Response] { 7 | 8 | def apply(req: Request, svc: Service[Request, Response]) = { 9 | req.headerMap.set("X-Consul-Token", token) 10 | svc(req) 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /consul/src/main/scala/io/buoyant/consul/SetHostFilter.scala: -------------------------------------------------------------------------------- 1 | package io.buoyant.consul 2 | 3 | import com.twitter.finagle.{Service, SimpleFilter} 4 | import com.twitter.finagle.http.{Request, Response} 5 | import java.net.InetSocketAddress 6 | 7 | class SetHostFilter(hostname: String, port: Int) extends SimpleFilter[Request, Response] { 8 | def this(addr: InetSocketAddress) = this(addr.getHostString, addr.getPort) 9 | 10 | val host: String = port match { 11 | case 80 | 443 => hostname 12 | case port => s"$hostname:$port" 13 | } 14 | 15 | def apply(req: Request, svc: Service[Request, Response]) = { 16 | req.host = host 17 | svc(req) 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /consul/src/main/scala/io/buoyant/consul/package.scala: -------------------------------------------------------------------------------- 1 | package io.buoyant 2 | 3 | import com.twitter.logging.Logger 4 | 5 | package object consul { 6 | private[consul] val log = Logger.get("consul") 7 | } 8 | -------------------------------------------------------------------------------- /consul/src/test/scala/io/buoyant/consul/v1/AgentApiTest.scala: -------------------------------------------------------------------------------- 1 | package io.buoyant.consul.v1 2 | 3 | import com.twitter.finagle.Service 4 | import com.twitter.finagle.http.{Request, Response} 5 | import com.twitter.io.Buf 6 | import com.twitter.util.Future 7 | import io.buoyant.test.{Awaits, Exceptions} 8 | import org.scalatest.FunSuite 9 | 10 | class AgentApiTest extends FunSuite with Awaits with Exceptions { 11 | val localAgentBuf = Buf.Utf8("""{ "Config": { "Domain": "acme.co." } }""") 12 | var lastUri = "" 13 | 14 | def stubService(buf: Buf) = Service.mk[Request, Response] { req => 15 | val rsp = Response() 16 | rsp.setContentTypeJson() 17 | rsp.content = buf 18 | rsp.headerMap.set("X-Consul-Index", "4") 19 | lastUri = req.uri 20 | Future.value(rsp) 21 | } 22 | 23 | test("localAgent returns LocalAgent instance") { 24 | val service = stubService(localAgentBuf) 25 | 26 | val result = await(AgentApi(service).localAgent()) 27 | assert(result.Config.get.Domain.get == "acme.co.") 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /consul/src/test/scala/io/buoyant/consul/v1/ConsistencyModeTest.scala: -------------------------------------------------------------------------------- 1 | package io.buoyant.consul.v1 2 | 3 | import io.buoyant.config.Parser 4 | import io.buoyant.test.Awaits 5 | import org.scalatest.FunSuite 6 | 7 | class ConsistencyModeTest extends FunSuite with Awaits { 8 | 9 | test("consistency nodes can be deserialized from lowercase names") { 10 | val yaml = "[default, consistent, stale]" 11 | val mapper = Parser.objectMapper(yaml, Iterable.empty) 12 | val modes = mapper.readValue[Seq[ConsistencyMode]](yaml) 13 | assert(modes == Seq(ConsistencyMode.Default, ConsistencyMode.Consistent, ConsistencyMode.Stale)) 14 | } 15 | 16 | } 17 | -------------------------------------------------------------------------------- /etcd/README.md: -------------------------------------------------------------------------------- 1 | # Finagle Etcd client # 2 | 3 | ## `io.buoyant.etcd` ## 4 | 5 | The `io.buoyant.etcd` package contains a generically useful Etcd 6 | client built on finagle-http. The client supports a variety of 7 | asynchronous `key` operations on etcd. It goes beyond the basic etcd 8 | operations to provide a `watch` utility that models a key's (or 9 | tree's) state as an `com.twitter.util.Activity`. 10 | 11 | ### TODO ### 12 | 13 | - `compareAndDelete` isn't yet implemented. minor oversight. 14 | - Use residual paths on the client to instrument chrooted key 15 | operations 16 | - Admin/Stats APIs 17 | - move to gRPC api 18 | -------------------------------------------------------------------------------- /finagle/README.md: -------------------------------------------------------------------------------- 1 | # Linkerd: Finagle extensions # 2 | 3 | This directory contains protocol extensions to Finagle published under 4 | the `io.buoyant` organization. 5 | -------------------------------------------------------------------------------- /finagle/h2/README.md: -------------------------------------------------------------------------------- 1 | # io.buoyant:finagle-h2 # 2 | 3 | Status: **Pre-production** -- currently testing for production-readiness 4 | 5 | -------------------------------------------------------------------------------- /finagle/h2/src/main/scala/com/twitter/finagle/buoyant/h2/netty4/Netty4H2Settings.scala: -------------------------------------------------------------------------------- 1 | package com.twitter.finagle.buoyant.h2.netty4 2 | 3 | import com.twitter.finagle.Stack 4 | import com.twitter.finagle.buoyant.h2.param.Settings._ 5 | import io.netty.handler.codec.http2.Http2Settings 6 | 7 | object Netty4H2Settings { 8 | def mk(params: Stack.Params): Http2Settings = { 9 | val s = new Http2Settings 10 | params[HeaderTableSize].size.foreach { n => s.headerTableSize(n.inBytes.toInt); () } 11 | params[InitialStreamWindowSize].size.foreach { n => s.initialWindowSize(n.inBytes.toInt); () } 12 | params[MaxConcurrentStreams].streams.foreach { n => s.maxConcurrentStreams(n); () } 13 | params[MaxFrameSize].size.foreach { n => s.maxFrameSize(n.inBytes.toInt); () } 14 | params[MaxHeaderListSize].size.foreach { n => s.maxHeaderListSize(n.inBytes.toInt); () } 15 | s 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /finagle/h2/src/main/scala/io/netty/handler/codec/http2/H2FrameStream.scala: -------------------------------------------------------------------------------- 1 | package io.netty.handler.codec.http2 2 | 3 | case class H2FrameStream(streamId: Int, streamState: Http2Stream.State) extends Http2FrameStream { 4 | override def state(): Http2Stream.State = streamState 5 | 6 | override def id(): Int = streamId 7 | } 8 | 9 | object H2FrameStream { 10 | def apply(stream: Http2FrameStream): H2FrameStream = 11 | H2FrameStream(stream.id(), stream.state()) 12 | } -------------------------------------------------------------------------------- /finagle/h2/src/main/scala/io/netty/handler/codec/http2/Http2FrameCodecServerUpgrader.scala: -------------------------------------------------------------------------------- 1 | package io.netty.handler.codec.http2 2 | 3 | import io.netty.channel.ChannelHandler 4 | 5 | /** 6 | * Adapt Http2ServerUpgradeCodec to be instantiated with an Http2FrameCodec 7 | */ 8 | class Http2FrameCodecServerUpgrader(name: String, framer: H2FrameCodec, handler: ChannelHandler) 9 | extends Http2ServerUpgradeCodec(framer.connectionHandler) { 10 | def this(framer: H2FrameCodec) = this(null, framer, framer.connectionHandler) 11 | } 12 | -------------------------------------------------------------------------------- /grpc/eg/src/main/protobuf/base.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | package io.buoyant.eg.base; 4 | 5 | message Unit {} 6 | message Exception { 7 | string message = 1; 8 | } 9 | -------------------------------------------------------------------------------- /grpc/gen/src/main/scala/io/buoyant/grpc/gen/Main.scala: -------------------------------------------------------------------------------- 1 | package io.buoyant.grpc.gen 2 | 3 | import com.twitter.app.App 4 | 5 | object Main extends App { 6 | 7 | def main(): Unit = { 8 | val req = Generator.parseRequest(System.in) 9 | val rsp = Generator.gen(req) 10 | rsp.writeTo(System.out) 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /grpc/interop/src/test/scala/io/buoyant/grpc/interop/LocalInteropTest.scala: -------------------------------------------------------------------------------- 1 | package io.buoyant.grpc.interop 2 | 3 | import com.twitter.util.Future 4 | import io.buoyant.test.FunSuite 5 | 6 | class LocalInteropTest extends FunSuite with InteropTestBase { 7 | 8 | override def withClient(f: Client => Future[Unit]): Future[Unit] = 9 | f(new Client(new Server)) 10 | } 11 | -------------------------------------------------------------------------------- /grpc/runtime/src/main/scala/io/buoyant/grpc/runtime/H2Headers.scala: -------------------------------------------------------------------------------- 1 | package io.buoyant.grpc.runtime 2 | 3 | import com.twitter.finagle.buoyant.h2 4 | 5 | private[runtime] object H2Headers { 6 | private[this] val AuthorityHeader = h2.Headers.Authority -> "" 7 | private[this] val ContentTypeHeader = "content-type" -> "application/grpc+proto" 8 | private[this] val MethodHeader = h2.Headers.Method -> h2.Method.Post.toString 9 | private[this] val SchemeHeader = h2.Headers.Scheme -> "http" 10 | 11 | def responseHeaders(http2Status: h2.Status = h2.Status.Ok): h2.Headers = h2.Headers( 12 | h2.Headers.Status -> http2Status.toString, 13 | ContentTypeHeader 14 | ) 15 | 16 | def requestHeaders(path: String): h2.Headers = h2.Headers( 17 | h2.Headers.Path -> path, 18 | AuthorityHeader, 19 | ContentTypeHeader, 20 | MethodHeader, 21 | SchemeHeader 22 | ) 23 | } 24 | -------------------------------------------------------------------------------- /grpc/runtime/src/test/scala/io/buoyant/grpc/runtime/GrpcStatusTest.scala: -------------------------------------------------------------------------------- 1 | package io.buoyant.grpc.runtime 2 | 3 | import com.twitter.concurrent.AsyncQueue 4 | import com.twitter.finagle.buoyant.h2 5 | import com.twitter.io.Buf 6 | import com.twitter.util._ 7 | import io.buoyant.test.FunSuite 8 | 9 | class GrcpStatusTest extends FunSuite { 10 | test("null message doesn't break trailers") { 11 | val s = GrpcStatus.Ok(null) 12 | val t = s.toTrailers 13 | assert(t.contains("grpc-status")) 14 | assert(!t.contains("grpc-message")) 15 | } 16 | 17 | test("empty message doesn't break trailers") { 18 | val s = GrpcStatus.Ok(null) 19 | val t = s.toTrailers 20 | assert(t.contains("grpc-status")) 21 | assert(!t.contains("grpc-message")) 22 | } 23 | 24 | test("message included in trailers") { 25 | val s = GrpcStatus.Ok("just a message") 26 | val t = s.toTrailers 27 | assert(t.contains("grpc-status")) 28 | assert(t.contains("grpc-message")) 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /interpreter/consul/src/main/resources/META-INF/services/io.buoyant.namer.InterpreterInitializer: -------------------------------------------------------------------------------- 1 | io.buoyant.interpreter.consul.ConsulInterpreterInitializer 2 | -------------------------------------------------------------------------------- /interpreter/fs/src/main/resources/META-INF/services/io.buoyant.namer.InterpreterInitializer: -------------------------------------------------------------------------------- 1 | io.buoyant.interpreter.fs.FsInterpreterInitializer 2 | -------------------------------------------------------------------------------- /interpreter/fs/src/main/scala/io/buoyant/interpreter/fs/FsInterpreterInitializer.scala: -------------------------------------------------------------------------------- 1 | package io.buoyant.interpreter.fs 2 | 3 | import io.buoyant.namer.InterpreterInitializer 4 | 5 | class FsInterpreterInitializer extends InterpreterInitializer { 6 | override def configClass: Class[_] = classOf[FsInterpreterConfig] 7 | override def configId: String = "io.l5d.fs" 8 | } 9 | 10 | object FsInterpreterInitializer extends FsInterpreterInitializer 11 | -------------------------------------------------------------------------------- /interpreter/fs/src/test/scala/io/buoyant/interpreter/fs/FsInterpreterTest.scala: -------------------------------------------------------------------------------- 1 | package io.buoyant.interpreter.fs 2 | 3 | import com.twitter.finagle.util.LoadService 4 | import io.buoyant.config.Parser 5 | import io.buoyant.namer.{InterpreterConfig, InterpreterInitializer} 6 | import java.io.File 7 | import org.scalatest.FunSuite 8 | 9 | class FsInterpreterTest extends FunSuite { 10 | 11 | test("interpreter registration") { 12 | assert(LoadService[InterpreterInitializer]().exists(_.isInstanceOf[FsInterpreterInitializer])) 13 | } 14 | 15 | test("parse config") { 16 | val dtabFile = File.createTempFile("example", ".dtab") 17 | try { 18 | val yaml = 19 | s"""|kind: io.l5d.fs 20 | |dtabFile: ${dtabFile.getPath} 21 | |""".stripMargin 22 | 23 | val mapper = Parser.objectMapper(yaml, Iterable(Seq(FsInterpreterInitializer))) 24 | val fs = mapper.readValue[InterpreterConfig](yaml).asInstanceOf[FsInterpreterConfig] 25 | assert(fs.dtabFile.path.toString == dtabFile.getPath) 26 | } finally { 27 | val _ = dtabFile.delete() 28 | } 29 | } 30 | } 31 | 32 | -------------------------------------------------------------------------------- /interpreter/istio/src/main/resources/META-INF/services/io.buoyant.namer.InterpreterInitializer: -------------------------------------------------------------------------------- 1 | io.buoyant.interpreter.k8s.istio.IstioInterpreterInitializer 2 | -------------------------------------------------------------------------------- /interpreter/k8s/src/main/resources/META-INF/services/io.buoyant.namer.InterpreterInitializer: -------------------------------------------------------------------------------- 1 | io.buoyant.interpreter.k8s.ConfigMapInterpreterInitializer 2 | -------------------------------------------------------------------------------- /interpreter/k8s/src/main/resources/META-INF/services/io.buoyant.namer.TransformerInitializer: -------------------------------------------------------------------------------- 1 | io.buoyant.transformer.k8s.DaemonSetTransformerInitializer 2 | io.buoyant.transformer.k8s.LocalNodeTransformerInitializer 3 | -------------------------------------------------------------------------------- /interpreter/mesh/src/main/resources/META-INF/services/io.buoyant.config.ConfigSerializer: -------------------------------------------------------------------------------- 1 | io.buoyant.interpreter.mesh.BoundNameTreeSerializer 2 | io.buoyant.interpreter.mesh.EndpointSerializer 3 | io.buoyant.interpreter.mesh.PathSerializer 4 | -------------------------------------------------------------------------------- /interpreter/mesh/src/main/resources/META-INF/services/io.buoyant.namer.InterpreterInitializer: -------------------------------------------------------------------------------- 1 | io.buoyant.interpreter.MeshInterpreterInitializer 2 | -------------------------------------------------------------------------------- /interpreter/mesh/src/main/scala/io/buoyant/interpreter/mesh/BufSerializers.scala: -------------------------------------------------------------------------------- 1 | package io.buoyant.interpreter.mesh 2 | 3 | import com.twitter.io.Buf 4 | 5 | object BufSerializers { 6 | 7 | def utf8(buf: Buf): String = Buf.Utf8.unapply(buf).get 8 | 9 | def path(path: Seq[Buf]): String = path.map(utf8).mkString("/", "/", "") 10 | 11 | def ipv4(addr: Buf): String = { 12 | val bytes = Buf.ByteArray.Owned.extract(addr).map(_ & (-1 >>> 24)) 13 | 14 | s"${bytes(0)}.${bytes(1)}.${bytes(2)}.${bytes(3)}" 15 | } 16 | 17 | } 18 | -------------------------------------------------------------------------------- /interpreter/mesh/src/main/scala/io/buoyant/interpreter/mesh/EndpointSerializer.scala: -------------------------------------------------------------------------------- 1 | package io.buoyant.interpreter.mesh 2 | 3 | import com.fasterxml.jackson.core.JsonGenerator 4 | import com.fasterxml.jackson.databind.SerializerProvider 5 | import io.buoyant.config.ConfigSerializer 6 | import io.linkerd.mesh 7 | import io.linkerd.mesh.Endpoint 8 | 9 | class EndpointSerializer extends ConfigSerializer[mesh.Endpoint] { 10 | 11 | override def serialize( 12 | value: Endpoint, 13 | gen: JsonGenerator, 14 | provider: SerializerProvider 15 | ): Unit = { 16 | gen.writeStartObject() 17 | for (iaf <- value.inetAf) gen.writeObjectField("inetAf", iaf) 18 | for (addr <- value.address) gen.writeStringField("address", BufSerializers.ipv4(addr)) 19 | for (meta <- value.meta) gen.writeObjectField("meta", meta) 20 | for (metadata <- value.metadata) gen.writeObjectField("metadata", metadata) 21 | for (port <- value.port) gen.writeStringField("port", port.toString) 22 | gen.writeEndObject() 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /interpreter/mesh/src/main/scala/io/buoyant/interpreter/mesh/PathSerializer.scala: -------------------------------------------------------------------------------- 1 | package io.buoyant.interpreter.mesh 2 | 3 | import com.fasterxml.jackson.core.JsonGenerator 4 | import com.fasterxml.jackson.databind.SerializerProvider 5 | import io.buoyant.config.ConfigSerializer 6 | import io.linkerd.mesh 7 | 8 | class PathSerializer extends ConfigSerializer[mesh.Path] { 9 | 10 | override def serialize( 11 | value: mesh.Path, 12 | gen: JsonGenerator, 13 | provider: SerializerProvider 14 | ): Unit = { 15 | gen.writeString(BufSerializers.path(value.elems)) 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /interpreter/mesh/src/test/scala/io/buoyant/interpreter/mesh/BufSerializersTest.scala: -------------------------------------------------------------------------------- 1 | package io.buoyant.interpreter.mesh 2 | 3 | import com.twitter.io.Buf 4 | import io.buoyant.test.FunSuite 5 | 6 | class BufSerializersTest extends FunSuite { 7 | 8 | test("Converts string ip byte array to string representation"){ 9 | val ipTable = Seq( 10 | (Buf.ByteArray(Seq(10, 120, 1, 137).map(_.toByte):_*), "10.120.1.137"), 11 | (Buf.ByteArray(Seq(127, 0, 0, 1).map(_.toByte):_*), "127.0.0.1"), 12 | (Buf.ByteArray(Seq(255, 255, 255, 255).map(_.toByte):_*),"255.255.255.255"), 13 | (Buf.ByteArray(Seq(1, 2, 3, 4).map(_.toByte):_*), "1.2.3.4"), 14 | (Buf.ByteArray(Seq(192, 168, 203, 255).map(_.toByte):_*), "192.168.203.255"), 15 | (Buf.ByteArray(Seq(0, 0, 0, 0).map(_.toByte):_*), "0.0.0.0") 16 | ) 17 | 18 | for(ip <- ipTable){ 19 | assert(BufSerializers.ipv4(ip._1) == ip._2) 20 | } 21 | } 22 | 23 | 24 | } 25 | -------------------------------------------------------------------------------- /interpreter/namerd/src/main/resources/META-INF/services/io.buoyant.namer.InterpreterInitializer: -------------------------------------------------------------------------------- 1 | io.buoyant.namerd.iface.NamerdHttpInterpreterInitializer 2 | io.buoyant.namerd.iface.NamerdInterpreterInitializer 3 | -------------------------------------------------------------------------------- /interpreter/namerd/src/main/scala/io/buoyant/namerd/iface/NamerdHandler.scala: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/linkerd/linkerd/ea82499d386e44e8958be58e0386f593e639645b/interpreter/namerd/src/main/scala/io/buoyant/namerd/iface/NamerdHandler.scala -------------------------------------------------------------------------------- /interpreter/per-host/src/main/resources/META-INF/services/io.buoyant.namer.TransformerInitializer: -------------------------------------------------------------------------------- 1 | io.buoyant.transformer.perHost.LocalhostTransformerInitializer 2 | io.buoyant.transformer.perHost.PortTransformerInitializer 3 | io.buoyant.transformer.perHost.SpecificHostTransformerInitializer 4 | -------------------------------------------------------------------------------- /interpreter/per-host/src/main/scala/io/buoyant/transformer/perHost/PortTransformerInitializer.scala: -------------------------------------------------------------------------------- 1 | package io.buoyant.transformer.perHost 2 | 3 | import com.fasterxml.jackson.annotation.JsonIgnore 4 | import com.twitter.finagle.{Path, Stack} 5 | import io.buoyant.config.types.Port 6 | import io.buoyant.namer.{NameTreeTransformer, TransformerConfig, TransformerInitializer} 7 | 8 | class PortTransformerInitializer extends TransformerInitializer { 9 | val configClass = classOf[PortTransformerConfig] 10 | override val configId = "io.l5d.port" 11 | } 12 | 13 | case class PortTransformerConfig(port: Port) extends TransformerConfig { 14 | 15 | @JsonIgnore 16 | val defaultPrefix = Path.read(s"/io.l5d.port/${port.port}") 17 | 18 | override def mk(params: Stack.Params): NameTreeTransformer = new PortTransformer(prefix, port.port) 19 | } 20 | -------------------------------------------------------------------------------- /interpreter/per-host/src/main/scala/io/buoyant/transformer/perHost/SpecificHostTransformerInitializer.scala: -------------------------------------------------------------------------------- 1 | package io.buoyant.transformer 2 | package perHost 3 | 4 | import com.fasterxml.jackson.annotation.JsonIgnore 5 | import com.twitter.finagle.{Path, Stack} 6 | import io.buoyant.namer.{NameTreeTransformer, TransformerConfig, TransformerInitializer} 7 | import java.net.InetAddress 8 | 9 | class SpecificHostTransformerInitializer extends TransformerInitializer { 10 | val configClass = classOf[SpecificHostTransformerConfig] 11 | override val configId = "io.l5d.specificHost" 12 | } 13 | 14 | case class SpecificHostTransformerConfig(host: String) extends TransformerConfig { 15 | assert(host != null, "io.l5d.specificHost: host property is required") 16 | 17 | @JsonIgnore 18 | val defaultPrefix = Path.read("/io.l5d.specificHost") 19 | 20 | @JsonIgnore 21 | override def mk(params: Stack.Params): NameTreeTransformer = { 22 | new SubnetLocalTransformer(prefix, Seq(InetAddress.getByName(host)), Netmask("255.255.255.255")) 23 | } 24 | 25 | } 26 | -------------------------------------------------------------------------------- /interpreter/subnet/src/main/scala/io/buoyant/transformer/SubnetLocalTransformer.scala: -------------------------------------------------------------------------------- 1 | package io.buoyant.transformer 2 | 3 | import com.twitter.finagle.{Address, Path} 4 | import java.net.InetAddress 5 | import io.buoyant.namer.FilteringNameTreeTransformer 6 | 7 | /** 8 | * The subnet local transformer filters the list of addresses down to 9 | * only addresses that are on the same subnet as a given local IP. 10 | */ 11 | class SubnetLocalTransformer(val prefix: Path, localIP: Seq[InetAddress], netmask: Netmask) 12 | extends FilteringNameTreeTransformer { 13 | 14 | /** Use all addresses in the same subnet as localIP. */ 15 | override protected val predicate: Address => Boolean = { 16 | case Address.Inet(addr, _) => localIP.exists(netmask.local(addr.getAddress, _)) 17 | case address => true // non-inet addresses assumed to be local (pipes, etc) 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /istio-proto/src/main/protobuf/google/rpc/README.md: -------------------------------------------------------------------------------- 1 | # Google RPC 2 | 3 | This package contains type definitions for general RPC systems. While 4 | [gRPC](https://github.com/grpc) is using these defintions, they 5 | are not designed specifically to support gRPC. -------------------------------------------------------------------------------- /istio/src/main/scala/io/buoyant/k8s/istio/IstioResponse.scala: -------------------------------------------------------------------------------- 1 | package io.buoyant.k8s.istio 2 | 3 | import com.twitter.util.Duration 4 | 5 | case class IstioResponse[Resp](statusCode: Int, duration: Duration, resp: Option[Resp]) { 6 | 7 | def responseCode: ResponseCodeIstioAttribute = ResponseCodeIstioAttribute(statusCode) 8 | 9 | def responseDuration: ResponseDurationIstioAttribute = ResponseDurationIstioAttribute(duration) 10 | 11 | } 12 | -------------------------------------------------------------------------------- /istio/src/main/scala/io/buoyant/k8s/istio/identifiers/IstioProtocolSpecificRequestHandler.scala: -------------------------------------------------------------------------------- 1 | package io.buoyant.k8s.istio.identifiers 2 | 3 | import com.twitter.util.Future 4 | import istio.proxy.v1.config.HTTPRedirect 5 | 6 | trait IstioProtocolSpecificRequestHandler[Req] { 7 | def redirectRequest(redir: HTTPRedirect, req: Req): Future[Nothing] 8 | def rewriteRequest(uri: String, authority: Option[String], req: Req): Unit 9 | } 10 | -------------------------------------------------------------------------------- /istio/src/main/scala/io/buoyant/k8s/istio/package.scala: -------------------------------------------------------------------------------- 1 | package io.buoyant.k8s 2 | 3 | package object istio { 4 | val DefaultDiscoveryHost = "istio-pilot" 5 | val DefaultDiscoveryPort = 8080 6 | 7 | val DefaultApiserverHost = "istio-pilot" 8 | val DefaultApiserverPort = 8081 9 | 10 | val DefaultMixerHost = "istio-mixer" 11 | val DefaultMixerPort = 9091 12 | 13 | val IngressAnnotationClass = "istio" 14 | } 15 | -------------------------------------------------------------------------------- /k8s/src/main/scala/io/buoyant/k8s/AuthFilter.scala: -------------------------------------------------------------------------------- 1 | package io.buoyant.k8s 2 | 3 | import com.twitter.finagle.http.{Request, Response} 4 | import com.twitter.finagle.{Service, SimpleFilter} 5 | 6 | class AuthFilter(token: String) extends SimpleFilter[Request, Response] { 7 | def apply(req: Request, service: Service[Request, Response]) = { 8 | req.headerMap("Authorization") = s"Bearer $token" 9 | service(req) 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /k8s/src/main/scala/io/buoyant/k8s/SerializationModule.scala: -------------------------------------------------------------------------------- 1 | package io.buoyant.k8s 2 | 3 | import com.fasterxml.jackson.databind.module.SimpleModule 4 | 5 | /** 6 | * Class used to provide LoadService-compatible serialization/deserialization plugins to k8s JSON parsing. 7 | */ 8 | abstract class SerializationModule { 9 | def module: SimpleModule 10 | } 11 | -------------------------------------------------------------------------------- /k8s/src/main/scala/io/buoyant/k8s/SetHostFilter.scala: -------------------------------------------------------------------------------- 1 | package io.buoyant.k8s 2 | 3 | import com.twitter.finagle.http.{Request, Response} 4 | import com.twitter.finagle.{Service, SimpleFilter} 5 | import java.net.InetSocketAddress 6 | 7 | class SetHostFilter(hostname: String, port: Int) extends SimpleFilter[Request, Response] { 8 | def this(addr: InetSocketAddress) = this(addr.getHostString, addr.getPort) 9 | 10 | val host: String = port match { 11 | case 80 | 443 => hostname 12 | case port => s"$hostname:$port" 13 | } 14 | 15 | def apply(req: Request, svc: Service[Request, Response]) = { 16 | req.host = host 17 | svc(req) 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /linkerd/admin/src/main/scala/io/buoyant/linkerd/admin/AdminFilter.scala: -------------------------------------------------------------------------------- 1 | package io.buoyant.linkerd.admin 2 | 3 | import com.twitter.finagle.{Service, SimpleFilter} 4 | import com.twitter.finagle.http.{MediaType, Request, Response} 5 | import com.twitter.util.Future 6 | 7 | /** 8 | * This filter builds a linkerd admin page by wrapping an html content blob with the linkerd 9 | * admin chome (navbar, stylesheets, etc.) 10 | */ 11 | class AdminFilter(adminHandler: AdminHandler, css: Seq[String] = Nil) extends SimpleFilter[Request, Response] { 12 | override def apply( 13 | request: Request, 14 | service: Service[Request, Response] 15 | ): Future[Response] = { 16 | service(request).map { rsp => 17 | val itemToHighlight = request.path.replace("/", "") 18 | if (rsp.contentType.contains(MediaType.Html)) 19 | rsp.contentString = adminHandler.html(rsp.contentString, csses = css, navHighlight = itemToHighlight) 20 | rsp 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /linkerd/admin/src/test/scala/io/buoyant/linkerd/admin/AdminHandlerTest.scala: -------------------------------------------------------------------------------- 1 | package io.buoyant.linkerd.admin 2 | 3 | import org.scalatest.FunSuite 4 | 5 | class AdminHandlerTest extends FunSuite { 6 | test("handles empty content") { 7 | val adminHandler = new AdminHandler(Nil) 8 | assert(adminHandler.html("").contains("linkerd admin")) 9 | } 10 | 11 | test("handles populated params") { 12 | val adminHandler = new AdminHandler(Nil) 13 | val content = "fake content" 14 | val tailContent = "fake tail content" 15 | val csses = Seq("foo.css", "bar.css") 16 | 17 | val html = adminHandler.html( 18 | content = content, 19 | tailContent = tailContent, 20 | csses = csses 21 | ) 22 | 23 | assert(html.contains(content)) 24 | assert(html.contains(tailContent)) 25 | csses.foreach { css => 26 | assert(html.contains(s"""css/$css""")) 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /linkerd/admin/src/test/scala/io/buoyant/linkerd/admin/DashboardHandlerTest.scala: -------------------------------------------------------------------------------- 1 | package io.buoyant.linkerd.admin 2 | 3 | import com.twitter.finagle.http.{Request, Status} 4 | import io.buoyant.admin.Build 5 | import io.buoyant.linkerd.{Linker, TestProtocol} 6 | import io.buoyant.test.Awaits 7 | import org.scalatest.FunSuite 8 | 9 | class DashboardHandlerTest extends FunSuite with Awaits { 10 | val handler = new DashboardHandler(new AdminHandler(Nil)) 11 | 12 | test("serves ok on /") { 13 | val rsp = await(handler(Request("/"))) 14 | assert(rsp.status == Status.Ok) 15 | } 16 | 17 | test("serves 404 other routes") { 18 | val rsp = await(handler(Request("/foo"))) 19 | assert(rsp.status == Status.NotFound) 20 | } 21 | 22 | test("serves linkerd admin and version") { 23 | val rsp = await(handler(Request("/"))) 24 | assert(rsp.contentString.contains("linkerd admin")) 25 | assert(rsp.contentString.contains(Build.load("/io/buoyant/linkerd/build.properties").version)) 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /linkerd/announcer/serversets/src/main/resources/META-INF/services/io.buoyant.linkerd.AnnouncerInitializer: -------------------------------------------------------------------------------- 1 | io.buoyant.linkerd.announcer.serversets.ServersetsAnnouncerInitializer 2 | -------------------------------------------------------------------------------- /linkerd/announcer/serversets/src/main/scala/com/twitter/finagle/zookeeper/buoyant/ZkAnnouncer.scala: -------------------------------------------------------------------------------- 1 | package com.twitter.finagle.zookeeper.buoyant 2 | 3 | import com.twitter.finagle.{Path, Announcement} 4 | import com.twitter.finagle.zookeeper.{ZkAnnouncer => FZkAnnouncer, DefaultZkClientFactory} 5 | import com.twitter.util.Future 6 | import io.buoyant.config.types.HostAndPort 7 | import io.buoyant.linkerd.FutureAnnouncer 8 | import java.net.InetSocketAddress 9 | 10 | class ZkAnnouncer(zkAddrs: Seq[HostAndPort], pathPrefix: Path) extends FutureAnnouncer { 11 | val scheme: String = "zk-serversets" 12 | 13 | private[this] val underlying = new FZkAnnouncer(DefaultZkClientFactory) 14 | 15 | private[this] val connect = zkAddrs.map(_.toString).mkString(",") 16 | 17 | protected def announceAsync(addr: InetSocketAddress, name: Path): Future[Announcement] = 18 | underlying.announce(addr, s"$connect!${(pathPrefix ++ name).show}!0") 19 | } 20 | -------------------------------------------------------------------------------- /linkerd/announcer/serversets/src/main/scala/io/buoyant/linkerd/announcer/serversets/ServersetsAnnouncerInitializer.scala: -------------------------------------------------------------------------------- 1 | package io.buoyant.linkerd.announcer.serversets 2 | 3 | import com.fasterxml.jackson.annotation.JsonIgnore 4 | import com.twitter.finagle.{Path, Stack} 5 | import com.twitter.finagle.zookeeper.buoyant.ZkAnnouncer 6 | import io.buoyant.config.types.HostAndPort 7 | import io.buoyant.linkerd.{Announcer, AnnouncerConfig, AnnouncerInitializer} 8 | 9 | class ServersetsAnnouncerInitializer extends AnnouncerInitializer { 10 | override def configClass = classOf[ServersetsConfig] 11 | override def configId = "io.l5d.serversets" 12 | } 13 | 14 | case class ServersetsConfig(zkAddrs: Seq[HostAndPort], pathPrefix: Option[Path]) extends AnnouncerConfig { 15 | 16 | @JsonIgnore 17 | override def defaultPrefix: Path = Path.read("/io.l5d.serversets") 18 | 19 | override def mk(params: Stack.Params): Announcer = new ZkAnnouncer(zkAddrs, pathPrefix.getOrElse(Path.read("/discovery"))) 20 | 21 | } 22 | -------------------------------------------------------------------------------- /linkerd/core/src/main/protobuf/usage.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | package io.buoyant.linkerd.usage; 4 | 5 | /** 6 | * For use by the UsageTelemeter 7 | */ 8 | 9 | message Router { 10 | string protocol = 1; 11 | string interpreter = 2; 12 | repeated string identifiers = 3; 13 | repeated string transformers = 4; 14 | } 15 | 16 | message Counter { 17 | string name = 1; 18 | uint64 value = 2; 19 | } 20 | 21 | message Gauge { 22 | string name = 1; 23 | double value = 2; 24 | } 25 | 26 | message UsageMessage { 27 | string pid = 1; 28 | string org_id = 2; 29 | string linkerd_version = 3; 30 | string container_manager = 4; 31 | string os_name = 5; 32 | string os_version = 6; 33 | string start_time = 7; // ISO 8601 34 | repeated Router routers = 8; 35 | repeated string namers = 9; 36 | repeated Counter counters = 10; 37 | repeated Gauge gauges = 11; 38 | } 39 | -------------------------------------------------------------------------------- /linkerd/core/src/main/scala/io/buoyant/linkerd/AnnouncerInitializer.scala: -------------------------------------------------------------------------------- 1 | package io.buoyant.linkerd 2 | 3 | import com.fasterxml.jackson.annotation.{JsonIgnore, JsonProperty} 4 | import com.twitter.finagle.{Path, Stack} 5 | import io.buoyant.config.{ConfigInitializer, PolymorphicConfig} 6 | import io.buoyant.namer.Paths 7 | 8 | abstract class AnnouncerConfig extends PolymorphicConfig { 9 | 10 | @JsonProperty("prefix") 11 | var _prefix: Option[Path] = None 12 | 13 | @JsonIgnore 14 | def defaultPrefix: Path 15 | 16 | @JsonIgnore 17 | def prefix: Path = Paths.ConfiguredNamerPrefix ++ _prefix.getOrElse(defaultPrefix) 18 | 19 | @JsonIgnore 20 | def mk(): Announcer = mk(Stack.Params.empty) 21 | 22 | @JsonIgnore 23 | def mk(params: Stack.Params): Announcer 24 | } 25 | 26 | trait AnnouncerInitializer extends ConfigInitializer 27 | -------------------------------------------------------------------------------- /linkerd/core/src/main/scala/io/buoyant/linkerd/IdentifierInitializer.scala: -------------------------------------------------------------------------------- 1 | package io.buoyant.linkerd 2 | 3 | import io.buoyant.config.ConfigInitializer 4 | 5 | abstract class IdentifierInitializer extends ConfigInitializer 6 | -------------------------------------------------------------------------------- /linkerd/core/src/main/scala/io/buoyant/linkerd/ProtocolException.scala: -------------------------------------------------------------------------------- 1 | package io.buoyant.linkerd 2 | 3 | import scala.util.control.NoStackTrace 4 | 5 | /** 6 | * An exception indicating an error in protocol compliance 7 | * 8 | * @param reason a message describing the cause of the error 9 | */ 10 | abstract class ProtocolException(reason: String) 11 | extends Exception(reason) 12 | with NoStackTrace 13 | -------------------------------------------------------------------------------- /linkerd/core/src/main/scala/io/buoyant/linkerd/RequestAuthorizerInitializer.scala: -------------------------------------------------------------------------------- 1 | package io.buoyant.linkerd 2 | 3 | import io.buoyant.config.ConfigInitializer 4 | 5 | abstract class RequestAuthorizerInitializer extends ConfigInitializer 6 | -------------------------------------------------------------------------------- /linkerd/core/src/main/scala/io/buoyant/linkerd/ResponseClassifierInitializer.scala: -------------------------------------------------------------------------------- 1 | package io.buoyant.linkerd 2 | 3 | import com.fasterxml.jackson.annotation.JsonIgnore 4 | import com.twitter.finagle.service.ResponseClassifier 5 | import io.buoyant.config.{PolymorphicConfig, ConfigInitializer} 6 | 7 | abstract class ResponseClassifierInitializer extends ConfigInitializer 8 | 9 | abstract class ResponseClassifierConfig extends PolymorphicConfig { 10 | @JsonIgnore 11 | def mk: ResponseClassifier 12 | } 13 | -------------------------------------------------------------------------------- /linkerd/core/src/main/scala/io/buoyant/linkerd/SessionConfig.scala: -------------------------------------------------------------------------------- 1 | package io.buoyant.linkerd 2 | 3 | import com.fasterxml.jackson.annotation.JsonIgnore 4 | import com.twitter.conversions.DurationOps._ 5 | import com.twitter.finagle.service.ExpiringService 6 | 7 | trait SessionConfig { 8 | 9 | def lifeTimeMs: Option[Int] 10 | def idleTimeMs: Option[Int] 11 | 12 | @JsonIgnore 13 | private[this] val default = ExpiringService.Param.param.default 14 | 15 | @JsonIgnore 16 | def param = ExpiringService.Param( 17 | lifeTime = lifeTimeMs.map(_.millis).getOrElse(default.lifeTime), 18 | idleTime = idleTimeMs.map(_.millis).getOrElse(default.idleTime) 19 | ) 20 | 21 | } 22 | -------------------------------------------------------------------------------- /linkerd/core/src/main/scala/io/buoyant/linkerd/TracePropagatorInitializer.scala: -------------------------------------------------------------------------------- 1 | package io.buoyant.linkerd 2 | 3 | import io.buoyant.config.ConfigInitializer 4 | 5 | abstract class TracePropagatorInitializer extends ConfigInitializer 6 | -------------------------------------------------------------------------------- /linkerd/docs/protocol-mux.md: -------------------------------------------------------------------------------- 1 | # Mux Protocol (experimental) 2 | 3 | >A mux router configuration that routes requests to port 9001 4 | 5 | ```yaml 6 | 7 | routers: 8 | - protocol: mux 9 | label: power-level-router 10 | dstPrefix: /overNineThousand 11 | dtab: | 12 | /overNineThousand => /$/inet/127.0.1/9001; 13 | ``` 14 | 15 | protocol: `mux` 16 | 17 | Linkerd experimentally supports the [mux 18 | protocol](https://twitter.github.io/finagle/guide/Protocols.html#mux). 19 | 20 | ## Mux Router Parameters 21 | 22 | Key | Default Value | Description 23 | --- | ------------- | ----------- 24 | dstPrefix | `/svc` | A path prefix used in `dtab`. 25 | 26 | ## Mux Server Parameters 27 | 28 | Key | Default Value | Description 29 | --- | ------------- | ----------- 30 | port | `4141` | The TCP port number. 31 | 32 | 33 | 34 | -------------------------------------------------------------------------------- /linkerd/examples/access-logs.yaml: -------------------------------------------------------------------------------- 1 | # Simple config for testing multiple access logs. 2 | # `curl localhost:4140`: 3 | # linkerd1:4140 -> linkerd2:4141 -> admin:9990 4 | 5 | admin: 6 | ip: 0.0.0.0 7 | port: 9990 8 | 9 | telemetry: 10 | - kind: io.l5d.recentRequests 11 | sampleRate: 1.0 12 | 13 | routers: 14 | - protocol: http 15 | httpAccessLog: logs/access1.log 16 | label: linkerd1 17 | dtab: | 18 | /svc => /$/inet/127.1/4141; 19 | servers: 20 | - port: 4140 21 | ip: 0.0.0.0 22 | - protocol: http 23 | httpAccessLog: logs/access2.log 24 | label: linkerd2 25 | dtab: | 26 | /svc => /$/inet/127.1/9990; 27 | servers: 28 | - port: 4141 29 | ip: 0.0.0.0 30 | -------------------------------------------------------------------------------- /linkerd/examples/admin-tls.yaml: -------------------------------------------------------------------------------- 1 | # Config for serving linkerd admin page over TLS 2 | 3 | admin: 4 | ip: 0.0.0.0 5 | port: 9990 6 | tls: 7 | certPath: finagle/h2/src/e2e/resources/linkerd-tls-e2e-cert.pem 8 | keyPath: finagle/h2/src/e2e/resources/linkerd-tls-e2e-key.pem 9 | caCertPath: finagle/h2/src/e2e/resources/cacert.pem 10 | 11 | telemetry: 12 | - kind: io.l5d.recentRequests 13 | sampleRate: 1.0 14 | 15 | routers: 16 | - protocol: http 17 | label: linkerd-admin 18 | dtab: | 19 | /svc/* => /$/inet/127.1/9990; 20 | servers: 21 | - port: 4140 22 | ip: 0.0.0.0 23 | -------------------------------------------------------------------------------- /linkerd/examples/consul-interpreter.yaml: -------------------------------------------------------------------------------- 1 | admin: 2 | ip: 0.0.0.0 3 | port: 9990 4 | namers: 5 | - kind: io.l5d.consul 6 | includeTag: false 7 | useHealthCheck: false 8 | routers: 9 | - protocol: http 10 | servers: 11 | - port: 4140 12 | ip: 0.0.0.0 13 | interpreter: 14 | kind: io.l5d.consul.interpreter 15 | host: localhost 16 | port: 8500 17 | namespace: internal 18 | -------------------------------------------------------------------------------- /linkerd/examples/consul.yaml: -------------------------------------------------------------------------------- 1 | # Do service discovery lookups against the Consul service catalog. 2 | namers: 3 | - kind: io.l5d.consul 4 | includeTag: true 5 | useHealthCheck: false 6 | 7 | routers: 8 | - protocol: http 9 | dtab: | 10 | /svc => /#/io.l5d.consul/dc1/prod; 11 | servers: 12 | - port: 4140 13 | ip: 0.0.0.0 14 | -------------------------------------------------------------------------------- /linkerd/examples/curator.yaml: -------------------------------------------------------------------------------- 1 | # Do service discovery lookups against Curator. 2 | namers: 3 | - kind: io.l5d.curator 4 | experimental: true 5 | zkAddrs: 6 | - host: localhost 7 | port: 2181 8 | basePath: /curator 9 | 10 | routers: 11 | - protocol: http 12 | dtab: | 13 | /svc => /#/io.l5d.curator; 14 | servers: 15 | - port: 4140 16 | ip: 0.0.0.0 17 | -------------------------------------------------------------------------------- /linkerd/examples/disable-tls.yaml: -------------------------------------------------------------------------------- 1 | namers: [] 2 | 3 | routers: 4 | - protocol: http 5 | dtab: | 6 | /svc/foo => /$/inet/www.google.com/443 ; 7 | /svc/bar => /$/inet/localhost/7777 8 | 9 | servers: 10 | - port: 4140 11 | 12 | client: 13 | kind: io.l5d.static 14 | configs: 15 | - prefix: "/$/inet/{service}" 16 | tls: 17 | commonName: "{service}" 18 | - prefix: /$/inet/localhost 19 | tls: 20 | enabled: false 21 | -------------------------------------------------------------------------------- /linkerd/examples/example.dtab: -------------------------------------------------------------------------------- 1 | /svc/* => /$/inet/127.1/9999; 2 | -------------------------------------------------------------------------------- /linkerd/examples/fs.yaml: -------------------------------------------------------------------------------- 1 | # This config loads both the dtab and the service discovery registry from the 2 | # local filesystem. It establishes watches on the filesystem so that updates 3 | # will be respected at runtime. 4 | namers: 5 | - kind: io.l5d.fs 6 | rootDir: linkerd/examples/io.l5d.fs 7 | 8 | routers: 9 | - protocol: http 10 | interpreter: 11 | kind: io.l5d.fs 12 | dtabFile: linkerd/examples/example.dtab 13 | servers: 14 | - port: 4140 15 | -------------------------------------------------------------------------------- /linkerd/examples/h2.yaml: -------------------------------------------------------------------------------- 1 | # HTTP/2 protocol. 2 | namers: 3 | - kind: io.l5d.fs 4 | rootDir: linkerd/examples/io.l5d.fs 5 | 6 | routers: 7 | - protocol: h2 8 | h2AccessLog: logs/access.log 9 | h2AccessLogRollPolicy: daily 10 | h2AccessLogAppend: true 11 | h2AccessLogRotateCount: -1 12 | dtab: | 13 | /srv => /#/io.l5d.fs; 14 | /svc/localhost:4142 => /$/inet/127.1/8888; 15 | /svc => /srv; 16 | identifier: 17 | kind: io.l5d.header.token 18 | header: ":authority" 19 | servers: 20 | - port: 4142 21 | maxConcurrentStreamsPerConnection: 300 22 | addForwardedHeader: 23 | by: {kind: "ip:port"} 24 | for: {kind: "ip:port"} 25 | initialStreamWindowBytes: 1048576 # 1MB 26 | client: 27 | initialStreamWindowBytes: 1048576 # 1MB 28 | -------------------------------------------------------------------------------- /linkerd/examples/h2spec.yaml: -------------------------------------------------------------------------------- 1 | admin: 2 | port: 9990 3 | ip: 0.0.0.0 4 | 5 | usage: 6 | enabled: false 7 | 8 | routers: 9 | - protocol: h2 10 | label: h2 11 | servers: 12 | - port: 4140 13 | ip: 0.0.0.0 14 | dtab: /svc => /$/inet/127.1/8080 15 | -------------------------------------------------------------------------------- /linkerd/examples/http.yaml: -------------------------------------------------------------------------------- 1 | # HTTP protocol 2 | namers: 3 | - kind: io.l5d.fs 4 | rootDir: linkerd/examples/io.l5d.fs 5 | 6 | routers: 7 | - protocol: http 8 | httpAccessLog: logs/access.log 9 | httpAccessLogRollPolicy: daily 10 | httpAccessLogAppend: true 11 | httpAccessLogRotateCount: -1 12 | maxHeadersKB: 16 13 | maxInitialLineKB: 16 14 | streamAfterContentLengthKB: 102400 # 100MB 15 | compressionLevel: 9 16 | identifier: {kind: io.l5d.header} 17 | dtab: | 18 | /svc => /#/io.l5d.fs; 19 | servers: 20 | - port: 4140 21 | ip: 0.0.0.0 22 | addForwardedHeader: 23 | by: {kind: "ip:port"} 24 | for: {kind: "ip"} 25 | -------------------------------------------------------------------------------- /linkerd/examples/influxdb.yaml: -------------------------------------------------------------------------------- 1 | namers: 2 | - kind: io.l5d.fs 3 | rootDir: linkerd/examples/io.l5d.fs 4 | 5 | routers: 6 | - protocol: http 7 | dtab: /svc => /#/io.l5d.fs; 8 | servers: 9 | - port: 4140 10 | 11 | telemetry: 12 | - kind: io.l5d.influxdb 13 | -------------------------------------------------------------------------------- /linkerd/examples/io.l5d.fs/cat: -------------------------------------------------------------------------------- 1 | 127.1 8888 2 | -------------------------------------------------------------------------------- /linkerd/examples/io.l5d.fs/default: -------------------------------------------------------------------------------- 1 | 127.1 9990 2 | -------------------------------------------------------------------------------- /linkerd/examples/io.l5d.fs/dog: -------------------------------------------------------------------------------- 1 | 127.1 8888 2 | -------------------------------------------------------------------------------- /linkerd/examples/io.l5d.fs/thrift: -------------------------------------------------------------------------------- 1 | 127.1 9991 -------------------------------------------------------------------------------- /linkerd/examples/k8s.yaml: -------------------------------------------------------------------------------- 1 | # Do service discovery lookups against the k8s endpoints API. 2 | namers: 3 | # This namer resolves to the internal IP of the destination pod. 4 | - kind: io.l5d.k8s 5 | host: localhost 6 | port: 8001 7 | # This namer resolves to the external facing load balancer IP of the service 8 | - kind: io.l5d.k8s.external 9 | experimental: true 10 | host: localhost 11 | port: 8001 12 | 13 | routers: 14 | - protocol: http 15 | dtab: | 16 | /svc => /#/io.l5d.k8s/foo/http; 17 | servers: 18 | - port: 4140 19 | -------------------------------------------------------------------------------- /linkerd/examples/loadbalancer.yaml: -------------------------------------------------------------------------------- 1 | # A config that demonstrates various load balancer options. 2 | namers: 3 | - kind: io.l5d.fs 4 | rootDir: linkerd/examples/io.l5d.fs 5 | 6 | routers: 7 | - protocol: http 8 | dtab: | 9 | /svc => /#/io.l5d.fs 10 | servers: 11 | - port: 4140 12 | maxConcurrentRequests: 10000 13 | client: 14 | loadBalancer: 15 | kind: p2c 16 | maxEffort: 10 17 | hostConnectionPool: 18 | minSize: 0 19 | maxSize: 1000 20 | idleTimeMs: 10000 21 | maxWaiters: 5000 22 | failureAccrual: 23 | kind: io.l5d.successRate 24 | successRate: 0.9 25 | requests: 1000 26 | backoff: 27 | kind: jittered 28 | minMs: 5000 29 | maxMs: 300000 30 | -------------------------------------------------------------------------------- /linkerd/examples/marathon-leader.yaml: -------------------------------------------------------------------------------- 1 | #notest 2 | # Do service discovery lookups against the marathon app API. The marathon 3 | # master itself is discovered by leader election. 4 | namers: 5 | - kind: io.l5d.marathon 6 | prefix: /io.l5d.marathon 7 | # Discover the marathon master by leader election 8 | dst: /$/io.buoyant.namer.zk.leader/localhost:2181/marathon/leader 9 | uriPrefix: /marathon 10 | ttlMs: 300 11 | 12 | routers: 13 | - protocol: http 14 | identifier: 15 | kind: io.l5d.methodAndHost 16 | httpUriInDst: true 17 | dtab: | 18 | /marathonId => /#/io.l5d.marathon; 19 | /host => /$/io.buoyant.http.domainToPathPfx/marathonId; 20 | /svc/1.1/* => /host; 21 | servers: 22 | - port: 4140 23 | ip: 0.0.0.0 24 | -------------------------------------------------------------------------------- /linkerd/examples/marathon.yaml: -------------------------------------------------------------------------------- 1 | # Do service discovery lookups against the marathon app API. 2 | namers: 3 | - kind: io.l5d.marathon 4 | prefix: /io.l5d.marathon 5 | # The host and port of the marathon master are statically configured here. 6 | host: localhost 7 | port: 80 8 | uriPrefix: /marathon 9 | ttlMs: 300 10 | useHealthCheck: false 11 | 12 | routers: 13 | - protocol: http 14 | identifier: 15 | kind: io.l5d.methodAndHost 16 | httpUriInDst: true 17 | dtab: | 18 | /marathonId => /#/io.l5d.marathon; 19 | /host => /$/io.buoyant.http.domainToPathPfx/marathonId; 20 | /svc/1.1/* => /host; 21 | servers: 22 | - port: 4140 23 | ip: 0.0.0.0 24 | -------------------------------------------------------------------------------- /linkerd/examples/mutualTls.yaml: -------------------------------------------------------------------------------- 1 | # A router that receives encrypted traffic with TLS and requires client auth. 2 | namers: 3 | - kind: io.l5d.fs 4 | rootDir: linkerd/examples/io.l5d.fs 5 | 6 | routers: 7 | - protocol: http 8 | dtab: | 9 | /svc => /#/io.l5d.fs 10 | client: 11 | tls: 12 | commonName: foo 13 | trustCertsBundle: /foo/cacert.pem 14 | clientAuth: 15 | certPath: /foo/clientCert.pem 16 | keyPath: /foo/clientKey.pem 17 | intermediateCertsPath: /foo/intermediateCa.pem 18 | servers: 19 | - port: 4140 20 | # Expect the incoming connection to be encrypted. 21 | tls: 22 | certPath: /foo/cert.pem 23 | keyPath: /foo/key.pem 24 | # clients must provide a valid certificate 25 | requireClientAuth: true 26 | # the authority used to validate client certificates 27 | caCertPath: /foo/cacert.pem 28 | -------------------------------------------------------------------------------- /linkerd/examples/namerd-http.yaml: -------------------------------------------------------------------------------- 1 | # Use the experimental namerd HTTP API for name resolution. 2 | routers: 3 | - protocol: http 4 | interpreter: 5 | kind: io.l5d.namerd.http 6 | experimental: true 7 | dst: /$/inet/localhost/4180 8 | namespace: default 9 | servers: 10 | - port: 4140 11 | ip: 0.0.0.0 12 | -------------------------------------------------------------------------------- /linkerd/examples/namerd-mesh.yaml: -------------------------------------------------------------------------------- 1 | # Use the namerd gRPC API for name resolution. 2 | routers: 3 | - protocol: http 4 | interpreter: 5 | kind: io.l5d.mesh 6 | dst: /$/inet/127.1/4321 7 | root: /default 8 | servers: 9 | - port: 4140 10 | ip: 0.0.0.0 11 | -------------------------------------------------------------------------------- /linkerd/examples/namerd.yaml: -------------------------------------------------------------------------------- 1 | # Use namerd for name resolution. 2 | routers: 3 | - protocol: http 4 | label: named-thrift 5 | interpreter: 6 | kind: io.l5d.namerd 7 | dst: /$/inet/localhost/4100 8 | namespace: default 9 | servers: 10 | - port: 4140 11 | ip: 0.0.0.0 12 | - protocol: http 13 | label: named-http 14 | interpreter: 15 | kind: io.l5d.namerd.http 16 | experimental: true 17 | dst: /$/inet/localhost/4180 18 | namespace: default 19 | servers: 20 | - port: 4141 21 | ip: 0.0.0.0 22 | - protocol: http 23 | label: named-grpc 24 | interpreter: 25 | kind: io.l5d.mesh 26 | dst: /$/inet/localhost/4321 27 | root: /default 28 | servers: 29 | - port: 4142 30 | ip: 0.0.0.0 31 | -------------------------------------------------------------------------------- /linkerd/examples/prometheus.yaml: -------------------------------------------------------------------------------- 1 | namers: 2 | - kind: io.l5d.fs 3 | rootDir: linkerd/examples/io.l5d.fs 4 | 5 | routers: 6 | - protocol: http 7 | dtab: /svc => /#/io.l5d.fs; 8 | servers: 9 | - port: 4140 10 | 11 | telemetry: 12 | - kind: io.l5d.prometheus 13 | -------------------------------------------------------------------------------- /linkerd/examples/proxy.yaml: -------------------------------------------------------------------------------- 1 | namers: [] 2 | 3 | # Simply proxies requests according to the hostname. If a port is specified 4 | # in the hostname, that port is used. Otherwise, port 80 is used by default. 5 | # If port 443 is specified, linkerd will speak https to the destination. 6 | # 7 | # This dtab can be a useful fallback if the specified service isn't found in 8 | # service discovery. 9 | routers: 10 | - protocol: http 11 | dtab: | 12 | /ph => /$/io.buoyant.rinet ; 13 | /svc => /ph/80 ; 14 | /svc => /$/io.buoyant.porthostPfx/ph ; 15 | servers: 16 | - port: 4140 17 | client: 18 | kind: io.l5d.static 19 | configs: 20 | - prefix: /$/io.buoyant.rinet/443/{hostname} 21 | tls: 22 | commonName: "{hostname}" 23 | -------------------------------------------------------------------------------- /linkerd/examples/recent-requests.yaml: -------------------------------------------------------------------------------- 1 | namers: 2 | - kind: io.l5d.fs 3 | rootDir: linkerd/examples/io.l5d.fs 4 | 5 | routers: 6 | - protocol: http 7 | dtab: /svc/* => /$/inet/localhost/8888 8 | servers: 9 | - port: 4140 10 | 11 | telemetry: 12 | - kind: io.l5d.recentRequests 13 | sampleRate: 1.0 14 | -------------------------------------------------------------------------------- /linkerd/examples/rewriting.yaml: -------------------------------------------------------------------------------- 1 | # An example of how to use the rewriting namer to reorder path segments. This 2 | # resolves names like /http/foo/api to /#/io.l5d.fs/foo. 3 | namers: 4 | - kind: io.l5d.fs 5 | rootDir: linkerd/examples/io.l5d.fs 6 | - kind: io.l5d.rewrite 7 | prefix: /apiToSrv 8 | pattern: "/{service}/api" 9 | name: "/srv/{service}" 10 | 11 | routers: 12 | - protocol: http 13 | identifier: 14 | kind: io.l5d.path 15 | segments: 2 16 | dtab: | 17 | /srv => /#/io.l5d.fs ; 18 | /svc => /#/apiToSrv ; 19 | servers: 20 | - port: 4140 21 | -------------------------------------------------------------------------------- /linkerd/examples/serversets.yaml: -------------------------------------------------------------------------------- 1 | # Do service discovery lookups against the zookeeper serversets API. This 2 | # config will also announce its address to zookeeper serversets. 3 | namers: 4 | - kind: io.l5d.serversets 5 | zkAddrs: 6 | - host: 127.0.0.1 7 | port: 2181 8 | 9 | routers: 10 | - protocol: http 11 | announcers: 12 | - kind: io.l5d.serversets 13 | zkAddrs: 14 | - host: 127.0.0.1 15 | port: 2181 16 | pathPrefix: /discovery/prod 17 | dtab: | 18 | /svc => /#/io.l5d.serversets; 19 | /host/www.example.com => /#/io.l5d.serversets/discovery/prod/www/example:https; 20 | /host/www.example2.com => /#/io.l5d.serversets/discovery/prod/www/example2; 21 | /svc/1.1 => /$/io.buoyant.http.anyMethodPfx/host; 22 | client: 23 | kind: io.l5d.static 24 | configs: 25 | - prefix: "/#/io.l5d.serversets/discovery/prod/{role}/{service}:https" 26 | tls: 27 | commonName: "{role}.{service}.com" 28 | servers: 29 | - port: 4140 30 | ip: 0.0.0.0 31 | announce: 32 | - /#/io.l5d.serversets/foobar 33 | -------------------------------------------------------------------------------- /linkerd/examples/service-options.yaml: -------------------------------------------------------------------------------- 1 | # A config that demonstrates basic service options. 2 | routers: 3 | - protocol: http 4 | label: foobar 5 | dtab: | 6 | /svc/* => /$/inet/localhost/8080 7 | service: 8 | kind: io.l5d.static 9 | configs: 10 | - prefix: /svc # default for all services 11 | totalTimeoutMs: 500 12 | - prefix: /svc/foo 13 | totalTimeoutMs: 1000 # overrides the default from above 14 | - prefix: /svc/bar 15 | # applies only to the /svc/bar service 16 | retries: 17 | budget: 18 | minRetriesPerSec: 5 19 | percentCanRetry: 0.5 20 | ttlSecs: 15 21 | backoff: 22 | kind: jittered 23 | minMs: 10 24 | maxMs: 10000 25 | servers: 26 | - port: 4140 27 | -------------------------------------------------------------------------------- /linkerd/examples/socket-options.yaml: -------------------------------------------------------------------------------- 1 | namers: 2 | - kind: io.l5d.fs 3 | rootDir: linkerd/examples/io.l5d.fs 4 | routers: 5 | - protocol: http 6 | dtab: | 7 | /svc => /#/io.l5d.fs ; 8 | servers: 9 | - port: 4140 10 | socketOptions: 11 | noDelay: true 12 | reuseAddr: true 13 | reusePort: true 14 | readTimeoutMs: 61000 15 | writeTimeoutMs: 61000 16 | keepAlive: true 17 | backlog: 128 18 | -------------------------------------------------------------------------------- /linkerd/examples/static_namer.yaml: -------------------------------------------------------------------------------- 1 | # Routes all requests to localhost:8080 2 | routers: 3 | - protocol: http 4 | dtab: | 5 | /svc/* => /$/inet/localhost/8080; 6 | servers: 7 | - port: 4140 8 | ip: 0.0.0.0 9 | -------------------------------------------------------------------------------- /linkerd/examples/statsd.yaml: -------------------------------------------------------------------------------- 1 | # A fairly minimal linkerd configuration with statsd exporting. 2 | 3 | admin: 4 | port: 9990 5 | 6 | telemetry: 7 | - kind: io.l5d.statsd 8 | experimental: true 9 | prefix: linkerd 10 | hostname: 127.0.0.1 11 | port: 8125 12 | gaugeIntervalMs: 10000 13 | sampleRate: 1.0 # for testing only, setting this to 1.0 will increase linkerd latency 14 | 15 | # An example service discovery backend that uses the filesystem to resolve endpoints. 16 | namers: 17 | - kind: io.l5d.fs 18 | rootDir: linkerd/examples/io.l5d.fs 19 | 20 | routers: 21 | - protocol: http 22 | dtab: /svc => /#/io.l5d.fs 23 | servers: 24 | - port: 4140 25 | ip: 0.0.0.0 26 | -------------------------------------------------------------------------------- /linkerd/examples/thrift.yaml: -------------------------------------------------------------------------------- 1 | # A simplistic thrift-routing linkerd configuration. 2 | 3 | # An example service discovery that backend uses the filesystem to resolve endpoints. 4 | namers: 5 | - kind: io.l5d.fs 6 | rootDir: linkerd/examples/io.l5d.fs 7 | 8 | # A simple thrift router that looks up each thrift method in io.l5d.fs. 9 | routers: 10 | - protocol: thrift 11 | thriftProtocol: binary 12 | thriftMethodInDst: true 13 | dtab: | 14 | /svc => /#/io.l5d.fs/thrift; 15 | servers: 16 | - port: 4114 17 | thriftFramed: true 18 | client: 19 | thriftFramed: true 20 | attemptTTwitterUpgrade: true 21 | -------------------------------------------------------------------------------- /linkerd/examples/thriftmux.yaml: -------------------------------------------------------------------------------- 1 | # A simplistic thriftmux-routing linkerd configuration. 2 | 3 | # An example service discovery that backend uses the filesystem to resolve endpoints. 4 | namers: 5 | - kind: io.l5d.fs 6 | rootDir: linkerd/examples/io.l5d.fs 7 | 8 | # A simple thriftmux router that looks up each thriftmux method in io.l5d.fs. 9 | routers: 10 | - protocol: thriftmux 11 | thriftProtocol: binary 12 | thriftMethodInDst: true 13 | experimental: true 14 | dtab: | 15 | /svc => /#/io.l5d.fs/thrift; 16 | servers: 17 | - port: 4400 18 | thriftFramed: true 19 | client: 20 | thriftFramed: true 21 | attemptTTwitterUpgrade: true 22 | -------------------------------------------------------------------------------- /linkerd/examples/tls.yaml: -------------------------------------------------------------------------------- 1 | # A router that sends and receives encrypted traffic with TLS. 2 | namers: 3 | - kind: io.l5d.fs 4 | rootDir: linkerd/examples/io.l5d.fs 5 | 6 | routers: 7 | - protocol: http 8 | dtab: | 9 | /svc => /#/io.l5d.fs 10 | client: 11 | # Establish encrypted outgoing connections. 12 | kind: io.l5d.static 13 | configs: 14 | - prefix: "/#/io.l5d.fs/{service}" 15 | tls: 16 | trustCertsBundle: /foo/caCert.pem 17 | commonName: "{service}" 18 | servers: 19 | - port: 4140 20 | # Expect the incoming connection to be encrypted. 21 | tls: 22 | certPath: /foo/cert.pem 23 | keyPath: /foo/key.pem 24 | intermediateCertsPath: /foo/ca-chain.pem 25 | -------------------------------------------------------------------------------- /linkerd/examples/tracelog.yaml: -------------------------------------------------------------------------------- 1 | # Log trace data to the console. 2 | namers: 3 | - kind: io.l5d.fs 4 | rootDir: linkerd/examples/io.l5d.fs 5 | 6 | routers: 7 | - protocol: http 8 | dtab: | 9 | /svc => /#/io.l5d.fs 10 | servers: 11 | - port: 4140 12 | 13 | telemetry: 14 | - kind: io.l5d.tracelog 15 | sampleRate: 0.2 16 | level: INFO 17 | -------------------------------------------------------------------------------- /linkerd/examples/zipkin.yaml: -------------------------------------------------------------------------------- 1 | # Send distributed trace data to Zipkin 2 | namers: 3 | - kind: io.l5d.fs 4 | rootDir: linkerd/examples/io.l5d.fs 5 | 6 | routers: 7 | - protocol: http 8 | dtab: | 9 | /svc => /#/io.l5d.fs 10 | servers: 11 | - port: 4140 12 | 13 | telemetry: 14 | - kind: io.l5d.zipkin 15 | host: localhost 16 | port: 9410 17 | sampleRate: 1.0 18 | -------------------------------------------------------------------------------- /linkerd/failure-accrual/src/main/resources/META-INF/services/io.buoyant.linkerd.FailureAccrualInitializer: -------------------------------------------------------------------------------- 1 | io.buoyant.linkerd.failureAccrual.ConsecutiveFailuresInitializer 2 | io.buoyant.linkerd.failureAccrual.NoneInitializer 3 | io.buoyant.linkerd.failureAccrual.SuccessRateInitializer 4 | io.buoyant.linkerd.failureAccrual.SuccessRateWindowedInitializer 5 | -------------------------------------------------------------------------------- /linkerd/failure-accrual/src/main/scala/io/buoyant/linkerd/failureAccrual/ConsecutiveFailuresInitializer.scala: -------------------------------------------------------------------------------- 1 | package io.buoyant.linkerd.failureAccrual 2 | 3 | import com.fasterxml.jackson.annotation.JsonIgnore 4 | import com.twitter.finagle.liveness.FailureAccrualPolicy 5 | import io.buoyant.linkerd.{FailureAccrualConfig, FailureAccrualInitializer} 6 | 7 | class ConsecutiveFailuresInitializer extends FailureAccrualInitializer { 8 | val configClass = classOf[ConsecutiveFailuresConfig] 9 | override def configId = "io.l5d.consecutiveFailures" 10 | } 11 | 12 | object ConsecutiveFailuresInitializer extends ConsecutiveFailuresInitializer 13 | 14 | case class ConsecutiveFailuresConfig(failures: Int) extends FailureAccrualConfig { 15 | @JsonIgnore 16 | override def policy = 17 | () => FailureAccrualPolicy.consecutiveFailures(failures, backoffOrDefault) 18 | } 19 | -------------------------------------------------------------------------------- /linkerd/failure-accrual/src/main/scala/io/buoyant/linkerd/failureAccrual/NoneInitializer.scala: -------------------------------------------------------------------------------- 1 | package io.buoyant.linkerd.failureAccrual 2 | 3 | import com.fasterxml.jackson.annotation.JsonIgnore 4 | import com.twitter.finagle.liveness.FailureAccrualPolicy 5 | import com.twitter.util.Duration 6 | import io.buoyant.linkerd.{FailureAccrualConfig, FailureAccrualInitializer} 7 | 8 | class NoneInitializer extends FailureAccrualInitializer { 9 | val configClass = classOf[NoneConfig] 10 | override def configId = "none" 11 | } 12 | 13 | object NoneInitializer extends NoneInitializer 14 | 15 | class NoneConfig extends FailureAccrualConfig { 16 | @JsonIgnore 17 | override def policy = () => NonePolicy 18 | } 19 | 20 | object NonePolicy extends FailureAccrualPolicy { 21 | override def recordSuccess(): Unit = {} 22 | override def markDeadOnFailure(): Option[Duration] = None 23 | override def revived(): Unit = {} 24 | 25 | override def name: String = "NonePolicy" 26 | 27 | override def show(): String = "" 28 | } 29 | -------------------------------------------------------------------------------- /linkerd/failure-accrual/src/main/scala/io/buoyant/linkerd/failureAccrual/SuccessRateInitializer.scala: -------------------------------------------------------------------------------- 1 | package io.buoyant.linkerd.failureAccrual 2 | 3 | import com.fasterxml.jackson.annotation.JsonIgnore 4 | import com.twitter.finagle.liveness.FailureAccrualPolicy 5 | import io.buoyant.linkerd.{FailureAccrualConfig, FailureAccrualInitializer} 6 | 7 | class SuccessRateInitializer extends FailureAccrualInitializer { 8 | val configClass = classOf[SuccessRateConfig] 9 | override def configId = "io.l5d.successRate" 10 | } 11 | 12 | object SuccessRateInitializer extends SuccessRateInitializer 13 | 14 | case class SuccessRateConfig(successRate: Double, requests: Int) extends FailureAccrualConfig { 15 | @JsonIgnore 16 | override def policy = 17 | () => FailureAccrualPolicy.successRate(successRate, requests, backoffOrDefault) 18 | } 19 | -------------------------------------------------------------------------------- /linkerd/failure-accrual/src/main/scala/io/buoyant/linkerd/failureAccrual/SuccessRateWindowedInitializer.scala: -------------------------------------------------------------------------------- 1 | package io.buoyant.linkerd.failureAccrual 2 | 3 | import com.fasterxml.jackson.annotation.JsonIgnore 4 | import com.twitter.conversions.DurationOps._ 5 | import com.twitter.finagle.liveness.FailureAccrualPolicy 6 | import io.buoyant.linkerd.{FailureAccrualConfig, FailureAccrualInitializer} 7 | 8 | class SuccessRateWindowedInitializer extends FailureAccrualInitializer { 9 | val configClass = classOf[SuccessRateWindowedConfig] 10 | override def configId = "io.l5d.successRateWindowed" 11 | } 12 | 13 | object SuccessRateWindowedInitializer extends SuccessRateWindowedInitializer 14 | 15 | case class SuccessRateWindowedConfig( 16 | successRate: Double, 17 | window: Int 18 | ) extends FailureAccrualConfig { 19 | @JsonIgnore 20 | override def policy = 21 | () => 22 | FailureAccrualPolicy.successRateWithinDuration(successRate, window.seconds, backoffOrDefault) 23 | } 24 | -------------------------------------------------------------------------------- /linkerd/failure-accrual/src/test/scala/io/buoyant/linkerd/failureAccrual/NoneTest.scala: -------------------------------------------------------------------------------- 1 | package io.buoyant.linkerd.failureAccrual 2 | 3 | import com.twitter.finagle.liveness.FailureAccrualPolicy 4 | import com.twitter.finagle.util.LoadService 5 | import io.buoyant.linkerd.FailureAccrualInitializer 6 | import io.buoyant.test.FunSuite 7 | 8 | class NoneTest extends FunSuite { 9 | test("sanity") { 10 | val config = new NoneConfig 11 | assert(config.policy().isInstanceOf[FailureAccrualPolicy]) 12 | } 13 | 14 | test("service registration") { 15 | assert(LoadService[FailureAccrualInitializer].exists(_.isInstanceOf[NoneInitializer])) 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /linkerd/failure-accrual/src/test/scala/io/buoyant/linkerd/failureAccrual/SuccessRateTest.scala: -------------------------------------------------------------------------------- 1 | package io.buoyant.linkerd.failureAccrual 2 | 3 | import com.twitter.finagle.liveness.FailureAccrualPolicy 4 | import com.twitter.finagle.util.LoadService 5 | import io.buoyant.linkerd.FailureAccrualInitializer 6 | import io.buoyant.test.FunSuite 7 | 8 | class SuccessRateTest extends FunSuite { 9 | test("sanity") { 10 | val successRate = 0.6 11 | val requests = 3 12 | val config = SuccessRateConfig(successRate, requests) 13 | assert(config.policy().isInstanceOf[FailureAccrualPolicy]) 14 | assert(config.successRate == successRate) 15 | assert(config.requests == requests) 16 | } 17 | 18 | test("service registration") { 19 | assert(LoadService[FailureAccrualInitializer].exists(_.isInstanceOf[SuccessRateInitializer])) 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /linkerd/failure-accrual/src/test/scala/io/buoyant/linkerd/failureAccrual/SuccessRateWindowedTest.scala: -------------------------------------------------------------------------------- 1 | package io.buoyant.linkerd.failureAccrual 2 | 3 | import com.twitter.finagle.liveness.FailureAccrualPolicy 4 | import com.twitter.finagle.util.LoadService 5 | import io.buoyant.linkerd.FailureAccrualInitializer 6 | import io.buoyant.test.FunSuite 7 | 8 | class SuccessRateWindowedTest extends FunSuite { 9 | test("sanity") { 10 | val successRate = 0.4 11 | val window = 3 12 | val config = SuccessRateWindowedConfig(successRate, window) 13 | assert(config.policy().isInstanceOf[FailureAccrualPolicy]) 14 | assert(config.successRate == successRate) 15 | assert(config.window == window) 16 | } 17 | 18 | test("service registration") { 19 | assert(LoadService[FailureAccrualInitializer].exists(_.isInstanceOf[SuccessRateWindowedInitializer])) 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /linkerd/main/src/main/resources/log4j.properties: -------------------------------------------------------------------------------- 1 | log4j.rootLogger=WARN, stderr 2 | log4j.appender.stderr=org.apache.log4j.AsyncAppender 3 | log4j.appender.stderr.layout=org.apache.log4j.PatternLayout 4 | log4j.appender.stderr.layout.ConversionPattern=%p %d{MMdd HH:mm:ss.SSS z} %t: %m%n 5 | log4j.appender.async=org.apache.log4j.AsyncAppender 6 | log4j.appender.async.appenders=console 7 | -------------------------------------------------------------------------------- /linkerd/protocol/h2/src/main/resources/META-INF/services/io.buoyant.linkerd.IdentifierInitializer: -------------------------------------------------------------------------------- 1 | io.buoyant.linkerd.protocol.h2.HeaderTokenIdentifierInitializer 2 | io.buoyant.linkerd.protocol.h2.HeaderPathIdentifierInitializer 3 | io.buoyant.linkerd.protocol.h2.IngressIdentifierInitializer 4 | io.buoyant.linkerd.protocol.h2.istio.IstioIdentifierInitializer 5 | io.buoyant.linkerd.protocol.h2.istio.IstioIngressIdentifierInitializer 6 | -------------------------------------------------------------------------------- /linkerd/protocol/h2/src/main/resources/META-INF/services/io.buoyant.linkerd.ProtocolInitializer: -------------------------------------------------------------------------------- 1 | io.buoyant.linkerd.protocol.H2Initializer 2 | -------------------------------------------------------------------------------- /linkerd/protocol/h2/src/main/resources/META-INF/services/io.buoyant.linkerd.RequestAuthorizerInitializer: -------------------------------------------------------------------------------- 1 | io.buoyant.linkerd.protocol.h2.istio.IstioRequestAuthorizerInitializer 2 | -------------------------------------------------------------------------------- /linkerd/protocol/h2/src/main/resources/META-INF/services/io.buoyant.linkerd.ResponseClassifierInitializer: -------------------------------------------------------------------------------- 1 | io.buoyant.linkerd.protocol.h2.NonRetryable5XXInitializer 2 | io.buoyant.linkerd.protocol.h2.RetryableAll5XXInitializer 3 | io.buoyant.linkerd.protocol.h2.RetryableIdempotent5XXInitializer 4 | io.buoyant.linkerd.protocol.h2.RetryableRead5XXInitializer 5 | io.buoyant.linkerd.protocol.h2.AllSuccessfulInitializer 6 | io.buoyant.linkerd.protocol.h2.grpc.AlwaysRetryableInitializer 7 | io.buoyant.linkerd.protocol.h2.grpc.DefaultInitializer 8 | io.buoyant.linkerd.protocol.h2.grpc.CompliantInitializer 9 | io.buoyant.linkerd.protocol.h2.grpc.NeverRetryableInitializer 10 | io.buoyant.linkerd.protocol.h2.grpc.RetryableStatusCodesInitializer 11 | -------------------------------------------------------------------------------- /linkerd/protocol/h2/src/main/resources/META-INF/services/io.buoyant.linkerd.TracePropagatorInitializer: -------------------------------------------------------------------------------- 1 | io.buoyant.linkerd.protocol.h2.LinkerdTracePropagatorInitializer 2 | io.buoyant.linkerd.protocol.h2.ZipkinTracePropagatorInitializer 3 | -------------------------------------------------------------------------------- /linkerd/protocol/h2/src/main/scala/io/buoyant/linkerd/protocol/h2/ClientConfig.scala: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/linkerd/linkerd/ea82499d386e44e8958be58e0386f593e639645b/linkerd/protocol/h2/src/main/scala/io/buoyant/linkerd/protocol/h2/ClientConfig.scala -------------------------------------------------------------------------------- /linkerd/protocol/h2/src/main/scala/io/buoyant/linkerd/protocol/h2/H2ClassifierConfig.scala: -------------------------------------------------------------------------------- 1 | package io.buoyant.linkerd.protocol.h2 2 | 3 | import com.fasterxml.jackson.annotation.JsonIgnore 4 | import com.twitter.finagle.buoyant.h2.service.H2Classifier 5 | import io.buoyant.config.PolymorphicConfig 6 | 7 | abstract class H2ClassifierConfig extends PolymorphicConfig { 8 | @JsonIgnore 9 | def mk: H2Classifier 10 | } 11 | -------------------------------------------------------------------------------- /linkerd/protocol/h2/src/main/scala/io/buoyant/linkerd/protocol/h2/istio/H2IstioRequest.scala: -------------------------------------------------------------------------------- 1 | package io.buoyant.linkerd.protocol.h2.istio 2 | 3 | import com.twitter.finagle.Path 4 | import com.twitter.finagle.buoyant.h2.Request 5 | import io.buoyant.k8s.istio.IstioRequest 6 | 7 | object H2IstioRequest { 8 | def apply(req: Request, istioPath: Option[Path]): IstioRequest[Request] = 9 | new IstioRequest(req.path, req.scheme, req.method.toString, req.authority, req.headers.get, req, istioPath) 10 | } 11 | 12 | -------------------------------------------------------------------------------- /linkerd/protocol/h2/src/main/scala/io/buoyant/linkerd/protocol/h2/istio/H2IstioRequestHandler.scala: -------------------------------------------------------------------------------- 1 | package io.buoyant.linkerd.protocol.h2.istio 2 | 3 | import com.twitter.finagle.buoyant.h2.{Headers, Request, Response, Status, Stream} 4 | import com.twitter.util.Future 5 | import io.buoyant.k8s.istio.identifiers.IstioProtocolSpecificRequestHandler 6 | import io.buoyant.linkerd.protocol.h2.ErrorReseter.H2ResponseException 7 | import istio.proxy.v1.config.HTTPRedirect 8 | 9 | class H2IstioRequestHandler extends IstioProtocolSpecificRequestHandler[Request] { 10 | def redirectRequest(redir: HTTPRedirect, req: Request): Future[Nothing] = { 11 | val resp = Response(Status.Found, Stream.empty()) 12 | resp.headers.set(Headers.Path, redir.`uri`.getOrElse(req.path)) 13 | resp.headers.set(Headers.Authority, redir.`authority`.getOrElse(req.authority)) 14 | Future.exception(H2ResponseException(resp)) 15 | } 16 | 17 | def rewriteRequest(uri: String, authority: Option[String], req: Request): Unit = { 18 | req.headers.set(Headers.Path, uri) 19 | req.headers.set(Headers.Authority, authority.getOrElse("")) 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /linkerd/protocol/h2/src/main/scala/io/buoyant/linkerd/protocol/h2/istio/H2IstioResponse.scala: -------------------------------------------------------------------------------- 1 | package io.buoyant.linkerd.protocol.h2.istio 2 | 3 | import com.twitter.finagle.buoyant.h2.Response 4 | import com.twitter.finagle.http.Status 5 | import com.twitter.util.{Duration, Try} 6 | import io.buoyant.k8s.istio.IstioResponse 7 | 8 | object H2IstioResponse { 9 | def apply(resp: Try[Response], duration: Duration): IstioResponse[Response] = { 10 | val maybeInt = resp.toOption.map(_.status.code) 11 | new IstioResponse(maybeInt.getOrElse(Status.InternalServerError.code), duration, resp.toOption) 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /linkerd/protocol/h2/src/test/scala/io/buoyant/linkerd/protocol/h2/istio/H2IstioRequestTest.scala: -------------------------------------------------------------------------------- 1 | package io.buoyant.linkerd.protocol.h2.istio 2 | 3 | import com.twitter.finagle.buoyant.h2.{Method, Request} 4 | import org.scalatest.FunSuite 5 | 6 | class H2IstioRequestTest extends FunSuite { 7 | test("generates istio request with expected attributes") { 8 | val httpRequest = Request("http", Method.Connect, "localhost", "/echo/123", null) 9 | val istioRequest = H2IstioRequest(httpRequest, None) 10 | 11 | assert(istioRequest.uri == httpRequest.path) 12 | assert(istioRequest.scheme == "http") 13 | assert(istioRequest.method == "CONNECT") 14 | assert(istioRequest.authority == "localhost") 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /linkerd/protocol/http/src/main/resources/META-INF/services/io.buoyant.linkerd.IdentifierInitializer: -------------------------------------------------------------------------------- 1 | io.buoyant.linkerd.protocol.http.HeaderIdentifierInitializer 2 | io.buoyant.linkerd.protocol.http.HeaderTokenIdentifierInitializer 3 | io.buoyant.linkerd.protocol.http.MethodAndHostIdentifierInitializer 4 | io.buoyant.linkerd.protocol.http.PathIdentifierInitializer 5 | io.buoyant.linkerd.protocol.http.StaticIdentifierInitializer 6 | io.buoyant.linkerd.protocol.http.IngressIdentifierInitializer 7 | io.buoyant.linkerd.protocol.http.istio.IstioIdentifierInitializer 8 | io.buoyant.linkerd.protocol.http.istio.IstioIngressIdentifierInitializer 9 | -------------------------------------------------------------------------------- /linkerd/protocol/http/src/main/resources/META-INF/services/io.buoyant.linkerd.ProtocolInitializer: -------------------------------------------------------------------------------- 1 | io.buoyant.linkerd.protocol.HttpInitializer 2 | -------------------------------------------------------------------------------- /linkerd/protocol/http/src/main/resources/META-INF/services/io.buoyant.linkerd.RequestAuthorizerInitializer: -------------------------------------------------------------------------------- 1 | io.buoyant.linkerd.protocol.http.istio.IstioRequestAuthorizerInitializer 2 | -------------------------------------------------------------------------------- /linkerd/protocol/http/src/main/resources/META-INF/services/io.buoyant.linkerd.ResponseClassifierInitializer: -------------------------------------------------------------------------------- 1 | io.buoyant.linkerd.protocol.http.NonRetryable5XXInitializer 2 | io.buoyant.linkerd.protocol.http.RetryableIdempotent5XXInitializer 3 | io.buoyant.linkerd.protocol.http.RetryableRead5XXInitializer 4 | io.buoyant.linkerd.protocol.http.AllSuccessfulInitializer 5 | io.buoyant.linkerd.protocol.http.RetryableAll5XXInitializer 6 | -------------------------------------------------------------------------------- /linkerd/protocol/http/src/main/resources/META-INF/services/io.buoyant.linkerd.TracePropagatorInitializer: -------------------------------------------------------------------------------- 1 | io.buoyant.linkerd.protocol.LinkerdTracePropagatorInitializer 2 | io.buoyant.linkerd.protocol.ZipkinTracePropagatorInitializer 3 | -------------------------------------------------------------------------------- /linkerd/protocol/http/src/main/scala/com/twitter/finagle/buoyant/linkerd/DelayedRelease.scala: -------------------------------------------------------------------------------- 1 | package com.twitter.finagle 2 | package buoyant.linkerd 3 | 4 | import com.twitter.finagle.client.StackClient 5 | import com.twitter.finagle.http.{DelayedReleaseService, Request, Response} 6 | 7 | object DelayedRelease { 8 | 9 | val module: Stackable[ServiceFactory[Request, Response]] = 10 | new Stack.Module0[ServiceFactory[Request, Response]] { 11 | val role = StackClient.Role.prepFactory 12 | val description = "Prevents an HTTP service from being closed until its response completes" 13 | def make(next: ServiceFactory[Request, Response]) = 14 | next.map(new DelayedReleaseService(_)) 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /linkerd/protocol/http/src/main/scala/io/buoyant/linkerd/protocol/HttpIdentifierConfig.scala: -------------------------------------------------------------------------------- 1 | package io.buoyant.linkerd.protocol 2 | 3 | import com.fasterxml.jackson.annotation.JsonIgnore 4 | import com.twitter.finagle.http.Request 5 | import com.twitter.finagle.{Dtab, Path, Stack} 6 | import io.buoyant.config.PolymorphicConfig 7 | import io.buoyant.router.RoutingFactory.Identifier 8 | 9 | abstract class HttpIdentifierConfig extends PolymorphicConfig { 10 | @JsonIgnore 11 | def newIdentifier( 12 | prefix: Path, 13 | baseDtab: () => Dtab = () => Dtab.base, 14 | routerParams: Stack.Params = Stack.Params.empty 15 | ): Identifier[Request] 16 | } 17 | -------------------------------------------------------------------------------- /linkerd/protocol/http/src/main/scala/io/buoyant/linkerd/protocol/http/istio/HttpIstioRequest.scala: -------------------------------------------------------------------------------- 1 | package io.buoyant.linkerd.protocol.http.istio 2 | 3 | import com.twitter.finagle.Path 4 | import com.twitter.finagle.http.Request 5 | import io.buoyant.k8s.istio.IstioRequest 6 | 7 | object HttpIstioRequest { 8 | def apply(req: Request, istioPath: Option[Path]): IstioRequest[Request] = 9 | //TODO: match on request scheme 10 | IstioRequest(req.path, "", req.method.toString, req.host.getOrElse(""), req.headerMap.get, req, istioPath) 11 | } 12 | 13 | -------------------------------------------------------------------------------- /linkerd/protocol/http/src/main/scala/io/buoyant/linkerd/protocol/http/istio/HttpIstioRequestHandler.scala: -------------------------------------------------------------------------------- 1 | package io.buoyant.linkerd.protocol.http.istio 2 | 3 | import com.twitter.finagle.http.{Request, Response, Status} 4 | import com.twitter.util.Future 5 | import io.buoyant.k8s.istio.identifiers.IstioProtocolSpecificRequestHandler 6 | import io.buoyant.linkerd.protocol.http.ErrorResponder.HttpResponseException 7 | import istio.proxy.v1.config.HTTPRedirect 8 | 9 | class HttpIstioRequestHandler extends IstioProtocolSpecificRequestHandler[Request] { 10 | def redirectRequest(redir: HTTPRedirect, req: Request): Future[Nothing] = { 11 | val redirect = Response(Status.Found) 12 | redirect.location = redir.`uri`.getOrElse(req.uri) 13 | redirect.host = redir.`authority`.orElse(req.host).getOrElse("") 14 | Future.exception(HttpResponseException(redirect)) 15 | } 16 | 17 | def rewriteRequest(uri: String, authority: Option[String], req: Request): Unit = { 18 | req.uri = uri 19 | req.host = authority.getOrElse("") 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /linkerd/protocol/http/src/main/scala/io/buoyant/linkerd/protocol/http/istio/HttpIstioResponse.scala: -------------------------------------------------------------------------------- 1 | package io.buoyant.linkerd.protocol.http.istio 2 | 3 | import com.twitter.finagle.http.{Response, Status} 4 | import com.twitter.util.{Duration, Try} 5 | import io.buoyant.k8s.istio.IstioResponse 6 | 7 | object HttpIstioResponse { 8 | def apply(resp: Try[Response], duration: Duration): IstioResponse[Response] = { 9 | val status = resp.toOption.map(_.status).getOrElse(Status.InternalServerError) 10 | new IstioResponse(status.code, duration, resp.toOption) 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /linkerd/protocol/mux/src/main/resources/META-INF/services/io.buoyant.linkerd.ProtocolInitializer: -------------------------------------------------------------------------------- 1 | io.buoyant.linkerd.protocol.MuxInitializer 2 | -------------------------------------------------------------------------------- /linkerd/protocol/mux/src/main/scala/io/buoyant/linkerd/protocol/MuxInitializer.scala: -------------------------------------------------------------------------------- 1 | package io.buoyant.linkerd 2 | package protocol 3 | 4 | import com.fasterxml.jackson.annotation.JsonIgnore 5 | import com.twitter.finagle.Path 6 | import io.buoyant.config.Parser 7 | import io.buoyant.router.{Mux, RoutingFactory} 8 | 9 | class MuxInitializer extends ProtocolInitializer.Simple { 10 | val name = "mux" 11 | 12 | protected type Req = com.twitter.finagle.mux.Request 13 | protected type Rsp = com.twitter.finagle.mux.Response 14 | 15 | protected val defaultRouter = Mux.router 16 | 17 | protected val defaultServer = Mux.server 18 | 19 | override def defaultServerPort: Int = 4141 20 | 21 | val configClass = classOf[MuxConfig] 22 | } 23 | 24 | object MuxInitializer extends MuxInitializer 25 | 26 | class MuxConfig extends RouterConfig { 27 | 28 | var servers: Seq[ServerConfig] = Nil 29 | var service: Option[Svc] = None 30 | var client: Option[Client] = None 31 | 32 | @JsonIgnore 33 | override def protocol = MuxInitializer 34 | } 35 | -------------------------------------------------------------------------------- /linkerd/protocol/thrift/src/main/resources/META-INF/services/io.buoyant.config.ConfigDeserializer: -------------------------------------------------------------------------------- 1 | io.buoyant.config.types.ThriftProtocolDeserializer 2 | -------------------------------------------------------------------------------- /linkerd/protocol/thrift/src/main/resources/META-INF/services/io.buoyant.config.ConfigSerializer: -------------------------------------------------------------------------------- 1 | io.buoyant.config.types.ThriftProtocolSerializer 2 | -------------------------------------------------------------------------------- /linkerd/protocol/thrift/src/main/resources/META-INF/services/io.buoyant.linkerd.ProtocolInitializer: -------------------------------------------------------------------------------- 1 | io.buoyant.linkerd.protocol.ThriftInitializer 2 | -------------------------------------------------------------------------------- /linkerd/protocol/thrift/src/main/scala/com/twitter/finagle/buoyant/linkerd/ThriftTraceInitializer.scala: -------------------------------------------------------------------------------- 1 | package com.twitter.finagle.buoyant.linkerd 2 | 3 | import com.twitter.finagle._ 4 | import com.twitter.finagle.tracing.{Trace, TraceInitializerFilter, Tracer} 5 | 6 | object ThriftTraceInitializer { 7 | val role = TraceInitializerFilter.role 8 | 9 | def serverModule[Req, Rep]: Stackable[ServiceFactory[Req, Rep]] = 10 | new Stack.Module1[param.Tracer, ServiceFactory[Req, Rep]] { 11 | val role = ThriftTraceInitializer.role 12 | val description = "Ensure that there is a trace id set" 13 | 14 | def make(_tracer: param.Tracer, next: ServiceFactory[Req, Rep]) = { 15 | val param.Tracer(tracer) = _tracer 16 | new ServerFilter(tracer) andThen next 17 | } 18 | } 19 | 20 | class ServerFilter[Req, Rep](tracer: Tracer) 21 | extends SimpleFilter[Req, Rep] { 22 | 23 | def apply(req: Req, service: Service[Req, Rep]) = { 24 | if (!Trace.hasId) 25 | Trace.letTracerAndNextId(tracer) { 26 | service(req) 27 | } 28 | else service(req) 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /linkerd/protocol/thriftmux/src/main/resources/META-INF/services/io.buoyant.linkerd.ProtocolInitializer: -------------------------------------------------------------------------------- 1 | io.buoyant.linkerd.protocol.ThriftMuxInitializer 2 | -------------------------------------------------------------------------------- /linkerd/telemeter/usage/src/main/resources/META-INF/services/io.buoyant.telemetry.TelemeterInitializer: -------------------------------------------------------------------------------- 1 | io.buoyant.linkerd.telemeter.UsageDataTelemeterInitializer 2 | -------------------------------------------------------------------------------- /mesh/README.md: -------------------------------------------------------------------------------- 1 | # io.linkerd.mesh # 2 | 3 | A set of gRPC APIs that can be used to control Linkerd. 4 | 5 | The mesh consists of client-side plugin interfaces (initially, just 6 | NameInterpreter) as well as server-side service implementations 7 | (initially, in Namerd's 'io.l5d.mesh' iface). 8 | 9 | **Status**: Experimental; the API will break in an upcoming release. 10 | 11 | ## TODO ## 12 | 13 | - [] needs automated tests 14 | - [] io.l5d.mesh interpreter is definitely not resilient to errors 15 | - [] determine if additional observation caching is necessary 16 | 17 | ## gRPC services ## 18 | 19 | - `io.linkerd.mesh.Codec` -- Supports parsing/formatting for thin clients. 20 | - `io.linkerd.mesh.Delegator` -- Supports name tree diagnostics. 21 | - `io.linkerd.mesh.Interpreter` -- Supports name tree binding. 22 | - `io.linkerd.mesh.Resolver` -- Supports lookup of address sets (essentially, service discovery). 23 | 24 | -------------------------------------------------------------------------------- /namer/consul/src/main/resources/META-INF/services/io.buoyant.namer.NamerInitializer: -------------------------------------------------------------------------------- 1 | io.buoyant.namer.consul.ConsulInitializer 2 | -------------------------------------------------------------------------------- /namer/consul/src/main/scala/io/buoyant/namer/consul/package.scala: -------------------------------------------------------------------------------- 1 | package io.buoyant.namer 2 | 3 | import com.twitter.finagle.Path 4 | import com.twitter.logging.Logger 5 | import com.twitter.util.{Activity, Updatable, Var} 6 | 7 | package object consul { 8 | private[consul]type VarUp[T] = Var[T] with Updatable[T] 9 | private[consul]type ActUp[T] = VarUp[Activity.State[T]] 10 | val log = Logger.get("io.buoyant.namer.consul") 11 | 12 | /** path-extracted schema to make consul api calls */ 13 | private[consul] case class PathScheme( 14 | dc: String, 15 | service: SvcKey, 16 | id: Path, 17 | subpath: Path 18 | ) 19 | 20 | } 21 | -------------------------------------------------------------------------------- /namer/core/src/main/resources/META-INF/services/io.buoyant.namer.NamerInitializer: -------------------------------------------------------------------------------- 1 | io.buoyant.namer.RewritingNamerInitializer 2 | -------------------------------------------------------------------------------- /namer/core/src/main/resources/META-INF/services/io.buoyant.namer.TransformerInitializer: -------------------------------------------------------------------------------- 1 | io.buoyant.namer.ConstTransformerInitializer 2 | io.buoyant.namer.ReplaceTransformerInitializer 3 | -------------------------------------------------------------------------------- /namer/core/src/main/scala/io/buoyant/RewritingNamer.scala: -------------------------------------------------------------------------------- 1 | package io.buoyant 2 | 3 | import com.twitter.finagle.{Name, NameTree, Namer, Path} 4 | import com.twitter.util.{Activity} 5 | 6 | /** 7 | * A helper 8 | */ 9 | private[buoyant] trait RewritingNamer extends Namer { 10 | protected[this] def rewrite(orig: Path): Option[Path] 11 | 12 | def lookup(path: Path): Activity[NameTree[Name]] = 13 | Activity.value(path match { 14 | case path if path.size > 0 => 15 | rewrite(path) match { 16 | case Some(path) => NameTree.Leaf(Name.Path(path)) 17 | case None => NameTree.Neg 18 | } 19 | case _ => NameTree.Neg 20 | }) 21 | } 22 | -------------------------------------------------------------------------------- /namer/core/src/main/scala/io/buoyant/namer/ConstTransformer.scala: -------------------------------------------------------------------------------- 1 | package io.buoyant.namer 2 | 3 | import com.twitter.finagle.Name.Bound 4 | import com.twitter.finagle.naming.NameInterpreter 5 | import com.twitter.finagle.{Dtab, Name, NameTree, Path} 6 | import com.twitter.util.Activity 7 | 8 | /** Bind the given path and use that instead of the tree. */ 9 | class ConstTransformer(prefix: Path, path: Path) extends NameTreeTransformer { 10 | 11 | private[this] lazy val bound = NameInterpreter.global.bind(Dtab.empty, path).map { tree => 12 | tree.map { b => 13 | Name.Bound(b.addr, prefix ++ path, b.path) 14 | } 15 | } 16 | 17 | override protected def transform(tree: NameTree[Bound]): Activity[NameTree[Bound]] = 18 | bound 19 | } 20 | -------------------------------------------------------------------------------- /namer/core/src/main/scala/io/buoyant/namer/ConstTransformerInitializer.scala: -------------------------------------------------------------------------------- 1 | package io.buoyant.namer 2 | 3 | import com.fasterxml.jackson.annotation.JsonIgnore 4 | import com.twitter.finagle.{Path, Stack} 5 | 6 | class ConstTransformerInitializer extends TransformerInitializer { 7 | val configClass = classOf[ConstTransformerConfig] 8 | override val configId = "io.l5d.const" 9 | } 10 | 11 | object ConstTransformerInitializer extends ConstTransformerInitializer 12 | 13 | case class ConstTransformerConfig(path: Path) extends TransformerConfig { 14 | 15 | @JsonIgnore 16 | val defaultPrefix = Path.read("/io.l5d.const") 17 | 18 | override def mk(params: Stack.Params): NameTreeTransformer = new ConstTransformer(prefix, path) 19 | } 20 | -------------------------------------------------------------------------------- /namer/core/src/main/scala/io/buoyant/namer/Delegator.scala: -------------------------------------------------------------------------------- 1 | package io.buoyant.namer 2 | 3 | import com.twitter.finagle.{Dentry, Dtab, Name, NameTree, Path} 4 | import com.twitter.util.{Activity, Future} 5 | 6 | trait Delegator { 7 | 8 | def delegate( 9 | dtab: Dtab, 10 | tree: NameTree[Name.Path] 11 | ): Future[DelegateTree[Name.Bound]] 12 | 13 | final def delegate( 14 | dtab: Dtab, 15 | path: Path 16 | ): Future[DelegateTree[Name.Bound]] = 17 | delegate(dtab, NameTree.Leaf(Name.Path(path))) 18 | 19 | def dtab: Activity[Dtab] 20 | } 21 | -------------------------------------------------------------------------------- /namer/core/src/main/scala/io/buoyant/namer/EnumeratingNamer.scala: -------------------------------------------------------------------------------- 1 | package io.buoyant.namer 2 | 3 | import com.twitter.finagle.{Name, NameTree, Path, Namer} 4 | import com.twitter.util.{Activity, Future} 5 | 6 | trait EnumeratingNamer extends Namer { 7 | def getAllNames: Activity[Set[Path]] 8 | } 9 | -------------------------------------------------------------------------------- /namer/core/src/main/scala/io/buoyant/namer/JNamer.scala: -------------------------------------------------------------------------------- 1 | package io.buoyant.namer 2 | 3 | import com.twitter.finagle.Namer 4 | 5 | /** For better java compatibility */ 6 | abstract class JNamer extends Namer 7 | -------------------------------------------------------------------------------- /namer/core/src/main/scala/io/buoyant/namer/Metadata.scala: -------------------------------------------------------------------------------- 1 | package io.buoyant.namer 2 | 3 | import com.twitter.finagle.addr.WeightedAddress 4 | 5 | object Metadata { 6 | val authority = "authority" // HTTP/1.1 Host or HTTP/2.0 :authority 7 | val nodeName = "nodeName" 8 | val endpointWeight = WeightedAddress.weightKey 9 | } 10 | -------------------------------------------------------------------------------- /namer/core/src/main/scala/io/buoyant/namer/Param.scala: -------------------------------------------------------------------------------- 1 | package io.buoyant.namer 2 | 3 | import com.twitter.finagle.{Stack, Namer, Path} 4 | 5 | object Param { 6 | case class Namers(namers: Seq[(Path, Namer)]) 7 | implicit object Namers extends Stack.Param[Namers] { 8 | val default = Namers(Nil) 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /namer/core/src/main/scala/io/buoyant/namer/Paths.scala: -------------------------------------------------------------------------------- 1 | package io.buoyant.namer 2 | 3 | import com.twitter.finagle.Path 4 | import com.twitter.io.Buf 5 | 6 | object Paths { 7 | val ConfiguredNamerBuf = Buf.Utf8("#") 8 | val ConfiguredNamerPrefix = Path(ConfiguredNamerBuf) 9 | val LoadedNamerBuf = Buf.Utf8("quot;) 10 | val LoadedNamerPrefix = Path(LoadedNamerBuf) 11 | val NotHashOrDollar: Buf => Boolean = 12 | (b: Buf) => !(b == ConfiguredNamerBuf || b == LoadedNamerBuf) 13 | val TransformerPrefix = Path.Utf8("%") 14 | 15 | def stripTransformerPrefix(p: Path): Path = 16 | if (p.startsWith(TransformerPrefix)) 17 | Path(p.elems.dropWhile(NotHashOrDollar): _*) 18 | else 19 | p 20 | } 21 | -------------------------------------------------------------------------------- /namer/core/src/main/scala/io/buoyant/namer/ReplaceTransformerInitializer.scala: -------------------------------------------------------------------------------- 1 | package io.buoyant.namer 2 | 3 | import com.fasterxml.jackson.annotation.JsonIgnore 4 | import com.twitter.finagle.{Path, Stack} 5 | 6 | class ReplaceTransformerInitializer extends TransformerInitializer { 7 | val configClass = classOf[ReplaceTransformerConfig] 8 | override val configId = "io.l5d.replace" 9 | } 10 | 11 | case class ReplaceTransformerConfig(path: Path) extends TransformerConfig { 12 | 13 | @JsonIgnore 14 | val defaultPrefix = Path.read("/io.l5d.replace") 15 | 16 | @JsonIgnore 17 | override def mk(params: Stack.Params): NameTreeTransformer = new ReplaceTransformer(prefix, path) 18 | } 19 | -------------------------------------------------------------------------------- /namer/core/src/main/scala/io/buoyant/namer/RewritingNamer.scala: -------------------------------------------------------------------------------- 1 | package io.buoyant.namer 2 | 3 | import com.twitter.finagle.{Name, NameTree, Namer, Path} 4 | import com.twitter.finagle.buoyant.PathMatcher 5 | import com.twitter.util.Activity 6 | 7 | class RewritingNamer(matcher: PathMatcher, pattern: String) extends Namer { 8 | override def lookup(path: Path): Activity[NameTree[Name]] = matcher.substitutePath(path, pattern) match { 9 | case Some(result) => Activity.value(NameTree.Leaf(Name(result))) 10 | case None => Activity.value(NameTree.Neg) 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /namer/core/src/main/scala/io/buoyant/namer/RewritingNamerInitializer.scala: -------------------------------------------------------------------------------- 1 | package io.buoyant.namer 2 | 3 | import com.fasterxml.jackson.annotation.JsonIgnore 4 | import com.twitter.finagle.{Namer, Path} 5 | import com.twitter.finagle.buoyant.PathMatcher 6 | import com.twitter.finagle.Stack.Params 7 | 8 | class RewritingNamerInitializer extends NamerInitializer { 9 | override val configId = "io.l5d.rewrite" 10 | val configClass = classOf[RewritingNamerConfig] 11 | } 12 | 13 | case class RewritingNamerConfig( 14 | pattern: String, 15 | name: String 16 | ) extends NamerConfig { 17 | assert(pattern != null) 18 | assert(name != null) 19 | 20 | @JsonIgnore 21 | override def defaultPrefix: Path = 22 | throw new IllegalArgumentException("The 'prefix' property is required for the io.l5d.rewrite namer.") 23 | 24 | @JsonIgnore override protected def newNamer(params: Params): Namer = new RewritingNamer(PathMatcher(pattern), name) 25 | } 26 | -------------------------------------------------------------------------------- /namer/core/src/main/scala/io/buoyant/namer/TransformerConfig.scala: -------------------------------------------------------------------------------- 1 | package io.buoyant.namer 2 | 3 | import com.fasterxml.jackson.annotation.{JsonIgnore, JsonProperty} 4 | import com.twitter.finagle.{Path, Stack} 5 | import io.buoyant.config.{ConfigInitializer, PolymorphicConfig} 6 | 7 | abstract class TransformerConfig extends PolymorphicConfig { 8 | 9 | def defaultPrefix: Path 10 | 11 | @JsonProperty("prefix") 12 | var _prefix: Option[Path] = None 13 | 14 | @JsonIgnore 15 | def prefix = Paths.TransformerPrefix ++ _prefix.getOrElse(defaultPrefix) 16 | 17 | @JsonIgnore 18 | def mk(params: Stack.Params): NameTreeTransformer 19 | } 20 | 21 | trait TransformerInitializer extends ConfigInitializer 22 | -------------------------------------------------------------------------------- /namer/core/src/main/scala/io/buoyant/namer/package.scala: -------------------------------------------------------------------------------- 1 | package io.buoyant 2 | 3 | import com.twitter.util.{Activity, Future, Var} 4 | 5 | package object namer { 6 | implicit class RichActivity[T](val activity: Activity[T]) extends AnyVal { 7 | /** A Future representing the first non-pending value of this Activity */ 8 | def toFuture: Future[T] = activity.values.toFuture.flatMap(Future.const) 9 | 10 | def dedup: Activity[T] = Activity(Var.async[Activity.State[T]](Activity.Pending) { up => 11 | activity.states.dedup.respond(up.update) 12 | }) 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /namer/core/src/main/scala/io/buoyant/rinet.scala: -------------------------------------------------------------------------------- 1 | package io.buoyant 2 | 3 | import com.twitter.finagle._ 4 | import com.twitter.util.Activity 5 | 6 | /** 7 | * An alternate form of the inet namer that expects port before host. 8 | * 9 | * /$/io.buoyant.rinet/<port>/<host> 10 | * 11 | * is equivalent to 12 | * 13 | * /$/inet/<host>/<port> 14 | */ 15 | class rinet extends Namer { 16 | override def lookup(path: Path): Activity[NameTree[Name]] = path.take(2) match { 17 | case Path.Utf8(port, host) => 18 | Activity.value(NameTree.Leaf( 19 | Resolver.eval(s"inet!$host:$port") match { 20 | case bound: Name.Bound => Name.Bound(bound.addr, rinet.prefix ++ Path.read(s"/$port/$host"), path.drop(2)) 21 | case name => name 22 | } 23 | )) 24 | case _ => 25 | Activity.value(NameTree.Neg) 26 | } 27 | } 28 | 29 | object rinet { 30 | val prefix = Path.read("/$/io.buoyant.rinet") 31 | } 32 | -------------------------------------------------------------------------------- /namer/core/src/test/scala/io/buoyant/RinetTest.scala: -------------------------------------------------------------------------------- 1 | package io.buoyant 2 | 3 | import com.twitter.finagle._ 4 | import com.twitter.util.Future 5 | import io.buoyant.namer.RichActivity 6 | import io.buoyant.test.FunSuite 7 | 8 | class RinetTest extends FunSuite { 9 | 10 | test("rinet binds") { 11 | val path = Path.read("/$/io.buoyant.rinet/12345/localhost/residual") 12 | val tree = await(Namer.global.bind(NameTree.Leaf(path)).toFuture) 13 | val NameTree.Leaf(bound) = tree 14 | assert(bound.id == Path.read("/$/io.buoyant.rinet/12345/localhost")) 15 | assert(bound.path == Path.read("/residual")) 16 | val addr = await(bound.addr.changes.filter(_ != Addr.Pending).toFuture()) 17 | val Addr.Bound(addresses, _) = addr 18 | assert(addresses == Set(Address("localhost", 12345))) 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /namer/core/src/test/scala/io/buoyant/namer/InterpreterInitializerTest.scala: -------------------------------------------------------------------------------- 1 | package io.buoyant.namer 2 | 3 | import com.twitter.finagle.naming.NameInterpreter 4 | import com.twitter.finagle.{Dtab, NameTree, Path, Stack} 5 | import io.buoyant.config.Parser 6 | import org.scalatest.FunSuite 7 | 8 | class InterpreterInitializerTest extends FunSuite { 9 | 10 | def parse(config: String): NameInterpreter = { 11 | val mapper = Parser.objectMapper(config, Iterable(Seq(TestInterpreterInitializer))) 12 | val interpCfg = mapper.readValue[InterpreterConfig](config) 13 | interpCfg.interpreter(Stack.Params.empty) 14 | } 15 | 16 | test("parse and initialize") { 17 | val yaml = 18 | s"""kind: test 19 | |alwaysFail: true 20 | |""".stripMargin 21 | 22 | val interpreter = parse(yaml) 23 | val activity = interpreter.bind(Dtab.read("/foo => /bar"), Path.read("/foo")) 24 | assert(activity.sample == NameTree.Neg) // since alwaysFail is true 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /namer/core/src/test/scala/io/buoyant/namer/NamerTestUtil.scala: -------------------------------------------------------------------------------- 1 | package io.buoyant.namer 2 | 3 | import com.twitter.finagle.{Name, NameTree, Namer, Path} 4 | import com.twitter.util.{Await, Future} 5 | import org.scalatest.FunSuite 6 | 7 | trait NamerTestUtil { self: FunSuite => 8 | 9 | protected def lookup(namer: Namer, p: Path): NameTree[Name] = 10 | Await.result(namer.lookup(p).toFuture) 11 | 12 | protected def lookupBound(namer: Namer, p: Path): Seq[Name.Bound] = 13 | lookup(namer, p).eval.toSeq.flatten.collect { 14 | case bound: Name.Bound => bound 15 | } 16 | 17 | protected def assertBoundIdAutobinds(namer: Namer, path: Path, prefix: Path): Unit = { 18 | assert(path.startsWith(prefix)) 19 | for (bound <- lookupBound(namer, path.drop(prefix.size))) { 20 | val idPath = bound.id.asInstanceOf[Path] 21 | assert(path.startsWith(prefix)) 22 | val NameTree.Leaf(rebound: Name.Bound) = lookup(namer, idPath.drop(prefix.size)) 23 | assert(rebound.id == bound.id) 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /namer/core/src/test/scala/io/buoyant/namer/TestInterpreter.scala: -------------------------------------------------------------------------------- 1 | package io.buoyant.namer 2 | 3 | import com.twitter.finagle._ 4 | import com.twitter.finagle.naming.NameInterpreter 5 | import com.twitter.util.Activity 6 | 7 | class TestInterpreterInitializer extends InterpreterInitializer { 8 | val configClass = classOf[TestInterpreterConfig] 9 | override val configId = TestInterpreterConfig.kind 10 | } 11 | 12 | object TestInterpreterInitializer extends TestInterpreterInitializer 13 | 14 | case class TestInterpreterConfig(alwaysFail: Option[Boolean]) extends InterpreterConfig { 15 | def newInterpreter(params: Stack.Params): NameInterpreter = { 16 | TestInterpreter(alwaysFail getOrElse false) 17 | } 18 | } 19 | 20 | object TestInterpreterConfig { 21 | def kind = "test" 22 | } 23 | 24 | case class TestInterpreter(alwaysFail: Boolean) extends NameInterpreter { 25 | override def bind(dtab: Dtab, path: Path): Activity[NameTree[Name.Bound]] = 26 | if (alwaysFail) 27 | Activity.value(NameTree.Neg) 28 | else 29 | Activity.pending 30 | } 31 | -------------------------------------------------------------------------------- /namer/core/src/test/scala/io/buoyant/namer/TestTransformer.scala: -------------------------------------------------------------------------------- 1 | package io.buoyant.namer 2 | 3 | import com.fasterxml.jackson.annotation.JsonIgnore 4 | import com.twitter.finagle.Name.Bound 5 | import com.twitter.finagle.{NameTree, Path, Stack} 6 | import com.twitter.util.Activity 7 | 8 | class TestTransformer extends NameTreeTransformer { 9 | override protected def transform(tree: NameTree[Bound]): Activity[NameTree[Bound]] = 10 | Activity.value(NameTree.Empty) 11 | } 12 | 13 | object TestTransformerInitializer extends TransformerInitializer { 14 | val configClass = classOf[TestTransformerConfig] 15 | override val configId = "io.l5d.empty" 16 | } 17 | 18 | class TestTransformerConfig extends TransformerConfig { 19 | 20 | @JsonIgnore 21 | val defaultPrefix = Path.read("/io.l5d.empty") 22 | 23 | @JsonIgnore 24 | override def mk(params: Stack.Params): NameTreeTransformer = new TestTransformer 25 | } 26 | -------------------------------------------------------------------------------- /namer/core/src/test/scala/io/buoyant/namer/booNamer.scala: -------------------------------------------------------------------------------- 1 | package io.buoyant.namer 2 | 3 | import com.twitter.finagle.Path 4 | 5 | class booNamer extends TestNamerConfig { 6 | override def defaultPrefix = Path.read("/boo") 7 | } 8 | -------------------------------------------------------------------------------- /namer/core/src/test/scala/io/buoyant/namer/booNamerInitializer.scala: -------------------------------------------------------------------------------- 1 | package io.buoyant.namer 2 | 3 | object booNamerInitializer extends NamerInitializer { 4 | override def configClass = classOf[booNamer] 5 | } 6 | -------------------------------------------------------------------------------- /namer/core/src/test/scala/io/buoyant/namer/booUrnsNamer.scala: -------------------------------------------------------------------------------- 1 | package io.buoyant.namer 2 | 3 | import com.twitter.finagle.Path 4 | 5 | class booUrnsNamer extends TestNamerConfig { 6 | override def defaultPrefix = Path.read("/boo/urns") 7 | } 8 | -------------------------------------------------------------------------------- /namer/core/src/test/scala/io/buoyant/namer/booUrnsNamerInitializer.scala: -------------------------------------------------------------------------------- 1 | package io.buoyant.namer 2 | 3 | object booUrnsNamerInitializer extends NamerInitializer { 4 | override def configClass = classOf[booUrnsNamer] 5 | } 6 | -------------------------------------------------------------------------------- /namer/curator/src/main/resources/META-INF/services/io.buoyant.namer.NamerInitializer: -------------------------------------------------------------------------------- 1 | io.buoyant.namer.curator.CuratorInitializer 2 | -------------------------------------------------------------------------------- /namer/dnssrv/src/main/resources/META-INF/services/io.buoyant.namer.NamerInitializer: -------------------------------------------------------------------------------- 1 | io.buoyant.namer.dnssrv.DnsSrvNamerInitializer -------------------------------------------------------------------------------- /namer/fs/src/main/resources/META-INF/services/io.buoyant.config.ConfigSerializer: -------------------------------------------------------------------------------- 1 | io.buoyant.namer.fs.UpRegSerializer 2 | -------------------------------------------------------------------------------- /namer/fs/src/main/resources/META-INF/services/io.buoyant.namer.NamerInitializer: -------------------------------------------------------------------------------- 1 | io.buoyant.namer.fs.FsInitializer 2 | -------------------------------------------------------------------------------- /namer/fs/src/main/scala/io/buoyant/namer/fs/FsInitializer.scala: -------------------------------------------------------------------------------- 1 | package io.buoyant.namer.fs 2 | 3 | import com.fasterxml.jackson.annotation.JsonIgnore 4 | import com.twitter.finagle.{Path, Stack} 5 | import io.buoyant.config.types.Directory 6 | import io.buoyant.namer.{NamerConfig, NamerInitializer} 7 | 8 | class FsInitializer extends NamerInitializer { 9 | val configClass = classOf[FsConfig] 10 | override def configId = "io.l5d.fs" 11 | } 12 | 13 | object FsInitializer extends FsInitializer 14 | 15 | case class FsConfig(rootDir: Directory) extends NamerConfig { 16 | @JsonIgnore 17 | override def defaultPrefix: Path = Path.read("/io.l5d.fs") 18 | 19 | /** 20 | * Construct a namer. 21 | */ 22 | @JsonIgnore 23 | def newNamer(params: Stack.Params) = new WatchingNamer(rootDir.path, prefix) 24 | } 25 | -------------------------------------------------------------------------------- /namer/fs/src/main/scala/io/buoyant/namer/fs/UpRegSerializer.scala: -------------------------------------------------------------------------------- 1 | package io.buoyant.namer.fs 2 | 3 | import com.fasterxml.jackson.core.JsonGenerator 4 | import com.fasterxml.jackson.databind.SerializerProvider 5 | import com.twitter.io.Buf 6 | import com.twitter.util.Activity 7 | import io.buoyant.config.ConfigSerializer 8 | 9 | class UpRegSerializer extends ConfigSerializer[Watcher.File.UpReg] { 10 | 11 | override def serialize( 12 | value: Watcher.File.UpReg, 13 | gen: JsonGenerator, 14 | provider: SerializerProvider 15 | ): Unit = { 16 | value.buf.sample() match { 17 | case Activity.Ok(Buf.Utf8(s)) => gen.writeString(s) 18 | case Activity.Pending => gen.writeString("pending") 19 | case Activity.Failed(e) => gen.writeString("error: " + e.getMessage) 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /namer/fs/src/main/scala/io/buoyant/namer/fs/WatchState.scala: -------------------------------------------------------------------------------- 1 | package io.buoyant.namer.fs 2 | 3 | import com.fasterxml.jackson.annotation.JsonIgnore 4 | import com.twitter.util.Time 5 | import java.nio.file.{Path, WatchEvent} 6 | 7 | class WatchState { 8 | @JsonIgnore 9 | def recordEvent(ev: WatchEvent[_]): Unit = synchronized { 10 | lastEventKind = Some(ev.kind().name()) 11 | ev.context() match { 12 | case p: Path => lastEventName = Some(p.toString) 13 | case _ => 14 | } 15 | lastEventAt = Some(Time.now.toString) 16 | } 17 | 18 | // These fields exist to be serialized. 19 | protected var lastEventKind: Option[String] = None 20 | protected var lastEventName: Option[String] = None 21 | protected var lastEventAt: Option[String] = None 22 | } 23 | -------------------------------------------------------------------------------- /namer/istio/src/main/resources/services/io.buoyant.namer.NamerInitializer: -------------------------------------------------------------------------------- 1 | io.buoyant.namer.k8s.istio.IstioInitializer 2 | -------------------------------------------------------------------------------- /namer/k8s/src/main/resources/META-INF/services/io.buoyant.namer.NamerInitializer: -------------------------------------------------------------------------------- 1 | io.buoyant.namer.k8s.K8sExternalInitializer 2 | io.buoyant.namer.k8s.K8sInitializer 3 | io.buoyant.namer.k8s.K8sNamespacedInitializer 4 | -------------------------------------------------------------------------------- /namer/marathon/src/main/resources/META-INF/services/io.buoyant.namer.NamerInitializer: -------------------------------------------------------------------------------- 1 | io.buoyant.namer.marathon.MarathonInitializer 2 | -------------------------------------------------------------------------------- /namer/marathon/src/main/scala/io/buoyant/namer/marathon/BasicAuthenticatorFilter.scala: -------------------------------------------------------------------------------- 1 | package io.buoyant.namer.marathon 2 | 3 | import com.twitter.finagle.{http, SimpleFilter, Service} 4 | import com.twitter.util.Future 5 | import io.buoyant.marathon.v2.Api 6 | 7 | class BasicAuthenticatorFilter(http_auth_token: String) extends SimpleFilter[http.Request, http.Response] { 8 | 9 | def apply(req: http.Request, svc: Service[http.Request, http.Response]): Future[http.Response] = { 10 | req.headerMap.set("Authorization", s"Basic $http_auth_token") 11 | svc(req) 12 | } 13 | } -------------------------------------------------------------------------------- /namer/rancher/src/main/resources/META-INF/services/io.buoyant.namer.NamerInitializer: -------------------------------------------------------------------------------- 1 | io.buoyant.namer.rancher.RancherInitializer 2 | -------------------------------------------------------------------------------- /namer/serversets/src/main/resources/META-INF/services/io.buoyant.namer.NamerInitializer: -------------------------------------------------------------------------------- 1 | io.buoyant.namer.serversets.ServersetsInitializer 2 | -------------------------------------------------------------------------------- /namer/serversets/src/main/scala/com/twitter/finagle/serversets2/BouyantZkResolver.scala: -------------------------------------------------------------------------------- 1 | package com.twitter.finagle.serverset2 2 | 3 | import com.twitter.finagle.{Addr, Path, Resolver} 4 | import com.twitter.finagle.serverset2.naming.ServersetPath 5 | import com.twitter.util.Var 6 | 7 | trait BouyantZkResolver { 8 | protected[this] def addrOf( 9 | zkHosts: String, 10 | zkPath: String, 11 | endpoint: Option[String], 12 | shardId: Option[Int] 13 | ): Var[Addr] 14 | 15 | def resolve(path: Path): Var[Addr] = ServersetPath.of(path) match { 16 | case Some(ServersetPath(zkHosts, zkPath, endpoint, shardId)) => 17 | addrOf(zkHosts, zkPath.show, endpoint, shardId) 18 | case _ => 19 | Var.value(Addr.Neg) 20 | } 21 | } 22 | 23 | class BouyantZkResolverImpl(zk2: Zk2Resolver) extends BouyantZkResolver { 24 | def this() = this(Resolver.get(classOf[Zk2Resolver]).get) 25 | 26 | protected[this] def addrOf( 27 | zkHosts: String, 28 | zkPath: String, 29 | endpoint: Option[String], 30 | shardId: Option[Int] 31 | ): Var[Addr] = 32 | zk2.addrOf(zkHosts, zkPath, endpoint, shardId) 33 | } 34 | -------------------------------------------------------------------------------- /namer/serversets/src/main/scala/io/buoyant/namer/serversets/ServersetsInitializer.scala: -------------------------------------------------------------------------------- 1 | package io.buoyant.namer.serversets 2 | 3 | import com.fasterxml.jackson.annotation.JsonIgnore 4 | import com.twitter.finagle.{Stack, Path} 5 | import io.buoyant.config.types.{HostAndPort, Port} 6 | import io.buoyant.namer.{NamerConfig, NamerInitializer} 7 | 8 | class ServersetsInitializer extends NamerInitializer { 9 | val configClass = classOf[ServersetsConfig] 10 | override def configId = "io.l5d.serversets" 11 | } 12 | 13 | object ServersetsInitializer extends ServersetsInitializer 14 | 15 | case class ServersetsConfig(zkAddrs: Seq[HostAndPort]) extends NamerConfig { 16 | @JsonIgnore 17 | override def defaultPrefix: Path = Path.read("/io.l5d.serversets") 18 | 19 | @JsonIgnore 20 | val connectString = zkAddrs.map(_.toString(defaultPort = Port(2181))).mkString(",") 21 | 22 | /** 23 | * Construct a namer. 24 | */ 25 | def newNamer(params: Stack.Params) = new ServersetNamer(connectString, prefix) 26 | } 27 | -------------------------------------------------------------------------------- /namer/zk-leader/src/main/resources/META-INF/services/io.buoyant.namer.NamerInitializer: -------------------------------------------------------------------------------- 1 | io.buoyant.namer.zk.ZkLeaderNamerInitializer 2 | -------------------------------------------------------------------------------- /namer/zk-leader/src/main/scala/io/buoyant/namer/zk/ZkLeaderNamerInitializer.scala: -------------------------------------------------------------------------------- 1 | package io.buoyant.namer.zk 2 | 3 | import com.fasterxml.jackson.annotation.JsonIgnore 4 | import com.twitter.finagle.Stack.Params 5 | import com.twitter.finagle.{Namer, Path} 6 | import io.buoyant.config.types.HostAndPort 7 | import io.buoyant.namer.{NamerConfig, NamerInitializer} 8 | 9 | class ZkLeaderNamerInitializer extends NamerInitializer { 10 | override def configClass = classOf[ZkLeaderNamerConfig] 11 | override def configId: String = "io.l5d.zkLeader" 12 | } 13 | 14 | case class ZkLeaderNamerConfig(zkAddrs: Seq[HostAndPort]) extends NamerConfig { 15 | @JsonIgnore 16 | override def defaultPrefix: Path = Path.Utf8("io.l5d.zkLeader") 17 | 18 | @JsonIgnore 19 | override def newNamer(params: Params): Namer = new ZkLeaderNamer(prefix, zkAddrs) 20 | } 21 | -------------------------------------------------------------------------------- /namer/zk-leader/src/test/scala/io/buoyant/namer/zk/ZkLeaderTest.scala: -------------------------------------------------------------------------------- 1 | package io.buoyant.namer.zk 2 | 3 | import com.twitter.finagle.{Path, Stack} 4 | import com.twitter.finagle.util.LoadService 5 | import io.buoyant.config.types.{HostAndPort, Port} 6 | import io.buoyant.namer.NamerInitializer 7 | import org.scalatest.FunSuite 8 | 9 | class ZkLeaderTest extends FunSuite { 10 | 11 | test("sanity") { 12 | // ensure it doesn't totally blowup 13 | val _ = ZkLeaderNamerConfig(Seq(HostAndPort(Some("localhost"), Some(Port(1234))))) 14 | .newNamer(Stack.Params.empty) 15 | } 16 | 17 | test("service registration") { 18 | assert(LoadService[NamerInitializer]().exists(_.isInstanceOf[ZkLeaderNamerInitializer])) 19 | } 20 | 21 | test("parse zk hosts") { 22 | val namer = new leader() 23 | .zkLeaderNamer(Path.read("/1.2.3.4:8000::5.6.7.8:8001/path/to/group")) 24 | assert(namer.zkAddrs == 25 | Seq( 26 | HostAndPort(Some("1.2.3.4"), Some(Port(8000))), 27 | HostAndPort(Some("5.6.7.8"), Some(Port(8001))) 28 | )) 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /namerd/core/src/main/scala/io/buoyant/namerd/DtabStoreInitializer.scala: -------------------------------------------------------------------------------- 1 | package io.buoyant.namerd 2 | 3 | import com.fasterxml.jackson.annotation.{JsonIgnore, JsonProperty} 4 | import com.twitter.finagle.Stack 5 | import io.buoyant.config.{ConfigInitializer, PolymorphicConfig} 6 | 7 | abstract class DtabStoreInitializer extends ConfigInitializer 8 | 9 | abstract class DtabStoreConfig extends PolymorphicConfig { 10 | /** This property must be set to true in order to use this dtab store if it is experimental */ 11 | @JsonProperty("experimental") 12 | var _experimentalEnabled: Option[Boolean] = None 13 | 14 | /** 15 | * Indicates whether this is an experimental dtab store. Experimental dtab stores must have the 16 | * `experimental` property set to true to be used 17 | */ 18 | @JsonIgnore 19 | def experimentalRequired: Boolean = false 20 | 21 | /** If this dtab store is experimental but has not set the `experimental` property. */ 22 | @JsonIgnore 23 | def disabled = experimentalRequired && !_experimentalEnabled.contains(true) 24 | 25 | @JsonIgnore 26 | def mkDtabStore(params: Stack.Params): DtabStore 27 | } 28 | -------------------------------------------------------------------------------- /namerd/core/src/main/scala/io/buoyant/namerd/Namerd.scala: -------------------------------------------------------------------------------- 1 | package io.buoyant.namerd 2 | 3 | import com.twitter.finagle.{Dtab, Path, Namer, ListeningServer} 4 | import com.twitter.util.Activity 5 | import io.buoyant.admin.Admin 6 | import io.buoyant.telemetry.Telemeter 7 | 8 | private[namerd] case class Namerd( 9 | interfaces: Seq[Servable], 10 | dtabStore: DtabStore, 11 | namers: Map[Path, Namer], 12 | admin: Admin, 13 | telemeters: Seq[Telemeter] 14 | ) { 15 | 16 | def extractDtab(ns: Ns): Activity[Dtab] = dtabStore.observe(ns).map { 17 | case Some(dtab) => dtab.dtab 18 | case None => Dtab.empty 19 | } 20 | } 21 | 22 | trait Servable { 23 | def kind: String 24 | def serve(): ListeningServer 25 | } 26 | -------------------------------------------------------------------------------- /namerd/core/src/main/scala/io/buoyant/namerd/package.scala: -------------------------------------------------------------------------------- 1 | package io.buoyant 2 | 3 | package object namerd { 4 | type Ns = String 5 | } 6 | -------------------------------------------------------------------------------- /namerd/core/src/test/scala/io/buoyant/namerd/NullDtabStore.scala: -------------------------------------------------------------------------------- 1 | package io.buoyant.namerd 2 | 3 | import com.twitter.finagle.Dtab 4 | import com.twitter.util.{Activity, Future} 5 | import io.buoyant.namerd.DtabStore.Version 6 | 7 | object NullDtabStore extends DtabStore { 8 | override def list(): Activity[Set[Ns]] = Activity.pending 9 | override def update(ns: Ns, dtab: Dtab, version: Version): Future[Unit] = Future.never 10 | override def put(ns: Ns, dtab: Dtab): Future[Unit] = Future.never 11 | override def observe(ns: Ns): Activity[Option[VersionedDtab]] = Activity.pending 12 | override def delete(ns: Ns): Future[Unit] = Future.never 13 | override def create(ns: Ns, dtab: Dtab): Future[Unit] = Future.never 14 | } 15 | -------------------------------------------------------------------------------- /namerd/core/src/test/scala/io/buoyant/namerd/TestNamerInterface.scala: -------------------------------------------------------------------------------- 1 | package io.buoyant.namerd 2 | 3 | import com.twitter.finagle.stats.StatsReceiver 4 | import com.twitter.finagle.{Path, Namer, NullServer} 5 | import com.twitter.finagle.naming.NameInterpreter 6 | import java.net.InetSocketAddress 7 | 8 | class TestInterpreterInterfaceConfig extends InterpreterInterfaceConfig { 9 | val defaultAddr: InetSocketAddress = new InetSocketAddress(0) 10 | 11 | def mk( 12 | interpreters: Ns => NameInterpreter, 13 | namers: Map[Path, Namer], 14 | store: DtabStore, 15 | stats: StatsReceiver 16 | ): Servable = 17 | TestNamerInterfaceServable 18 | } 19 | 20 | object TestNamerInterfaceServable extends Servable { 21 | def kind = "test" 22 | def serve() = NullServer 23 | } 24 | 25 | case class TestNamerInterface(namers: Ns => NameInterpreter) 26 | 27 | class TestNamerInterfaceInitializer extends InterfaceInitializer { 28 | override def configId = "test" 29 | override def configClass = classOf[TestInterpreterInterfaceConfig] 30 | } 31 | 32 | object TestNamerInterfaceInitializer extends TestNamerInterfaceInitializer 33 | -------------------------------------------------------------------------------- /namerd/examples/all.yaml: -------------------------------------------------------------------------------- 1 | admin: 2 | port: 9991 3 | 4 | storage: 5 | kind: io.l5d.inMemory 6 | namespaces: 7 | default: /svc => /#/io.l5d.fs; 8 | 9 | namers: 10 | - kind: io.l5d.fs 11 | rootDir: namerd/examples/disco 12 | 13 | interfaces: 14 | - kind: io.l5d.httpController 15 | - kind: io.l5d.mesh 16 | - kind: io.l5d.thriftNameInterpreter 17 | -------------------------------------------------------------------------------- /namerd/examples/basic.yaml: -------------------------------------------------------------------------------- 1 | admin: 2 | port: 9991 3 | storage: 4 | kind: io.l5d.inMemory 5 | namespaces: 6 | default: | 7 | /svc => /#/io.l5d.fs; 8 | namers: 9 | - kind: io.l5d.fs 10 | rootDir: namerd/examples/disco 11 | interfaces: 12 | - kind: io.l5d.thriftNameInterpreter 13 | - kind: io.l5d.httpController 14 | telemetry: 15 | - kind: io.l5d.prometheus 16 | - kind: io.l5d.influxdb 17 | -------------------------------------------------------------------------------- /namerd/examples/certs/namerd-cacert.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIICujCCAaICCQDBBDBp58kubDANBgkqhkiG9w0BAQsFADAfMQswCQYDVQQGEwJV 3 | UzEQMA4GA1UEAwwHVGVzdCBDQTAeFw0xNjA5MjgxNzEyMDFaFw0xNjEwMjgxNzEy 4 | MDFaMB8xCzAJBgNVBAYTAlVTMRAwDgYDVQQDDAdUZXN0IENBMIIBIjANBgkqhkiG 5 | 9w0BAQEFAAOCAQ8AMIIBCgKCAQEA70oRir+Kb/vGppIesZwL6arlSfVmMLT7TwY1 6 | iHOOcReZ2d8x1F8IvZmWPERh3aenjtTeef0hpfg5zp7MDJklxjP3N7ZqBKVBLOWO 7 | dr+GKg94tIBjQrFQCwQUEVsso9TRBHkZ5eeVYKQMC3wl1bY8ZbGVY86C8/b0ZI/v 8 | 0OvC5FEUfU8FPLEbrzsZ2SCpTfLpQBAmWJN9KJH7azki34ObbbdF0HONN31C/KYJ 9 | mtFJBuPiggEYv1fF4ve6T1VwTgaMRXKgGZmZ1uhxCUXgKxyBQG1HYkWh8p3pKpAA 10 | SSHjiN66rdz7DD3gQhxp24BdizGNRLmldG7px8ijBtyh+OqmUQIDAQABMA0GCSqG 11 | SIb3DQEBCwUAA4IBAQCpEsrcaadpKBYlMYCRYl7942ahYcVYD4OliZXopctvvEZ0 12 | giHkGMG4spkUhvE27ETXGqWx81KoCgxdrNLIBHpytbVHWK/hMsK3sViTLICurmqo 13 | Ghn40HHMHdU/CtVXU65YvF7OpaGVMscKQ88f0ZMUZtSkoCHbIMB3VswalS8qYSfO 14 | 1bW3dAmx72VQVdXkLzA94M0x6xot+E/EyTJ4Gyta1lA8lvRoKdP3qDiPhofFpdPG 15 | WzIqazXyx2BTzQFot07DhTzun/e+k5tO4c8Zm126UND+u0mLoyCKCcqDUBlpBWFc 16 | 6x2iJ1Iz89jXaRg6b6fKOa+MlEO7H80uaBdabixj 17 | -----END CERTIFICATE----- 18 | -------------------------------------------------------------------------------- /namerd/examples/consul-storage.yaml: -------------------------------------------------------------------------------- 1 | admin: 2 | ip: 0.0.0.0 3 | port: 9991 4 | interfaces: 5 | - kind: io.l5d.httpController 6 | storage: 7 | kind: io.l5d.consul 8 | -------------------------------------------------------------------------------- /namerd/examples/consul.yaml: -------------------------------------------------------------------------------- 1 | namers: 2 | - kind: io.l5d.consul 3 | useHealthCheck: true 4 | admin: 5 | port: 9991 6 | storage: 7 | kind: io.l5d.inMemory 8 | namespaces: 9 | default: "" 10 | interfaces: 11 | - kind: io.l5d.thriftNameInterpreter 12 | - kind: io.l5d.httpController 13 | -------------------------------------------------------------------------------- /namerd/examples/destination.yaml: -------------------------------------------------------------------------------- 1 | admin: 2 | port: 9991 3 | storage: 4 | kind: io.l5d.k8s 5 | host: localhost 6 | port: 8001 7 | namers: 8 | - kind: io.l5d.k8s 9 | host: localhost 10 | port: 8001 11 | interfaces: 12 | - kind: io.l5d.destination 13 | namespace: internal 14 | - kind: io.l5d.httpController 15 | 16 | 17 | -------------------------------------------------------------------------------- /namerd/examples/disco/default: -------------------------------------------------------------------------------- 1 | 127.1 9990 2 | 127.2 9990 3 | -------------------------------------------------------------------------------- /namerd/examples/etcd.yaml: -------------------------------------------------------------------------------- 1 | admin: 2 | port: 9991 3 | storage: 4 | kind: io.l5d.etcd 5 | pathPrefix: /dtabs 6 | namers: [] 7 | interfaces: 8 | - kind: io.l5d.thriftNameInterpreter 9 | - kind: io.l5d.httpController 10 | -------------------------------------------------------------------------------- /namerd/examples/k8s-mesh.yaml: -------------------------------------------------------------------------------- 1 | admin: 2 | port: 9990 3 | namers: 4 | - kind: io.l5d.k8s 5 | host: localhost 6 | port: 8001 7 | prefix: /ds 8 | transformers: 9 | - kind: io.l5d.k8s.daemonset 10 | namespace: default 11 | port: incoming 12 | service: l5d 13 | - kind: io.l5d.k8s 14 | host: localhost 15 | port: 8001 16 | 17 | storage: 18 | kind: io.l5d.inMemory 19 | namespaces: 20 | incoming: | 21 | /svc => /#/io.l5d.k8s/default/http ; 22 | outgoing: | 23 | /svc => /#/ds/default/http ; 24 | 25 | interfaces: 26 | - kind: io.l5d.thriftNameInterpreter 27 | ip: 0.0.0.0 28 | port: 4100 29 | - kind: io.l5d.httpController 30 | ip: 0.0.0.0 31 | port: 4180 32 | -------------------------------------------------------------------------------- /namerd/examples/k8s.yaml: -------------------------------------------------------------------------------- 1 | # This file assumes you are running through `kubectl proxy` on localhost:8001. 2 | storage: 3 | kind: io.l5d.k8s 4 | host: localhost 5 | port: 8001 # Running through kubectl proxy 6 | interfaces: 7 | - kind: io.l5d.thriftNameInterpreter 8 | - kind: io.l5d.httpController 9 | namers: 10 | - kind: io.l5d.fs 11 | rootDir: namerd/examples/disco 12 | -------------------------------------------------------------------------------- /namerd/examples/k8s/3rdparty.yaml: -------------------------------------------------------------------------------- 1 | kind: CustomResourceDefinition 2 | apiVersion: apiextensions.k8s.io/v1beta1 3 | metadata: 4 | name: dtabs.l5d.io 5 | spec: 6 | scope: Namespaced 7 | group: l5d.io 8 | version: v1alpha1 9 | names: 10 | kind: DTab 11 | plural: dtabs 12 | singular: dtab -------------------------------------------------------------------------------- /namerd/examples/mesh.yaml: -------------------------------------------------------------------------------- 1 | admin: 2 | port: 9991 3 | 4 | storage: 5 | kind: io.l5d.inMemory 6 | namespaces: 7 | default: /grpc => /#/io.l5d.fs; 8 | 9 | namers: 10 | - kind: io.l5d.fs 11 | rootDir: namerd/examples/disco 12 | 13 | interfaces: 14 | - kind: io.l5d.httpController 15 | - kind: io.l5d.mesh 16 | port: 4321 17 | -------------------------------------------------------------------------------- /namerd/examples/minimal.yaml: -------------------------------------------------------------------------------- 1 | # example config demonstrating the minimal set of namerd config keys required 2 | storage: 3 | kind: io.l5d.inMemory 4 | interfaces: 5 | - kind: io.l5d.httpController 6 | -------------------------------------------------------------------------------- /namerd/examples/src/test/scala/io/buoyant/namerd/examples/ExamplesTest.scala: -------------------------------------------------------------------------------- 1 | package io.buoyant.namerd.examples 2 | 3 | import io.buoyant.namerd.NamerdConfig 4 | import java.io.{File, FilenameFilter} 5 | import org.scalatest.FunSuite 6 | import scala.io.Source 7 | 8 | class ExamplesTest extends FunSuite { 9 | 10 | val examplesDir = new File("namerd/examples") 11 | val files = examplesDir.listFiles(new FilenameFilter { 12 | override def accept(dir: File, name: String): Boolean = name.endsWith(".yaml") 13 | }) 14 | 15 | for (file <- files) { 16 | test(file.getName) { 17 | val source = Source.fromFile(file) 18 | try { 19 | val lines = source.getLines().toSeq 20 | val firstLine = lines.headOption 21 | if (!firstLine.contains("#notest")) { 22 | val config = lines.mkString("\n") 23 | val _ = NamerdConfig.loadNamerd(config) 24 | } 25 | } finally { 26 | source.close() 27 | } 28 | } 29 | } 30 | } 31 | 32 | -------------------------------------------------------------------------------- /namerd/examples/static.yaml: -------------------------------------------------------------------------------- 1 | admin: 2 | port: 9991 3 | storage: 4 | kind: io.l5d.inMemory 5 | namespaces: 6 | default: | 7 | /svc/* => /$/inet/localhost/8080 8 | namers: [] 9 | interfaces: 10 | - kind: io.l5d.thriftNameInterpreter 11 | - kind: io.l5d.httpController 12 | -------------------------------------------------------------------------------- /namerd/examples/zk.yaml: -------------------------------------------------------------------------------- 1 | storage: 2 | kind: io.l5d.zk 3 | pathPrefix: /dtabs 4 | zkAddrs: 5 | - host: localhost 6 | port: 2181 7 | authInfo: 8 | scheme: digest 9 | auth: user_123:password_123 10 | acls: 11 | - scheme: auth 12 | id: "" 13 | perms: crwda 14 | interfaces: 15 | - kind: io.l5d.thriftNameInterpreter 16 | - kind: io.l5d.httpController 17 | namers: 18 | - kind: io.l5d.fs 19 | rootDir: namerd/examples/disco 20 | -------------------------------------------------------------------------------- /namerd/iface/control-http/src/main/resources/META-INF/services/io.buoyant.namerd.InterfaceInitializer: -------------------------------------------------------------------------------- 1 | io.buoyant.namerd.iface.HttpControlServiceInitializer 2 | -------------------------------------------------------------------------------- /namerd/iface/destination/src/main/protobuf/net.proto: -------------------------------------------------------------------------------- 1 | //This file is added using pull-destination-proto.sh. DO NOT EDIT! 2 | syntax = "proto3"; 3 | 4 | package io.linkerd.proxy.net; 5 | 6 | option go_package = "github.com/linkerd/linkerd2-proxy-api/go/net"; 7 | 8 | message IPAddress { 9 | oneof ip { 10 | fixed32 ipv4 = 1; 11 | IPv6 ipv6 = 2; 12 | } 13 | } 14 | 15 | message IPv6 { 16 | fixed64 first = 1; // hextets 1-4 17 | fixed64 last = 2; // hextets 5-8 18 | } 19 | 20 | message TcpAddress { 21 | IPAddress ip = 1; 22 | uint32 port = 2; 23 | } -------------------------------------------------------------------------------- /namerd/iface/destination/src/main/resources/META-INF/services/io.buoyant.namerd.InterfaceInitializer: -------------------------------------------------------------------------------- 1 | io.buoyant.namerd.iface.DestinationIfaceInitializer 2 | -------------------------------------------------------------------------------- /namerd/iface/destination/src/main/resources/pull-destination-proto.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | cd ../protobuf || exit 3 | LATEST_API_RELEASE=v0.1.1 4 | BASE_PROTO_URL=https://raw.githubusercontent.com/linkerd/linkerd2-proxy-api/$LATEST_API_RELEASE/proto 5 | PROTOFILES="destination.proto net.proto" 6 | 7 | 8 | for proto_file in $PROTOFILES; do 9 | echo "Copying $proto_file from linkerd2 repo" 10 | printf "//This file is added using pull-destination-proto.sh. DO NOT EDIT!\\n%s" "$(curl $BASE_PROTO_URL/"$proto_file")" > "$proto_file" 11 | done 12 | 13 | -------------------------------------------------------------------------------- /namerd/iface/interpreter-thrift/src/main/resources/META-INF/services/io.buoyant.config.ConfigSerializer: -------------------------------------------------------------------------------- 1 | io.buoyant.namerd.iface.AddrReqSerializer 2 | io.buoyant.namerd.iface.AddrSerializer 3 | io.buoyant.namerd.iface.BindReqSerializer 4 | io.buoyant.namerd.iface.BoundSerializer 5 | -------------------------------------------------------------------------------- /namerd/iface/interpreter-thrift/src/main/resources/META-INF/services/io.buoyant.namerd.InterfaceInitializer: -------------------------------------------------------------------------------- 1 | io.buoyant.namerd.iface.ThriftInterpreterInterfaceInitializer 2 | -------------------------------------------------------------------------------- /namerd/iface/interpreter-thrift/src/main/scala/io/buoyant/namerd/iface/AddrReqSerializer.scala: -------------------------------------------------------------------------------- 1 | package io.buoyant.namerd.iface 2 | 3 | import com.fasterxml.jackson.core.JsonGenerator 4 | import com.fasterxml.jackson.databind.SerializerProvider 5 | import io.buoyant.config.ConfigSerializer 6 | import io.buoyant.namerd.iface.{thriftscala => thrift} 7 | 8 | class AddrReqSerializer extends ConfigSerializer[thrift.AddrReq] { 9 | import ByteBufferSerializers._ 10 | 11 | override def serialize( 12 | value: thrift.AddrReq, 13 | gen: JsonGenerator, 14 | provider: SerializerProvider 15 | ): Unit = { 16 | gen.writeStartObject() 17 | gen.writeStringField("name", path(value.name.name)) 18 | gen.writeStringField("stamp", stamp(value.name.stamp)) 19 | gen.writeStringField("namespace", value.name.ns) 20 | gen.writeStringField("clientId", path(value.clientId)) 21 | gen.writeEndObject() 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /namerd/iface/interpreter-thrift/src/main/scala/io/buoyant/namerd/iface/BindReqSerializer.scala: -------------------------------------------------------------------------------- 1 | package io.buoyant.namerd.iface 2 | 3 | import com.fasterxml.jackson.core.JsonGenerator 4 | import com.fasterxml.jackson.databind.SerializerProvider 5 | import io.buoyant.config.ConfigSerializer 6 | import io.buoyant.namerd.iface.{thriftscala => thrift} 7 | 8 | class BindReqSerializer extends ConfigSerializer[thrift.BindReq] { 9 | import ByteBufferSerializers._ 10 | 11 | override def serialize( 12 | value: thrift.BindReq, 13 | gen: JsonGenerator, 14 | provider: SerializerProvider 15 | ): Unit = { 16 | gen.writeStartObject() 17 | gen.writeStringField("name", path(value.name.name)) 18 | gen.writeStringField("dtab", value.dtab) 19 | gen.writeStringField("stamp", stamp(value.name.stamp)) 20 | gen.writeStringField("namespace", value.name.ns) 21 | gen.writeStringField("clientId", path(value.clientId)) 22 | gen.writeEndObject() 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /namerd/iface/interpreter-thrift/src/main/scala/io/buoyant/namerd/iface/ByteBufferSerializers.scala: -------------------------------------------------------------------------------- 1 | package io.buoyant.namerd.iface 2 | 3 | import com.twitter.io.Buf 4 | import java.nio.ByteBuffer 5 | 6 | object ByteBufferSerializers { 7 | 8 | def utf8(bb: ByteBuffer): String = 9 | Buf.Utf8.unapply(Buf.ByteBuffer.Shared(bb)).get 10 | 11 | def path(path: Seq[ByteBuffer]): String = 12 | path.map(utf8).mkString("/", "/", "") 13 | 14 | def stamp(bb: ByteBuffer): String = { 15 | if (bb.remaining() >= 8) bb.duplicate().getLong.toString else "" 16 | } 17 | 18 | def ipv4(bb: ByteBuffer): String = { 19 | val dup = bb.duplicate() 20 | if (dup.remaining() >= 4) 21 | s"${dup.get()}.${dup.get()}.${dup.get()}.${dup.get()}" 22 | else "" 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /namerd/iface/interpreter-thrift/src/main/scala/io/buoyant/namerd/iface/PollState.scala: -------------------------------------------------------------------------------- 1 | package io.buoyant.namerd.iface 2 | 3 | import com.fasterxml.jackson.annotation.JsonIgnore 4 | import com.twitter.util.{Return, Throw, Time, Try} 5 | 6 | class PollState[Req, Rep] { 7 | 8 | @JsonIgnore 9 | def recordApiCall(req: Req): Unit = synchronized { 10 | request = Some(req) 11 | lastRequestAt = Some(Time.now.toString) 12 | } 13 | 14 | @JsonIgnore 15 | def recordResponse(rep: Try[Rep]): Unit = synchronized { 16 | lastResponseAt = Some(Time.now.toString) 17 | rep match { 18 | case Return(r) => 19 | response = Some(r) 20 | error = None 21 | case Throw(e) => 22 | error = Some(e.getMessage) 23 | response = None 24 | } 25 | } 26 | 27 | // These fields exist to be serialized. 28 | protected var request: Option[Req] = None 29 | protected var lastRequestAt: Option[String] = None 30 | protected var response: Option[Rep] = None 31 | protected var lastResponseAt: Option[String] = None 32 | protected var error: Option[String] = None 33 | } 34 | -------------------------------------------------------------------------------- /namerd/iface/mesh/src/main/resources/META-INF/services/io.buoyant.namerd.InterfaceInitializer: -------------------------------------------------------------------------------- 1 | io.buoyant.namerd.iface.MeshIfaceInitializer 2 | -------------------------------------------------------------------------------- /namerd/iface/mesh/src/main/scala/io/buoyant/namerd/iface/mesh/Errors.scala: -------------------------------------------------------------------------------- 1 | package io.buoyant.namerd.iface.mesh 2 | 3 | import com.twitter.finagle.Path 4 | import io.buoyant.grpc.runtime.GrpcStatus 5 | 6 | object Errors { 7 | val NoRoot = GrpcStatus.InvalidArgument("No namespace specified") 8 | val RootNotFound = GrpcStatus.NotFound("Root not found") 9 | val NoName = GrpcStatus.NotFound("No name given") 10 | def InvalidRoot(p: Path) = 11 | GrpcStatus.InvalidArgument(s"Invalid root: `${p.show}` must have exactly one segment") 12 | } 13 | -------------------------------------------------------------------------------- /namerd/iface/mesh/src/test/scala/io/buoyant/namerd/iface/mesh/DelegatorServiceTest.scala: -------------------------------------------------------------------------------- 1 | package io.buoyant.namerd.iface.mesh 2 | 3 | import com.google.protobuf.CodedOutputStream 4 | import com.twitter.finagle._ 5 | import io.buoyant.namer.DelegateTree 6 | import io.buoyant.test.FunSuite 7 | import io.linkerd.mesh.DelegateTreeRsp 8 | import java.io.ByteArrayOutputStream 9 | 10 | class DelegatorServiceTest extends FunSuite { 11 | test("Delegator service responding with DelegateTree Exception shouldn't throw a null pointer exception"){ 12 | val out = CodedOutputStream.newInstance(new ByteArrayOutputStream()) 13 | DelegateTreeRsp.codec.encode(DelegatorService.toDelegateTreeRsp(DelegateTree.Exception(Path.read("/svc"), Dentry.nop, new Throwable)), out) 14 | assert(out.getTotalBytesWritten > 0) 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /namerd/main/src/main/resources/log4j.properties: -------------------------------------------------------------------------------- 1 | log4j.rootLogger=WARN, stderr 2 | log4j.appender.stderr=org.apache.log4j.AsyncAppender 3 | log4j.appender.stderr.layout=org.apache.log4j.PatternLayout 4 | log4j.appender.stderr.layout.ConversionPattern=%p %d{MMdd HH:mm:ss.SSS z} %t: %m%n 5 | log4j.appender.async=org.apache.log4j.AsyncAppender 6 | log4j.appender.async.appenders=console 7 | -------------------------------------------------------------------------------- /namerd/storage/consul/src/main/resources/META-INF/services/io.buoyant.namerd.DtabStoreInitializer: -------------------------------------------------------------------------------- 1 | io.buoyant.namerd.storage.consul.ConsulDtabStoreInitializer 2 | -------------------------------------------------------------------------------- /namerd/storage/etcd/src/main/resources/META-INF/services/io.buoyant.namerd.DtabStoreInitializer: -------------------------------------------------------------------------------- 1 | io.buoyant.namerd.storage.etcd.EtcdDtabStoreInitializer 2 | -------------------------------------------------------------------------------- /namerd/storage/in-memory/src/main/resources/META-INF/services/io.buoyant.namerd.DtabStoreInitializer: -------------------------------------------------------------------------------- 1 | io.buoyant.namerd.storage.InMemoryDtabStoreInitializer 2 | -------------------------------------------------------------------------------- /namerd/storage/in-memory/src/main/scala/io/buoyant/namerd/storage/InMemoryDtabStoreInitializer.scala: -------------------------------------------------------------------------------- 1 | package io.buoyant.namerd.storage 2 | 3 | import com.fasterxml.jackson.annotation.JsonIgnore 4 | import com.twitter.finagle.Dtab 5 | import com.twitter.finagle.Stack 6 | import io.buoyant.namerd.{DtabStore, DtabStoreConfig, DtabStoreInitializer} 7 | 8 | class InMemoryDtabStoreInitializer extends DtabStoreInitializer { 9 | override def configClass = classOf[InMemoryConfig] 10 | override def configId = "io.l5d.inMemory" 11 | } 12 | 13 | case class InMemoryConfig(namespaces: Option[Map[String, Dtab]]) extends DtabStoreConfig { 14 | @JsonIgnore 15 | override def mkDtabStore(params: Stack.Params): DtabStore = new InMemoryDtabStore(namespaces.getOrElse(Map.empty)) 16 | } 17 | -------------------------------------------------------------------------------- /namerd/storage/k8s/src/main/resources/META-INF/services/io.buoyant.k8s.SerializationModule: -------------------------------------------------------------------------------- 1 | io.buoyant.namerd.storage.kubernetes.DtabSerializationModule 2 | -------------------------------------------------------------------------------- /namerd/storage/k8s/src/main/resources/META-INF/services/io.buoyant.namerd.DtabStoreInitializer: -------------------------------------------------------------------------------- 1 | io.buoyant.namerd.storage.K8sDtabStoreInitializer 2 | -------------------------------------------------------------------------------- /namerd/storage/k8s/src/main/scala/io/buoyant/namerd/storage/K8sDtabStoreInitializer.scala: -------------------------------------------------------------------------------- 1 | package io.buoyant.namerd.storage 2 | 3 | import com.fasterxml.jackson.annotation.JsonIgnore 4 | import com.twitter.finagle.Stack 5 | import io.buoyant.config.types.Port 6 | import io.buoyant.k8s.ClientConfig 7 | import io.buoyant.namerd.{DtabStore, DtabStoreConfig, DtabStoreInitializer} 8 | 9 | case class K8sConfig( 10 | host: Option[String], 11 | port: Option[Port], 12 | namespace: Option[String] 13 | ) extends DtabStoreConfig with ClientConfig { 14 | 15 | @JsonIgnore 16 | def portNum = port.map(_.port) 17 | 18 | @JsonIgnore 19 | override def mkDtabStore(params: Stack.Params): DtabStore = { 20 | val client = mkClient(params) 21 | 22 | new K8sDtabStore(client, dst, namespace.getOrElse(DefaultNamespace)) 23 | } 24 | } 25 | 26 | class K8sDtabStoreInitializer extends DtabStoreInitializer { 27 | override def configClass = classOf[K8sConfig] 28 | override def configId = "io.l5d.k8s" 29 | } 30 | 31 | object K8sDtabStoreInitializer extends K8sDtabStoreInitializer 32 | -------------------------------------------------------------------------------- /namerd/storage/k8s/src/main/scala/io/buoyant/namerd/storage/kubernetes/Dtab.scala: -------------------------------------------------------------------------------- 1 | package io.buoyant.namerd.storage.kubernetes 2 | 3 | import com.twitter.finagle.Dentry 4 | import io.buoyant.k8s.{KubeObject, ObjectMeta} 5 | 6 | /** 7 | * This is the Dtab JSON format stored in the k8s thirdparty API. It should 8 | * largely mirror the JSON format returned from namerd HTTP APIs. 9 | */ 10 | case class Dtab( 11 | dentries: Seq[Dentry], 12 | kind: Option[String] = Some("DTab"), 13 | metadata: Option[ObjectMeta] = None, 14 | apiVersion: Option[String] = None 15 | ) extends KubeObject 16 | -------------------------------------------------------------------------------- /namerd/storage/k8s/src/main/scala/io/buoyant/namerd/storage/kubernetes/DtabDescriptor.scala: -------------------------------------------------------------------------------- 1 | package io.buoyant.namerd.storage.kubernetes 2 | 3 | import com.twitter.finagle.{Dtab => FDtab} 4 | import io.buoyant.k8s._ 5 | 6 | object DtabDescriptor extends ObjectDescriptor[Dtab, DtabWatch] { 7 | def listName = "dtabs" 8 | def toWatch(d: Dtab) = DtabModified(d) 9 | } 10 | 11 | -------------------------------------------------------------------------------- /namerd/storage/k8s/src/main/scala/io/buoyant/namerd/storage/kubernetes/DtabList.scala: -------------------------------------------------------------------------------- 1 | package io.buoyant.namerd.storage.kubernetes 2 | 3 | import com.fasterxml.jackson.annotation.JsonProperty 4 | import io.buoyant.k8s.{KubeList, ObjectMeta} 5 | 6 | case class DtabList( 7 | @JsonProperty("items") items: Seq[Dtab], 8 | kind: Option[String] = None, 9 | metadata: Option[ObjectMeta] = None, 10 | apiVersion: Option[String] = None 11 | ) extends KubeList[Dtab] 12 | -------------------------------------------------------------------------------- /namerd/storage/k8s/src/main/scala/io/buoyant/namerd/storage/kubernetes/DtabSerializationModule.scala: -------------------------------------------------------------------------------- 1 | package io.buoyant.namerd.storage.kubernetes 2 | 3 | import io.buoyant.k8s.SerializationModule 4 | import io.buoyant.namerd.DtabCodec 5 | 6 | class DtabSerializationModule extends SerializationModule { 7 | def module = DtabCodec.module 8 | } 9 | -------------------------------------------------------------------------------- /namerd/storage/k8s/src/test/scala/io/buoyant/namerd/storage/K8sConfigTest.scala: -------------------------------------------------------------------------------- 1 | package io.buoyant.namerd.storage 2 | 3 | import io.buoyant.config.Parser 4 | import io.buoyant.config.types.Port 5 | import io.buoyant.namerd.DtabStoreConfig 6 | import io.buoyant.test.FunSuite 7 | import org.scalatest.OptionValues 8 | 9 | class K8sConfigTest extends FunSuite with OptionValues { 10 | test("parse config") { 11 | val yaml = 12 | """ 13 | |kind: io.l5d.k8s 14 | |host: localhost 15 | |port: 8001 16 | |namespace: default 17 | """.stripMargin 18 | 19 | val mapper = Parser.objectMapper(yaml, Iterable(Seq(K8sDtabStoreInitializer))) 20 | val k8s = mapper.readValue[DtabStoreConfig](yaml).asInstanceOf[K8sConfig] 21 | assert(k8s.host.value == "localhost") 22 | assert(k8s.port.value == Port(8001)) 23 | assert(k8s.namespace.value == "default") 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /namerd/storage/zk/src/main/resources/META-INF/services/io.buoyant.namerd.DtabStoreInitializer: -------------------------------------------------------------------------------- 1 | io.buoyant.namerd.storage.ZkDtabStoreInitializer 2 | -------------------------------------------------------------------------------- /namerd/storage/zk/src/test/scala/io/buoyant/namerd/storage/AclTest.scala: -------------------------------------------------------------------------------- 1 | package io.buoyant.namerd.storage 2 | 3 | import io.buoyant.test.Exceptions 4 | import org.scalatest.FunSuite 5 | 6 | class AclTest extends FunSuite with Exceptions { 7 | 8 | test("unrecognized perm") { 9 | assertThrows[IllegalArgumentException](Acl("scheme", "id", "crwdaz")) 10 | } 11 | 12 | test("duplicate perm") { 13 | assertThrows[IllegalArgumentException](Acl("scheme", "id", "cc")) 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /project/Finagle.scala: -------------------------------------------------------------------------------- 1 | import pl.project13.scala.sbt.JmhPlugin 2 | import sbt.Keys.publishArtifact 3 | import sbt._ 4 | 5 | /** Finagle protocol extensions. */ 6 | object Finagle extends Base { 7 | 8 | val buoyantCore = projectDir("finagle/buoyant") 9 | .withTwitterLibs(Deps.finagle("netty4")) 10 | .withTests() 11 | 12 | val h2 = projectDir("finagle/h2") 13 | .dependsOn(buoyantCore) 14 | .withLibs( 15 | Deps.netty4("codec-http2"), Deps.netty4("handler"), Deps.boringssl 16 | ) 17 | .withTests() 18 | .withE2e() 19 | 20 | val benchmark = projectDir("finagle/benchmark") 21 | .dependsOn(h2, buoyantCore, testUtil) 22 | .enablePlugins(JmhPlugin) 23 | .settings(publishArtifact := false) 24 | .withTwitterLib(Deps.twitterUtil("benchmark")) 25 | 26 | val all = aggregateDir("finagle", buoyantCore, h2, benchmark) 27 | } 28 | -------------------------------------------------------------------------------- /project/build.properties: -------------------------------------------------------------------------------- 1 | sbt.version=0.13.16 2 | -------------------------------------------------------------------------------- /protoc: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -e 4 | 5 | if [ "$(uname -s)" = "Darwin" ]; then 6 | os=osx 7 | else 8 | os=linux 9 | fi 10 | arch=$(uname -m) 11 | 12 | protocbin=.protoc 13 | protocversion=3.0.0 14 | protocurl="https://github.com/google/protobuf/releases/download/v${protocversion}/protoc-${protocversion}-${os}-${arch}.zip" 15 | 16 | if [ ! -f "$protocbin" ]; then 17 | tmp=$(mktemp -d -t protoc.XXX) 18 | pushd "$tmp" > /dev/null 19 | curl -L --silent --fail -o "$protocbin.zip" "$protocurl" 20 | jar xf "$protocbin.zip" 21 | chmod +x bin/protoc 22 | popd > /dev/null 23 | mv "$tmp/bin/protoc" "$protocbin" 24 | rm -rf "$tmp" 25 | fi 26 | 27 | ./$protocbin "$@" 28 | -------------------------------------------------------------------------------- /router/base-http/src/main/scala/io/buoyant/router/http/HeadersLike.scala: -------------------------------------------------------------------------------- 1 | package io.buoyant.router.http 2 | 3 | /** 4 | * Type class for HTTP headers object 5 | * @tparam H Headers object type 6 | */ 7 | trait HeadersLike[H] { 8 | def toSeq(headers: H): Seq[(String, String)] 9 | def contains(headers: H, k: String): Boolean 10 | def get(headers: H, k: String): Option[String] 11 | def getAll(headers: H, k: String): Seq[String] 12 | def add(headers: H, k: String, v: String): Unit 13 | def set(headers: H, k: String, v: String): Unit 14 | def remove(headers: H, key: String): Seq[String] 15 | } 16 | -------------------------------------------------------------------------------- /router/base-http/src/main/scala/io/buoyant/router/http/RequestLike.scala: -------------------------------------------------------------------------------- 1 | package io.buoyant.router.http 2 | 3 | /** 4 | * Type class for HTTP 1.1/2 requests 5 | * @tparam R Request type 6 | * @tparam H Headers object type 7 | */ 8 | abstract class RequestLike[R, H: HeadersLike] { 9 | def headers(request: R): H 10 | } 11 | -------------------------------------------------------------------------------- /router/core/src/main/scala/com/twitter/finagle/buoyant/EncodeResidual.scala: -------------------------------------------------------------------------------- 1 | package com.twitter.finagle.buoyant 2 | 3 | import com.twitter.finagle._ 4 | 5 | object EncodeResidual { 6 | val role = Stack.Role("EncodeResidual") 7 | val description = "Supports rewriting of downstream requests" 8 | 9 | trait Module[Req, Rsp] extends Stack.Module1[Dst.Bound, ServiceFactory[Req, Rsp]] { 10 | val role = EncodeResidual.role 11 | val description = EncodeResidual.description 12 | 13 | final def make(dst: Dst.Bound, next: ServiceFactory[Req, Rsp]) = 14 | mkFilter(dst).andThen(next) 15 | 16 | def mkFilter(dst: Dst.Bound): Filter[Req, Rsp, Req, Rsp] 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /router/core/src/main/scala/com/twitter/finagle/buoyant/package.scala: -------------------------------------------------------------------------------- 1 | package io.buoyant 2 | 3 | /** 4 | * The [[io.buoyant.router]] and [[com.twitter.finagle.buoyant]] 5 | * packages provide a library for building RPC routers with Finagle. 6 | */ 7 | package object router 8 | -------------------------------------------------------------------------------- /router/core/src/main/scala/com/twitter/finagle/naming/buoyant/RichNoBrokersAvailableException.scala: -------------------------------------------------------------------------------- 1 | package com.twitter.finagle.naming.buoyant 2 | 3 | import com.twitter.finagle._ 4 | import com.twitter.finagle.buoyant.Dst 5 | import com.twitter.util.Future 6 | 7 | class RichNoBrokersAvailableException( 8 | path: Dst.Path, 9 | dtab: Option[Dtab] 10 | ) extends RequestException { 11 | 12 | private[this] def formatDtab(d: Dtab): String = 13 | d.map { dentry => 14 | s" ${dentry.prefix.show} => ${dentry.dst.show}" 15 | }.mkString("\n") 16 | 17 | override def exceptionMessage(): String = 18 | s"""Unable to route request! 19 | 20 | service name: ${path.path.show} 21 | dtab: 22 | ${formatDtab(dtab.getOrElse(Dtab.empty))} 23 | base dtab: 24 | ${formatDtab(path.baseDtab)} 25 | override dtab: 26 | ${formatDtab(path.localDtab)} 27 | """.stripMargin 28 | } 29 | 30 | object RichNoBrokersAvailableException { 31 | 32 | def apply(path: Dst.Path): Future[Nothing] = 33 | Future.exception(new RichNoBrokersAvailableException(path, None)) 34 | } 35 | -------------------------------------------------------------------------------- /router/core/src/main/scala/io/buoyant/router/Originator.scala: -------------------------------------------------------------------------------- 1 | package io.buoyant.router 2 | 3 | import com.twitter.finagle.Stack 4 | 5 | /** 6 | * Originator.Param is a boolean stack param that is used to configure a 7 | * [[io.buoyant.router.Router Router]]. If the param is set to true, it 8 | * indicates that the router is the first hop in a linker-to-linker request, 9 | * and the router's stats are updated to reflect that. 10 | * 11 | * @see com.twitter.finagle.Stack.Param 12 | */ 13 | object Originator { 14 | case class Param(originator: Boolean) { 15 | def mk(): (Param, Stack.Param[Param]) = 16 | (this, Param.param) 17 | } 18 | 19 | object Param { 20 | implicit val param: Stack.Param[Param] = 21 | Stack.Param(Param(false)) 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /router/core/src/main/scala/io/buoyant/router/PerDstPathFilter.scala: -------------------------------------------------------------------------------- 1 | package io.buoyant.router 2 | 3 | import com.twitter.finagle.{Path, Filter, Service, SimpleFilter} 4 | import com.twitter.finagle.buoyant.Dst 5 | import com.twitter.finagle.service.StatsFilter 6 | import com.twitter.util.{Future, Memoize} 7 | import io.buoyant.router.context.DstPathCtx 8 | 9 | class PerDstPathFilter[Req, Rsp](mk: Path => Filter[Req, Rsp, Req, Rsp]) 10 | extends SimpleFilter[Req, Rsp] { 11 | 12 | private[this] val getFilter = Memoize(mk) 13 | 14 | def apply(req: Req, service: Service[Req, Rsp]): Future[Rsp] = 15 | DstPathCtx.current match { 16 | case None | Some(Dst.Path(Path.empty, _, _)) => 17 | service(req) 18 | 19 | case Some(Dst.Path(path, _, _)) => 20 | val filter = getFilter(path) 21 | filter(req, service) 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /router/core/src/main/scala/io/buoyant/router/RouterLabel.scala: -------------------------------------------------------------------------------- 1 | package io.buoyant.router 2 | 3 | import com.twitter.finagle.Stack 4 | 5 | /** 6 | * RouterLabel.Param is a string stack param containing the router's label. 7 | * 8 | * @see com.twitter.finagle.Stack.Param 9 | */ 10 | object RouterLabel { 11 | case class Param(label: String) { 12 | def mk(): (Param, Stack.Param[Param]) = 13 | (this, Param.param) 14 | } 15 | 16 | object Param { 17 | implicit val param: Stack.Param[Param] = 18 | Stack.Param(Param("")) 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /router/core/src/main/scala/io/buoyant/router/context/dst.scala: -------------------------------------------------------------------------------- 1 | package io.buoyant.router.context 2 | 3 | import com.twitter.finagle.buoyant.Dst 4 | 5 | object DstPathCtx extends LocalKey[Dst.Path]("Dst.Path") 6 | 7 | object DstBoundCtx extends LocalKey[Dst.Bound]("Dst.Bound") 8 | -------------------------------------------------------------------------------- /router/core/src/main/scala/io/buoyant/router/context/responseClassifier.scala: -------------------------------------------------------------------------------- 1 | package io.buoyant.router.context 2 | 3 | import com.twitter.finagle.param 4 | 5 | /** 6 | * We set the ResponseClassifier from the path stack into a local context 7 | * so that when the request enters the client stack, it can use the path stack's 8 | * response classifier for reporting stats 9 | */ 10 | object ResponseClassifierCtx extends LocalKey[param.ResponseClassifier]("ResponseClassifier") 11 | -------------------------------------------------------------------------------- /router/h2/src/main/scala/io/buoyant/router/context/h2/H2ClassifierCtx.scala: -------------------------------------------------------------------------------- 1 | package io.buoyant.router.context.h2 2 | 3 | import com.twitter.finagle.buoyant.h2.param 4 | import io.buoyant.router.context.LocalKey 5 | 6 | /** 7 | * We set the H2Classifier from the path stack into a local context 8 | * so that when the request enters the client stack, it can use the path stack's 9 | * stream classifier for reporting stats. 10 | * 11 | * This is based directly on `ResponseClassifierCtx` in the H1 protocol 12 | */ 13 | object H2ClassifierCtx extends LocalKey[param.H2Classifier]("H2Classifier") 14 | -------------------------------------------------------------------------------- /router/h2/src/main/scala/io/buoyant/router/h2/DupRequest.scala: -------------------------------------------------------------------------------- 1 | package io.buoyant.router.h2 2 | 3 | import com.twitter.finagle.{Service, ServiceFactory, SimpleFilter, Stack, Stackable} 4 | import com.twitter.finagle.buoyant.h2.{Request, Response} 5 | 6 | object DupRequest { 7 | val role = Stack.Role("DupRequest") 8 | 9 | object filter extends SimpleFilter[Request, Response] { 10 | def apply(req: Request, service: Service[Request, Response]) = service(req.dup()) 11 | } 12 | 13 | val module: Stackable[ServiceFactory[Request, Response]] = 14 | new Stack.Module0[ServiceFactory[Request, Response]] { 15 | val role = DupRequest.role 16 | val description = "Provides the rest of the subsequent stack with a duplicate request" 17 | def make(next: ServiceFactory[Request, Response]) = filter.andThen(next) 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /router/h2/src/test/scala/io/buoyant/router/H2Test.scala: -------------------------------------------------------------------------------- 1 | package io.buoyant.router 2 | 3 | import com.twitter.finagle.Stack 4 | import io.buoyant.router.h2.{LocalClassifierStreamStatsFilter, PerDstPathStreamStatsFilter} 5 | import io.buoyant.test.FunSuite 6 | 7 | class H2Test extends FunSuite { 8 | 9 | def get[T](stk: Stack[T], role: Stack.Role): Option[Stack[T]] = { 10 | var found: Option[Stack[T]] = None 11 | stk.foreach { s => 12 | if (s.head.role == role) found = Some(s) 13 | } 14 | found 15 | } 16 | 17 | test("client stack contains classified stream filter") { 18 | val statsModule = get(H2.router.client.stack, LocalClassifierStreamStatsFilter.role).get 19 | assert(statsModule.head.description == LocalClassifierStreamStatsFilter.description) 20 | 21 | val perDstStats = get(H2.router.client.stack, PerDstPathStreamStatsFilter.module.role).get 22 | assert(perDstStats.head.description == "perdstpathstatsfilter, using H2 stream classification") 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /router/h2/src/test/scala/io/buoyant/router/h2/DupRequestTest.scala: -------------------------------------------------------------------------------- 1 | package io.buoyant.router.h2 2 | 3 | import com.twitter.finagle.Service 4 | import com.twitter.finagle.buoyant.h2._ 5 | import com.twitter.util.Future 6 | import io.buoyant.test.Awaits 7 | import org.scalatest.FunSuite 8 | 9 | class DupRequestTest extends FunSuite with Awaits { 10 | 11 | test("changes in service don't impact original") { 12 | val service = DupRequest.filter.andThen(Service.mk[Request, Response] { req => 13 | req.headers.set("badness", "true") 14 | Future.value(Response(Status.Ok, Stream.empty())) 15 | }) 16 | 17 | val req = Request("http", Method.Get, "hihost", "/", Stream.empty()) 18 | await(service(req)) 19 | assert(!req.headers.contains("badness")) 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /router/http/src/main/scala/io/buoyant/http/status.scala: -------------------------------------------------------------------------------- 1 | package io.buoyant.http 2 | 3 | import com.twitter.finagle.{Path, Service, ServiceNamer} 4 | import com.twitter.finagle.http._ 5 | import com.twitter.util.{Future, Try} 6 | 7 | /** 8 | * A service namer that accepts names in the form: 9 | * 10 | * /400/resource/name 11 | * 12 | * and binds the name to an Http service that always responds with the 13 | * given status code (i.e. 400). 14 | */ 15 | class status extends ServiceNamer[Request, Response] { 16 | 17 | private[this] object Code { 18 | def unapply(s: String): Option[Status] = 19 | Try(s.toInt).toOption.filter { s => 100 <= s && s < 600 }.map(Status.fromCode(_)) 20 | } 21 | 22 | def lookupService(path: Path): Option[Service[Request, Response]] = path.take(1) match { 23 | case Path.Utf8(Code(status)) => Some(Service.const(Future.value(Response(status)))) 24 | case _ => None 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /router/http/src/main/scala/io/buoyant/router/http/StaticIdentifier.scala: -------------------------------------------------------------------------------- 1 | package io.buoyant.router.http 2 | 3 | import com.twitter.finagle.buoyant.Dst 4 | import com.twitter.finagle.{Dtab, Path} 5 | import com.twitter.finagle.http.Request 6 | import com.twitter.util.Future 7 | import io.buoyant.router.RoutingFactory 8 | import io.buoyant.router.RoutingFactory.{IdentifiedRequest, RequestIdentification} 9 | 10 | class StaticIdentifier( 11 | path: Path, 12 | baseDtab: () => Dtab = () => Dtab.base 13 | ) extends RoutingFactory.Identifier[Request] { 14 | 15 | def apply(req: Request): Future[RequestIdentification[Request]] = { 16 | val dst = Dst.Path(path, baseDtab(), Dtab.local) 17 | Future.value(new IdentifiedRequest(dst, req)) 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /router/http/src/test/scala/io/buoyant/http/StatusTest.scala: -------------------------------------------------------------------------------- 1 | package io.buoyant.http 2 | 3 | import com.twitter.finagle.{Status => _, _} 4 | import com.twitter.finagle.http._ 5 | import io.buoyant.test.FunSuite 6 | 7 | class StatusTest extends FunSuite { 8 | 9 | def lookup(path: Path) = 10 | await(Namer.global.lookup(path).values.toFuture).get 11 | 12 | test("status") { 13 | val client = Http.newService("/$/io.buoyant.http.status/401/foo/bar.a.b/bah") 14 | val rsp = await(client(Request())) 15 | assert(rsp.status == Status.Unauthorized) 16 | } 17 | 18 | test("status: invalid") { 19 | val path = Path.read("/$/io.buoyant.http.status/foo/bar.a.b/bah") 20 | assert(lookup(path) == NameTree.Neg) 21 | } 22 | 23 | test("status: no code") { 24 | val path = Path.read("/$/io.buoyant.http.status") 25 | assert(lookup(path) == NameTree.Neg) 26 | } 27 | 28 | } 29 | -------------------------------------------------------------------------------- /router/mux/src/main/scala/io/buoyant/router/MuxEncodeResidual.scala: -------------------------------------------------------------------------------- 1 | package io.buoyant.router 2 | 3 | import com.twitter.finagle.{Path, Service, ServiceFactory, SimpleFilter, Stack} 4 | import com.twitter.finagle.buoyant.{Dst, EncodeResidual} 5 | import com.twitter.finagle.mux.{Request, Response} 6 | import com.twitter.util._ 7 | 8 | object MuxEncodeResidual extends Stack.Module1[Dst.Bound, ServiceFactory[Request, Response]] { 9 | val role = EncodeResidual.role 10 | val description = EncodeResidual.description 11 | def make(bound: Dst.Bound, factory: ServiceFactory[Request, Response]) = 12 | new ResidualFilter(bound.path) andThen factory 13 | 14 | class ResidualFilter(path: Path) extends SimpleFilter[Request, Response] { 15 | def apply(req: Request, service: Service[Request, Response]) = 16 | service(Request(path, req.body)) 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /router/thrift-idl/src/main/thrift/ping.thrift: -------------------------------------------------------------------------------- 1 | #@namespace scala io.buoyant.router.thriftscala 2 | 3 | service PingService { 4 | string ping( 5 | 1: string msg 6 | ) 7 | } 8 | -------------------------------------------------------------------------------- /router/thrift/src/main/scala/io/buoyant/router/thrift/Dest.scala: -------------------------------------------------------------------------------- 1 | package io.buoyant.router.thrift 2 | 3 | import com.twitter.finagle.Path 4 | import com.twitter.util.Local 5 | 6 | /** A thread local context for storing the destination of the current request. */ 7 | object Dest { 8 | private val l = new Local[Path] 9 | 10 | def local: Path = l().getOrElse(Path.empty) 11 | 12 | def local_=(path: Path): Unit = l() = path 13 | } 14 | -------------------------------------------------------------------------------- /telemetry/core/src/test/resources/META-INF/services/io.buoyant.telemetry.TelemeterInitializer: -------------------------------------------------------------------------------- 1 | io.buoyant.telemetry.TestTelemeterInitializer 2 | -------------------------------------------------------------------------------- /telemetry/influxdb/src/main/resources/META-INF/services/io.buoyant.telemetry.TelemeterInitializer: -------------------------------------------------------------------------------- 1 | io.buoyant.telemetry.influxdb.InfluxDbTelemeterInitializer 2 | -------------------------------------------------------------------------------- /telemetry/influxdb/src/main/scala/io/buoyant/telemetry/influxdb/InfluxDbTelemeterInitializer.scala: -------------------------------------------------------------------------------- 1 | package io.buoyant.telemetry.influxdb 2 | 3 | import com.fasterxml.jackson.annotation.JsonIgnore 4 | import com.twitter.finagle.Stack 5 | import io.buoyant.telemetry.{MetricsTree, Telemeter, TelemeterConfig, TelemeterInitializer} 6 | 7 | class InfluxDbTelemeterInitializer extends TelemeterInitializer { 8 | type Config = InfluxDbConfig 9 | val configClass = classOf[InfluxDbConfig] 10 | override val configId = "io.l5d.influxdb" 11 | } 12 | 13 | object InfluxDbTelemeterInitializer extends InfluxDbTelemeterInitializer 14 | 15 | class InfluxDbConfig extends TelemeterConfig { 16 | @JsonIgnore def mk(params: Stack.Params): Telemeter = new InfluxDbTelemeter(params[MetricsTree]) 17 | } 18 | -------------------------------------------------------------------------------- /telemetry/influxdb/src/test/scala/io/buoyant/telemetry/influxdb/InfluxDbTelemeterInitializerTest.scala: -------------------------------------------------------------------------------- 1 | package io.buoyant.telemetry.influxdb 2 | 3 | import com.twitter.finagle.Stack 4 | import com.twitter.finagle.util.LoadService 5 | import io.buoyant.config.Parser 6 | import io.buoyant.telemetry.{TelemeterConfig, TelemeterInitializer} 7 | import io.buoyant.test.FunSuite 8 | 9 | class InfluxDbTelemeterInitializerTest extends FunSuite { 10 | 11 | test("io.l5d.influxdb telemeter loads") { 12 | val yaml = 13 | """|kind: io.l5d.influxdb 14 | |""".stripMargin 15 | 16 | val config = Parser.objectMapper(yaml, Seq(LoadService[TelemeterInitializer])) 17 | .readValue[TelemeterConfig](yaml) 18 | 19 | val telemeter = config.mk(Stack.Params.empty) 20 | assert(telemeter.isInstanceOf[InfluxDbTelemeter]) 21 | assert(telemeter.stats.isNull) 22 | assert(telemeter.tracer.isNull) 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /telemetry/prometheus/src/main/resources/META-INF/services/io.buoyant.telemetry.TelemeterInitializer: -------------------------------------------------------------------------------- 1 | io.buoyant.telemetry.prometheus.PrometheusTelemeterInitializer 2 | -------------------------------------------------------------------------------- /telemetry/prometheus/src/main/scala/io/buoyant/telemetry/prometheus/PrometheusTelemeterInitializer.scala: -------------------------------------------------------------------------------- 1 | package io.buoyant.telemetry.prometheus 2 | 3 | import com.fasterxml.jackson.annotation.JsonIgnore 4 | import com.twitter.finagle.Stack 5 | import io.buoyant.telemetry.{MetricsTree, Telemeter, TelemeterConfig, TelemeterInitializer} 6 | 7 | class PrometheusTelemeterInitializer extends TelemeterInitializer { 8 | type Config = PrometheusConfig 9 | val configClass = classOf[PrometheusConfig] 10 | override val configId = "io.l5d.prometheus" 11 | } 12 | 13 | object PrometheusTelemeterInitializer extends PrometheusTelemeterInitializer 14 | 15 | class PrometheusConfig(path: Option[String], prefix: Option[String]) extends TelemeterConfig { 16 | @JsonIgnore def mk(params: Stack.Params): Telemeter = 17 | new PrometheusTelemeter( 18 | params[MetricsTree], 19 | path.getOrElse("/admin/metrics/prometheus"), 20 | prefix.getOrElse("") 21 | ) 22 | } 23 | -------------------------------------------------------------------------------- /telemetry/recent-requests/src/main/resources/META-INF/services/io.buoyant.telemetry.TelemeterInitializer: -------------------------------------------------------------------------------- 1 | io.buoyant.telemetry.recentRequests.RecentRequestsInitializer 2 | -------------------------------------------------------------------------------- /telemetry/recent-requests/src/main/scala/io/buoyant/telemetry/recentRequests/RecentRequestsTelemeter.scala: -------------------------------------------------------------------------------- 1 | package io.buoyant.telemetry.recentRequests 2 | 3 | import com.twitter.finagle.stats.NullStatsReceiver 4 | import io.buoyant.admin.Admin.{Handler, NavItem, WithHandlers, WithNavItems} 5 | import io.buoyant.telemetry.Telemeter 6 | 7 | class RecentRequestsTelemeter(sampleRate: Double, capacity: Int) extends Telemeter with WithHandlers with WithNavItems { 8 | val stats = NullStatsReceiver 9 | val tracer = new RecentRequetsTracer(sampleRate, capacity) 10 | def run() = Telemeter.nopRun 11 | 12 | private[this] val handler = new RecentRequestsAdminHandler(tracer) 13 | 14 | override def adminHandlers: Seq[Handler] = Seq(Handler("/requests", handler)) 15 | 16 | override def navItems: Seq[NavItem] = Seq(NavItem("requests", "requests")) 17 | } 18 | -------------------------------------------------------------------------------- /telemetry/statsd/src/main/resources/META-INF/services/io.buoyant.telemetry.TelemeterInitializer: -------------------------------------------------------------------------------- 1 | io.buoyant.telemetry.StatsDInitializer 2 | -------------------------------------------------------------------------------- /telemetry/tracelog/src/main/resources/META-INF/services/io.buoyant.telemetry.TelemeterInitializer: -------------------------------------------------------------------------------- 1 | io.buoyant.telemetry.TracelogInitializer 2 | -------------------------------------------------------------------------------- /telemetry/zipkin/src/main/resources/META-INF/services/io.buoyant.telemetry.TelemeterInitializer: -------------------------------------------------------------------------------- 1 | io.buoyant.telemetry.ZipkinInitializer 2 | -------------------------------------------------------------------------------- /test-util/src/main/scala/io/buoyant/test/BudgetedRetries.scala: -------------------------------------------------------------------------------- 1 | package io.buoyant.test 2 | 3 | import org.scalatest.{Canceled, Failed, Outcome, Retries} 4 | 5 | /** 6 | * Mixin trait for tests to support a retry budget. 7 | */ 8 | trait BudgetedRetries extends FunSuite with Retries { 9 | 10 | /** 11 | * The number of retries permitted before a test is failed. 12 | * 13 | * Tests that mix in `BudgetedRetries` 14 | */ 15 | def retries = 4 16 | 17 | override def withFixture(test: NoArgTest) = 18 | if (isRetryable(test)) withRetries(test, retries) 19 | else super.withFixture(test) 20 | 21 | private[this] def withRetries(test: NoArgTest, remaining: Int): Outcome = 22 | super.withFixture(test) match { 23 | case Failed(_) | Canceled(_) if remaining == 1 => super.withFixture(test) 24 | case Failed(_) | Canceled(_) => withRetries(test, remaining - 1) 25 | case other => other 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /test-util/src/main/scala/io/buoyant/test/Exceptions.scala: -------------------------------------------------------------------------------- 1 | package io.buoyant.test 2 | 3 | import org.scalatest.Assertions 4 | 5 | trait Exceptions extends Assertions { 6 | 7 | // assertThrows is like intercept, but discards the intercepted exception. 8 | def assertThrows[T <: AnyRef: Manifest](f: => Any): Unit = { 9 | val _ = intercept[T](f) 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /test-util/src/main/scala/io/buoyant/test/FunSuite.scala: -------------------------------------------------------------------------------- 1 | package io.buoyant.test 2 | 3 | trait FunSuite extends org.scalatest.FunSuite 4 | with Awaits 5 | with Exceptions 6 | with Logging 7 | with Json 8 | -------------------------------------------------------------------------------- /test-util/src/main/scala/io/buoyant/test/Logging.scala: -------------------------------------------------------------------------------- 1 | package io.buoyant.test 2 | 3 | import com.twitter.logging._ 4 | import com.twitter.util.{Await, Duration, Future, Time, TimeoutException} 5 | 6 | trait Logging { _: org.scalatest.FunSuite => 7 | 8 | val log = Logger.get(getClass.getName) 9 | 10 | def setLogLevel(level: Level): Unit = 11 | Logger.configure(List(LoggerFactory( 12 | node = "", 13 | level = Some(level), 14 | handlers = List(ConsoleHandler()) 15 | ))) 16 | 17 | } 18 | --------------------------------------------------------------------------------