2023-08-11 13:12:13 +00:00
|
|
|
// Copyright (c) HashiCorp, Inc.
|
|
|
|
// SPDX-License-Identifier: BUSL-1.1
|
|
|
|
|
2023-07-17 22:15:22 +00:00
|
|
|
package util
|
|
|
|
|
|
|
|
import (
|
2023-11-02 19:25:48 +00:00
|
|
|
"context"
|
|
|
|
"crypto/tls"
|
2023-07-17 22:15:22 +00:00
|
|
|
"fmt"
|
|
|
|
"net/http"
|
|
|
|
"net/url"
|
|
|
|
"strconv"
|
|
|
|
|
2023-11-02 19:25:48 +00:00
|
|
|
"github.com/hashicorp/consul-server-connection-manager/discovery"
|
2023-07-17 22:15:22 +00:00
|
|
|
"github.com/hashicorp/consul/api"
|
|
|
|
"github.com/hashicorp/go-cleanhttp"
|
2023-11-02 19:25:48 +00:00
|
|
|
"github.com/hashicorp/go-hclog"
|
|
|
|
"google.golang.org/grpc"
|
2023-07-17 22:15:22 +00:00
|
|
|
)
|
|
|
|
|
2023-11-02 19:25:48 +00:00
|
|
|
func DialExposedGRPCConn(
|
|
|
|
ctx context.Context, logger hclog.Logger,
|
|
|
|
exposedServerGRPCPort int, token string,
|
|
|
|
tlsConfig *tls.Config,
|
|
|
|
) (*grpc.ClientConn, func(), error) {
|
|
|
|
if exposedServerGRPCPort <= 0 {
|
|
|
|
return nil, nil, fmt.Errorf("cannot dial server grpc on port %d", exposedServerGRPCPort)
|
|
|
|
}
|
|
|
|
|
|
|
|
cfg := discovery.Config{
|
|
|
|
Addresses: "127.0.0.1",
|
|
|
|
GRPCPort: exposedServerGRPCPort,
|
|
|
|
// Disable server watch because we only need to get server IPs once.
|
|
|
|
ServerWatchDisabled: true,
|
|
|
|
TLS: tlsConfig,
|
|
|
|
|
|
|
|
Credentials: discovery.Credentials{
|
|
|
|
Type: discovery.CredentialsTypeStatic,
|
|
|
|
Static: discovery.StaticTokenCredential{
|
|
|
|
Token: token,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
}
|
|
|
|
watcher, err := discovery.NewWatcher(ctx, cfg, logger.Named("consul-server-connection-manager"))
|
|
|
|
if err != nil {
|
|
|
|
return nil, nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
go watcher.Run()
|
|
|
|
|
|
|
|
// We recycle the GRPC connection from the discovery client because it
|
|
|
|
// should have all the necessary dial options, including the resolver that
|
|
|
|
// continuously updates Consul server addresses. Otherwise, a lot of code from consul-server-connection-manager
|
|
|
|
// would need to be duplicated
|
|
|
|
state, err := watcher.State()
|
|
|
|
if err != nil {
|
|
|
|
watcher.Stop()
|
|
|
|
return nil, nil, fmt.Errorf("unable to get connection manager state: %w", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
return state.GRPCConn, func() { watcher.Stop() }, nil
|
|
|
|
}
|
|
|
|
|
2023-07-17 22:15:22 +00:00
|
|
|
func ProxyNotPooledAPIClient(proxyPort int, containerIP string, containerPort int, token string) (*api.Client, error) {
|
|
|
|
return proxyAPIClient(cleanhttp.DefaultTransport(), proxyPort, containerIP, containerPort, token)
|
|
|
|
}
|
|
|
|
|
|
|
|
func ProxyAPIClient(proxyPort int, containerIP string, containerPort int, token string) (*api.Client, error) {
|
|
|
|
return proxyAPIClient(cleanhttp.DefaultPooledTransport(), proxyPort, containerIP, containerPort, token)
|
|
|
|
}
|
|
|
|
|
|
|
|
func proxyAPIClient(baseTransport *http.Transport, proxyPort int, containerIP string, containerPort int, token string) (*api.Client, error) {
|
|
|
|
if proxyPort <= 0 {
|
|
|
|
return nil, fmt.Errorf("cannot use an http proxy on port %d", proxyPort)
|
|
|
|
}
|
|
|
|
if containerIP == "" {
|
|
|
|
return nil, fmt.Errorf("container IP is required")
|
|
|
|
}
|
|
|
|
if containerPort <= 0 {
|
|
|
|
return nil, fmt.Errorf("cannot dial api client on port %d", containerPort)
|
|
|
|
}
|
|
|
|
|
|
|
|
proxyURL, err := url.Parse("http://127.0.0.1:" + strconv.Itoa(proxyPort))
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
cfg := api.DefaultConfig()
|
|
|
|
cfg.Transport = baseTransport
|
|
|
|
cfg.Transport.Proxy = http.ProxyURL(proxyURL)
|
|
|
|
cfg.Address = fmt.Sprintf("http://%s:%d", containerIP, containerPort)
|
|
|
|
cfg.Token = token
|
|
|
|
return api.NewClient(cfg)
|
|
|
|
}
|
|
|
|
|
|
|
|
func ProxyNotPooledHTTPTransport(proxyPort int) (*http.Transport, error) {
|
|
|
|
return proxyHTTPTransport(cleanhttp.DefaultTransport(), proxyPort)
|
|
|
|
}
|
|
|
|
|
|
|
|
func ProxyHTTPTransport(proxyPort int) (*http.Transport, error) {
|
|
|
|
return proxyHTTPTransport(cleanhttp.DefaultPooledTransport(), proxyPort)
|
|
|
|
}
|
|
|
|
|
|
|
|
func proxyHTTPTransport(baseTransport *http.Transport, proxyPort int) (*http.Transport, error) {
|
|
|
|
if proxyPort <= 0 {
|
|
|
|
return nil, fmt.Errorf("cannot use an http proxy on port %d", proxyPort)
|
|
|
|
}
|
|
|
|
proxyURL, err := url.Parse("http://127.0.0.1:" + strconv.Itoa(proxyPort))
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
baseTransport.Proxy = http.ProxyURL(proxyURL)
|
|
|
|
return baseTransport, nil
|
|
|
|
}
|