1
0

remove etcd

This commit is contained in:
Konstantin Demin 2024-07-01 11:30:10 +03:00
parent eae51feabb
commit aec5ff55fd
7 changed files with 0 additions and 638 deletions

View File

@ -46,7 +46,6 @@ import (
physCockroachDB "github.com/hashicorp/vault/physical/cockroachdb"
physConsul "github.com/hashicorp/vault/physical/consul"
physCouchDB "github.com/hashicorp/vault/physical/couchdb"
physEtcd "github.com/hashicorp/vault/physical/etcd"
physFoundationDB "github.com/hashicorp/vault/physical/foundationdb"
physMySQL "github.com/hashicorp/vault/physical/mysql"
physOCI "github.com/hashicorp/vault/physical/oci"
@ -178,7 +177,6 @@ var (
"consul": physConsul.NewConsulBackend,
"couchdb_transactional": physCouchDB.NewTransactionalCouchDBBackend,
"couchdb": physCouchDB.NewCouchDBBackend,
"etcd": physEtcd.NewEtcdBackend,
"file_transactional": physFile.NewTransactionalFileBackend,
"file": physFile.NewFileBackend,
"foundationdb": physFoundationDB.NewFDBBackend,

10
go.mod
View File

@ -159,9 +159,6 @@ require (
github.com/shirou/gopsutil/v3 v3.22.6
github.com/stretchr/testify v1.8.4
go.etcd.io/bbolt v1.3.7
go.etcd.io/etcd/client/pkg/v3 v3.5.7
go.etcd.io/etcd/client/v2 v2.305.5
go.etcd.io/etcd/client/v3 v3.5.7
go.opentelemetry.io/otel v1.22.0
go.opentelemetry.io/otel/sdk v1.22.0
go.opentelemetry.io/otel/trace v1.22.0
@ -229,8 +226,6 @@ require (
github.com/containerd/continuity v0.4.2 // indirect
github.com/containerd/log v0.1.0 // indirect
github.com/coreos/go-oidc/v3 v3.5.0 // indirect
github.com/coreos/go-semver v0.3.0 // indirect
github.com/coreos/go-systemd/v22 v22.5.0 // indirect
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
github.com/denverdino/aliyungo v0.0.0-20190125010748-a747050bb1ba // indirect
github.com/dgryski/go-metro v0.0.0-20180109044635-280f6062b5bc // indirect
@ -363,19 +358,14 @@ require (
github.com/yuin/gopher-lua v0.0.0-20210529063254-f4c35e4016d9 // indirect
github.com/yusufpapurcu/wmi v1.2.2 // indirect
github.com/zclconf/go-cty v1.12.1 // indirect
go.etcd.io/etcd/api/v3 v3.5.7 // indirect
go.mongodb.org/mongo-driver v1.11.6 // indirect
go.opencensus.io v0.24.0 // indirect
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.47.0 // indirect
go.opentelemetry.io/otel/metric v1.22.0 // indirect
go.uber.org/multierr v1.7.0 // indirect
go.uber.org/zap v1.19.1 // indirect
golang.org/x/mod v0.15.0 // indirect
golang.org/x/time v0.5.0 // indirect
google.golang.org/api v0.163.0 // indirect
google.golang.org/appengine v1.6.8 // indirect
google.golang.org/genproto v0.0.0-20240116215550-a9fa1716bcac // indirect
google.golang.org/genproto/googleapis/api v0.0.0-20240102182953-50ed04b92917 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20240125205218-1f4bbc51befe // indirect
gopkg.in/inf.v0 v0.9.1 // indirect
gopkg.in/jcmturner/goidentity.v3 v3.0.0 // indirect

15
go.sum
View File

@ -998,7 +998,6 @@ github.com/aws/aws-sdk-go v1.49.22/go.mod h1:LF8svs817+Nz+DmiMQKTO3ubZ/6IaTpq3Tj
github.com/axiomhq/hyperloglog v0.0.0-20220105174342-98591331716a h1:eqjiAL3qooftPm8b9C1GsSSRcmlw7iOva8vdBTmV2PY=
github.com/axiomhq/hyperloglog v0.0.0-20220105174342-98591331716a/go.mod h1:2stgcRjl6QmW+gU2h5E7BQXg4HU0gzxKWDuT5HviN9s=
github.com/benbjohnson/clock v1.0.3/go.mod h1:bGMdMPoPVvcYyt1gHDf4J2KE153Yf9BuiUKYMaxlTDM=
github.com/benbjohnson/clock v1.1.0 h1:Q92kusRqC1XV2MjkWETPvjJVqKetz1OzxZB7mHJLju8=
github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA=
github.com/beorn7/perks v0.0.0-20160804104726-4c0e84591b9a/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
@ -1248,7 +1247,6 @@ github.com/coreos/go-oidc v2.1.0+incompatible/go.mod h1:CgnwVTmzoESiwO9qyAFEMiHo
github.com/coreos/go-oidc/v3 v3.5.0 h1:VxKtbccHZxs8juq7RdJntSqtXFtde9YpNpGn0yqgEHw=
github.com/coreos/go-oidc/v3 v3.5.0/go.mod h1:ecXRtV4romGPeO6ieExAsUK9cb/3fp9hXNz1tlv8PIM=
github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
github.com/coreos/go-semver v0.3.0 h1:wkHLiw0WNATZnSG7epLsujiMCgPAc9xhjJ4tgnAxmfM=
github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
github.com/coreos/go-systemd v0.0.0-20161114122254-48702e0da86b/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
github.com/coreos/go-systemd v0.0.0-20180511133405-39ca1b05acc7/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
@ -1259,7 +1257,6 @@ github.com/coreos/go-systemd v0.0.0-20191104093116-d3cd4ed1dbcf/go.mod h1:F5haX7
github.com/coreos/go-systemd/v22 v22.0.0/go.mod h1:xO0FLkIi5MaZafQlIrOotqXZ90ih+1atmu1JpKERPPk=
github.com/coreos/go-systemd/v22 v22.1.0/go.mod h1:xO0FLkIi5MaZafQlIrOotqXZ90ih+1atmu1JpKERPPk=
github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
github.com/coreos/go-systemd/v22 v22.5.0 h1:RrqgGjYQKalulkV8NGVIfkXQf6YYmOyiJKk8iXXhfZs=
github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
github.com/coreos/pkg v0.0.0-20160727233714-3ac0863d7acf/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=
github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=
@ -2900,19 +2897,12 @@ go.etcd.io/bbolt v1.3.7/go.mod h1:N9Mkw9X8x5fupy0IKsmuqVtoGDyxsaDlbk4Rd05IAQw=
go.etcd.io/etcd v0.5.0-alpha.5.0.20200910180754-dd1b699fc489/go.mod h1:yVHk9ub3CSBatqGNg7GRmsnfLWtoW60w4eDYfh7vHDg=
go.etcd.io/etcd/api/v3 v3.5.0/go.mod h1:cbVKeC6lCfl7j/8jBhAK6aIYO9XOjdptoxU/nLQcPvs=
go.etcd.io/etcd/api/v3 v3.5.5/go.mod h1:KFtNaxGDw4Yx/BA4iPPwevUTAuqcsPxzyX8PHydchN8=
go.etcd.io/etcd/api/v3 v3.5.7 h1:sbcmosSVesNrWOJ58ZQFitHMdncusIifYcrBfwrlJSY=
go.etcd.io/etcd/api/v3 v3.5.7/go.mod h1:9qew1gCdDDLu+VwmeG+iFpL+QlpHTo7iubavdVDgCAA=
go.etcd.io/etcd/client/pkg/v3 v3.5.0/go.mod h1:IJHfcCEKxYu1Os13ZdwCwIUTUVGYTSAM3YSwc9/Ac1g=
go.etcd.io/etcd/client/pkg/v3 v3.5.5/go.mod h1:ggrwbk069qxpKPq8/FKkQ3Xq9y39kbFR4LnKszpRXeQ=
go.etcd.io/etcd/client/pkg/v3 v3.5.7 h1:y3kf5Gbp4e4q7egZdn5T7W9TSHUvkClN6u+Rq9mEOmg=
go.etcd.io/etcd/client/pkg/v3 v3.5.7/go.mod h1:o0Abi1MK86iad3YrWhgUsbGx1pmTS+hrORWc2CamuhY=
go.etcd.io/etcd/client/v2 v2.305.0/go.mod h1:h9puh54ZTgAKtEbut2oe9P4L/oqKCVB6xsXlzd7alYQ=
go.etcd.io/etcd/client/v2 v2.305.5 h1:DktRP60//JJpnPC0VBymAN/7V71GHMdjDCBt4ZPXDjI=
go.etcd.io/etcd/client/v2 v2.305.5/go.mod h1:zQjKllfqfBVyVStbt4FaosoX2iYd8fV/GRy/PbowgP4=
go.etcd.io/etcd/client/v3 v3.5.0/go.mod h1:AIKXXVX/DQXtfTEqBryiLTUXwON+GuvO6Z7lLS/oTh0=
go.etcd.io/etcd/client/v3 v3.5.5/go.mod h1:aApjR4WGlSumpnJ2kloS75h6aHUmAyaPLjHMxpc7E7c=
go.etcd.io/etcd/client/v3 v3.5.7 h1:u/OhpiuCgYY8awOHlhIhmGIGpxfBU/GZBUP3m/3/Iz4=
go.etcd.io/etcd/client/v3 v3.5.7/go.mod h1:sOWmj9DZUMyAngS7QQwCyAXXAL6WhgTOPLNS/NabQgw=
go.etcd.io/etcd/pkg/v3 v3.5.0/go.mod h1:UzJGatBQ1lXChBkQF0AuAtkRQMYnHubxAEYIrC3MSsE=
go.etcd.io/etcd/pkg/v3 v3.5.5/go.mod h1:6ksYFxttiUGzC2uxyqiyOEvhAiD0tuIqSZkX3TyPdaE=
go.etcd.io/etcd/raft/v3 v3.5.0/go.mod h1:UFOHSIvO/nKwd4lhkwabrTD3cqW5yVyYYf/KlD00Szc=
@ -3024,7 +3014,6 @@ go.uber.org/atomic v1.11.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0
go.uber.org/automaxprocs v1.5.1/go.mod h1:BF4eumQw0P9GtnuxxovUd06vwm1o18oMzFtK66vU6XU=
go.uber.org/goleak v1.0.0/go.mod h1:8a7PlsEVH3e/a/GLqe5IIrQx6GzcnRmZEufDUTk4A7A=
go.uber.org/goleak v1.1.10/go.mod h1:8a7PlsEVH3e/a/GLqe5IIrQx6GzcnRmZEufDUTk4A7A=
go.uber.org/goleak v1.1.11-0.20210813005559-691160354723/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ=
go.uber.org/goleak v1.1.12/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ=
go.uber.org/goleak v1.2.1 h1:NBol2c7O1ZokfZ0LEU9K6Whx/KnwvepVetCUhtKja4A=
go.uber.org/goleak v1.2.1/go.mod h1:qlT2yGI9QafXHhZZLxlSuNsMw3FFLxBr+tBRlmO1xH4=
@ -3032,16 +3021,12 @@ go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/
go.uber.org/multierr v1.3.0/go.mod h1:VgVr7evmIr6uPjLBxg28wmKNXyqE9akIJ5XnfpiKl+4=
go.uber.org/multierr v1.5.0/go.mod h1:FeouvMocqHpRaaGuG9EjoKcStLC43Zu/fmqdUMPcKYU=
go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU=
go.uber.org/multierr v1.7.0 h1:zaiO/rmgFjbmCXdSYJWQcdvOCsthmdaHfr3Gm2Kx4Ec=
go.uber.org/multierr v1.7.0/go.mod h1:7EAYxJLBy9rStEaz58O2t4Uvip6FSURkq8/ppBp95ak=
go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA=
go.uber.org/zap v1.9.1/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
go.uber.org/zap v1.13.0/go.mod h1:zwrFLgMcdUuIBviXEYEH1YKNaOBnKXsx2IPda5bBwHM=
go.uber.org/zap v1.17.0/go.mod h1:MXVU+bhUf/A7Xi2HNOnopQOrmycQ5Ih87HtOu4q5SSo=
go.uber.org/zap v1.19.0/go.mod h1:xg/QME4nWcxGxrpdeYfq7UvYrLh66cuVKdrbD1XF/NI=
go.uber.org/zap v1.19.1 h1:ue41HOKd1vGURxrmeKIgELGb3jPW9DMUDGtsinblHwI=
go.uber.org/zap v1.19.1/go.mod h1:j3DNczoxDZroyBnOT1L/Q79cfUMGZxlv/9dzN7SM1rI=
golang.org/x/crypto v0.0.0-20171113213409-9f005a07e0d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20181009213950-7c1a557ab941/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=

View File

@ -1,90 +0,0 @@
// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: BUSL-1.1
package etcd
import (
"context"
"fmt"
"net/url"
"os"
"testing"
"time"
"github.com/hashicorp/vault/sdk/helper/docker"
clientv3 "go.etcd.io/etcd/client/v3"
)
type Config struct {
docker.ServiceURL
}
// PrepareTestContainer creates etcd docker container. If environment variabe
// ETCD_ADDR is set, the tests are executed against specified address and etcd
// container is not launched.
func PrepareTestContainer(t *testing.T, version string) (func(), *Config) {
if addr := os.Getenv("ETCD_ADDR"); addr != "" {
url, err := docker.NewServiceURLParse(addr)
if err != nil {
t.Fatal(err)
}
return func() {}, &Config{ServiceURL: *url}
}
// Check https://github.com/etcd-io/etcd/releases for latest releases.
runner, err := docker.NewServiceRunner(docker.RunOptions{
ContainerName: "etcd",
ImageRepo: "gcr.io/etcd-development/etcd",
ImageTag: version,
Cmd: []string{
"/usr/local/bin/etcd",
"--name", "s1",
"--listen-client-urls", "http://0.0.0.0:2379",
"--advertise-client-urls", "http://0.0.0.0:2379",
"--listen-peer-urls", "http://0.0.0.0:2380",
"--initial-advertise-peer-urls", "http://0.0.0.0:2380",
"--initial-cluster", "s1=http://0.0.0.0:2380",
"--initial-cluster-token", "tkn",
"--initial-cluster-state", "new",
"--log-level", "info",
"--logger", "zap",
"--log-outputs", "stderr",
},
Ports: []string{"2379/tcp"},
})
if err != nil {
t.Fatalf("Could not start docker etcd container: %s", err)
}
svc, err := runner.StartService(context.Background(), func(ctx context.Context, host string, port int) (docker.ServiceConfig, error) {
address := fmt.Sprintf("%s:%d", host, port)
s := docker.NewServiceURL(url.URL{
Scheme: "http",
Host: address,
})
client, err := clientv3.New(clientv3.Config{
Endpoints: []string{address},
DialTimeout: 2 * time.Minute,
})
if err != nil {
return nil, fmt.Errorf("could not connect to etcd container: %w", err)
}
// Enable authentication for the tests.
client.RoleAdd(ctx, "root")
client.UserAdd(ctx, "root", "insecure")
client.UserGrantRole(ctx, "root", "root")
client.AuthEnable(ctx)
client.Close()
return &Config{
ServiceURL: *s,
}, nil
})
if err != nil {
t.Fatalf("Could not start docker etcd container: %s", err)
}
return svc.Cleanup, svc.Config.(*Config)
}

View File

@ -1,92 +0,0 @@
// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: BUSL-1.1
package etcd
import (
"errors"
"fmt"
"net/url"
"os"
"strings"
log "github.com/hashicorp/go-hclog"
"github.com/hashicorp/vault/sdk/physical"
"go.etcd.io/etcd/client/v2"
)
var (
EtcdMultipleBootstrapError = errors.New("client setup failed: multiple discovery or bootstrap flags specified, use either \"address\" or \"discovery_srv\"")
EtcdAddressError = errors.New("client setup failed: address must be valid URL (ex. 'scheme://host:port')")
EtcdLockHeldError = errors.New("lock already held")
EtcdLockNotHeldError = errors.New("lock not held")
EtcdVersionUnknown = errors.New("etcd: unknown API version")
)
// NewEtcdBackend constructs a etcd backend using a given machine address.
func NewEtcdBackend(conf map[string]string, logger log.Logger) (physical.Backend, error) {
var (
apiVersion string
ok bool
)
if apiVersion, ok = conf["etcd_api"]; !ok {
apiVersion = os.Getenv("ETCD_API")
}
if apiVersion == "" {
apiVersion = "v3"
}
switch apiVersion {
case "3", "etcd3", "v3":
return newEtcd3Backend(conf, logger)
default:
return nil, EtcdVersionUnknown
}
}
// Retrieves the config option in order of priority:
// 1. The named environment variable if it exist
// 2. The key in the config map
func getEtcdOption(conf map[string]string, confKey, envVar string) (string, bool) {
confVal, inConf := conf[confKey]
envVal, inEnv := os.LookupEnv(envVar)
if inEnv {
return envVal, true
}
return confVal, inConf
}
func getEtcdEndpoints(conf map[string]string) ([]string, error) {
address, staticBootstrap := getEtcdOption(conf, "address", "ETCD_ADDR")
domain, useSrv := getEtcdOption(conf, "discovery_srv", "ETCD_DISCOVERY_SRV")
if useSrv && staticBootstrap {
return nil, EtcdMultipleBootstrapError
}
if staticBootstrap {
endpoints := strings.Split(address, ",")
// Verify that the machines are valid URLs
for _, e := range endpoints {
u, urlErr := url.Parse(e)
if urlErr != nil || u.Scheme == "" {
return nil, EtcdAddressError
}
}
return endpoints, nil
}
if useSrv {
srvName, _ := getEtcdOption(conf, "discovery_srv_name", "ETCD_DISCOVERY_SRV_NAME")
discoverer := client.NewSRVDiscover()
endpoints, err := discoverer.Discover(domain, srvName)
if err != nil {
return nil, fmt.Errorf("failed to discover etcd endpoints through SRV discovery: %w", err)
}
return endpoints, nil
}
// Set a default endpoints list if no option was set
return []string{"http://127.0.0.1:2379"}, nil
}

View File

@ -1,383 +0,0 @@
// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: BUSL-1.1
package etcd
import (
"context"
"errors"
"fmt"
"os"
"path"
"strconv"
"strings"
"sync"
"time"
"github.com/armon/go-metrics"
log "github.com/hashicorp/go-hclog"
"github.com/hashicorp/go-secure-stdlib/parseutil"
"github.com/hashicorp/go-secure-stdlib/strutil"
"github.com/hashicorp/vault/sdk/physical"
"go.etcd.io/etcd/client/pkg/v3/transport"
clientv3 "go.etcd.io/etcd/client/v3"
"go.etcd.io/etcd/client/v3/concurrency"
)
// EtcdBackend is a physical backend that stores data at specific
// prefix within etcd. It is used for most production situations as
// it allows Vault to run on multiple machines in a highly-available manner.
type EtcdBackend struct {
logger log.Logger
path string
haEnabled bool
lockTimeout time.Duration
requestTimeout time.Duration
permitPool *physical.PermitPool
etcd *clientv3.Client
}
// Verify EtcdBackend satisfies the correct interfaces
var (
_ physical.Backend = (*EtcdBackend)(nil)
_ physical.HABackend = (*EtcdBackend)(nil)
_ physical.Lock = (*EtcdLock)(nil)
)
// newEtcd3Backend constructs a etcd3 backend.
func newEtcd3Backend(conf map[string]string, logger log.Logger) (physical.Backend, error) {
// Get the etcd path form the configuration.
path, ok := conf["path"]
if !ok {
path = "/vault"
}
// Ensure path is prefixed.
if !strings.HasPrefix(path, "/") {
path = "/" + path
}
endpoints, err := getEtcdEndpoints(conf)
if err != nil {
return nil, err
}
cfg := clientv3.Config{
Endpoints: endpoints,
}
haEnabled := os.Getenv("ETCD_HA_ENABLED")
if haEnabled == "" {
haEnabled = conf["ha_enabled"]
}
if haEnabled == "" {
haEnabled = "false"
}
haEnabledBool, err := strconv.ParseBool(haEnabled)
if err != nil {
return nil, fmt.Errorf("value [%v] of 'ha_enabled' could not be understood", haEnabled)
}
cert, hasCert := conf["tls_cert_file"]
key, hasKey := conf["tls_key_file"]
ca, hasCa := conf["tls_ca_file"]
if (hasCert && hasKey) || hasCa {
tls := transport.TLSInfo{
TrustedCAFile: ca,
CertFile: cert,
KeyFile: key,
}
tlscfg, err := tls.ClientConfig()
if err != nil {
return nil, err
}
cfg.TLS = tlscfg
}
// Set credentials.
username := os.Getenv("ETCD_USERNAME")
if username == "" {
username, _ = conf["username"]
}
password := os.Getenv("ETCD_PASSWORD")
if password == "" {
password, _ = conf["password"]
}
if username != "" && password != "" {
cfg.Username = username
cfg.Password = password
}
if maxReceive, ok := conf["max_receive_size"]; ok {
// grpc converts this to uint32 internally, so parse as that to avoid passing invalid values
val, err := strconv.ParseUint(maxReceive, 10, 32)
if err != nil {
return nil, fmt.Errorf("value of 'max_receive_size' (%v) could not be understood: %w", maxReceive, err)
}
cfg.MaxCallRecvMsgSize = int(val)
}
etcd, err := clientv3.New(cfg)
if err != nil {
return nil, err
}
sReqTimeout := conf["request_timeout"]
if sReqTimeout == "" {
// etcd3 default request timeout is set to 5s. It should be long enough
// for most cases, even with internal retry.
sReqTimeout = "5s"
}
reqTimeout, err := parseutil.ParseDurationSecond(sReqTimeout)
if err != nil {
return nil, fmt.Errorf("value [%v] of 'request_timeout' could not be understood: %w", sReqTimeout, err)
}
ssync, ok := conf["sync"]
if !ok {
ssync = "true"
}
sync, err := strconv.ParseBool(ssync)
if err != nil {
return nil, fmt.Errorf("value of 'sync' (%v) could not be understood: %w", ssync, err)
}
if sync {
ctx, cancel := context.WithTimeout(context.Background(), reqTimeout)
err := etcd.Sync(ctx)
cancel()
if err != nil {
return nil, err
}
}
sLock := conf["lock_timeout"]
if sLock == "" {
// etcd3 default lease duration is 60s. set to 15s for faster recovery.
sLock = "15s"
}
lock, err := parseutil.ParseDurationSecond(sLock)
if err != nil {
return nil, fmt.Errorf("value [%v] of 'lock_timeout' could not be understood: %w", sLock, err)
}
return &EtcdBackend{
path: path,
etcd: etcd,
permitPool: physical.NewPermitPool(physical.DefaultParallelOperations),
logger: logger,
haEnabled: haEnabledBool,
lockTimeout: lock,
requestTimeout: reqTimeout,
}, nil
}
func (c *EtcdBackend) Put(ctx context.Context, entry *physical.Entry) error {
defer metrics.MeasureSince([]string{"etcd", "put"}, time.Now())
c.permitPool.Acquire()
defer c.permitPool.Release()
ctx, cancel := context.WithTimeout(context.Background(), c.requestTimeout)
defer cancel()
_, err := c.etcd.Put(ctx, path.Join(c.path, entry.Key), string(entry.Value))
return err
}
func (c *EtcdBackend) Get(ctx context.Context, key string) (*physical.Entry, error) {
defer metrics.MeasureSince([]string{"etcd", "get"}, time.Now())
c.permitPool.Acquire()
defer c.permitPool.Release()
ctx, cancel := context.WithTimeout(context.Background(), c.requestTimeout)
defer cancel()
resp, err := c.etcd.Get(ctx, path.Join(c.path, key))
if err != nil {
return nil, err
}
if len(resp.Kvs) == 0 {
return nil, nil
}
if len(resp.Kvs) > 1 {
return nil, errors.New("unexpected number of keys from a get request")
}
return &physical.Entry{
Key: key,
Value: resp.Kvs[0].Value,
}, nil
}
func (c *EtcdBackend) Delete(ctx context.Context, key string) error {
defer metrics.MeasureSince([]string{"etcd", "delete"}, time.Now())
c.permitPool.Acquire()
defer c.permitPool.Release()
ctx, cancel := context.WithTimeout(context.Background(), c.requestTimeout)
defer cancel()
_, err := c.etcd.Delete(ctx, path.Join(c.path, key))
if err != nil {
return err
}
return nil
}
func (c *EtcdBackend) List(ctx context.Context, prefix string) ([]string, error) {
defer metrics.MeasureSince([]string{"etcd", "list"}, time.Now())
c.permitPool.Acquire()
defer c.permitPool.Release()
ctx, cancel := context.WithTimeout(context.Background(), c.requestTimeout)
defer cancel()
prefix = path.Join(c.path, prefix) + "/"
resp, err := c.etcd.Get(ctx, prefix, clientv3.WithPrefix(), clientv3.WithKeysOnly())
if err != nil {
return nil, err
}
keys := []string{}
for _, kv := range resp.Kvs {
key := strings.TrimPrefix(string(kv.Key), prefix)
key = strings.TrimPrefix(key, "/")
if len(key) == 0 {
continue
}
if i := strings.Index(key, "/"); i == -1 {
keys = append(keys, key)
} else if i != -1 {
keys = strutil.AppendIfMissing(keys, key[:i+1])
}
}
return keys, nil
}
func (e *EtcdBackend) HAEnabled() bool {
return e.haEnabled
}
// EtcdLock implements a lock using and etcd backend.
type EtcdLock struct {
lock sync.Mutex
held bool
timeout time.Duration
requestTimeout time.Duration
etcdSession *concurrency.Session
etcdMu *concurrency.Mutex
prefix string
value string
etcd *clientv3.Client
}
// Lock is used for mutual exclusion based on the given key.
func (c *EtcdBackend) LockWith(key, value string) (physical.Lock, error) {
p := path.Join(c.path, key)
return &EtcdLock{
prefix: p,
value: value,
etcd: c.etcd,
timeout: c.lockTimeout,
requestTimeout: c.requestTimeout,
}, nil
}
func (c *EtcdLock) Lock(stopCh <-chan struct{}) (<-chan struct{}, error) {
c.lock.Lock()
defer c.lock.Unlock()
if c.etcdMu == nil {
if err := c.initMu(); err != nil {
return nil, err
}
}
if c.held {
return nil, EtcdLockHeldError
}
select {
case _, ok := <-c.etcdSession.Done():
if !ok {
// The session's done channel is closed, so the session is over,
// and we need a new lock with a new session.
if err := c.initMu(); err != nil {
return nil, err
}
}
default:
}
ctx, cancel := context.WithCancel(context.Background())
go func() {
<-stopCh
cancel()
}()
if err := c.etcdMu.Lock(ctx); err != nil {
if err == context.Canceled {
return nil, nil
}
return nil, err
}
pctx, cancel := context.WithTimeout(context.Background(), c.requestTimeout)
defer cancel()
if _, err := c.etcd.Put(pctx, c.etcdMu.Key(), c.value, clientv3.WithLease(c.etcdSession.Lease())); err != nil {
return nil, err
}
c.held = true
return c.etcdSession.Done(), nil
}
func (c *EtcdLock) Unlock() error {
c.lock.Lock()
defer c.lock.Unlock()
if !c.held {
return EtcdLockNotHeldError
}
ctx, cancel := context.WithTimeout(context.Background(), c.requestTimeout)
defer cancel()
return c.etcdMu.Unlock(ctx)
}
func (c *EtcdLock) Value() (bool, string, error) {
ctx, cancel := context.WithTimeout(context.Background(), c.requestTimeout)
defer cancel()
resp, err := c.etcd.Get(ctx,
c.prefix, clientv3.WithPrefix(),
clientv3.WithSort(clientv3.SortByCreateRevision, clientv3.SortAscend))
if err != nil {
return false, "", err
}
if len(resp.Kvs) == 0 {
return false, "", nil
}
return true, string(resp.Kvs[0].Value), nil
}
func (c *EtcdLock) initMu() error {
session, err := concurrency.NewSession(c.etcd, concurrency.WithTTL(int(c.timeout.Seconds())))
if err != nil {
return err
}
c.etcdSession = session
c.etcdMu = concurrency.NewMutex(session, c.prefix)
return nil
}

View File

@ -1,46 +0,0 @@
// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: BUSL-1.1
package etcd
import (
"fmt"
"testing"
"time"
log "github.com/hashicorp/go-hclog"
"github.com/hashicorp/vault/helper/testhelpers/etcd"
"github.com/hashicorp/vault/sdk/helper/logging"
"github.com/hashicorp/vault/sdk/physical"
)
func TestEtcd3Backend(t *testing.T) {
cleanup, config := etcd.PrepareTestContainer(t, "v3.5.0")
defer cleanup()
logger := logging.NewVaultLogger(log.Debug)
configMap := map[string]string{
"address": config.URL().String(),
"path": fmt.Sprintf("/vault-%d", time.Now().Unix()),
"etcd_api": "3",
"username": "root",
"password": "insecure",
// Syncing advertised client urls should be disabled since docker port mapping confuses the client.
"sync": "false",
}
b, err := NewEtcdBackend(configMap, logger)
if err != nil {
t.Fatalf("err: %s", err)
}
b2, err := NewEtcdBackend(configMap, logger)
if err != nil {
t.Fatalf("err: %s", err)
}
physical.ExerciseBackend(t, b)
physical.ExerciseBackend_ListPrefix(t, b)
physical.ExerciseHABackend(t, b.(physical.HABackend), b2.(physical.HABackend))
}