mirror of https://github.com/status-im/consul.git
website: Go native integration with Connect
This commit is contained in:
parent
f522249e6b
commit
03131398c4
|
@ -3,7 +3,7 @@ layout: "docs"
|
||||||
page_title: "Connect - Native Application Integration"
|
page_title: "Connect - Native Application Integration"
|
||||||
sidebar_current: "docs-connect-native"
|
sidebar_current: "docs-connect-native"
|
||||||
description: |-
|
description: |-
|
||||||
TODO
|
Applications can natively integrate with the Connect API to support accepting and establishing connections to other Connect services without the overhead of a proxy sidecar.
|
||||||
---
|
---
|
||||||
|
|
||||||
# Connect-Native App Integration
|
# Connect-Native App Integration
|
||||||
|
|
|
@ -0,0 +1,221 @@
|
||||||
|
---
|
||||||
|
layout: "docs"
|
||||||
|
page_title: "Connect - Native Application Integration - Go"
|
||||||
|
sidebar_current: "docs-connect-native-go"
|
||||||
|
description: |-
|
||||||
|
We provide a library that makes it drop-in simple to integrate Connect with most Go applications. For most Go applications, Connect can be natively integrated in just a single line of code excluding imports and struct initialization.
|
||||||
|
---
|
||||||
|
|
||||||
|
# Connect-Native Integration with Go
|
||||||
|
|
||||||
|
We provide a library that makes it drop-in simple to integrate Connect
|
||||||
|
with most [Go](https://golang.org/) applications. This page shows examples
|
||||||
|
of integrating this library for accepting or establishing Connect-based
|
||||||
|
connections. For most Go applications, Connect can be natively integrated
|
||||||
|
in just a single line of code excluding imports and struct initialization.
|
||||||
|
|
||||||
|
In addition to this, please read and understand the
|
||||||
|
[overview of Connect-Native integrations](/docs/connect/native.html).
|
||||||
|
In particular, after integrating applications with Connect, they must declare
|
||||||
|
that they accept Connect-based connections via their service definitions.
|
||||||
|
|
||||||
|
## Accepting Connections
|
||||||
|
|
||||||
|
Any server that supports TLS (HTTP, gRPC, net/rpc, etc.) can begin
|
||||||
|
accepting Connect-based connections in just a few lines of code. For most
|
||||||
|
existing applications, converting the server to accept Connect-based
|
||||||
|
connections will require only a one-line change excluding imports and
|
||||||
|
structure initialization.
|
||||||
|
|
||||||
|
The
|
||||||
|
Go library exposes a `*tls.Config` that _automatically_ communicates with
|
||||||
|
Consul to load certificates and authorize inbound connections during the
|
||||||
|
TLS handshake. This also automatically starts goroutines to update any
|
||||||
|
changing certs.
|
||||||
|
|
||||||
|
Example, followed by more details:
|
||||||
|
|
||||||
|
```go
|
||||||
|
import(
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"github.com/hashicorp/consul/api"
|
||||||
|
"github.com/hashicorp/consul/connect"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
// Create a Consul API client
|
||||||
|
client, _ := api.NewClient(api.DefaultConfig())
|
||||||
|
|
||||||
|
// Create an instance representing this service. "my-service" is the
|
||||||
|
// name of _this_ service. The service should be cleaned up via Close.
|
||||||
|
svc, _ := connect.NewService("my-service", client)
|
||||||
|
defer svc.Close()
|
||||||
|
|
||||||
|
// Creating an HTTP server that serves via Connect
|
||||||
|
server := &http.Server{
|
||||||
|
Addr: ":8080",
|
||||||
|
TLSConfig: svc.ServerTLSConfig(),
|
||||||
|
// ... other standard fields
|
||||||
|
}
|
||||||
|
|
||||||
|
// Serve!
|
||||||
|
server.ListenAndServerTLS("", "")
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
The first step is to create a Consul API client. This is almost always the
|
||||||
|
default configuration with an ACL token set, since you want to communicate
|
||||||
|
to the local agent. The Go library will use this client to request certificates,
|
||||||
|
authorize connections, and more.
|
||||||
|
|
||||||
|
Next, `connect.NewService` is called to create a service structure representing
|
||||||
|
the _currently running service_. This structure maintains all the state
|
||||||
|
for accepting and establishing connections. An application should generally
|
||||||
|
create one service and reuse that one service for all servers and clients.
|
||||||
|
|
||||||
|
Finally, a standard `*http.Server` is created. The magic line is the `TLSConfig`
|
||||||
|
value. This is set to a TLS configuration returned by the service structure.
|
||||||
|
This TLS configuration is configured to automatically load certificates
|
||||||
|
in the background, cache them, and authorize inbound connections. This
|
||||||
|
also automatically handles maintaining blocking queries to update certificates
|
||||||
|
in the background if they change.
|
||||||
|
|
||||||
|
Since the service returns a standard `*tls.Config`, _any_ server that supports
|
||||||
|
TLS can be configured. This includes gRPC, net/rpc, basic TCP, and more.
|
||||||
|
Another example is shown below with just a plain TLS listener:
|
||||||
|
|
||||||
|
```go
|
||||||
|
import(
|
||||||
|
"crypto/tls"
|
||||||
|
|
||||||
|
"github.com/hashicorp/consul/api"
|
||||||
|
"github.com/hashicorp/consul/connect"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
// Create a Consul API client
|
||||||
|
client, _ := api.NewClient(api.DefaultConfig())
|
||||||
|
|
||||||
|
// Create an instance representing this service. "my-service" is the
|
||||||
|
// name of _this_ service. The service should be cleaned up via Close.
|
||||||
|
svc, _ := connect.NewService("my-service", client)
|
||||||
|
defer svc.Close()
|
||||||
|
|
||||||
|
// Creating an HTTP server that serves via Connect
|
||||||
|
listener, _ := tls.Listen("tcp", ":8080", svc.ServeTLSConfig())
|
||||||
|
defer listener.Close()
|
||||||
|
|
||||||
|
// Accept
|
||||||
|
go acceptLoop(listener)
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## HTTP Clients
|
||||||
|
|
||||||
|
For Go applications that need to Connect to HTTP-based upstream dependencies,
|
||||||
|
the Go library can construct an `*http.Client` that automatically establishes
|
||||||
|
Connect-based connections as long as Consul-based service discovery is used.
|
||||||
|
|
||||||
|
Example, followed by more details:
|
||||||
|
|
||||||
|
```go
|
||||||
|
import(
|
||||||
|
"github.com/hashicorp/consul/api"
|
||||||
|
"github.com/hashicorp/consul/connect"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
// Create a Consul API client
|
||||||
|
client, _ := api.NewClient(api.DefaultConfig())
|
||||||
|
|
||||||
|
// Create an instance representing this service. "my-service" is the
|
||||||
|
// name of _this_ service. The service should be cleaned up via Close.
|
||||||
|
svc, _ := connect.NewService("my-service", client)
|
||||||
|
defer svc.Close()
|
||||||
|
|
||||||
|
// Get an HTTP client
|
||||||
|
httpClient := svc.HTTPClient()
|
||||||
|
|
||||||
|
// Perform a request, then use the standard response
|
||||||
|
resp, _ := httpClient.Get("https://userinfo.service.consul/user/mitchellh")
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
The first step is to create a Consul API client and service. These are the
|
||||||
|
same steps as accepting connections and are explained in detail in the
|
||||||
|
section above. If your application is both a client and server, both the
|
||||||
|
API client and service structure can be shared and reused.
|
||||||
|
|
||||||
|
Next, we call `svc.HTTPClient()` to return a specially configured
|
||||||
|
`*http.Client`. This client will automatically established Connect-based
|
||||||
|
connections using Consul service discovery.
|
||||||
|
|
||||||
|
Finally, we perform an HTTP `GET` request to a hypothetical user service.
|
||||||
|
The HTTP client configuration automatically sends the correct client
|
||||||
|
certificate, verifies the server certificate, and manages background
|
||||||
|
goroutines for updating our certificates as necessary.
|
||||||
|
|
||||||
|
-> **Important:** The HTTP client _requires_ the hostname is a Consul
|
||||||
|
DNS name. Static IP addresses and external DNS cannot be used with the
|
||||||
|
HTTP client. For these values, please use `svc.Dial` directly.
|
||||||
|
|
||||||
|
If the application already uses a manually constructed `*http.Client`,
|
||||||
|
the `svc.HTTPDialTLS` function can be used to configure the
|
||||||
|
`http.Transport.DialTLS` field to achieve equivalent behavior.
|
||||||
|
|
||||||
|
## Raw TLS Connection
|
||||||
|
|
||||||
|
For a raw `net.Conn` TLS connection, the `svc.Dial` function can be used.
|
||||||
|
This will establish a connection to the desired service via Connect and
|
||||||
|
return the `net.Conn`. This connection can then be used as desired.
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
````go
|
||||||
|
import(
|
||||||
|
"context"
|
||||||
|
|
||||||
|
"github.com/hashicorp/consul/api"
|
||||||
|
"github.com/hashicorp/consul/connect"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
// Create a Consul API client
|
||||||
|
client, _ := api.NewClient(api.DefaultConfig())
|
||||||
|
|
||||||
|
// Create an instance representing this service. "my-service" is the
|
||||||
|
// name of _this_ service. The service should be cleaned up via Close.
|
||||||
|
svc, _ := connect.NewService("my-service", client)
|
||||||
|
defer svc.Close()
|
||||||
|
|
||||||
|
// Connect to the "userinfo" Consul service.
|
||||||
|
conn, _ := svc.Dial(context.Background(), &connect.ConsulResolver{
|
||||||
|
Client: client,
|
||||||
|
Name: "userinfo",
|
||||||
|
})
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
This uses a familiar `Dial`-like function to establish raw `net.Conn` values.
|
||||||
|
The second parameter to dial is an implementation of the `connect.Resolver`
|
||||||
|
interface. The example above uses the `*connect.ConsulResolver` implementation
|
||||||
|
to perform Consul-based service discovery. This also automatically determines
|
||||||
|
the correct certificate metadata we expect the remote service to serve.
|
||||||
|
|
||||||
|
## Static Addresses, Custom Resolvers
|
||||||
|
|
||||||
|
In the raw TLS connection example, you see the use of a `connect.Resolver`
|
||||||
|
implementation. This interface can be implemented to perform address
|
||||||
|
resolution. This must return the address and also the URI SAN expected
|
||||||
|
in the TLS certificate served by the remote service.
|
||||||
|
|
||||||
|
The Go library provides two built-in resolvers:
|
||||||
|
|
||||||
|
* `*connect.StaticResolver` can be used for static addresses where no
|
||||||
|
service discovery is required. The expected cert URI SAN must be
|
||||||
|
manually specified.
|
||||||
|
|
||||||
|
* `*connect.ConsulResolver` which resolves services and prepared queries
|
||||||
|
via the Consul API. This also automatically determines the expected
|
||||||
|
cert URI SAN.
|
Loading…
Reference in New Issue