consul/agent/hcp/client/client.go
hashicorp-copywrite[bot] 5fb9df1640
[COMPLIANCE] License changes (#18443)
* Adding explicit MPL license for sub-package

This directory and its subdirectories (packages) contain files licensed with the MPLv2 `LICENSE` file in this directory and are intentionally licensed separately from the BSL `LICENSE` file at the root of this repository.

* Adding explicit MPL license for sub-package

This directory and its subdirectories (packages) contain files licensed with the MPLv2 `LICENSE` file in this directory and are intentionally licensed separately from the BSL `LICENSE` file at the root of this repository.

* Updating the license from MPL to Business Source License

Going forward, this project will be licensed under the Business Source License v1.1. Please see our blog post for more details at <Blog URL>, FAQ at www.hashicorp.com/licensing-faq, and details of the license at www.hashicorp.com/bsl.

* add missing license headers

* Update copyright file headers to BUSL-1.1

* Update copyright file headers to BUSL-1.1

* Update copyright file headers to BUSL-1.1

* Update copyright file headers to BUSL-1.1

* Update copyright file headers to BUSL-1.1

* Update copyright file headers to BUSL-1.1

* Update copyright file headers to BUSL-1.1

* Update copyright file headers to BUSL-1.1

* Update copyright file headers to BUSL-1.1

* Update copyright file headers to BUSL-1.1

* Update copyright file headers to BUSL-1.1

* Update copyright file headers to BUSL-1.1

* Update copyright file headers to BUSL-1.1

* Update copyright file headers to BUSL-1.1

* Update copyright file headers to BUSL-1.1

---------

Co-authored-by: hashicorp-copywrite[bot] <110428419+hashicorp-copywrite[bot]@users.noreply.github.com>
2023-08-11 09:12:13 -04:00

264 lines
7.5 KiB
Go

// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: BUSL-1.1
package client
import (
"context"
"fmt"
"strconv"
"time"
httptransport "github.com/go-openapi/runtime/client"
"github.com/go-openapi/strfmt"
hcptelemetry "github.com/hashicorp/hcp-sdk-go/clients/cloud-consul-telemetry-gateway/preview/2023-04-14/client/consul_telemetry_service"
hcpgnm "github.com/hashicorp/hcp-sdk-go/clients/cloud-global-network-manager-service/preview/2022-02-15/client/global_network_manager_service"
gnmmod "github.com/hashicorp/hcp-sdk-go/clients/cloud-global-network-manager-service/preview/2022-02-15/models"
"github.com/hashicorp/hcp-sdk-go/httpclient"
"github.com/hashicorp/hcp-sdk-go/resource"
"github.com/hashicorp/consul/agent/hcp/config"
"github.com/hashicorp/consul/version"
)
// metricsGatewayPath is the default path for metrics export request on the Telemetry Gateway.
const metricsGatewayPath = "/v1/metrics"
// Client interface exposes HCP operations that can be invoked by Consul
//
//go:generate mockery --name Client --with-expecter --inpackage
type Client interface {
FetchBootstrap(ctx context.Context) (*BootstrapConfig, error)
FetchTelemetryConfig(ctx context.Context) (*TelemetryConfig, error)
PushServerStatus(ctx context.Context, status *ServerStatus) error
DiscoverServers(ctx context.Context) ([]string, error)
}
type BootstrapConfig struct {
Name string
BootstrapExpect int
GossipKey string
TLSCert string
TLSCertKey string
TLSCAs []string
ConsulConfig string
ManagementToken string
}
type hcpClient struct {
hc *httptransport.Runtime
cfg config.CloudConfig
gnm hcpgnm.ClientService
tgw hcptelemetry.ClientService
resource resource.Resource
}
func NewClient(cfg config.CloudConfig) (Client, error) {
client := &hcpClient{
cfg: cfg,
}
var err error
client.resource, err = resource.FromString(cfg.ResourceID)
if err != nil {
return nil, err
}
client.hc, err = httpClient(cfg)
if err != nil {
return nil, err
}
client.gnm = hcpgnm.New(client.hc, nil)
client.tgw = hcptelemetry.New(client.hc, nil)
return client, nil
}
func httpClient(c config.CloudConfig) (*httptransport.Runtime, error) {
cfg, err := c.HCPConfig()
if err != nil {
return nil, err
}
return httpclient.New(httpclient.Config{
HCPConfig: cfg,
SourceChannel: "consul " + version.GetHumanVersion(),
})
}
// FetchTelemetryConfig obtains telemetry configuration from the Telemetry Gateway.
func (c *hcpClient) FetchTelemetryConfig(ctx context.Context) (*TelemetryConfig, error) {
params := hcptelemetry.NewAgentTelemetryConfigParamsWithContext(ctx).
WithLocationOrganizationID(c.resource.Organization).
WithLocationProjectID(c.resource.Project).
WithClusterID(c.resource.ID)
resp, err := c.tgw.AgentTelemetryConfig(params, nil)
if err != nil {
return nil, fmt.Errorf("failed to fetch from HCP: %w", err)
}
if err := validateAgentTelemetryConfigPayload(resp); err != nil {
return nil, fmt.Errorf("invalid response payload: %w", err)
}
return convertAgentTelemetryResponse(ctx, resp, c.cfg)
}
func (c *hcpClient) FetchBootstrap(ctx context.Context) (*BootstrapConfig, error) {
version := version.GetHumanVersion()
params := hcpgnm.NewAgentBootstrapConfigParamsWithContext(ctx).
WithID(c.resource.ID).
WithLocationOrganizationID(c.resource.Organization).
WithLocationProjectID(c.resource.Project).
WithConsulVersion(&version)
resp, err := c.gnm.AgentBootstrapConfig(params, nil)
if err != nil {
return nil, err
}
return bootstrapConfigFromHCP(resp.Payload), nil
}
func bootstrapConfigFromHCP(res *gnmmod.HashicorpCloudGlobalNetworkManager20220215AgentBootstrapResponse) *BootstrapConfig {
var serverTLS gnmmod.HashicorpCloudGlobalNetworkManager20220215ServerTLS
if res.Bootstrap.ServerTLS != nil {
serverTLS = *res.Bootstrap.ServerTLS
}
return &BootstrapConfig{
Name: res.Bootstrap.ID,
BootstrapExpect: int(res.Bootstrap.BootstrapExpect),
GossipKey: res.Bootstrap.GossipKey,
TLSCert: serverTLS.Cert,
TLSCertKey: serverTLS.PrivateKey,
TLSCAs: serverTLS.CertificateAuthorities,
ConsulConfig: res.Bootstrap.ConsulConfig,
ManagementToken: res.Bootstrap.ManagementToken,
}
}
func (c *hcpClient) PushServerStatus(ctx context.Context, s *ServerStatus) error {
params := hcpgnm.NewAgentPushServerStateParamsWithContext(ctx).
WithID(c.resource.ID).
WithLocationOrganizationID(c.resource.Organization).
WithLocationProjectID(c.resource.Project)
params.SetBody(hcpgnm.AgentPushServerStateBody{
ServerState: serverStatusToHCP(s),
})
_, err := c.gnm.AgentPushServerState(params, nil)
return err
}
type ServerStatus struct {
ID string
Name string
Version string
LanAddress string
GossipPort int
RPCPort int
Datacenter string
Autopilot ServerAutopilot
Raft ServerRaft
TLS ServerTLSInfo
ACL ServerACLInfo
ScadaStatus string
}
type ServerAutopilot struct {
FailureTolerance int
Healthy bool
MinQuorum int
NumServers int
NumVoters int
}
type ServerRaft struct {
IsLeader bool
KnownLeader bool
AppliedIndex uint64
TimeSinceLastContact time.Duration
}
type ServerACLInfo struct {
Enabled bool
}
type ServerTLSInfo struct {
Enabled bool
CertExpiry time.Time
CertName string
CertSerial string
VerifyIncoming bool
VerifyOutgoing bool
VerifyServerHostname bool
}
func serverStatusToHCP(s *ServerStatus) *gnmmod.HashicorpCloudGlobalNetworkManager20220215ServerState {
if s == nil {
return nil
}
return &gnmmod.HashicorpCloudGlobalNetworkManager20220215ServerState{
Autopilot: &gnmmod.HashicorpCloudGlobalNetworkManager20220215AutoPilotInfo{
FailureTolerance: int32(s.Autopilot.FailureTolerance),
Healthy: s.Autopilot.Healthy,
MinQuorum: int32(s.Autopilot.MinQuorum),
NumServers: int32(s.Autopilot.NumServers),
NumVoters: int32(s.Autopilot.NumVoters),
},
GossipPort: int32(s.GossipPort),
ID: s.ID,
LanAddress: s.LanAddress,
Name: s.Name,
Raft: &gnmmod.HashicorpCloudGlobalNetworkManager20220215RaftInfo{
AppliedIndex: strconv.FormatUint(s.Raft.AppliedIndex, 10),
IsLeader: s.Raft.IsLeader,
KnownLeader: s.Raft.KnownLeader,
TimeSinceLastContact: s.Raft.TimeSinceLastContact.String(),
},
RPCPort: int32(s.RPCPort),
TLS: &gnmmod.HashicorpCloudGlobalNetworkManager20220215TLSInfo{
CertExpiry: strfmt.DateTime(s.TLS.CertExpiry),
CertName: s.TLS.CertName,
CertSerial: s.TLS.CertSerial,
Enabled: s.TLS.Enabled,
VerifyIncoming: s.TLS.VerifyIncoming,
VerifyOutgoing: s.TLS.VerifyOutgoing,
VerifyServerHostname: s.TLS.VerifyServerHostname,
},
Version: s.Version,
ScadaStatus: s.ScadaStatus,
ACL: &gnmmod.HashicorpCloudGlobalNetworkManager20220215ACLInfo{
Enabled: s.ACL.Enabled,
},
Datacenter: s.Datacenter,
}
}
func (c *hcpClient) DiscoverServers(ctx context.Context) ([]string, error) {
params := hcpgnm.NewAgentDiscoverParamsWithContext(ctx).
WithID(c.resource.ID).
WithLocationOrganizationID(c.resource.Organization).
WithLocationProjectID(c.resource.Project)
resp, err := c.gnm.AgentDiscover(params, nil)
if err != nil {
return nil, err
}
var servers []string
for _, srv := range resp.Payload.Servers {
if srv != nil {
servers = append(servers, fmt.Sprintf("%s:%d", srv.LanAddress, srv.GossipPort))
}
}
return servers, nil
}