mirror of https://github.com/hashicorp/consul
173 lines
4.4 KiB
Go
173 lines
4.4 KiB
Go
// Copyright (c) HashiCorp, Inc.
|
|
// SPDX-License-Identifier: BUSL-1.1
|
|
|
|
package client
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
|
|
"google.golang.org/grpc"
|
|
"google.golang.org/grpc/credentials"
|
|
"google.golang.org/grpc/credentials/insecure"
|
|
"google.golang.org/grpc/metadata"
|
|
|
|
"github.com/hashicorp/consul/proto-public/pbresource"
|
|
)
|
|
|
|
const (
|
|
HeaderConsulToken = "x-consul-token"
|
|
)
|
|
|
|
type GRPCClient struct {
|
|
Client pbresource.ResourceServiceClient
|
|
Config *GRPCConfig
|
|
Conn *grpc.ClientConn
|
|
}
|
|
|
|
func NewGRPCClient(config *GRPCConfig) (*GRPCClient, error) {
|
|
conn, err := dial(config)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("error dialing grpc: %+v", err)
|
|
}
|
|
return &GRPCClient{
|
|
Client: pbresource.NewResourceServiceClient(conn),
|
|
Config: config,
|
|
Conn: conn,
|
|
}, nil
|
|
}
|
|
|
|
func (client *GRPCClient) Apply(parsedResource *pbresource.Resource) (*pbresource.Resource, error) {
|
|
token, err := client.Config.GetToken()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
ctx := context.Background()
|
|
if token != "" {
|
|
ctx = metadata.AppendToOutgoingContext(ctx, HeaderConsulToken, token)
|
|
}
|
|
|
|
defer client.Conn.Close()
|
|
writeRsp, err := client.Client.Write(ctx, &pbresource.WriteRequest{Resource: parsedResource})
|
|
if err != nil {
|
|
return nil, fmt.Errorf("error writing resource: %+v", err)
|
|
}
|
|
|
|
return writeRsp.Resource, err
|
|
}
|
|
|
|
func (client *GRPCClient) Read(resourceType *pbresource.Type, resourceTenancy *pbresource.Tenancy, resourceName string, stale bool) (*pbresource.Resource, error) {
|
|
token, err := client.Config.GetToken()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
ctx := context.Background()
|
|
if !stale {
|
|
ctx = metadata.AppendToOutgoingContext(ctx, "x-consul-consistency-mode", "consistent")
|
|
}
|
|
if token != "" {
|
|
ctx = metadata.AppendToOutgoingContext(ctx, HeaderConsulToken, token)
|
|
}
|
|
|
|
defer client.Conn.Close()
|
|
readRsp, err := client.Client.Read(ctx, &pbresource.ReadRequest{
|
|
Id: &pbresource.ID{
|
|
Type: resourceType,
|
|
Tenancy: resourceTenancy,
|
|
Name: resourceName,
|
|
},
|
|
})
|
|
|
|
if err != nil {
|
|
return nil, fmt.Errorf("error reading resource: %+v", err)
|
|
}
|
|
|
|
return readRsp.Resource, err
|
|
}
|
|
|
|
func (client *GRPCClient) List(resourceType *pbresource.Type, resourceTenancy *pbresource.Tenancy, prefix string, stale bool) ([]*pbresource.Resource, error) {
|
|
token, err := client.Config.GetToken()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
ctx := context.Background()
|
|
if !stale {
|
|
ctx = metadata.AppendToOutgoingContext(ctx, "x-consul-consistency-mode", "consistent")
|
|
}
|
|
if token != "" {
|
|
ctx = metadata.AppendToOutgoingContext(context.Background(), HeaderConsulToken, token)
|
|
}
|
|
|
|
defer client.Conn.Close()
|
|
listRsp, err := client.Client.List(ctx, &pbresource.ListRequest{
|
|
Type: resourceType,
|
|
Tenancy: resourceTenancy,
|
|
NamePrefix: prefix,
|
|
})
|
|
|
|
if err != nil {
|
|
return nil, fmt.Errorf("error listing resource: %+v", err)
|
|
}
|
|
|
|
return listRsp.Resources, err
|
|
}
|
|
|
|
func (client *GRPCClient) Delete(resourceType *pbresource.Type, resourceTenancy *pbresource.Tenancy, resourceName string) error {
|
|
token, err := client.Config.GetToken()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
ctx := context.Background()
|
|
if token != "" {
|
|
ctx = metadata.AppendToOutgoingContext(context.Background(), HeaderConsulToken, token)
|
|
}
|
|
|
|
defer client.Conn.Close()
|
|
_, err = client.Client.Delete(ctx, &pbresource.DeleteRequest{
|
|
Id: &pbresource.ID{
|
|
Type: resourceType,
|
|
Tenancy: resourceTenancy,
|
|
Name: resourceName,
|
|
},
|
|
})
|
|
|
|
if err != nil {
|
|
return fmt.Errorf("error deleting resource: %+v", err)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func dial(c *GRPCConfig) (*grpc.ClientConn, error) {
|
|
err := checkCertificates(c)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
var dialOpts []grpc.DialOption
|
|
if c.GRPCTLS {
|
|
tlsConfig, err := SetupTLSConfig(c)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to setup tls config when tried to establish grpc call: %w", err)
|
|
}
|
|
dialOpts = append(dialOpts, grpc.WithTransportCredentials(credentials.NewTLS(tlsConfig)))
|
|
} else {
|
|
dialOpts = append(dialOpts, grpc.WithTransportCredentials(insecure.NewCredentials()))
|
|
}
|
|
|
|
return grpc.Dial(c.Address, dialOpts...)
|
|
}
|
|
|
|
func checkCertificates(c *GRPCConfig) error {
|
|
if c.GRPCTLS {
|
|
certFileEmpty := c.CertFile == ""
|
|
keyFileEmpty := c.CertFile == ""
|
|
|
|
// both files need to be empty or both files need to be provided
|
|
if certFileEmpty != keyFileEmpty {
|
|
return fmt.Errorf("you have to provide client certificate file and key file at the same time " +
|
|
"if you intend to communicate in TLS/SSL mode")
|
|
}
|
|
}
|
|
return nil
|
|
}
|