merge master
This commit is contained in:
commit
374748dafc
|
@ -0,0 +1,3 @@
|
|||
```release-note:improvement
|
||||
server: break up Intention.Apply monolithic method
|
||||
```
|
|
@ -0,0 +1,3 @@
|
|||
```release-note:feature
|
||||
cli: snapshot inspect command provides KV usage breakdown
|
||||
```
|
|
@ -0,0 +1,3 @@
|
|||
```release-note:feature
|
||||
agent: return the default ACL policy to callers as a header
|
||||
```
|
|
@ -0,0 +1,3 @@
|
|||
```release-note:improvement
|
||||
chore: update to Go 1.14.11 with mitigation for [golang/go#42138](https://github.com/golang/go/issues/42138)
|
||||
```
|
|
@ -0,0 +1,3 @@
|
|||
```release-note:feature
|
||||
autopilot: Added a new `consul operator autopilot state` command to retrieve and view the Autopilot state from consul.
|
||||
```
|
|
@ -0,0 +1,3 @@
|
|||
```release-note:improvement
|
||||
server: remove config entry CAS in legacy intention API bridge code
|
||||
```
|
|
@ -0,0 +1,3 @@
|
|||
```release-note:bug
|
||||
namespace: **(Enterprise Only)** Fixed a bug that could case snapshot restoration to fail when it contained a namespace marked for deletion while still containing other resources in that namespace.
|
||||
```
|
|
@ -0,0 +1,3 @@
|
|||
```release-note:deprecation
|
||||
telemetry: the disable_compat_1.9 config will cover more metrics deprecations in future 1.9 point releases. These metrics will be emitted twice for backwards compatibility - if the flag is true, only the new metric name will be written.
|
||||
```
|
|
@ -0,0 +1,18 @@
|
|||
```release-note:bug
|
||||
server: skip deleted and deleting namespaces when migrating intentions to config entries
|
||||
```
|
||||
|
||||
```release-note:breaking-change
|
||||
server: **(Enterprise only)** Pre-existing intentions defined with
|
||||
non-existent destination namespaces were non-functional and are erased during
|
||||
the upgrade process. This should not matter as these intentions had nothing to
|
||||
enforce.
|
||||
```
|
||||
|
||||
```release-note:breaking-change
|
||||
server: **(OSS only)** Pre-existing intentions defined with either a source or
|
||||
destination namespace value that is not "default" are rewritten or deleted
|
||||
during the upgrade process. Wildcards first attempt to downgrade to "default"
|
||||
unless an intention already exists, otherwise these non-functional intentions
|
||||
are deleted.
|
||||
```
|
|
@ -0,0 +1,3 @@
|
|||
```release-note:bug
|
||||
namespace: **(Enterprise Only)** Fixed an issue where namespaced services and checks were not being deleted when the containing namespace was deleted.
|
||||
```
|
|
@ -3,7 +3,7 @@ version: 2
|
|||
|
||||
references:
|
||||
images:
|
||||
go: &GOLANG_IMAGE docker.mirror.hashicorp.services/circleci/golang:1.14.11
|
||||
go: &GOLANG_IMAGE docker.mirror.hashicorp.services/circleci/golang:1.15.5
|
||||
ember: &EMBER_IMAGE docker.mirror.hashicorp.services/circleci/node:12-browsers
|
||||
|
||||
paths:
|
||||
|
@ -243,7 +243,7 @@ jobs:
|
|||
-tags="$GOTAGS" -p 2 \
|
||||
-race -gcflags=all=-d=checkptr=0 \
|
||||
./agent/{ae,cache,cache-types,checks,config,pool,proxycfg,router}/... \
|
||||
./agent/consul/{authmethod,autopilot,fsm,state,stream}/... \
|
||||
./agent/consul/{authmethod,fsm,state,stream}/... \
|
||||
./agent/{grpc,rpc,rpcclient,submatview}/... \
|
||||
./snapshot
|
||||
|
||||
|
@ -344,7 +344,7 @@ jobs:
|
|||
<<: *build-distros
|
||||
environment:
|
||||
<<: *build-env
|
||||
XC_OS: "darwin freebsd linux windows"
|
||||
XC_OS: "freebsd linux windows"
|
||||
XC_ARCH: "386"
|
||||
|
||||
# build all amd64 architecture supported OS binaries
|
||||
|
@ -821,7 +821,7 @@ jobs:
|
|||
# only runs on master: checks latest commit to see if the PR associated has a backport/* or docs* label to cherry-pick
|
||||
cherry-picker:
|
||||
docker:
|
||||
- image: docker.mirror.hashicorp.services/alpine:3.11
|
||||
- image: docker.mirror.hashicorp.services/alpine:3.12
|
||||
steps:
|
||||
- run: apk add --no-cache --no-progress git bash curl ncurses jq openssh-client
|
||||
- checkout
|
||||
|
@ -833,7 +833,7 @@ jobs:
|
|||
|
||||
trigger-oss-merge:
|
||||
docker:
|
||||
- image: docker.mirror.hashicorp.services/alpine:3.11
|
||||
- image: docker.mirror.hashicorp.services/alpine:3.12
|
||||
steps:
|
||||
- run: apk add --no-cache --no-progress curl jq
|
||||
- run:
|
||||
|
@ -894,6 +894,7 @@ workflows:
|
|||
branches:
|
||||
only:
|
||||
- master
|
||||
- /release\/\d+\.\d+\.x$/
|
||||
requires:
|
||||
- build-static-assets
|
||||
- dev-build:
|
||||
|
|
|
@ -133,7 +133,7 @@ pr_number=$(echo "$resp" | jq '.items[].number')
|
|||
|
||||
# comment on the PR with the build number to make it easy to re-run the job when
|
||||
# cherry-pick labels are added in the future
|
||||
github_message=":cherries: Starting backport cherry picking.\n\nTo cherry-pick post-merge, add backport labels and re-run ${CIRCLE_BUILD_URL}."
|
||||
github_message=":cherries: If backport labels were added before merging, cherry-picking will start automatically.\n\nTo retroactively trigger a backport after merging, add backport labels and re-run ${CIRCLE_BUILD_URL}."
|
||||
curl -f -s -H "Authorization: token ${GITHUB_TOKEN}" \
|
||||
-X POST \
|
||||
-d "{ \"body\": \"${github_message}\"}" \
|
||||
|
|
20
CHANGELOG.md
20
CHANGELOG.md
|
@ -1,5 +1,25 @@
|
|||
## UNRELEASED
|
||||
|
||||
## 1.9.0-beta3 (November 10, 2020)
|
||||
|
||||
BREAKING CHANGES:
|
||||
|
||||
* connect: Switch the default gateway port from 443 to 8443 to avoid assumption of Envoy running as root. [[GH-9113](https://github.com/hashicorp/consul/issues/9113)]
|
||||
* raft: Raft protocol v2 is no longer supported. If currently using protocol v2 then an intermediate upgrade to a version supporting both v2 and v3 protocols will be necessary (1.0.0 - 1.8.x). Note that the Raft protocol configured with the `raft_protocol` setting and the Consul RPC protocol configured with the `protocol` setting and output by the `consul version` command are distinct and supported Consul RPC protocol versions are not altered. [[GH-9103](https://github.com/hashicorp/consul/issues/9103)]
|
||||
|
||||
FEATURES:
|
||||
|
||||
* autopilot: A new `/v1/operator/autopilot/state` HTTP API was created to give greater visibility into what autopilot is doing and how it has classified all the servers it is tracking. [[GH-9103](https://github.com/hashicorp/consul/issues/9103)]
|
||||
|
||||
IMPROVEMENTS:
|
||||
|
||||
* autopilot: **(Enterprise Only)** Autopilot now supports using both Redundancy Zones and Automated Upgrades together. [[GH-9103](https://github.com/hashicorp/consul/issues/9103)]
|
||||
* chore: update to Go 1.14.11 with mitigation for [golang/go#42138](https://github.com/golang/go/issues/42138) [[GH-9119](https://github.com/hashicorp/consul/issues/9119)]
|
||||
|
||||
BUG FIXES:
|
||||
|
||||
* autopilot: **(Enterprise Only)** Previously servers in other zones would not be promoted when all servers in a second zone had failed. Now the actual behavior matches the docs and autopilot will promote a healthy non-voter from any zone to replace failure of an entire zone. [[GH-9103](https://github.com/hashicorp/consul/issues/9103)]
|
||||
|
||||
## 1.9.0-beta2 (November 07, 2020)
|
||||
|
||||
BREAKING CHANGES:
|
||||
|
|
|
@ -77,8 +77,8 @@ type RuntimeConfig struct {
|
|||
|
||||
// ACLDefaultPolicy is used to control the ACL interaction when
|
||||
// there is no defined policy. This can be "allow" which means
|
||||
// ACLs are used to black-list, or "deny" which means ACLs are
|
||||
// white-lists.
|
||||
// ACLs are used to deny-list, or "deny" which means ACLs are
|
||||
// allow-lists.
|
||||
//
|
||||
// hcl: acl.default_policy = ("allow"|"deny")
|
||||
ACLDefaultPolicy string
|
||||
|
|
|
@ -268,8 +268,8 @@ type Config struct {
|
|||
|
||||
// ACLDefaultPolicy is used to control the ACL interaction when
|
||||
// there is no defined policy. This can be "allow" which means
|
||||
// ACLs are used to black-list, or "deny" which means ACLs are
|
||||
// white-lists.
|
||||
// ACLs are used to deny-list, or "deny" which means ACLs are
|
||||
// allow-lists.
|
||||
ACLDefaultPolicy string
|
||||
|
||||
// ACLDownPolicy controls the behavior of ACLs if the ACLDatacenter
|
||||
|
|
|
@ -48,10 +48,6 @@ type ConfigEntry struct {
|
|||
|
||||
// Apply does an upsert of the given config entry.
|
||||
func (c *ConfigEntry) Apply(args *structs.ConfigEntryRequest, reply *bool) error {
|
||||
return c.applyInternal(args, reply, nil)
|
||||
}
|
||||
|
||||
func (c *ConfigEntry) applyInternal(args *structs.ConfigEntryRequest, reply *bool, normalizeAndValidateFn func(structs.ConfigEntry) error) error {
|
||||
if err := c.srv.validateEnterpriseRequest(args.Entry.GetEnterpriseMeta(), true); err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -76,17 +72,11 @@ func (c *ConfigEntry) applyInternal(args *structs.ConfigEntryRequest, reply *boo
|
|||
}
|
||||
|
||||
// Normalize and validate the incoming config entry as if it came from a user.
|
||||
if normalizeAndValidateFn == nil {
|
||||
if err := args.Entry.Normalize(); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := args.Entry.Validate(); err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
if err := normalizeAndValidateFn(args.Entry); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := args.Entry.Normalize(); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := args.Entry.Validate(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if authz != nil && !args.Entry.CanWrite(authz) {
|
||||
|
@ -483,6 +473,11 @@ func (c *ConfigEntry) ResolveServiceConfig(args *structs.ServiceConfigRequest, r
|
|||
func (c *ConfigEntry) preflightCheck(kind string) error {
|
||||
switch kind {
|
||||
case structs.ServiceIntentions:
|
||||
// Exit early if Connect hasn't been enabled.
|
||||
if !c.srv.config.ConnectEnabled {
|
||||
return ErrConnectNotEnabled
|
||||
}
|
||||
|
||||
usingConfigEntries, err := c.srv.fsm.State().AreIntentionsInConfigEntries()
|
||||
if err != nil {
|
||||
return fmt.Errorf("system metadata lookup failed: %v", err)
|
||||
|
|
|
@ -382,6 +382,11 @@ func (c *FSM) applyIntentionOperation(buf []byte, index uint64) interface{} {
|
|||
[]metrics.Label{{Name: "op", Value: string(req.Op)}})
|
||||
defer metrics.MeasureSinceWithLabels([]string{"fsm", "intention"}, time.Now(),
|
||||
[]metrics.Label{{Name: "op", Value: string(req.Op)}})
|
||||
|
||||
if req.Mutation != nil {
|
||||
return c.state.IntentionMutation(index, req.Op, req.Mutation)
|
||||
}
|
||||
|
||||
switch req.Op {
|
||||
case structs.IntentionOpCreate, structs.IntentionOpUpdate:
|
||||
//nolint:staticcheck
|
||||
|
|
|
@ -33,22 +33,11 @@ var (
|
|||
ErrIntentionNotFound = errors.New("Intention not found")
|
||||
)
|
||||
|
||||
// NewIntentionEndpoint returns a new Intention endpoint.
|
||||
func NewIntentionEndpoint(srv *Server, logger hclog.Logger) *Intention {
|
||||
return &Intention{
|
||||
srv: srv,
|
||||
logger: logger,
|
||||
configEntryEndpoint: &ConfigEntry{srv},
|
||||
}
|
||||
}
|
||||
|
||||
// Intention manages the Connect intentions.
|
||||
type Intention struct {
|
||||
// srv is a pointer back to the server.
|
||||
srv *Server
|
||||
logger hclog.Logger
|
||||
|
||||
configEntryEndpoint *ConfigEntry
|
||||
}
|
||||
|
||||
func (s *Intention) checkIntentionID(id string) (bool, error) {
|
||||
|
@ -62,25 +51,139 @@ func (s *Intention) checkIntentionID(id string) (bool, error) {
|
|||
return true, nil
|
||||
}
|
||||
|
||||
// prepareApplyCreate validates that the requester has permissions to create
|
||||
// the new intention, generates a new uuid for the intention and generally
|
||||
// validates that the request is well-formed
|
||||
//
|
||||
// Returns an existing service-intentions config entry for this destination if
|
||||
// one exists.
|
||||
func (s *Intention) prepareApplyCreate(
|
||||
ident structs.ACLIdentity,
|
||||
var ErrIntentionsNotUpgradedYet = errors.New("Intentions are read only while being upgraded to config entries")
|
||||
|
||||
// legacyUpgradeCheck fast fails a write request using the legacy intention
|
||||
// RPCs if the system is known to be mid-upgrade. This is purely a perf
|
||||
// optimization and the actual real enforcement happens in the FSM. It would be
|
||||
// wasteful to round trip all the way through raft to have it fail for
|
||||
// known-up-front reasons, hence why we check it twice.
|
||||
func (s *Intention) legacyUpgradeCheck() error {
|
||||
usingConfigEntries, err := s.srv.fsm.State().AreIntentionsInConfigEntries()
|
||||
if err != nil {
|
||||
return fmt.Errorf("system metadata lookup failed: %v", err)
|
||||
}
|
||||
if !usingConfigEntries {
|
||||
return ErrIntentionsNotUpgradedYet
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Apply creates or updates an intention in the data store.
|
||||
func (s *Intention) Apply(args *structs.IntentionRequest, reply *string) error {
|
||||
// Exit early if Connect hasn't been enabled.
|
||||
if !s.srv.config.ConnectEnabled {
|
||||
return ErrConnectNotEnabled
|
||||
}
|
||||
|
||||
// Ensure that all service-intentions config entry writes go to the primary
|
||||
// datacenter. These will then be replicated to all the other datacenters.
|
||||
args.Datacenter = s.srv.config.PrimaryDatacenter
|
||||
|
||||
if done, err := s.srv.ForwardRPC("Intention.Apply", args, args, reply); done {
|
||||
return err
|
||||
}
|
||||
defer metrics.MeasureSince([]string{"consul", "intention", "apply"}, time.Now())
|
||||
defer metrics.MeasureSince([]string{"intention", "apply"}, time.Now())
|
||||
|
||||
if err := s.legacyUpgradeCheck(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if args.Mutation != nil {
|
||||
return fmt.Errorf("Mutation field is internal only and must not be set via RPC")
|
||||
}
|
||||
|
||||
// Always set a non-nil intention to avoid nil-access below
|
||||
if args.Intention == nil {
|
||||
args.Intention = &structs.Intention{}
|
||||
}
|
||||
|
||||
// Get the ACL token for the request for the checks below.
|
||||
var entMeta structs.EnterpriseMeta
|
||||
ident, authz, err := s.srv.ResolveTokenIdentityAndDefaultMeta(args.Token, &entMeta, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var accessorID string
|
||||
if ident != nil {
|
||||
accessorID = ident.ID()
|
||||
}
|
||||
|
||||
var (
|
||||
mut *structs.IntentionMutation
|
||||
legacyWrite bool
|
||||
)
|
||||
switch args.Op {
|
||||
case structs.IntentionOpCreate:
|
||||
legacyWrite = true
|
||||
mut, err = s.computeApplyChangesLegacyCreate(accessorID, authz, &entMeta, args)
|
||||
case structs.IntentionOpUpdate:
|
||||
legacyWrite = true
|
||||
mut, err = s.computeApplyChangesLegacyUpdate(accessorID, authz, &entMeta, args)
|
||||
case structs.IntentionOpUpsert:
|
||||
legacyWrite = false
|
||||
mut, err = s.computeApplyChangesUpsert(accessorID, authz, &entMeta, args)
|
||||
case structs.IntentionOpDelete:
|
||||
if args.Intention.ID == "" {
|
||||
legacyWrite = false
|
||||
mut, err = s.computeApplyChangesDelete(accessorID, authz, &entMeta, args)
|
||||
} else {
|
||||
legacyWrite = true
|
||||
mut, err = s.computeApplyChangesLegacyDelete(accessorID, authz, &entMeta, args)
|
||||
}
|
||||
case structs.IntentionOpDeleteAll:
|
||||
// This is an internal operation initiated by the leader and is not
|
||||
// exposed for general RPC use.
|
||||
return fmt.Errorf("Invalid Intention operation: %v", args.Op)
|
||||
default:
|
||||
return fmt.Errorf("Invalid Intention operation: %v", args.Op)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if legacyWrite {
|
||||
*reply = args.Intention.ID
|
||||
} else {
|
||||
*reply = ""
|
||||
}
|
||||
|
||||
// Switch to the config entry manipulating flavor:
|
||||
args.Mutation = mut
|
||||
args.Intention = nil
|
||||
|
||||
resp, err := s.srv.raftApply(structs.IntentionRequestType, args)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if respErr, ok := resp.(error); ok {
|
||||
return respErr
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *Intention) computeApplyChangesLegacyCreate(
|
||||
accessorID string,
|
||||
authz acl.Authorizer,
|
||||
entMeta *structs.EnterpriseMeta,
|
||||
args *structs.IntentionRequest,
|
||||
) (*structs.ServiceIntentionsConfigEntry, error) {
|
||||
) (*structs.IntentionMutation, error) {
|
||||
// This variant is just for legacy UUID-based intentions.
|
||||
|
||||
args.Intention.DefaultNamespaces(entMeta)
|
||||
|
||||
if !args.Intention.CanWrite(authz) {
|
||||
var accessorID string
|
||||
if ident != nil {
|
||||
accessorID = ident.ID()
|
||||
}
|
||||
sn := args.Intention.SourceServiceName()
|
||||
dn := args.Intention.DestinationServiceName()
|
||||
// todo(kit) Migrate intention access denial logging over to audit logging when we implement it
|
||||
s.logger.Warn("Intention creation denied due to ACLs", "intention", args.Intention.ID, "accessorID", accessorID)
|
||||
s.logger.Warn("Intention creation denied due to ACLs",
|
||||
"source", sn.String(),
|
||||
"destination", dn.String(),
|
||||
"accessorID", accessorID)
|
||||
return nil, acl.ErrPermissionDenied
|
||||
}
|
||||
|
||||
|
@ -106,8 +209,6 @@ func (s *Intention) prepareApplyCreate(
|
|||
args.Intention.SourceType = structs.IntentionSourceConsul
|
||||
}
|
||||
|
||||
args.Intention.DefaultNamespaces(entMeta)
|
||||
|
||||
if err := s.validateEnterpriseIntention(args.Intention); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -117,74 +218,59 @@ func (s *Intention) prepareApplyCreate(
|
|||
return nil, err
|
||||
}
|
||||
|
||||
_, configEntry, err := s.srv.fsm.State().ConfigEntry(nil, structs.ServiceIntentions, args.Intention.DestinationName, args.Intention.DestinationEnterpriseMeta())
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("service-intentions config entry lookup failed: %v", err)
|
||||
} else if configEntry == nil {
|
||||
return nil, nil
|
||||
// NOTE: if the append of this source causes a duplicate source name the
|
||||
// config entry validation will fail so we don't have to check that
|
||||
// explicitly here.
|
||||
|
||||
mut := &structs.IntentionMutation{
|
||||
Destination: args.Intention.DestinationServiceName(),
|
||||
Value: args.Intention.ToSourceIntention(true),
|
||||
}
|
||||
|
||||
return configEntry.(*structs.ServiceIntentionsConfigEntry), nil
|
||||
// Set the created/updated times. If this is an update instead of an insert
|
||||
// the UpdateOver() will fix it up appropriately.
|
||||
now := time.Now().UTC()
|
||||
mut.Value.LegacyCreateTime = timePointer(now)
|
||||
mut.Value.LegacyUpdateTime = timePointer(now)
|
||||
|
||||
return mut, nil
|
||||
}
|
||||
|
||||
// prepareApplyUpdateLegacy validates that the requester has permissions on both the updated and existing
|
||||
// intention as well as generally validating that the request is well-formed
|
||||
//
|
||||
// Returns an existing service-intentions config entry for this destination if
|
||||
// one exists.
|
||||
func (s *Intention) prepareApplyUpdateLegacy(
|
||||
ident structs.ACLIdentity,
|
||||
func (s *Intention) computeApplyChangesLegacyUpdate(
|
||||
accessorID string,
|
||||
authz acl.Authorizer,
|
||||
entMeta *structs.EnterpriseMeta,
|
||||
args *structs.IntentionRequest,
|
||||
) (*structs.ServiceIntentionsConfigEntry, error) {
|
||||
if !args.Intention.CanWrite(authz) {
|
||||
var accessorID string
|
||||
if ident != nil {
|
||||
accessorID = ident.ID()
|
||||
}
|
||||
// todo(kit) Migrate intention access denial logging over to audit logging when we implement it
|
||||
s.logger.Warn("Update operation on intention denied due to ACLs", "intention", args.Intention.ID, "accessorID", accessorID)
|
||||
return nil, acl.ErrPermissionDenied
|
||||
}
|
||||
) (*structs.IntentionMutation, error) {
|
||||
// This variant is just for legacy UUID-based intentions.
|
||||
|
||||
_, configEntry, ixn, err := s.srv.fsm.State().IntentionGet(nil, args.Intention.ID)
|
||||
_, _, ixn, err := s.srv.fsm.State().IntentionGet(nil, args.Intention.ID)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Intention lookup failed: %v", err)
|
||||
}
|
||||
if ixn == nil || configEntry == nil {
|
||||
if ixn == nil {
|
||||
return nil, fmt.Errorf("Cannot modify non-existent intention: '%s'", args.Intention.ID)
|
||||
}
|
||||
|
||||
// Perform the ACL check that we have write to the old intention too,
|
||||
// which must be true to perform any rename. This is the only ACL enforcement
|
||||
// done for deletions and a secondary enforcement for updates.
|
||||
if !ixn.CanWrite(authz) {
|
||||
var accessorID string
|
||||
if ident != nil {
|
||||
accessorID = ident.ID()
|
||||
}
|
||||
// todo(kit) Migrate intention access denial logging over to audit logging when we implement it
|
||||
s.logger.Warn("Update operation on intention denied due to ACLs", "intention", args.Intention.ID, "accessorID", accessorID)
|
||||
return nil, acl.ErrPermissionDenied
|
||||
}
|
||||
|
||||
args.Intention.DefaultNamespaces(entMeta)
|
||||
|
||||
// Prior to v1.9.0 renames of the destination side of an intention were
|
||||
// allowed, but that behavior doesn't work anymore.
|
||||
if ixn.DestinationServiceName() != args.Intention.DestinationServiceName() {
|
||||
return nil, fmt.Errorf("Cannot modify DestinationNS or DestinationName for an intention once it exists.")
|
||||
}
|
||||
|
||||
// We always update the updatedat field.
|
||||
args.Intention.UpdatedAt = time.Now().UTC()
|
||||
|
||||
// Default source type
|
||||
if args.Intention.SourceType == "" {
|
||||
args.Intention.SourceType = structs.IntentionSourceConsul
|
||||
}
|
||||
|
||||
args.Intention.DefaultNamespaces(entMeta)
|
||||
|
||||
if err := s.validateEnterpriseIntention(args.Intention); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -196,329 +282,149 @@ func (s *Intention) prepareApplyUpdateLegacy(
|
|||
return nil, err
|
||||
}
|
||||
|
||||
return configEntry, nil
|
||||
mut := &structs.IntentionMutation{
|
||||
ID: args.Intention.ID,
|
||||
Value: args.Intention.ToSourceIntention(true),
|
||||
}
|
||||
|
||||
// Set the created/updated times. If this is an update instead of an insert
|
||||
// the UpdateOver() will fix it up appropriately.
|
||||
now := time.Now().UTC()
|
||||
mut.Value.LegacyCreateTime = timePointer(now)
|
||||
mut.Value.LegacyUpdateTime = timePointer(now)
|
||||
|
||||
return mut, nil
|
||||
}
|
||||
|
||||
// prepareApplyDeleteLegacy ensures that the intention specified by the ID in the request exists
|
||||
// and that the requester is authorized to delete it
|
||||
//
|
||||
// Returns an existing service-intentions config entry for this destination if
|
||||
// one exists.
|
||||
func (s *Intention) prepareApplyDeleteLegacy(
|
||||
ident structs.ACLIdentity,
|
||||
func (s *Intention) computeApplyChangesUpsert(
|
||||
accessorID string,
|
||||
authz acl.Authorizer,
|
||||
entMeta *structs.EnterpriseMeta,
|
||||
args *structs.IntentionRequest,
|
||||
) (*structs.ServiceIntentionsConfigEntry, error) {
|
||||
// If this is not a create, then we have to verify the ID.
|
||||
_, configEntry, ixn, err := s.srv.fsm.State().IntentionGet(nil, args.Intention.ID)
|
||||
) (*structs.IntentionMutation, error) {
|
||||
// This variant is just for config-entry based intentions.
|
||||
|
||||
if args.Intention.ID != "" {
|
||||
// This is a new-style only endpoint
|
||||
return nil, fmt.Errorf("ID must not be specified")
|
||||
}
|
||||
|
||||
args.Intention.DefaultNamespaces(entMeta)
|
||||
|
||||
if !args.Intention.CanWrite(authz) {
|
||||
sn := args.Intention.SourceServiceName()
|
||||
dn := args.Intention.DestinationServiceName()
|
||||
// todo(kit) Migrate intention access denial logging over to audit logging when we implement it
|
||||
s.logger.Warn("Intention upsert denied due to ACLs",
|
||||
"source", sn.String(),
|
||||
"destination", dn.String(),
|
||||
"accessorID", accessorID)
|
||||
return nil, acl.ErrPermissionDenied
|
||||
}
|
||||
|
||||
_, prevEntry, err := s.srv.fsm.State().ConfigEntry(nil, structs.ServiceIntentions, args.Intention.DestinationName, args.Intention.DestinationEnterpriseMeta())
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Intention lookup failed: %v", err)
|
||||
}
|
||||
if ixn == nil || configEntry == nil {
|
||||
|
||||
if prevEntry == nil {
|
||||
// Meta is NOT permitted here, as it would need to be persisted on
|
||||
// the enclosing config entry.
|
||||
if len(args.Intention.Meta) > 0 {
|
||||
return nil, fmt.Errorf("Meta must not be specified")
|
||||
}
|
||||
} else {
|
||||
if len(args.Intention.Meta) > 0 {
|
||||
// Meta is NOT permitted here, but there is one exception. If
|
||||
// you are updating a previous record, but that record lives
|
||||
// within a config entry that itself has Meta, then you may
|
||||
// incidentally ship the Meta right back to consul.
|
||||
//
|
||||
// In that case if Meta is provided, it has to be a perfect
|
||||
// match for what is already on the enclosing config entry so
|
||||
// it's safe to discard.
|
||||
if !equalStringMaps(prevEntry.GetMeta(), args.Intention.Meta) {
|
||||
return nil, fmt.Errorf("Meta must not be specified, or should be unchanged during an update.")
|
||||
}
|
||||
|
||||
// Now it is safe to discard
|
||||
args.Intention.Meta = nil
|
||||
}
|
||||
}
|
||||
|
||||
return &structs.IntentionMutation{
|
||||
Destination: args.Intention.DestinationServiceName(),
|
||||
Source: args.Intention.SourceServiceName(),
|
||||
Value: args.Intention.ToSourceIntention(false),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (s *Intention) computeApplyChangesLegacyDelete(
|
||||
accessorID string,
|
||||
authz acl.Authorizer,
|
||||
entMeta *structs.EnterpriseMeta,
|
||||
args *structs.IntentionRequest,
|
||||
) (*structs.IntentionMutation, error) {
|
||||
_, _, ixn, err := s.srv.fsm.State().IntentionGet(nil, args.Intention.ID)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Intention lookup failed: %v", err)
|
||||
}
|
||||
if ixn == nil {
|
||||
return nil, fmt.Errorf("Cannot delete non-existent intention: '%s'", args.Intention.ID)
|
||||
}
|
||||
|
||||
// Perform the ACL check that we have write to the old intention. This is
|
||||
// the only ACL enforcement done for deletions and a secondary enforcement
|
||||
// for updates.
|
||||
if !ixn.CanWrite(authz) {
|
||||
var accessorID string
|
||||
if ident != nil {
|
||||
accessorID = ident.ID()
|
||||
}
|
||||
// todo(kit) Migrate intention access denial logging over to audit logging when we implement it
|
||||
s.logger.Warn("Deletion operation on intention denied due to ACLs", "intention", args.Intention.ID, "accessorID", accessorID)
|
||||
return nil, acl.ErrPermissionDenied
|
||||
}
|
||||
|
||||
return configEntry, nil
|
||||
return &structs.IntentionMutation{
|
||||
ID: args.Intention.ID,
|
||||
}, nil
|
||||
}
|
||||
|
||||
var ErrIntentionsNotUpgradedYet = errors.New("Intentions are read only while being upgraded to config entries")
|
||||
|
||||
// legacyUpgradeCheck fast fails a write request using the legacy intention
|
||||
// RPCs if the system is known to be mid-upgrade. This is purely a perf
|
||||
// optimization and the actual real enforcement happens in the FSM. It would be
|
||||
// wasteful to round trip all the way through raft to have it fail for
|
||||
// known-up-front reasons, hence why we check it twice.
|
||||
func (s *Intention) legacyUpgradeCheck() error {
|
||||
usingConfigEntries, err := s.srv.fsm.State().AreIntentionsInConfigEntries()
|
||||
if err != nil {
|
||||
return fmt.Errorf("system metadata lookup failed: %v", err)
|
||||
}
|
||||
if !usingConfigEntries {
|
||||
return ErrIntentionsNotUpgradedYet
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Apply creates or updates an intention in the data store.
|
||||
func (s *Intention) Apply(
|
||||
func (s *Intention) computeApplyChangesDelete(
|
||||
accessorID string,
|
||||
authz acl.Authorizer,
|
||||
entMeta *structs.EnterpriseMeta,
|
||||
args *structs.IntentionRequest,
|
||||
reply *string) error {
|
||||
|
||||
// Ensure that all service-intentions config entry writes go to the primary
|
||||
// datacenter. These will then be replicated to all the other datacenters.
|
||||
args.Datacenter = s.srv.config.PrimaryDatacenter
|
||||
|
||||
if done, err := s.srv.ForwardRPC("Intention.Apply", args, args, reply); done {
|
||||
return err
|
||||
}
|
||||
defer metrics.MeasureSince([]string{"consul", "intention", "apply"}, time.Now())
|
||||
defer metrics.MeasureSince([]string{"intention", "apply"}, time.Now())
|
||||
|
||||
if err := s.legacyUpgradeCheck(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Always set a non-nil intention to avoid nil-access below
|
||||
if args.Intention == nil {
|
||||
args.Intention = &structs.Intention{}
|
||||
}
|
||||
|
||||
// Get the ACL token for the request for the checks below.
|
||||
var entMeta structs.EnterpriseMeta
|
||||
ident, authz, err := s.srv.ResolveTokenIdentityAndDefaultMeta(args.Token, &entMeta, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var (
|
||||
prevEntry *structs.ServiceIntentionsConfigEntry
|
||||
upsertEntry *structs.ServiceIntentionsConfigEntry
|
||||
legacyWrite bool
|
||||
noop bool
|
||||
)
|
||||
switch args.Op {
|
||||
case structs.IntentionOpCreate:
|
||||
legacyWrite = true
|
||||
|
||||
// This variant is just for legacy UUID-based intentions.
|
||||
prevEntry, err = s.prepareApplyCreate(ident, authz, &entMeta, args)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if prevEntry == nil {
|
||||
upsertEntry = args.Intention.ToConfigEntry(true)
|
||||
} else {
|
||||
upsertEntry = prevEntry.Clone()
|
||||
upsertEntry.Sources = append(upsertEntry.Sources, args.Intention.ToSourceIntention(true))
|
||||
}
|
||||
|
||||
case structs.IntentionOpUpdate:
|
||||
// This variant is just for legacy UUID-based intentions.
|
||||
legacyWrite = true
|
||||
|
||||
prevEntry, err = s.prepareApplyUpdateLegacy(ident, authz, &entMeta, args)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
upsertEntry = prevEntry.Clone()
|
||||
for i, src := range upsertEntry.Sources {
|
||||
if src.LegacyID == args.Intention.ID {
|
||||
upsertEntry.Sources[i] = args.Intention.ToSourceIntention(true)
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
case structs.IntentionOpUpsert:
|
||||
// This variant is just for config-entry based intentions.
|
||||
legacyWrite = false
|
||||
|
||||
if args.Intention.ID != "" {
|
||||
// This is a new-style only endpoint
|
||||
return fmt.Errorf("ID must not be specified")
|
||||
}
|
||||
|
||||
args.Intention.DefaultNamespaces(&entMeta)
|
||||
|
||||
prevEntry, err = s.getServiceIntentionsConfigEntry(args.Intention.DestinationName, args.Intention.DestinationEnterpriseMeta())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
) (*structs.IntentionMutation, error) {
|
||||
args.Intention.DefaultNamespaces(entMeta)
|
||||
|
||||
if !args.Intention.CanWrite(authz) {
|
||||
sn := args.Intention.SourceServiceName()
|
||||
|
||||
// TODO(intentions): have service-intentions validation functions
|
||||
// return structured errors so that we can rewrite the field prefix
|
||||
// here so that the validation errors are not misleading.
|
||||
if prevEntry == nil {
|
||||
// Meta is NOT permitted here, as it would need to be persisted on
|
||||
// the enclosing config entry.
|
||||
if len(args.Intention.Meta) > 0 {
|
||||
return fmt.Errorf("Meta must not be specified")
|
||||
}
|
||||
|
||||
upsertEntry = args.Intention.ToConfigEntry(false)
|
||||
} else {
|
||||
upsertEntry = prevEntry.Clone()
|
||||
|
||||
if len(args.Intention.Meta) > 0 {
|
||||
// Meta is NOT permitted here, but there is one exception. If
|
||||
// you are updating a previous record, but that record lives
|
||||
// within a config entry that itself has Meta, then you may
|
||||
// incidentally ship the Meta right back to consul.
|
||||
//
|
||||
// In that case if Meta is provided, it has to be a perfect
|
||||
// match for what is already on the enclosing config entry so
|
||||
// it's safe to discard.
|
||||
if !equalStringMaps(upsertEntry.Meta, args.Intention.Meta) {
|
||||
return fmt.Errorf("Meta must not be specified, or should be unchanged during an update.")
|
||||
}
|
||||
|
||||
// Now it is safe to discard
|
||||
args.Intention.Meta = nil
|
||||
}
|
||||
|
||||
found := false
|
||||
for i, src := range upsertEntry.Sources {
|
||||
if src.SourceServiceName() == sn {
|
||||
upsertEntry.Sources[i] = args.Intention.ToSourceIntention(false)
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
upsertEntry.Sources = append(upsertEntry.Sources, args.Intention.ToSourceIntention(false))
|
||||
}
|
||||
}
|
||||
|
||||
case structs.IntentionOpDelete:
|
||||
// There are two ways to get this request:
|
||||
//
|
||||
// 1) legacy: the ID field is populated
|
||||
// 2) config-entry: the ID field is NOT populated
|
||||
|
||||
if args.Intention.ID == "" {
|
||||
// config-entry style: no LegacyID
|
||||
legacyWrite = false
|
||||
|
||||
args.Intention.DefaultNamespaces(&entMeta)
|
||||
|
||||
prevEntry, err = s.getServiceIntentionsConfigEntry(args.Intention.DestinationName, args.Intention.DestinationEnterpriseMeta())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// NOTE: validation errors may be misleading!
|
||||
noop = true
|
||||
if prevEntry != nil {
|
||||
sn := args.Intention.SourceServiceName()
|
||||
|
||||
upsertEntry = prevEntry.Clone()
|
||||
for i, src := range upsertEntry.Sources {
|
||||
if src.SourceServiceName() == sn {
|
||||
// Delete slice element: https://github.com/golang/go/wiki/SliceTricks#delete
|
||||
// a = append(a[:i], a[i+1:]...)
|
||||
upsertEntry.Sources = append(upsertEntry.Sources[:i], upsertEntry.Sources[i+1:]...)
|
||||
|
||||
if len(upsertEntry.Sources) == 0 {
|
||||
upsertEntry.Sources = nil
|
||||
}
|
||||
noop = false
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
// legacy style: LegacyID required
|
||||
legacyWrite = true
|
||||
|
||||
prevEntry, err = s.prepareApplyDeleteLegacy(ident, authz, args)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
upsertEntry = prevEntry.Clone()
|
||||
for i, src := range upsertEntry.Sources {
|
||||
if src.LegacyID == args.Intention.ID {
|
||||
// Delete slice element: https://github.com/golang/go/wiki/SliceTricks#delete
|
||||
// a = append(a[:i], a[i+1:]...)
|
||||
upsertEntry.Sources = append(upsertEntry.Sources[:i], upsertEntry.Sources[i+1:]...)
|
||||
|
||||
if len(upsertEntry.Sources) == 0 {
|
||||
upsertEntry.Sources = nil
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
case structs.IntentionOpDeleteAll:
|
||||
// This is an internal operation initiated by the leader and is not
|
||||
// exposed for general RPC use.
|
||||
fallthrough
|
||||
default:
|
||||
return fmt.Errorf("Invalid Intention operation: %v", args.Op)
|
||||
dn := args.Intention.DestinationServiceName()
|
||||
// todo(kit) Migrate intention access denial logging over to audit logging when we implement it
|
||||
s.logger.Warn("Intention delete denied due to ACLs",
|
||||
"source", sn.String(),
|
||||
"destination", dn.String(),
|
||||
"accessorID", accessorID)
|
||||
return nil, acl.ErrPermissionDenied
|
||||
}
|
||||
|
||||
if !noop && prevEntry != nil && legacyWrite && !prevEntry.LegacyIDFieldsAreAllSet() {
|
||||
sn := prevEntry.DestinationServiceName()
|
||||
return fmt.Errorf("cannot use legacy intention API to edit intentions with a destination of %q after editing them via a service-intentions config entry", sn.String())
|
||||
// Pre-flight to avoid pointless raft operations.
|
||||
_, _, ixn, err := s.srv.fsm.State().IntentionGetExact(nil, args.Intention.ToExact())
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Intention lookup failed: %v", err)
|
||||
}
|
||||
if ixn == nil {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// setup the reply which will have been filled in by one of the preparedApply* funcs
|
||||
if legacyWrite {
|
||||
*reply = args.Intention.ID
|
||||
} else {
|
||||
*reply = ""
|
||||
}
|
||||
|
||||
if noop {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Commit indirectly by invoking the other RPC handler directly.
|
||||
configReq := &structs.ConfigEntryRequest{
|
||||
Datacenter: args.Datacenter,
|
||||
WriteRequest: args.WriteRequest,
|
||||
}
|
||||
if upsertEntry == nil || len(upsertEntry.Sources) == 0 {
|
||||
configReq.Op = structs.ConfigEntryDelete
|
||||
configReq.Entry = &structs.ServiceIntentionsConfigEntry{
|
||||
Kind: structs.ServiceIntentions,
|
||||
Name: prevEntry.Name,
|
||||
EnterpriseMeta: prevEntry.EnterpriseMeta,
|
||||
}
|
||||
|
||||
var ignored struct{}
|
||||
return s.configEntryEndpoint.Delete(configReq, &ignored)
|
||||
} else {
|
||||
// Update config entry CAS
|
||||
configReq.Op = structs.ConfigEntryUpsertCAS
|
||||
configReq.Entry = upsertEntry
|
||||
|
||||
var normalizeAndValidateFn func(raw structs.ConfigEntry) error
|
||||
if legacyWrite {
|
||||
normalizeAndValidateFn = func(raw structs.ConfigEntry) error {
|
||||
entry := raw.(*structs.ServiceIntentionsConfigEntry)
|
||||
if err := entry.LegacyNormalize(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return entry.LegacyValidate()
|
||||
}
|
||||
}
|
||||
|
||||
var applied bool
|
||||
err := s.configEntryEndpoint.applyInternal(configReq, &applied, normalizeAndValidateFn)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !applied {
|
||||
return fmt.Errorf("config entry failed to persist due to CAS failure: kind=%q, name=%q", upsertEntry.Kind, upsertEntry.Name)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
return &structs.IntentionMutation{
|
||||
Destination: args.Intention.DestinationServiceName(),
|
||||
Source: args.Intention.SourceServiceName(),
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Get returns a single intention by ID.
|
||||
func (s *Intention) Get(
|
||||
args *structs.IntentionQueryRequest,
|
||||
reply *structs.IndexedIntentions) error {
|
||||
func (s *Intention) Get(args *structs.IntentionQueryRequest, reply *structs.IndexedIntentions) error {
|
||||
// Exit early if Connect hasn't been enabled.
|
||||
if !s.srv.config.ConnectEnabled {
|
||||
return ErrConnectNotEnabled
|
||||
}
|
||||
|
||||
// Forward if necessary
|
||||
if done, err := s.srv.ForwardRPC("Intention.Get", args, args, reply); done {
|
||||
return err
|
||||
|
@ -591,9 +497,12 @@ func (s *Intention) Get(
|
|||
}
|
||||
|
||||
// List returns all the intentions.
|
||||
func (s *Intention) List(
|
||||
args *structs.IntentionListRequest,
|
||||
reply *structs.IndexedIntentions) error {
|
||||
func (s *Intention) List(args *structs.IntentionListRequest, reply *structs.IndexedIntentions) error {
|
||||
// Exit early if Connect hasn't been enabled.
|
||||
if !s.srv.config.ConnectEnabled {
|
||||
return ErrConnectNotEnabled
|
||||
}
|
||||
|
||||
// Forward if necessary
|
||||
if done, err := s.srv.ForwardRPC("Intention.List", args, args, reply); done {
|
||||
return err
|
||||
|
@ -658,9 +567,12 @@ func (s *Intention) List(
|
|||
}
|
||||
|
||||
// Match returns the set of intentions that match the given source/destination.
|
||||
func (s *Intention) Match(
|
||||
args *structs.IntentionQueryRequest,
|
||||
reply *structs.IndexedIntentionMatches) error {
|
||||
func (s *Intention) Match(args *structs.IntentionQueryRequest, reply *structs.IndexedIntentionMatches) error {
|
||||
// Exit early if Connect hasn't been enabled.
|
||||
if !s.srv.config.ConnectEnabled {
|
||||
return ErrConnectNotEnabled
|
||||
}
|
||||
|
||||
// Forward if necessary
|
||||
if done, err := s.srv.ForwardRPC("Intention.Match", args, args, reply); done {
|
||||
return err
|
||||
|
@ -729,9 +641,12 @@ func (s *Intention) Match(
|
|||
// Note: Whenever the logic for this method is changed, you should take
|
||||
// a look at the agent authorize endpoint (agent/agent_endpoint.go) since
|
||||
// the logic there is similar.
|
||||
func (s *Intention) Check(
|
||||
args *structs.IntentionQueryRequest,
|
||||
reply *structs.IntentionQueryCheckResponse) error {
|
||||
func (s *Intention) Check(args *structs.IntentionQueryRequest, reply *structs.IntentionQueryCheckResponse) error {
|
||||
// Exit early if Connect hasn't been enabled.
|
||||
if !s.srv.config.ConnectEnabled {
|
||||
return ErrConnectNotEnabled
|
||||
}
|
||||
|
||||
// Forward maybe
|
||||
if done, err := s.srv.ForwardRPC("Intention.Check", args, args, reply); done {
|
||||
return err
|
||||
|
@ -851,23 +766,6 @@ func (s *Intention) validateEnterpriseIntention(ixn *structs.Intention) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func (s *Intention) getServiceIntentionsConfigEntry(name string, entMeta *structs.EnterpriseMeta) (*structs.ServiceIntentionsConfigEntry, error) {
|
||||
_, raw, err := s.srv.fsm.State().ConfigEntry(nil, structs.ServiceIntentions, name, entMeta)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Intention lookup failed: %v", err)
|
||||
}
|
||||
|
||||
if raw == nil {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
configEntry, ok := raw.(*structs.ServiceIntentionsConfigEntry)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("invalid service config type %T", raw)
|
||||
}
|
||||
return configEntry, nil
|
||||
}
|
||||
|
||||
func equalStringMaps(a, b map[string]string) bool {
|
||||
if len(a) != len(b) {
|
||||
return false
|
||||
|
|
|
@ -1033,18 +1033,32 @@ func (s *Server) bootstrapConfigEntries(entries []structs.ConfigEntry) error {
|
|||
|
||||
state := s.fsm.State()
|
||||
|
||||
// Do a quick preflight check to see if someone is trying to upgrade from
|
||||
// an older pre-1.9.0 version of consul with intentions AND are trying to
|
||||
// bootstrap a service-intentions config entry at the same time.
|
||||
// Do some quick preflight checks to see if someone is doing something
|
||||
// that's not allowed at this time:
|
||||
//
|
||||
// - Trying to upgrade from an older pre-1.9.0 version of consul with
|
||||
// intentions AND are trying to bootstrap a service-intentions config entry
|
||||
// at the same time.
|
||||
//
|
||||
// - Trying to insert service-intentions config entries when connect is
|
||||
// disabled.
|
||||
|
||||
usingConfigEntries, err := s.fsm.State().AreIntentionsInConfigEntries()
|
||||
if err != nil {
|
||||
return fmt.Errorf("Failed to determine if we are migrating intentions yet: %v", err)
|
||||
}
|
||||
if !usingConfigEntries {
|
||||
|
||||
if !usingConfigEntries || !s.config.ConnectEnabled {
|
||||
for _, entry := range entries {
|
||||
if entry.GetKind() == structs.ServiceIntentions {
|
||||
return fmt.Errorf("Refusing to apply configuration entry %q / %q because intentions are still being migrated to config entries: %v",
|
||||
entry.GetKind(), entry.GetName(), err)
|
||||
if !s.config.ConnectEnabled {
|
||||
return fmt.Errorf("Refusing to apply configuration entry %q / %q because Connect must be enabled to bootstrap intentions",
|
||||
entry.GetKind(), entry.GetName())
|
||||
}
|
||||
if !usingConfigEntries {
|
||||
return fmt.Errorf("Refusing to apply configuration entry %q / %q because intentions are still being migrated to config entries",
|
||||
entry.GetKind(), entry.GetName())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -100,6 +100,11 @@ func (s *Server) legacyIntentionMigration(ctx context.Context) error {
|
|||
return err
|
||||
}
|
||||
|
||||
entries, err = s.filterMigratedLegacyIntentions(entries)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Totally cheat and repurpose one part of config entry replication
|
||||
// here so we automatically get our writes rate limited.
|
||||
_, err = s.reconcileLocalConfig(ctx, entries, structs.ConfigEntryUpsert)
|
||||
|
|
|
@ -84,3 +84,7 @@ func migrateIntentionsToConfigEntries(ixns structs.Intentions) []*structs.Servic
|
|||
|
||||
return structs.MigrateIntentions(output)
|
||||
}
|
||||
|
||||
func (s *Server) filterMigratedLegacyIntentions(entries []structs.ConfigEntry) ([]structs.ConfigEntry, error) {
|
||||
return entries, nil
|
||||
}
|
||||
|
|
|
@ -11,6 +11,13 @@ import (
|
|||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func testLeader_LegacyIntentionMigrationHookEnterprise(_ *testing.T, _ *Server, _ bool) {
|
||||
}
|
||||
|
||||
func appendLegacyIntentionsForMigrationTestEnterprise(_ *testing.T, _ *Server, ixns []*structs.Intention) []*structs.Intention {
|
||||
return ixns
|
||||
}
|
||||
|
||||
func TestMigrateIntentionsToConfigEntries(t *testing.T) {
|
||||
compare := func(t *testing.T, got structs.Intentions, expect [][]string) {
|
||||
t.Helper()
|
||||
|
|
|
@ -425,6 +425,11 @@ func TestLeader_LegacyIntentionMigration(t *testing.T) {
|
|||
makeIxn("contractor", "*", false),
|
||||
makeIxn("*", "*", true),
|
||||
}
|
||||
ixns = appendLegacyIntentionsForMigrationTestEnterprise(t, s1pre, ixns)
|
||||
|
||||
testLeader_LegacyIntentionMigrationHookEnterprise(t, s1pre, true)
|
||||
|
||||
var retained []*structs.Intention
|
||||
for _, ixn := range ixns {
|
||||
ixn2 := *ixn
|
||||
resp, err := s1pre.raftApply(structs.IntentionRequestType, &structs.IntentionRequest{
|
||||
|
@ -435,6 +440,10 @@ func TestLeader_LegacyIntentionMigration(t *testing.T) {
|
|||
if respErr, ok := resp.(error); ok {
|
||||
t.Fatalf("respErr: %v", respErr)
|
||||
}
|
||||
|
||||
if _, present := ixn.Meta["unit-test-discarded"]; !present {
|
||||
retained = append(retained, ixn)
|
||||
}
|
||||
}
|
||||
|
||||
mapify := func(ixns []*structs.Intention) map[string]*structs.Intention {
|
||||
|
@ -465,7 +474,7 @@ func TestLeader_LegacyIntentionMigration(t *testing.T) {
|
|||
for k, expectV := range expect {
|
||||
gotV, ok := gotM[k]
|
||||
if !ok {
|
||||
r.Errorf("results are missing key %q", k)
|
||||
r.Errorf("results are missing key %q: %v", k, expectV)
|
||||
continue
|
||||
}
|
||||
|
||||
|
@ -483,8 +492,14 @@ func TestLeader_LegacyIntentionMigration(t *testing.T) {
|
|||
}
|
||||
|
||||
expectM := mapify(ixns)
|
||||
checkIntentions(t, s1pre, false, expectM)
|
||||
checkIntentions(t, s1pre, true, expectM)
|
||||
expectRetainedM := mapify(retained)
|
||||
|
||||
require.True(t, t.Run("check initial intentions", func(t *testing.T) {
|
||||
checkIntentions(t, s1pre, false, expectM)
|
||||
}))
|
||||
require.True(t, t.Run("check initial legacy intentions", func(t *testing.T) {
|
||||
checkIntentions(t, s1pre, true, expectM)
|
||||
}))
|
||||
|
||||
// Shutdown s1pre and restart it to trigger migration.
|
||||
s1pre.Shutdown()
|
||||
|
@ -500,8 +515,7 @@ func TestLeader_LegacyIntentionMigration(t *testing.T) {
|
|||
|
||||
testrpc.WaitForLeader(t, s1.RPC, "dc1")
|
||||
|
||||
// check that all 7 intentions are present before migration
|
||||
checkIntentions(t, s1, false, expectM)
|
||||
testLeader_LegacyIntentionMigrationHookEnterprise(t, s1, false)
|
||||
|
||||
// Wait until the migration routine is complete.
|
||||
retry.Run(t, func(r *retry.R) {
|
||||
|
@ -513,9 +527,13 @@ func TestLeader_LegacyIntentionMigration(t *testing.T) {
|
|||
})
|
||||
|
||||
// check that all 7 intentions are present the general way after migration
|
||||
checkIntentions(t, s1, false, expectM)
|
||||
// check that no intentions exist in the legacy table
|
||||
checkIntentions(t, s1, true, map[string]*structs.Intention{})
|
||||
require.True(t, t.Run("check migrated intentions", func(t *testing.T) {
|
||||
checkIntentions(t, s1, false, expectRetainedM)
|
||||
}))
|
||||
require.True(t, t.Run("check migrated legacy intentions", func(t *testing.T) {
|
||||
// check that no intentions exist in the legacy table
|
||||
checkIntentions(t, s1, true, map[string]*structs.Intention{})
|
||||
}))
|
||||
|
||||
mapifyConfigs := func(entries interface{}) map[structs.ConfigEntryKindName]*structs.ServiceIntentionsConfigEntry {
|
||||
m := make(map[structs.ConfigEntryKindName]*structs.ServiceIntentionsConfigEntry)
|
||||
|
@ -541,7 +559,7 @@ func TestLeader_LegacyIntentionMigration(t *testing.T) {
|
|||
require.NoError(t, err)
|
||||
gotConfigsM := mapifyConfigs(gotConfigs)
|
||||
|
||||
expectConfigs := structs.MigrateIntentions(ixns)
|
||||
expectConfigs := structs.MigrateIntentions(retained)
|
||||
for _, entry := range expectConfigs {
|
||||
require.NoError(t, entry.LegacyNormalize()) // tidy them up the same way the write would
|
||||
}
|
||||
|
|
|
@ -12,8 +12,17 @@ import (
|
|||
type LeaderRoutine func(ctx context.Context) error
|
||||
|
||||
type leaderRoutine struct {
|
||||
running bool
|
||||
cancel context.CancelFunc
|
||||
cancel context.CancelFunc
|
||||
stoppedCh chan struct{} // closed when no longer running
|
||||
}
|
||||
|
||||
func (r *leaderRoutine) running() bool {
|
||||
select {
|
||||
case <-r.stoppedCh:
|
||||
return false
|
||||
default:
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
type LeaderRoutineManager struct {
|
||||
|
@ -41,7 +50,7 @@ func (m *LeaderRoutineManager) IsRunning(name string) bool {
|
|||
defer m.lock.Unlock()
|
||||
|
||||
if routine, ok := m.routines[name]; ok {
|
||||
return routine.running
|
||||
return routine.running()
|
||||
}
|
||||
|
||||
return false
|
||||
|
@ -55,7 +64,7 @@ func (m *LeaderRoutineManager) StartWithContext(parentCtx context.Context, name
|
|||
m.lock.Lock()
|
||||
defer m.lock.Unlock()
|
||||
|
||||
if instance, ok := m.routines[name]; ok && instance.running {
|
||||
if instance, ok := m.routines[name]; ok && instance.running() {
|
||||
return nil
|
||||
}
|
||||
|
||||
|
@ -65,11 +74,15 @@ func (m *LeaderRoutineManager) StartWithContext(parentCtx context.Context, name
|
|||
|
||||
ctx, cancel := context.WithCancel(parentCtx)
|
||||
instance := &leaderRoutine{
|
||||
running: true,
|
||||
cancel: cancel,
|
||||
cancel: cancel,
|
||||
stoppedCh: make(chan struct{}),
|
||||
}
|
||||
|
||||
go func() {
|
||||
defer func() {
|
||||
close(instance.stoppedCh)
|
||||
}()
|
||||
|
||||
err := routine(ctx)
|
||||
if err != nil && err != context.DeadlineExceeded && err != context.Canceled {
|
||||
m.logger.Error("routine exited with error",
|
||||
|
@ -79,10 +92,6 @@ func (m *LeaderRoutineManager) StartWithContext(parentCtx context.Context, name
|
|||
} else {
|
||||
m.logger.Debug("stopped routine", "routine", name)
|
||||
}
|
||||
|
||||
m.lock.Lock()
|
||||
instance.running = false
|
||||
m.lock.Unlock()
|
||||
}()
|
||||
|
||||
m.routines[name] = instance
|
||||
|
@ -90,7 +99,19 @@ func (m *LeaderRoutineManager) StartWithContext(parentCtx context.Context, name
|
|||
return nil
|
||||
}
|
||||
|
||||
func (m *LeaderRoutineManager) Stop(name string) error {
|
||||
func (m *LeaderRoutineManager) Stop(name string) <-chan struct{} {
|
||||
instance := m.stopInstance(name)
|
||||
if instance == nil {
|
||||
// Fabricate a closed channel so it won't block forever.
|
||||
ch := make(chan struct{})
|
||||
close(ch)
|
||||
return ch
|
||||
}
|
||||
|
||||
return instance.stoppedCh
|
||||
}
|
||||
|
||||
func (m *LeaderRoutineManager) stopInstance(name string) *leaderRoutine {
|
||||
m.lock.Lock()
|
||||
defer m.lock.Unlock()
|
||||
|
||||
|
@ -100,15 +121,16 @@ func (m *LeaderRoutineManager) Stop(name string) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
if !instance.running {
|
||||
return nil
|
||||
if !instance.running() {
|
||||
return instance
|
||||
}
|
||||
|
||||
m.logger.Debug("stopping routine", "routine", name)
|
||||
instance.cancel()
|
||||
|
||||
delete(m.routines, name)
|
||||
return nil
|
||||
|
||||
return instance
|
||||
}
|
||||
|
||||
func (m *LeaderRoutineManager) StopAll() {
|
||||
|
@ -116,7 +138,7 @@ func (m *LeaderRoutineManager) StopAll() {
|
|||
defer m.lock.Unlock()
|
||||
|
||||
for name, routine := range m.routines {
|
||||
if !routine.running {
|
||||
if !routine.running() {
|
||||
continue
|
||||
}
|
||||
m.logger.Debug("stopping routine", "routine", name)
|
||||
|
|
|
@ -36,7 +36,9 @@ func TestLeaderRoutineManager(t *testing.T) {
|
|||
require.Equal(r, uint32(1), atomic.LoadUint32(&runs))
|
||||
require.Equal(r, uint32(1), atomic.LoadUint32(&running))
|
||||
})
|
||||
require.NoError(t, mgr.Stop("run"))
|
||||
doneCh := mgr.Stop("run")
|
||||
require.NotNil(t, doneCh)
|
||||
<-doneCh
|
||||
|
||||
// ensure the background go routine was actually cancelled
|
||||
retry.Run(t, func(r *retry.R) {
|
||||
|
@ -51,7 +53,10 @@ func TestLeaderRoutineManager(t *testing.T) {
|
|||
require.Equal(r, uint32(1), atomic.LoadUint32(&running))
|
||||
})
|
||||
|
||||
require.NoError(t, mgr.Stop("run"))
|
||||
doneCh = mgr.Stop("run")
|
||||
require.NotNil(t, doneCh)
|
||||
<-doneCh
|
||||
|
||||
retry.Run(t, func(r *retry.R) {
|
||||
require.Equal(r, uint32(0), atomic.LoadUint32(&running))
|
||||
})
|
||||
|
|
|
@ -1230,63 +1230,133 @@ func TestLeader_ConfigEntryBootstrap(t *testing.T) {
|
|||
func TestLeader_ConfigEntryBootstrap_Fail(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
pr, pw := io.Pipe()
|
||||
defer pw.Close()
|
||||
type testcase struct {
|
||||
name string
|
||||
entries []structs.ConfigEntry
|
||||
serverCB func(c *Config)
|
||||
expectMessage string
|
||||
}
|
||||
|
||||
ch := make(chan string, 1)
|
||||
go func() {
|
||||
defer pr.Close()
|
||||
scan := bufio.NewScanner(pr)
|
||||
for scan.Scan() {
|
||||
line := scan.Text()
|
||||
|
||||
if strings.Contains(line, "failed to establish leadership") {
|
||||
ch <- ""
|
||||
return
|
||||
}
|
||||
if strings.Contains(line, "successfully established leadership") {
|
||||
ch <- "leadership should not have gotten here if config entries properly failed"
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if scan.Err() != nil {
|
||||
ch <- fmt.Sprintf("ERROR: %v", scan.Err())
|
||||
} else {
|
||||
ch <- "should not get here"
|
||||
}
|
||||
}()
|
||||
|
||||
_, config := testServerConfig(t)
|
||||
config.Build = "1.6.0"
|
||||
config.ConfigEntryBootstrap = []structs.ConfigEntry{
|
||||
&structs.ServiceSplitterConfigEntry{
|
||||
Kind: structs.ServiceSplitter,
|
||||
Name: "web",
|
||||
Splits: []structs.ServiceSplit{
|
||||
{Weight: 100, Service: "web"},
|
||||
cases := []testcase{
|
||||
{
|
||||
name: "service-splitter without L7 protocol",
|
||||
entries: []structs.ConfigEntry{
|
||||
&structs.ServiceSplitterConfigEntry{
|
||||
Kind: structs.ServiceSplitter,
|
||||
Name: "web",
|
||||
Splits: []structs.ServiceSplit{
|
||||
{Weight: 100, Service: "web"},
|
||||
},
|
||||
},
|
||||
},
|
||||
expectMessage: `Failed to apply configuration entry "service-splitter" / "web": discovery chain "web" uses a protocol "tcp" that does not permit advanced routing or splitting behavior"`,
|
||||
},
|
||||
{
|
||||
name: "service-intentions without migration",
|
||||
entries: []structs.ConfigEntry{
|
||||
&structs.ServiceIntentionsConfigEntry{
|
||||
Kind: structs.ServiceIntentions,
|
||||
Name: "web",
|
||||
Sources: []*structs.SourceIntention{
|
||||
{
|
||||
Name: "debug",
|
||||
Action: structs.IntentionActionAllow,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
serverCB: func(c *Config) {
|
||||
c.OverrideInitialSerfTags = func(tags map[string]string) {
|
||||
tags["ft_si"] = "0"
|
||||
}
|
||||
},
|
||||
expectMessage: `Refusing to apply configuration entry "service-intentions" / "web" because intentions are still being migrated to config entries`,
|
||||
},
|
||||
{
|
||||
name: "service-intentions without Connect",
|
||||
entries: []structs.ConfigEntry{
|
||||
&structs.ServiceIntentionsConfigEntry{
|
||||
Kind: structs.ServiceIntentions,
|
||||
Name: "web",
|
||||
Sources: []*structs.SourceIntention{
|
||||
{
|
||||
Name: "debug",
|
||||
Action: structs.IntentionActionAllow,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
serverCB: func(c *Config) {
|
||||
c.ConnectEnabled = false
|
||||
},
|
||||
expectMessage: `Refusing to apply configuration entry "service-intentions" / "web" because Connect must be enabled to bootstrap intentions"`,
|
||||
},
|
||||
}
|
||||
|
||||
logger := hclog.NewInterceptLogger(&hclog.LoggerOptions{
|
||||
Name: config.NodeName,
|
||||
Level: hclog.Debug,
|
||||
Output: io.MultiWriter(pw, testutil.NewLogBuffer(t)),
|
||||
})
|
||||
for _, tc := range cases {
|
||||
tc := tc
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
pr, pw := io.Pipe()
|
||||
defer pw.Close()
|
||||
|
||||
deps := newDefaultDeps(t, config)
|
||||
deps.Logger = logger
|
||||
var (
|
||||
ch = make(chan string, 1)
|
||||
applyErrorLine string
|
||||
)
|
||||
go func() {
|
||||
defer pr.Close()
|
||||
scan := bufio.NewScanner(pr)
|
||||
for scan.Scan() {
|
||||
line := scan.Text()
|
||||
|
||||
srv, err := NewServer(config, deps)
|
||||
require.NoError(t, err)
|
||||
defer srv.Shutdown()
|
||||
if strings.Contains(line, "failed to establish leadership") {
|
||||
applyErrorLine = line
|
||||
ch <- ""
|
||||
return
|
||||
}
|
||||
if strings.Contains(line, "successfully established leadership") {
|
||||
ch <- "leadership should not have gotten here if config entries properly failed"
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
select {
|
||||
case result := <-ch:
|
||||
require.Empty(t, result)
|
||||
case <-time.After(time.Second):
|
||||
t.Fatal("timeout waiting for a result from tailing logs")
|
||||
if scan.Err() != nil {
|
||||
ch <- fmt.Sprintf("ERROR: %v", scan.Err())
|
||||
} else {
|
||||
ch <- "should not get here"
|
||||
}
|
||||
}()
|
||||
|
||||
_, config := testServerConfig(t)
|
||||
config.Build = "1.6.0"
|
||||
config.ConfigEntryBootstrap = tc.entries
|
||||
if tc.serverCB != nil {
|
||||
tc.serverCB(config)
|
||||
}
|
||||
|
||||
logger := hclog.NewInterceptLogger(&hclog.LoggerOptions{
|
||||
Name: config.NodeName,
|
||||
Level: hclog.Debug,
|
||||
Output: io.MultiWriter(pw, testutil.NewLogBuffer(t)),
|
||||
})
|
||||
|
||||
deps := newDefaultDeps(t, config)
|
||||
deps.Logger = logger
|
||||
|
||||
srv, err := NewServer(config, deps)
|
||||
require.NoError(t, err)
|
||||
defer srv.Shutdown()
|
||||
|
||||
select {
|
||||
case result := <-ch:
|
||||
require.Empty(t, result)
|
||||
if tc.expectMessage != "" {
|
||||
require.Contains(t, applyErrorLine, tc.expectMessage)
|
||||
}
|
||||
case <-time.After(time.Second):
|
||||
t.Fatal("timeout waiting for a result from tailing logs")
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -11,7 +11,7 @@ func init() {
|
|||
registerEndpoint(func(s *Server) interface{} { return &FederationState{s} })
|
||||
registerEndpoint(func(s *Server) interface{} { return &DiscoveryChain{s} })
|
||||
registerEndpoint(func(s *Server) interface{} { return &Health{s} })
|
||||
registerEndpoint(func(s *Server) interface{} { return NewIntentionEndpoint(s, s.loggers.Named(logging.Intentions)) })
|
||||
registerEndpoint(func(s *Server) interface{} { return &Intention{s, s.loggers.Named(logging.Intentions)} })
|
||||
registerEndpoint(func(s *Server) interface{} { return &Internal{s, s.loggers.Named(logging.Internal)} })
|
||||
registerEndpoint(func(s *Server) interface{} { return &KVS{s, s.loggers.Named(logging.KV)} })
|
||||
registerEndpoint(func(s *Server) interface{} { return &Operator{s, s.loggers.Named(logging.Operator)} })
|
||||
|
|
|
@ -925,7 +925,7 @@ func TestSession_Apply_BadTTL(t *testing.T) {
|
|||
if err == nil {
|
||||
t.Fatal("expected error")
|
||||
}
|
||||
if err.Error() != "Session TTL '10z' invalid: time: unknown unit z in duration 10z" {
|
||||
if err.Error() != `Session TTL '10z' invalid: time: unknown unit "z" in duration "10z"` {
|
||||
t.Fatalf("incorrect error message: %s", err.Error())
|
||||
}
|
||||
|
||||
|
|
|
@ -263,7 +263,7 @@ func (s *Snapshot) Checks(node string) (memdb.ResultIterator, error) {
|
|||
// performed within a single transaction to avoid race conditions on state
|
||||
// updates.
|
||||
func (s *Restore) Registration(idx uint64, req *structs.RegisterRequest) error {
|
||||
return s.store.ensureRegistrationTxn(s.tx, idx, true, req)
|
||||
return s.store.ensureRegistrationTxn(s.tx, idx, true, req, true)
|
||||
}
|
||||
|
||||
// EnsureRegistration is used to make sure a node, service, and check
|
||||
|
@ -273,7 +273,7 @@ func (s *Store) EnsureRegistration(idx uint64, req *structs.RegisterRequest) err
|
|||
tx := s.db.WriteTxn(idx)
|
||||
defer tx.Abort()
|
||||
|
||||
if err := s.ensureRegistrationTxn(tx, idx, false, req); err != nil {
|
||||
if err := s.ensureRegistrationTxn(tx, idx, false, req, false); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
|
@ -294,8 +294,8 @@ func (s *Store) ensureCheckIfNodeMatches(tx WriteTxn, idx uint64, preserveIndexe
|
|||
// ensureRegistrationTxn is used to make sure a node, service, and check
|
||||
// registration is performed within a single transaction to avoid race
|
||||
// conditions on state updates.
|
||||
func (s *Store) ensureRegistrationTxn(tx WriteTxn, idx uint64, preserveIndexes bool, req *structs.RegisterRequest) error {
|
||||
if _, err := validateRegisterRequestTxn(tx, req); err != nil {
|
||||
func (s *Store) ensureRegistrationTxn(tx WriteTxn, idx uint64, preserveIndexes bool, req *structs.RegisterRequest, restore bool) error {
|
||||
if _, err := validateRegisterRequestTxn(tx, req, restore); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
|
|
|
@ -40,7 +40,7 @@ func TestServiceHealthEventsFromChanges(t *testing.T) {
|
|||
Name: "service reg, new node",
|
||||
Mutate: func(s *Store, tx *txn) error {
|
||||
return s.ensureRegistrationTxn(tx, tx.Index, false,
|
||||
testServiceRegistration(t, "web"))
|
||||
testServiceRegistration(t, "web"), false)
|
||||
},
|
||||
WantEvents: []stream.Event{
|
||||
testServiceHealthEvent(t, "web"),
|
||||
|
@ -51,11 +51,11 @@ func TestServiceHealthEventsFromChanges(t *testing.T) {
|
|||
Name: "service reg, existing node",
|
||||
Setup: func(s *Store, tx *txn) error {
|
||||
return s.ensureRegistrationTxn(tx, tx.Index, false,
|
||||
testServiceRegistration(t, "db"))
|
||||
testServiceRegistration(t, "db"), false)
|
||||
},
|
||||
Mutate: func(s *Store, tx *txn) error {
|
||||
return s.ensureRegistrationTxn(tx, tx.Index, false,
|
||||
testServiceRegistration(t, "web"))
|
||||
testServiceRegistration(t, "web"), false)
|
||||
},
|
||||
WantEvents: []stream.Event{
|
||||
// Should only publish new service
|
||||
|
@ -67,11 +67,11 @@ func TestServiceHealthEventsFromChanges(t *testing.T) {
|
|||
Name: "service dereg, existing node",
|
||||
Setup: func(s *Store, tx *txn) error {
|
||||
if err := s.ensureRegistrationTxn(tx, tx.Index, false,
|
||||
testServiceRegistration(t, "db")); err != nil {
|
||||
testServiceRegistration(t, "db"), false); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := s.ensureRegistrationTxn(tx, tx.Index, false,
|
||||
testServiceRegistration(t, "web")); err != nil {
|
||||
testServiceRegistration(t, "web"), false); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
|
@ -88,10 +88,10 @@ func TestServiceHealthEventsFromChanges(t *testing.T) {
|
|||
{
|
||||
Name: "node dereg",
|
||||
Setup: func(s *Store, tx *txn) error {
|
||||
if err := s.ensureRegistrationTxn(tx, tx.Index, false, testServiceRegistration(t, "db")); err != nil {
|
||||
if err := s.ensureRegistrationTxn(tx, tx.Index, false, testServiceRegistration(t, "db"), false); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := s.ensureRegistrationTxn(tx, tx.Index, false, testServiceRegistration(t, "web")); err != nil {
|
||||
if err := s.ensureRegistrationTxn(tx, tx.Index, false, testServiceRegistration(t, "web"), false); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
|
@ -109,7 +109,7 @@ func TestServiceHealthEventsFromChanges(t *testing.T) {
|
|||
{
|
||||
Name: "connect native reg, new node",
|
||||
Mutate: func(s *Store, tx *txn) error {
|
||||
return s.ensureRegistrationTxn(tx, tx.Index, false, testServiceRegistration(t, "web", regConnectNative))
|
||||
return s.ensureRegistrationTxn(tx, tx.Index, false, testServiceRegistration(t, "web", regConnectNative), false)
|
||||
},
|
||||
WantEvents: []stream.Event{
|
||||
// We should see both a regular service health event as well as a connect
|
||||
|
@ -122,10 +122,10 @@ func TestServiceHealthEventsFromChanges(t *testing.T) {
|
|||
{
|
||||
Name: "connect native reg, existing node",
|
||||
Setup: func(s *Store, tx *txn) error {
|
||||
return s.ensureRegistrationTxn(tx, tx.Index, false, testServiceRegistration(t, "db"))
|
||||
return s.ensureRegistrationTxn(tx, tx.Index, false, testServiceRegistration(t, "db"), false)
|
||||
},
|
||||
Mutate: func(s *Store, tx *txn) error {
|
||||
return s.ensureRegistrationTxn(tx, tx.Index, false, testServiceRegistration(t, "web", regConnectNative))
|
||||
return s.ensureRegistrationTxn(tx, tx.Index, false, testServiceRegistration(t, "web", regConnectNative), false)
|
||||
},
|
||||
WantEvents: []stream.Event{
|
||||
// We should see both a regular service health event as well as a connect
|
||||
|
@ -143,11 +143,11 @@ func TestServiceHealthEventsFromChanges(t *testing.T) {
|
|||
{
|
||||
Name: "connect native dereg, existing node",
|
||||
Setup: func(s *Store, tx *txn) error {
|
||||
if err := s.ensureRegistrationTxn(tx, tx.Index, false, testServiceRegistration(t, "db")); err != nil {
|
||||
if err := s.ensureRegistrationTxn(tx, tx.Index, false, testServiceRegistration(t, "db"), false); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return s.ensureRegistrationTxn(tx, tx.Index, false, testServiceRegistration(t, "web", regConnectNative))
|
||||
return s.ensureRegistrationTxn(tx, tx.Index, false, testServiceRegistration(t, "web", regConnectNative), false)
|
||||
},
|
||||
Mutate: func(s *Store, tx *txn) error {
|
||||
return s.deleteServiceTxn(tx, tx.Index, "node1", "web", nil)
|
||||
|
@ -162,10 +162,10 @@ func TestServiceHealthEventsFromChanges(t *testing.T) {
|
|||
{
|
||||
Name: "connect sidecar reg, new node",
|
||||
Mutate: func(s *Store, tx *txn) error {
|
||||
if err := s.ensureRegistrationTxn(tx, tx.Index, false, testServiceRegistration(t, "web")); err != nil {
|
||||
if err := s.ensureRegistrationTxn(tx, tx.Index, false, testServiceRegistration(t, "web"), false); err != nil {
|
||||
return err
|
||||
}
|
||||
return s.ensureRegistrationTxn(tx, tx.Index, false, testServiceRegistration(t, "web", regSidecar))
|
||||
return s.ensureRegistrationTxn(tx, tx.Index, false, testServiceRegistration(t, "web", regSidecar), false)
|
||||
},
|
||||
WantEvents: []stream.Event{
|
||||
// We should see both a regular service health event for the web service
|
||||
|
@ -180,15 +180,15 @@ func TestServiceHealthEventsFromChanges(t *testing.T) {
|
|||
Name: "connect sidecar reg, existing node",
|
||||
Setup: func(s *Store, tx *txn) error {
|
||||
if err := s.ensureRegistrationTxn(tx, tx.Index, false,
|
||||
testServiceRegistration(t, "db")); err != nil {
|
||||
testServiceRegistration(t, "db"), false); err != nil {
|
||||
return err
|
||||
}
|
||||
return s.ensureRegistrationTxn(tx, tx.Index, false,
|
||||
testServiceRegistration(t, "web"))
|
||||
testServiceRegistration(t, "web"), false)
|
||||
},
|
||||
Mutate: func(s *Store, tx *txn) error {
|
||||
return s.ensureRegistrationTxn(tx, tx.Index, false,
|
||||
testServiceRegistration(t, "web", regSidecar))
|
||||
testServiceRegistration(t, "web", regSidecar), false)
|
||||
},
|
||||
WantEvents: []stream.Event{
|
||||
// We should see both a regular service health event for the proxy
|
||||
|
@ -202,15 +202,15 @@ func TestServiceHealthEventsFromChanges(t *testing.T) {
|
|||
Name: "connect sidecar dereg, existing node",
|
||||
Setup: func(s *Store, tx *txn) error {
|
||||
if err := s.ensureRegistrationTxn(tx, tx.Index, false,
|
||||
testServiceRegistration(t, "db")); err != nil {
|
||||
testServiceRegistration(t, "db"), false); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := s.ensureRegistrationTxn(tx, tx.Index, false,
|
||||
testServiceRegistration(t, "web")); err != nil {
|
||||
testServiceRegistration(t, "web"), false); err != nil {
|
||||
return err
|
||||
}
|
||||
return s.ensureRegistrationTxn(tx, tx.Index, false,
|
||||
testServiceRegistration(t, "web", regSidecar))
|
||||
testServiceRegistration(t, "web", regSidecar), false)
|
||||
},
|
||||
Mutate: func(s *Store, tx *txn) error {
|
||||
// Delete only the sidecar
|
||||
|
@ -227,20 +227,20 @@ func TestServiceHealthEventsFromChanges(t *testing.T) {
|
|||
Name: "connect sidecar mutate svc",
|
||||
Setup: func(s *Store, tx *txn) error {
|
||||
if err := s.ensureRegistrationTxn(tx, tx.Index, false,
|
||||
testServiceRegistration(t, "db")); err != nil {
|
||||
testServiceRegistration(t, "db"), false); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := s.ensureRegistrationTxn(tx, tx.Index, false,
|
||||
testServiceRegistration(t, "web")); err != nil {
|
||||
testServiceRegistration(t, "web"), false); err != nil {
|
||||
return err
|
||||
}
|
||||
return s.ensureRegistrationTxn(tx, tx.Index, false,
|
||||
testServiceRegistration(t, "web", regSidecar))
|
||||
testServiceRegistration(t, "web", regSidecar), false)
|
||||
},
|
||||
Mutate: func(s *Store, tx *txn) error {
|
||||
// Change port of the target service instance
|
||||
return s.ensureRegistrationTxn(tx, tx.Index, false,
|
||||
testServiceRegistration(t, "web", regMutatePort))
|
||||
testServiceRegistration(t, "web", regMutatePort), false)
|
||||
},
|
||||
WantEvents: []stream.Event{
|
||||
// We should see the service topic update but not connect since proxy
|
||||
|
@ -258,20 +258,20 @@ func TestServiceHealthEventsFromChanges(t *testing.T) {
|
|||
Name: "connect sidecar mutate sidecar",
|
||||
Setup: func(s *Store, tx *txn) error {
|
||||
if err := s.ensureRegistrationTxn(tx, tx.Index, false,
|
||||
testServiceRegistration(t, "db")); err != nil {
|
||||
testServiceRegistration(t, "db"), false); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := s.ensureRegistrationTxn(tx, tx.Index, false,
|
||||
testServiceRegistration(t, "web")); err != nil {
|
||||
testServiceRegistration(t, "web"), false); err != nil {
|
||||
return err
|
||||
}
|
||||
return s.ensureRegistrationTxn(tx, tx.Index, false,
|
||||
testServiceRegistration(t, "web", regSidecar))
|
||||
testServiceRegistration(t, "web", regSidecar), false)
|
||||
},
|
||||
Mutate: func(s *Store, tx *txn) error {
|
||||
// Change port of the sidecar service instance
|
||||
return s.ensureRegistrationTxn(tx, tx.Index, false,
|
||||
testServiceRegistration(t, "web", regSidecar, regMutatePort))
|
||||
testServiceRegistration(t, "web", regSidecar, regMutatePort), false)
|
||||
},
|
||||
WantEvents: []stream.Event{
|
||||
// We should see the proxy service topic update and a connect update
|
||||
|
@ -295,24 +295,24 @@ func TestServiceHealthEventsFromChanges(t *testing.T) {
|
|||
Name: "connect sidecar rename service",
|
||||
Setup: func(s *Store, tx *txn) error {
|
||||
if err := s.ensureRegistrationTxn(tx, tx.Index, false,
|
||||
testServiceRegistration(t, "db")); err != nil {
|
||||
testServiceRegistration(t, "db"), false); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := s.ensureRegistrationTxn(tx, tx.Index, false,
|
||||
testServiceRegistration(t, "web")); err != nil {
|
||||
testServiceRegistration(t, "web"), false); err != nil {
|
||||
return err
|
||||
}
|
||||
return s.ensureRegistrationTxn(tx, tx.Index, false,
|
||||
testServiceRegistration(t, "web", regSidecar))
|
||||
testServiceRegistration(t, "web", regSidecar), false)
|
||||
},
|
||||
Mutate: func(s *Store, tx *txn) error {
|
||||
// Change service name but not ID, update proxy too
|
||||
if err := s.ensureRegistrationTxn(tx, tx.Index, false,
|
||||
testServiceRegistration(t, "web", regRenameService)); err != nil {
|
||||
testServiceRegistration(t, "web", regRenameService), false); err != nil {
|
||||
return err
|
||||
}
|
||||
return s.ensureRegistrationTxn(tx, tx.Index, false,
|
||||
testServiceRegistration(t, "web", regSidecar, regRenameService))
|
||||
testServiceRegistration(t, "web", regSidecar, regRenameService), false)
|
||||
},
|
||||
WantEvents: []stream.Event{
|
||||
// We should see events to deregister the old service instance and the
|
||||
|
@ -353,25 +353,25 @@ func TestServiceHealthEventsFromChanges(t *testing.T) {
|
|||
Setup: func(s *Store, tx *txn) error {
|
||||
// Register a web_changed service
|
||||
if err := s.ensureRegistrationTxn(tx, tx.Index, false,
|
||||
testServiceRegistration(t, "web_changed")); err != nil {
|
||||
testServiceRegistration(t, "web_changed"), false); err != nil {
|
||||
return err
|
||||
}
|
||||
// Also a web
|
||||
if err := s.ensureRegistrationTxn(tx, tx.Index, false,
|
||||
testServiceRegistration(t, "web")); err != nil {
|
||||
testServiceRegistration(t, "web"), false); err != nil {
|
||||
return err
|
||||
}
|
||||
// And a sidecar initially for web, will be moved to target web_changed
|
||||
// in Mutate.
|
||||
return s.ensureRegistrationTxn(tx, tx.Index, false,
|
||||
testServiceRegistration(t, "web", regSidecar))
|
||||
testServiceRegistration(t, "web", regSidecar), false)
|
||||
},
|
||||
Mutate: func(s *Store, tx *txn) error {
|
||||
// Change only the destination service of the proxy without a service
|
||||
// rename or deleting and recreating the proxy. This is far fetched but
|
||||
// still valid.
|
||||
return s.ensureRegistrationTxn(tx, tx.Index, false,
|
||||
testServiceRegistration(t, "web", regSidecar, regRenameService))
|
||||
testServiceRegistration(t, "web", regSidecar, regRenameService), false)
|
||||
},
|
||||
WantEvents: []stream.Event{
|
||||
// We should only see service health events for the sidecar service
|
||||
|
@ -405,17 +405,17 @@ func TestServiceHealthEventsFromChanges(t *testing.T) {
|
|||
Setup: func(s *Store, tx *txn) error {
|
||||
// Register a db service
|
||||
if err := s.ensureRegistrationTxn(tx, tx.Index, false,
|
||||
testServiceRegistration(t, "db")); err != nil {
|
||||
testServiceRegistration(t, "db"), false); err != nil {
|
||||
return err
|
||||
}
|
||||
// Also a web
|
||||
if err := s.ensureRegistrationTxn(tx, tx.Index, false,
|
||||
testServiceRegistration(t, "web")); err != nil {
|
||||
testServiceRegistration(t, "web"), false); err != nil {
|
||||
return err
|
||||
}
|
||||
// With a connect sidecar
|
||||
if err := s.ensureRegistrationTxn(tx, tx.Index, false,
|
||||
testServiceRegistration(t, "web", regSidecar)); err != nil {
|
||||
testServiceRegistration(t, "web", regSidecar), false); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
|
@ -423,7 +423,7 @@ func TestServiceHealthEventsFromChanges(t *testing.T) {
|
|||
Mutate: func(s *Store, tx *txn) error {
|
||||
// Change only the node meta.
|
||||
return s.ensureRegistrationTxn(tx, tx.Index, false,
|
||||
testNodeRegistration(t, regNodeMeta))
|
||||
testNodeRegistration(t, regNodeMeta), false)
|
||||
},
|
||||
WantEvents: []stream.Event{
|
||||
// We should see updates for all services and a connect update for the
|
||||
|
@ -463,17 +463,17 @@ func TestServiceHealthEventsFromChanges(t *testing.T) {
|
|||
Setup: func(s *Store, tx *txn) error {
|
||||
// Register a db service
|
||||
if err := s.ensureRegistrationTxn(tx, tx.Index, false,
|
||||
testServiceRegistration(t, "db")); err != nil {
|
||||
testServiceRegistration(t, "db"), false); err != nil {
|
||||
return err
|
||||
}
|
||||
// Also a web
|
||||
if err := s.ensureRegistrationTxn(tx, tx.Index, false,
|
||||
testServiceRegistration(t, "web")); err != nil {
|
||||
testServiceRegistration(t, "web"), false); err != nil {
|
||||
return err
|
||||
}
|
||||
// With a connect sidecar
|
||||
if err := s.ensureRegistrationTxn(tx, tx.Index, false,
|
||||
testServiceRegistration(t, "web", regSidecar)); err != nil {
|
||||
testServiceRegistration(t, "web", regSidecar), false); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
|
@ -485,17 +485,17 @@ func TestServiceHealthEventsFromChanges(t *testing.T) {
|
|||
// services registered afterwards.
|
||||
// Register a db service
|
||||
if err := s.ensureRegistrationTxn(tx, tx.Index, false,
|
||||
testServiceRegistration(t, "db", regRenameNode)); err != nil {
|
||||
testServiceRegistration(t, "db", regRenameNode), false); err != nil {
|
||||
return err
|
||||
}
|
||||
// Also a web
|
||||
if err := s.ensureRegistrationTxn(tx, tx.Index, false,
|
||||
testServiceRegistration(t, "web", regRenameNode)); err != nil {
|
||||
testServiceRegistration(t, "web", regRenameNode), false); err != nil {
|
||||
return err
|
||||
}
|
||||
// With a connect sidecar
|
||||
if err := s.ensureRegistrationTxn(tx, tx.Index, false,
|
||||
testServiceRegistration(t, "web", regSidecar, regRenameNode)); err != nil {
|
||||
testServiceRegistration(t, "web", regSidecar, regRenameNode), false); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
|
@ -540,17 +540,17 @@ func TestServiceHealthEventsFromChanges(t *testing.T) {
|
|||
Setup: func(s *Store, tx *txn) error {
|
||||
// Register a db service
|
||||
if err := s.ensureRegistrationTxn(tx, tx.Index, false,
|
||||
testServiceRegistration(t, "db")); err != nil {
|
||||
testServiceRegistration(t, "db"), false); err != nil {
|
||||
return err
|
||||
}
|
||||
// Also a web
|
||||
if err := s.ensureRegistrationTxn(tx, tx.Index, false,
|
||||
testServiceRegistration(t, "web")); err != nil {
|
||||
testServiceRegistration(t, "web"), false); err != nil {
|
||||
return err
|
||||
}
|
||||
// With a connect sidecar
|
||||
if err := s.ensureRegistrationTxn(tx, tx.Index, false,
|
||||
testServiceRegistration(t, "web", regSidecar)); err != nil {
|
||||
testServiceRegistration(t, "web", regSidecar), false); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
|
@ -558,7 +558,7 @@ func TestServiceHealthEventsFromChanges(t *testing.T) {
|
|||
Mutate: func(s *Store, tx *txn) error {
|
||||
// Change only the node-level check status
|
||||
if err := s.ensureRegistrationTxn(tx, tx.Index, false,
|
||||
testServiceRegistration(t, "web", regNodeCheckFail)); err != nil {
|
||||
testServiceRegistration(t, "web", regNodeCheckFail), false); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
|
@ -600,17 +600,17 @@ func TestServiceHealthEventsFromChanges(t *testing.T) {
|
|||
Setup: func(s *Store, tx *txn) error {
|
||||
// Register a db service
|
||||
if err := s.ensureRegistrationTxn(tx, tx.Index, false,
|
||||
testServiceRegistration(t, "db")); err != nil {
|
||||
testServiceRegistration(t, "db"), false); err != nil {
|
||||
return err
|
||||
}
|
||||
// Also a web
|
||||
if err := s.ensureRegistrationTxn(tx, tx.Index, false,
|
||||
testServiceRegistration(t, "web")); err != nil {
|
||||
testServiceRegistration(t, "web"), false); err != nil {
|
||||
return err
|
||||
}
|
||||
// With a connect sidecar
|
||||
if err := s.ensureRegistrationTxn(tx, tx.Index, false,
|
||||
testServiceRegistration(t, "web", regSidecar)); err != nil {
|
||||
testServiceRegistration(t, "web", regSidecar), false); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
|
@ -618,7 +618,7 @@ func TestServiceHealthEventsFromChanges(t *testing.T) {
|
|||
Mutate: func(s *Store, tx *txn) error {
|
||||
// Change the service-level check status
|
||||
if err := s.ensureRegistrationTxn(tx, tx.Index, false,
|
||||
testServiceRegistration(t, "web", regServiceCheckFail)); err != nil {
|
||||
testServiceRegistration(t, "web", regServiceCheckFail), false); err != nil {
|
||||
return err
|
||||
}
|
||||
// Also change the service-level check status for the proxy. This is
|
||||
|
@ -626,7 +626,7 @@ func TestServiceHealthEventsFromChanges(t *testing.T) {
|
|||
// - the proxies check would get updated at roughly the same time as the
|
||||
// target service check updates.
|
||||
if err := s.ensureRegistrationTxn(tx, tx.Index, false,
|
||||
testServiceRegistration(t, "web", regSidecar, regServiceCheckFail)); err != nil {
|
||||
testServiceRegistration(t, "web", regSidecar, regServiceCheckFail), false); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
|
@ -663,17 +663,17 @@ func TestServiceHealthEventsFromChanges(t *testing.T) {
|
|||
Setup: func(s *Store, tx *txn) error {
|
||||
// Register a db service
|
||||
if err := s.ensureRegistrationTxn(tx, tx.Index, false,
|
||||
testServiceRegistration(t, "db")); err != nil {
|
||||
testServiceRegistration(t, "db"), false); err != nil {
|
||||
return err
|
||||
}
|
||||
// Also a web
|
||||
if err := s.ensureRegistrationTxn(tx, tx.Index, false,
|
||||
testServiceRegistration(t, "web")); err != nil {
|
||||
testServiceRegistration(t, "web"), false); err != nil {
|
||||
return err
|
||||
}
|
||||
// With a connect sidecar
|
||||
if err := s.ensureRegistrationTxn(tx, tx.Index, false,
|
||||
testServiceRegistration(t, "web", regSidecar)); err != nil {
|
||||
testServiceRegistration(t, "web", regSidecar), false); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
|
@ -717,17 +717,17 @@ func TestServiceHealthEventsFromChanges(t *testing.T) {
|
|||
Setup: func(s *Store, tx *txn) error {
|
||||
// Register a db service
|
||||
if err := s.ensureRegistrationTxn(tx, tx.Index, false,
|
||||
testServiceRegistration(t, "db")); err != nil {
|
||||
testServiceRegistration(t, "db"), false); err != nil {
|
||||
return err
|
||||
}
|
||||
// Also a web
|
||||
if err := s.ensureRegistrationTxn(tx, tx.Index, false,
|
||||
testServiceRegistration(t, "web")); err != nil {
|
||||
testServiceRegistration(t, "web"), false); err != nil {
|
||||
return err
|
||||
}
|
||||
// With a connect sidecar
|
||||
if err := s.ensureRegistrationTxn(tx, tx.Index, false,
|
||||
testServiceRegistration(t, "web", regSidecar)); err != nil {
|
||||
testServiceRegistration(t, "web", regSidecar), false); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
|
@ -777,19 +777,19 @@ func TestServiceHealthEventsFromChanges(t *testing.T) {
|
|||
|
||||
// Register a db service
|
||||
if err := s.ensureRegistrationTxn(tx, tx.Index, false,
|
||||
testServiceRegistration(t, "db")); err != nil {
|
||||
testServiceRegistration(t, "db"), false); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Node2
|
||||
// Also a web
|
||||
if err := s.ensureRegistrationTxn(tx, tx.Index, false,
|
||||
testServiceRegistration(t, "web", regNode2)); err != nil {
|
||||
testServiceRegistration(t, "web", regNode2), false); err != nil {
|
||||
return err
|
||||
}
|
||||
// With a connect sidecar
|
||||
if err := s.ensureRegistrationTxn(tx, tx.Index, false,
|
||||
testServiceRegistration(t, "web", regSidecar, regNode2)); err != nil {
|
||||
testServiceRegistration(t, "web", regSidecar, regNode2), false); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
|
@ -808,17 +808,17 @@ func TestServiceHealthEventsFromChanges(t *testing.T) {
|
|||
|
||||
// Register those on node1
|
||||
if err := s.ensureRegistrationTxn(tx, tx.Index, false,
|
||||
testServiceRegistration(t, "web")); err != nil {
|
||||
testServiceRegistration(t, "web"), false); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := s.ensureRegistrationTxn(tx, tx.Index, false,
|
||||
testServiceRegistration(t, "web", regSidecar)); err != nil {
|
||||
testServiceRegistration(t, "web", regSidecar), false); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// And for good measure, add a new connect-native service to node2
|
||||
if err := s.ensureRegistrationTxn(tx, tx.Index, false,
|
||||
testServiceRegistration(t, "api", regConnectNative, regNode2)); err != nil {
|
||||
testServiceRegistration(t, "api", regConnectNative, regNode2), false); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
|
|
|
@ -323,7 +323,7 @@ func catalogChecksForNodeService(tx ReadTxn, node string, service string, entMet
|
|||
return tx.Get("checks", "node_service", node, service)
|
||||
}
|
||||
|
||||
func validateRegisterRequestTxn(_ ReadTxn, _ *structs.RegisterRequest) (*structs.EnterpriseMeta, error) {
|
||||
func validateRegisterRequestTxn(_ ReadTxn, _ *structs.RegisterRequest, _ bool) (*structs.EnterpriseMeta, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
|
|
|
@ -180,7 +180,7 @@ func (s *Store) EnsureConfigEntry(idx uint64, conf structs.ConfigEntry, entMeta
|
|||
}
|
||||
|
||||
// ensureConfigEntryTxn upserts a config entry inside of a transaction.
|
||||
func ensureConfigEntryTxn(tx *txn, idx uint64, conf structs.ConfigEntry, entMeta *structs.EnterpriseMeta) error {
|
||||
func ensureConfigEntryTxn(tx WriteTxn, idx uint64, conf structs.ConfigEntry, entMeta *structs.EnterpriseMeta) error {
|
||||
// Check for existing configuration.
|
||||
existing, err := firstConfigEntryWithTxn(tx, conf.GetKind(), conf.GetName(), entMeta)
|
||||
if err != nil {
|
||||
|
@ -254,6 +254,14 @@ func (s *Store) DeleteConfigEntry(idx uint64, kind, name string, entMeta *struct
|
|||
tx := s.db.WriteTxn(idx)
|
||||
defer tx.Abort()
|
||||
|
||||
if err := deleteConfigEntryTxn(tx, idx, kind, name, entMeta); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return tx.Commit()
|
||||
}
|
||||
|
||||
func deleteConfigEntryTxn(tx WriteTxn, idx uint64, kind, name string, entMeta *structs.EnterpriseMeta) error {
|
||||
// Try to retrieve the existing config entry.
|
||||
existing, err := firstConfigEntryWithTxn(tx, kind, name, entMeta)
|
||||
if err != nil {
|
||||
|
@ -298,10 +306,10 @@ func (s *Store) DeleteConfigEntry(idx uint64, kind, name string, entMeta *struct
|
|||
return fmt.Errorf("failed updating index: %s", err)
|
||||
}
|
||||
|
||||
return tx.Commit()
|
||||
return nil
|
||||
}
|
||||
|
||||
func insertConfigEntryWithTxn(tx *txn, idx uint64, conf structs.ConfigEntry) error {
|
||||
func insertConfigEntryWithTxn(tx WriteTxn, idx uint64, conf structs.ConfigEntry) error {
|
||||
if conf == nil {
|
||||
return fmt.Errorf("cannot insert nil config entry")
|
||||
}
|
||||
|
|
|
@ -207,6 +207,294 @@ func (s *Store) legacyIntentionsListTxn(tx ReadTxn, ws memdb.WatchSet, entMeta *
|
|||
|
||||
var ErrLegacyIntentionsAreDisabled = errors.New("Legacy intention modifications are disabled after the config entry migration.")
|
||||
|
||||
func (s *Store) IntentionMutation(idx uint64, op structs.IntentionOp, mut *structs.IntentionMutation) error {
|
||||
tx := s.db.WriteTxn(idx)
|
||||
defer tx.Abort()
|
||||
|
||||
usingConfigEntries, err := areIntentionsInConfigEntries(tx, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !usingConfigEntries {
|
||||
return errors.New("state: IntentionMutation() is not allowed when intentions are not stored in config entries")
|
||||
}
|
||||
|
||||
switch op {
|
||||
case structs.IntentionOpCreate:
|
||||
if err := s.intentionMutationLegacyCreate(tx, idx, mut.Destination, mut.Value); err != nil {
|
||||
return err
|
||||
}
|
||||
case structs.IntentionOpUpdate:
|
||||
if err := s.intentionMutationLegacyUpdate(tx, idx, mut.ID, mut.Value); err != nil {
|
||||
return err
|
||||
}
|
||||
case structs.IntentionOpDelete:
|
||||
if mut.ID == "" {
|
||||
if err := s.intentionMutationDelete(tx, idx, mut.Destination, mut.Source); err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
if err := s.intentionMutationLegacyDelete(tx, idx, mut.ID); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
case structs.IntentionOpUpsert:
|
||||
if err := s.intentionMutationUpsert(tx, idx, mut.Destination, mut.Source, mut.Value); err != nil {
|
||||
return err
|
||||
}
|
||||
case structs.IntentionOpDeleteAll:
|
||||
// This is an internal operation initiated by the leader and is not
|
||||
// exposed for general RPC use.
|
||||
return fmt.Errorf("Invalid Intention mutation operation '%s'", op)
|
||||
default:
|
||||
return fmt.Errorf("Invalid Intention mutation operation '%s'", op)
|
||||
}
|
||||
|
||||
return tx.Commit()
|
||||
}
|
||||
|
||||
func (s *Store) intentionMutationLegacyCreate(
|
||||
tx WriteTxn,
|
||||
idx uint64,
|
||||
dest structs.ServiceName,
|
||||
value *structs.SourceIntention,
|
||||
) error {
|
||||
_, configEntry, err := configEntryTxn(tx, nil, structs.ServiceIntentions, dest.Name, &dest.EnterpriseMeta)
|
||||
if err != nil {
|
||||
return fmt.Errorf("service-intentions config entry lookup failed: %v", err)
|
||||
}
|
||||
|
||||
var upsertEntry *structs.ServiceIntentionsConfigEntry
|
||||
if configEntry == nil {
|
||||
upsertEntry = &structs.ServiceIntentionsConfigEntry{
|
||||
Kind: structs.ServiceIntentions,
|
||||
Name: dest.Name,
|
||||
EnterpriseMeta: dest.EnterpriseMeta,
|
||||
Sources: []*structs.SourceIntention{value},
|
||||
}
|
||||
} else {
|
||||
prevEntry := configEntry.(*structs.ServiceIntentionsConfigEntry)
|
||||
|
||||
if err := checkLegacyIntentionApplyAllowed(prevEntry); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
upsertEntry = prevEntry.Clone()
|
||||
upsertEntry.Sources = append(upsertEntry.Sources, value)
|
||||
}
|
||||
|
||||
if err := upsertEntry.LegacyNormalize(); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := upsertEntry.LegacyValidate(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := ensureConfigEntryTxn(tx, idx, upsertEntry, upsertEntry.GetEnterpriseMeta()); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *Store) intentionMutationLegacyUpdate(
|
||||
tx WriteTxn,
|
||||
idx uint64,
|
||||
legacyID string,
|
||||
value *structs.SourceIntention,
|
||||
) error {
|
||||
// This variant is just for legacy UUID-based intentions.
|
||||
|
||||
_, prevEntry, ixn, err := s.IntentionGet(nil, legacyID)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Intention lookup failed: %v", err)
|
||||
}
|
||||
if ixn == nil || prevEntry == nil {
|
||||
return fmt.Errorf("Cannot modify non-existent intention: '%s'", legacyID)
|
||||
}
|
||||
|
||||
if err := checkLegacyIntentionApplyAllowed(prevEntry); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
upsertEntry := prevEntry.Clone()
|
||||
|
||||
foundMatch := upsertEntry.UpdateSourceByLegacyID(
|
||||
legacyID,
|
||||
value,
|
||||
)
|
||||
if !foundMatch {
|
||||
return fmt.Errorf("Cannot modify non-existent intention: '%s'", legacyID)
|
||||
}
|
||||
|
||||
if err := upsertEntry.LegacyNormalize(); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := upsertEntry.LegacyValidate(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := ensureConfigEntryTxn(tx, idx, upsertEntry, upsertEntry.GetEnterpriseMeta()); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *Store) intentionMutationDelete(
|
||||
tx WriteTxn,
|
||||
idx uint64,
|
||||
dest structs.ServiceName,
|
||||
src structs.ServiceName,
|
||||
) error {
|
||||
_, configEntry, err := configEntryTxn(tx, nil, structs.ServiceIntentions, dest.Name, &dest.EnterpriseMeta)
|
||||
if err != nil {
|
||||
return fmt.Errorf("service-intentions config entry lookup failed: %v", err)
|
||||
}
|
||||
if configEntry == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
prevEntry := configEntry.(*structs.ServiceIntentionsConfigEntry)
|
||||
upsertEntry := prevEntry.Clone()
|
||||
|
||||
deleted := upsertEntry.DeleteSourceByName(src)
|
||||
if !deleted {
|
||||
return nil
|
||||
}
|
||||
|
||||
if upsertEntry == nil || len(upsertEntry.Sources) == 0 {
|
||||
return deleteConfigEntryTxn(
|
||||
tx,
|
||||
idx,
|
||||
structs.ServiceIntentions,
|
||||
dest.Name,
|
||||
&dest.EnterpriseMeta,
|
||||
)
|
||||
}
|
||||
|
||||
if err := upsertEntry.Normalize(); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := upsertEntry.Validate(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := ensureConfigEntryTxn(tx, idx, upsertEntry, upsertEntry.GetEnterpriseMeta()); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *Store) intentionMutationLegacyDelete(
|
||||
tx WriteTxn,
|
||||
idx uint64,
|
||||
legacyID string,
|
||||
) error {
|
||||
_, prevEntry, ixn, err := s.IntentionGet(nil, legacyID)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Intention lookup failed: %v", err)
|
||||
}
|
||||
if ixn == nil || prevEntry == nil {
|
||||
return fmt.Errorf("Cannot delete non-existent intention: '%s'", legacyID)
|
||||
}
|
||||
|
||||
if err := checkLegacyIntentionApplyAllowed(prevEntry); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
upsertEntry := prevEntry.Clone()
|
||||
|
||||
deleted := upsertEntry.DeleteSourceByLegacyID(legacyID)
|
||||
if !deleted {
|
||||
return fmt.Errorf("Cannot delete non-existent intention: '%s'", legacyID)
|
||||
}
|
||||
|
||||
if upsertEntry == nil || len(upsertEntry.Sources) == 0 {
|
||||
return deleteConfigEntryTxn(
|
||||
tx,
|
||||
idx,
|
||||
structs.ServiceIntentions,
|
||||
prevEntry.Name,
|
||||
&prevEntry.EnterpriseMeta,
|
||||
)
|
||||
}
|
||||
|
||||
if err := upsertEntry.LegacyNormalize(); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := upsertEntry.LegacyValidate(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := ensureConfigEntryTxn(tx, idx, upsertEntry, upsertEntry.GetEnterpriseMeta()); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *Store) intentionMutationUpsert(
|
||||
tx WriteTxn,
|
||||
idx uint64,
|
||||
dest structs.ServiceName,
|
||||
src structs.ServiceName,
|
||||
value *structs.SourceIntention,
|
||||
) error {
|
||||
// This variant is just for config-entry based intentions.
|
||||
|
||||
_, configEntry, err := configEntryTxn(tx, nil, structs.ServiceIntentions, dest.Name, &dest.EnterpriseMeta)
|
||||
if err != nil {
|
||||
return fmt.Errorf("service-intentions config entry lookup failed: %v", err)
|
||||
}
|
||||
|
||||
var prevEntry *structs.ServiceIntentionsConfigEntry
|
||||
if configEntry != nil {
|
||||
prevEntry = configEntry.(*structs.ServiceIntentionsConfigEntry)
|
||||
}
|
||||
|
||||
var upsertEntry *structs.ServiceIntentionsConfigEntry
|
||||
|
||||
if prevEntry == nil {
|
||||
upsertEntry = &structs.ServiceIntentionsConfigEntry{
|
||||
Kind: structs.ServiceIntentions,
|
||||
Name: dest.Name,
|
||||
EnterpriseMeta: dest.EnterpriseMeta,
|
||||
Sources: []*structs.SourceIntention{value},
|
||||
}
|
||||
} else {
|
||||
upsertEntry = prevEntry.Clone()
|
||||
|
||||
upsertEntry.UpsertSourceByName(src, value)
|
||||
}
|
||||
|
||||
if err := upsertEntry.Normalize(); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := upsertEntry.Validate(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := ensureConfigEntryTxn(tx, idx, upsertEntry, upsertEntry.GetEnterpriseMeta()); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func checkLegacyIntentionApplyAllowed(prevEntry *structs.ServiceIntentionsConfigEntry) error {
|
||||
if prevEntry == nil {
|
||||
return nil
|
||||
}
|
||||
if prevEntry.LegacyIDFieldsAreAllSet() {
|
||||
return nil
|
||||
}
|
||||
|
||||
sn := prevEntry.DestinationServiceName()
|
||||
return fmt.Errorf("cannot use legacy intention API to edit intentions with a destination of %q after editing them via a service-intentions config entry", sn.String())
|
||||
}
|
||||
|
||||
// LegacyIntentionSet creates or updates an intention.
|
||||
//
|
||||
// Deprecated: Edit service-intentions config entries directly.
|
||||
|
|
|
@ -13,6 +13,14 @@ import (
|
|||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
var (
|
||||
testLocation = time.FixedZone("UTC-8", -8*60*60)
|
||||
|
||||
testTimeA = time.Date(1955, 11, 5, 6, 15, 0, 0, testLocation)
|
||||
testTimeB = time.Date(1985, 10, 26, 1, 35, 0, 0, testLocation)
|
||||
testTimeC = time.Date(2015, 10, 21, 16, 29, 0, 0, testLocation)
|
||||
)
|
||||
|
||||
func testBothIntentionFormats(t *testing.T, f func(t *testing.T, s *Store, legacy bool)) {
|
||||
t.Helper()
|
||||
|
||||
|
@ -72,6 +80,8 @@ func TestStore_IntentionSetGet_basic(t *testing.T) {
|
|||
DestinationNS: "default",
|
||||
DestinationName: "web",
|
||||
Meta: map[string]string{},
|
||||
CreatedAt: testTimeA,
|
||||
UpdatedAt: testTimeA,
|
||||
}
|
||||
|
||||
// Inserting a with empty ID is disallowed.
|
||||
|
@ -89,6 +99,8 @@ func TestStore_IntentionSetGet_basic(t *testing.T) {
|
|||
DestinationNS: "default",
|
||||
DestinationName: "web",
|
||||
Meta: map[string]string{},
|
||||
CreatedAt: testTimeA,
|
||||
UpdatedAt: testTimeA,
|
||||
RaftIndex: structs.RaftIndex{
|
||||
CreateIndex: lastIndex,
|
||||
ModifyIndex: lastIndex,
|
||||
|
@ -103,10 +115,12 @@ func TestStore_IntentionSetGet_basic(t *testing.T) {
|
|||
Name: "web",
|
||||
Sources: []*structs.SourceIntention{
|
||||
{
|
||||
LegacyID: srcID,
|
||||
Name: "*",
|
||||
Action: structs.IntentionActionAllow,
|
||||
LegacyMeta: map[string]string{},
|
||||
LegacyID: srcID,
|
||||
Name: "*",
|
||||
Action: structs.IntentionActionAllow,
|
||||
LegacyMeta: map[string]string{},
|
||||
LegacyCreateTime: &testTimeA,
|
||||
LegacyUpdateTime: &testTimeA,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
@ -258,6 +272,534 @@ func TestStore_LegacyIntentionDelete_failsAfterUpgrade(t *testing.T) {
|
|||
testutil.RequireErrorContains(t, err, ErrLegacyIntentionsAreDisabled.Error())
|
||||
}
|
||||
|
||||
func TestStore_IntentionMutation(t *testing.T) {
|
||||
testBothIntentionFormats(t, func(t *testing.T, s *Store, legacy bool) {
|
||||
if legacy {
|
||||
mut := &structs.IntentionMutation{}
|
||||
err := s.IntentionMutation(1, structs.IntentionOpCreate, mut)
|
||||
testutil.RequireErrorContains(t, err, "state: IntentionMutation() is not allowed when intentions are not stored in config entries")
|
||||
} else {
|
||||
testStore_IntentionMutation(t, s)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func testStore_IntentionMutation(t *testing.T, s *Store) {
|
||||
lastIndex := uint64(1)
|
||||
|
||||
defaultEntMeta := structs.DefaultEnterpriseMeta()
|
||||
|
||||
var (
|
||||
id1 = testUUID()
|
||||
id2 = testUUID()
|
||||
id3 = testUUID()
|
||||
)
|
||||
|
||||
eqEntry := func(t *testing.T, expect, got *structs.ServiceIntentionsConfigEntry) {
|
||||
t.Helper()
|
||||
|
||||
// Zero out some fields for comparison.
|
||||
got = got.Clone()
|
||||
got.RaftIndex = structs.RaftIndex{}
|
||||
for _, src := range got.Sources {
|
||||
src.LegacyCreateTime = nil
|
||||
src.LegacyUpdateTime = nil
|
||||
if len(src.LegacyMeta) == 0 {
|
||||
src.LegacyMeta = nil
|
||||
}
|
||||
}
|
||||
require.Equal(t, expect, got)
|
||||
}
|
||||
|
||||
// Try to create an intention without an ID to prove that LegacyValidate is being called.
|
||||
testutil.RequireErrorContains(t, s.IntentionMutation(lastIndex, structs.IntentionOpCreate, &structs.IntentionMutation{
|
||||
Destination: structs.NewServiceName("api", defaultEntMeta),
|
||||
Value: &structs.SourceIntention{
|
||||
Name: "web",
|
||||
EnterpriseMeta: *defaultEntMeta,
|
||||
Action: structs.IntentionActionAllow,
|
||||
LegacyCreateTime: &testTimeA,
|
||||
LegacyUpdateTime: &testTimeA,
|
||||
},
|
||||
}), `Sources[0].LegacyID must be set`)
|
||||
|
||||
// Create intention and create config entry
|
||||
{
|
||||
require.NoError(t, s.IntentionMutation(lastIndex, structs.IntentionOpCreate, &structs.IntentionMutation{
|
||||
Destination: structs.NewServiceName("api", defaultEntMeta),
|
||||
Value: &structs.SourceIntention{
|
||||
Name: "web",
|
||||
EnterpriseMeta: *defaultEntMeta,
|
||||
Action: structs.IntentionActionAllow,
|
||||
LegacyID: id1,
|
||||
LegacyCreateTime: &testTimeA,
|
||||
LegacyUpdateTime: &testTimeA,
|
||||
},
|
||||
}))
|
||||
|
||||
// Ensure it's there now.
|
||||
idx, entry, ixn, err := s.IntentionGet(nil, id1)
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, entry)
|
||||
require.NotNil(t, ixn)
|
||||
require.Equal(t, lastIndex, idx)
|
||||
|
||||
eqEntry(t, &structs.ServiceIntentionsConfigEntry{
|
||||
Kind: structs.ServiceIntentions,
|
||||
Name: "api",
|
||||
EnterpriseMeta: *defaultEntMeta,
|
||||
Sources: []*structs.SourceIntention{
|
||||
{
|
||||
LegacyID: id1,
|
||||
Name: "web",
|
||||
EnterpriseMeta: *defaultEntMeta,
|
||||
Action: structs.IntentionActionAllow,
|
||||
Precedence: 9,
|
||||
Type: structs.IntentionSourceConsul,
|
||||
},
|
||||
},
|
||||
}, entry)
|
||||
|
||||
// only one
|
||||
_, entries, err := s.ConfigEntries(nil, nil)
|
||||
require.NoError(t, err)
|
||||
require.Len(t, entries, 1)
|
||||
|
||||
lastIndex++
|
||||
}
|
||||
|
||||
// Try to create a duplicate intention.
|
||||
{
|
||||
testutil.RequireErrorContains(t, s.IntentionMutation(lastIndex, structs.IntentionOpCreate, &structs.IntentionMutation{
|
||||
Destination: structs.NewServiceName("api", defaultEntMeta),
|
||||
Value: &structs.SourceIntention{
|
||||
Name: "web",
|
||||
EnterpriseMeta: *defaultEntMeta,
|
||||
Action: structs.IntentionActionDeny,
|
||||
LegacyID: id2,
|
||||
LegacyCreateTime: &testTimeB,
|
||||
LegacyUpdateTime: &testTimeB,
|
||||
},
|
||||
}), `more than once`)
|
||||
}
|
||||
|
||||
// Create intention with existing config entry
|
||||
{
|
||||
require.NoError(t, s.IntentionMutation(lastIndex, structs.IntentionOpCreate, &structs.IntentionMutation{
|
||||
Destination: structs.NewServiceName("api", defaultEntMeta),
|
||||
Value: &structs.SourceIntention{
|
||||
Name: "debug",
|
||||
EnterpriseMeta: *defaultEntMeta,
|
||||
Action: structs.IntentionActionDeny,
|
||||
LegacyID: id2,
|
||||
LegacyCreateTime: &testTimeB,
|
||||
LegacyUpdateTime: &testTimeB,
|
||||
},
|
||||
}))
|
||||
|
||||
// Ensure it's there now.
|
||||
idx, entry, ixn, err := s.IntentionGet(nil, id2)
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, entry)
|
||||
require.NotNil(t, ixn)
|
||||
require.Equal(t, lastIndex, idx)
|
||||
|
||||
eqEntry(t, &structs.ServiceIntentionsConfigEntry{
|
||||
Kind: structs.ServiceIntentions,
|
||||
Name: "api",
|
||||
EnterpriseMeta: *defaultEntMeta,
|
||||
Sources: []*structs.SourceIntention{
|
||||
{
|
||||
Name: "web",
|
||||
EnterpriseMeta: *defaultEntMeta,
|
||||
Action: structs.IntentionActionAllow,
|
||||
LegacyID: id1,
|
||||
Precedence: 9,
|
||||
Type: structs.IntentionSourceConsul,
|
||||
},
|
||||
{
|
||||
Name: "debug",
|
||||
EnterpriseMeta: *defaultEntMeta,
|
||||
Action: structs.IntentionActionDeny,
|
||||
LegacyID: id2,
|
||||
Precedence: 9,
|
||||
Type: structs.IntentionSourceConsul,
|
||||
},
|
||||
},
|
||||
}, entry)
|
||||
|
||||
// only one
|
||||
_, entries, err := s.ConfigEntries(nil, nil)
|
||||
require.NoError(t, err)
|
||||
require.Len(t, entries, 1)
|
||||
|
||||
lastIndex++
|
||||
}
|
||||
|
||||
// Try to update an intention without specifying an ID
|
||||
testutil.RequireErrorContains(t, s.IntentionMutation(lastIndex, structs.IntentionOpUpdate, &structs.IntentionMutation{
|
||||
ID: "",
|
||||
Destination: structs.NewServiceName("api", defaultEntMeta),
|
||||
Value: &structs.SourceIntention{
|
||||
Name: "web",
|
||||
EnterpriseMeta: *defaultEntMeta,
|
||||
Action: structs.IntentionActionAllow,
|
||||
},
|
||||
}), `failed config entry lookup: index error: UUID must be 36 characters`)
|
||||
|
||||
// Try to update a non-existent intention
|
||||
testutil.RequireErrorContains(t, s.IntentionMutation(lastIndex, structs.IntentionOpUpdate, &structs.IntentionMutation{
|
||||
ID: id3,
|
||||
Destination: structs.NewServiceName("api", defaultEntMeta),
|
||||
Value: &structs.SourceIntention{
|
||||
Name: "web",
|
||||
EnterpriseMeta: *defaultEntMeta,
|
||||
Action: structs.IntentionActionAllow,
|
||||
},
|
||||
}), `Cannot modify non-existent intention`)
|
||||
|
||||
// Update an existing intention by ID
|
||||
{
|
||||
require.NoError(t, s.IntentionMutation(lastIndex, structs.IntentionOpUpdate, &structs.IntentionMutation{
|
||||
ID: id2,
|
||||
Destination: structs.NewServiceName("api", defaultEntMeta),
|
||||
Value: &structs.SourceIntention{
|
||||
Name: "debug",
|
||||
EnterpriseMeta: *defaultEntMeta,
|
||||
Action: structs.IntentionActionDeny,
|
||||
LegacyID: id2,
|
||||
LegacyCreateTime: &testTimeB,
|
||||
LegacyUpdateTime: &testTimeC,
|
||||
Description: "op update",
|
||||
},
|
||||
}))
|
||||
|
||||
// Ensure it's there now.
|
||||
idx, entry, ixn, err := s.IntentionGet(nil, id2)
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, entry)
|
||||
require.NotNil(t, ixn)
|
||||
require.Equal(t, lastIndex, idx)
|
||||
|
||||
eqEntry(t, &structs.ServiceIntentionsConfigEntry{
|
||||
Kind: structs.ServiceIntentions,
|
||||
Name: "api",
|
||||
EnterpriseMeta: *defaultEntMeta,
|
||||
Sources: []*structs.SourceIntention{
|
||||
{
|
||||
Name: "web",
|
||||
EnterpriseMeta: *defaultEntMeta,
|
||||
Action: structs.IntentionActionAllow,
|
||||
LegacyID: id1,
|
||||
Precedence: 9,
|
||||
Type: structs.IntentionSourceConsul,
|
||||
},
|
||||
{
|
||||
Name: "debug",
|
||||
EnterpriseMeta: *defaultEntMeta,
|
||||
Action: structs.IntentionActionDeny,
|
||||
LegacyID: id2,
|
||||
Precedence: 9,
|
||||
Type: structs.IntentionSourceConsul,
|
||||
Description: "op update",
|
||||
},
|
||||
},
|
||||
}, entry)
|
||||
|
||||
// only one
|
||||
_, entries, err := s.ConfigEntries(nil, nil)
|
||||
require.NoError(t, err)
|
||||
require.Len(t, entries, 1)
|
||||
|
||||
lastIndex++
|
||||
}
|
||||
|
||||
// Try to delete a non-existent intention
|
||||
testutil.RequireErrorContains(t, s.IntentionMutation(lastIndex, structs.IntentionOpDelete, &structs.IntentionMutation{
|
||||
ID: id3,
|
||||
}), `Cannot delete non-existent intention`)
|
||||
|
||||
// delete by id
|
||||
{
|
||||
require.NoError(t, s.IntentionMutation(lastIndex, structs.IntentionOpDelete, &structs.IntentionMutation{
|
||||
ID: id1,
|
||||
}))
|
||||
|
||||
// only one
|
||||
_, entries, err := s.ConfigEntries(nil, nil)
|
||||
require.NoError(t, err)
|
||||
require.Len(t, entries, 1)
|
||||
|
||||
eqEntry(t, &structs.ServiceIntentionsConfigEntry{
|
||||
Kind: structs.ServiceIntentions,
|
||||
Name: "api",
|
||||
EnterpriseMeta: *defaultEntMeta,
|
||||
Sources: []*structs.SourceIntention{
|
||||
{
|
||||
Name: "debug",
|
||||
EnterpriseMeta: *defaultEntMeta,
|
||||
Action: structs.IntentionActionDeny,
|
||||
LegacyID: id2,
|
||||
Precedence: 9,
|
||||
Type: structs.IntentionSourceConsul,
|
||||
Description: "op update",
|
||||
},
|
||||
},
|
||||
}, entries[0].(*structs.ServiceIntentionsConfigEntry))
|
||||
|
||||
lastIndex++
|
||||
}
|
||||
|
||||
// delete last one by id
|
||||
{
|
||||
require.NoError(t, s.IntentionMutation(lastIndex, structs.IntentionOpDelete, &structs.IntentionMutation{
|
||||
ID: id2,
|
||||
}))
|
||||
|
||||
// none one
|
||||
_, entries, err := s.ConfigEntries(nil, nil)
|
||||
require.NoError(t, err)
|
||||
require.Empty(t, entries)
|
||||
|
||||
lastIndex++
|
||||
}
|
||||
|
||||
// upsert intention for first time
|
||||
{
|
||||
require.NoError(t, s.IntentionMutation(lastIndex, structs.IntentionOpUpsert, &structs.IntentionMutation{
|
||||
Destination: structs.NewServiceName("api", defaultEntMeta),
|
||||
Value: &structs.SourceIntention{
|
||||
Name: "web",
|
||||
EnterpriseMeta: *defaultEntMeta,
|
||||
Action: structs.IntentionActionAllow,
|
||||
},
|
||||
}))
|
||||
|
||||
// Ensure it's there now.
|
||||
idx, entry, ixn, err := s.IntentionGetExact(nil, &structs.IntentionQueryExact{
|
||||
SourceNS: "default",
|
||||
SourceName: "web",
|
||||
DestinationNS: "default",
|
||||
DestinationName: "api",
|
||||
})
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, entry)
|
||||
require.NotNil(t, ixn)
|
||||
require.Equal(t, lastIndex, idx)
|
||||
|
||||
eqEntry(t, &structs.ServiceIntentionsConfigEntry{
|
||||
Kind: structs.ServiceIntentions,
|
||||
Name: "api",
|
||||
EnterpriseMeta: *defaultEntMeta,
|
||||
Sources: []*structs.SourceIntention{
|
||||
{
|
||||
Name: "web",
|
||||
EnterpriseMeta: *defaultEntMeta,
|
||||
Action: structs.IntentionActionAllow,
|
||||
Precedence: 9,
|
||||
Type: structs.IntentionSourceConsul,
|
||||
},
|
||||
},
|
||||
}, entry)
|
||||
|
||||
// only one
|
||||
_, entries, err := s.ConfigEntries(nil, nil)
|
||||
require.NoError(t, err)
|
||||
require.Len(t, entries, 1)
|
||||
|
||||
lastIndex++
|
||||
}
|
||||
|
||||
// upsert over itself (REPLACE)
|
||||
{
|
||||
require.NoError(t, s.IntentionMutation(lastIndex, structs.IntentionOpUpsert, &structs.IntentionMutation{
|
||||
Destination: structs.NewServiceName("api", defaultEntMeta),
|
||||
Source: structs.NewServiceName("web", defaultEntMeta),
|
||||
Value: &structs.SourceIntention{
|
||||
Name: "web",
|
||||
EnterpriseMeta: *defaultEntMeta,
|
||||
Action: structs.IntentionActionAllow,
|
||||
Description: "upserted over",
|
||||
},
|
||||
}))
|
||||
|
||||
// Ensure it's there now.
|
||||
idx, entry, ixn, err := s.IntentionGetExact(nil, &structs.IntentionQueryExact{
|
||||
SourceNS: "default",
|
||||
SourceName: "web",
|
||||
DestinationNS: "default",
|
||||
DestinationName: "api",
|
||||
})
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, entry)
|
||||
require.NotNil(t, ixn)
|
||||
require.Equal(t, lastIndex, idx)
|
||||
require.Equal(t, "upserted over", ixn.Description)
|
||||
|
||||
eqEntry(t, &structs.ServiceIntentionsConfigEntry{
|
||||
Kind: structs.ServiceIntentions,
|
||||
Name: "api",
|
||||
EnterpriseMeta: *defaultEntMeta,
|
||||
Sources: []*structs.SourceIntention{
|
||||
{
|
||||
Name: "web",
|
||||
EnterpriseMeta: *defaultEntMeta,
|
||||
Action: structs.IntentionActionAllow,
|
||||
Precedence: 9,
|
||||
Type: structs.IntentionSourceConsul,
|
||||
Description: "upserted over",
|
||||
},
|
||||
},
|
||||
}, entry)
|
||||
|
||||
// only one
|
||||
_, entries, err := s.ConfigEntries(nil, nil)
|
||||
require.NoError(t, err)
|
||||
require.Len(t, entries, 1)
|
||||
|
||||
lastIndex++
|
||||
}
|
||||
|
||||
// upsert into existing config entry (APPEND)
|
||||
{
|
||||
require.NoError(t, s.IntentionMutation(lastIndex, structs.IntentionOpUpsert, &structs.IntentionMutation{
|
||||
Destination: structs.NewServiceName("api", defaultEntMeta),
|
||||
Source: structs.NewServiceName("debug", defaultEntMeta),
|
||||
Value: &structs.SourceIntention{
|
||||
Name: "debug",
|
||||
EnterpriseMeta: *defaultEntMeta,
|
||||
Action: structs.IntentionActionDeny,
|
||||
},
|
||||
}))
|
||||
|
||||
// Ensure it's there now.
|
||||
idx, entry, ixn, err := s.IntentionGetExact(nil, &structs.IntentionQueryExact{
|
||||
SourceNS: "default",
|
||||
SourceName: "debug",
|
||||
DestinationNS: "default",
|
||||
DestinationName: "api",
|
||||
})
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, entry)
|
||||
require.NotNil(t, ixn)
|
||||
require.Equal(t, lastIndex, idx)
|
||||
|
||||
eqEntry(t, &structs.ServiceIntentionsConfigEntry{
|
||||
Kind: structs.ServiceIntentions,
|
||||
Name: "api",
|
||||
EnterpriseMeta: *defaultEntMeta,
|
||||
Sources: []*structs.SourceIntention{
|
||||
{
|
||||
Name: "web",
|
||||
EnterpriseMeta: *defaultEntMeta,
|
||||
Action: structs.IntentionActionAllow,
|
||||
Precedence: 9,
|
||||
Type: structs.IntentionSourceConsul,
|
||||
Description: "upserted over",
|
||||
},
|
||||
{
|
||||
Name: "debug",
|
||||
EnterpriseMeta: *defaultEntMeta,
|
||||
Action: structs.IntentionActionDeny,
|
||||
Precedence: 9,
|
||||
Type: structs.IntentionSourceConsul,
|
||||
},
|
||||
},
|
||||
}, entry)
|
||||
|
||||
// only one
|
||||
_, entries, err := s.ConfigEntries(nil, nil)
|
||||
require.NoError(t, err)
|
||||
require.Len(t, entries, 1)
|
||||
|
||||
lastIndex++
|
||||
}
|
||||
|
||||
// Try to delete a non-existent intention by name
|
||||
require.NoError(t, s.IntentionMutation(lastIndex, structs.IntentionOpDelete, &structs.IntentionMutation{
|
||||
Destination: structs.NewServiceName("api", defaultEntMeta),
|
||||
Source: structs.NewServiceName("blurb", defaultEntMeta),
|
||||
}))
|
||||
|
||||
// delete by name
|
||||
{
|
||||
require.NoError(t, s.IntentionMutation(lastIndex, structs.IntentionOpDelete, &structs.IntentionMutation{
|
||||
Destination: structs.NewServiceName("api", defaultEntMeta),
|
||||
Source: structs.NewServiceName("web", defaultEntMeta),
|
||||
}))
|
||||
|
||||
// only one
|
||||
_, entries, err := s.ConfigEntries(nil, nil)
|
||||
require.NoError(t, err)
|
||||
require.Len(t, entries, 1)
|
||||
|
||||
eqEntry(t, &structs.ServiceIntentionsConfigEntry{
|
||||
Kind: structs.ServiceIntentions,
|
||||
Name: "api",
|
||||
EnterpriseMeta: *defaultEntMeta,
|
||||
Sources: []*structs.SourceIntention{
|
||||
{
|
||||
Name: "debug",
|
||||
EnterpriseMeta: *defaultEntMeta,
|
||||
Action: structs.IntentionActionDeny,
|
||||
Precedence: 9,
|
||||
Type: structs.IntentionSourceConsul,
|
||||
},
|
||||
},
|
||||
}, entries[0].(*structs.ServiceIntentionsConfigEntry))
|
||||
|
||||
lastIndex++
|
||||
}
|
||||
|
||||
// delete last one by name
|
||||
{
|
||||
require.NoError(t, s.IntentionMutation(lastIndex, structs.IntentionOpDelete, &structs.IntentionMutation{
|
||||
Destination: structs.NewServiceName("api", defaultEntMeta),
|
||||
Source: structs.NewServiceName("debug", defaultEntMeta),
|
||||
}))
|
||||
|
||||
// none one
|
||||
_, entries, err := s.ConfigEntries(nil, nil)
|
||||
require.NoError(t, err)
|
||||
require.Empty(t, entries)
|
||||
|
||||
lastIndex++
|
||||
}
|
||||
|
||||
// Try to update an intention with an ID on a non-legacy config entry.
|
||||
{
|
||||
idFake := testUUID()
|
||||
|
||||
require.NoError(t, s.EnsureConfigEntry(lastIndex, &structs.ServiceIntentionsConfigEntry{
|
||||
Kind: structs.ServiceIntentions,
|
||||
Name: "new",
|
||||
EnterpriseMeta: *defaultEntMeta,
|
||||
Sources: []*structs.SourceIntention{
|
||||
{
|
||||
Name: "web",
|
||||
EnterpriseMeta: *defaultEntMeta,
|
||||
Action: structs.IntentionActionAllow,
|
||||
Precedence: 9,
|
||||
Type: structs.IntentionSourceConsul,
|
||||
},
|
||||
},
|
||||
}, nil))
|
||||
|
||||
lastIndex++
|
||||
|
||||
// ...via create
|
||||
testutil.RequireErrorContains(t, s.IntentionMutation(lastIndex, structs.IntentionOpCreate, &structs.IntentionMutation{
|
||||
Destination: structs.NewServiceName("new", defaultEntMeta),
|
||||
Value: &structs.SourceIntention{
|
||||
Name: "old",
|
||||
EnterpriseMeta: *defaultEntMeta,
|
||||
Action: structs.IntentionActionAllow,
|
||||
LegacyID: idFake,
|
||||
},
|
||||
}), `cannot use legacy intention API to edit intentions with a destination`)
|
||||
}
|
||||
}
|
||||
|
||||
func TestStore_LegacyIntentionSet_emptyId(t *testing.T) {
|
||||
// note: irrelevant test for config entries variant
|
||||
s := testStateStore(t)
|
||||
|
@ -282,24 +824,27 @@ func TestStore_IntentionSet_updateCreatedAt(t *testing.T) {
|
|||
testBothIntentionFormats(t, func(t *testing.T, s *Store, legacy bool) {
|
||||
// Build a valid intention
|
||||
var (
|
||||
id = testUUID()
|
||||
createTime time.Time
|
||||
id = testUUID()
|
||||
)
|
||||
|
||||
if legacy {
|
||||
ixn := structs.Intention{
|
||||
ID: id,
|
||||
CreatedAt: time.Now().UTC(),
|
||||
ID: id,
|
||||
SourceNS: "default",
|
||||
SourceName: "*",
|
||||
DestinationNS: "default",
|
||||
DestinationName: "web",
|
||||
Action: structs.IntentionActionAllow,
|
||||
CreatedAt: testTimeA,
|
||||
UpdatedAt: testTimeA,
|
||||
}
|
||||
|
||||
// Insert
|
||||
require.NoError(t, s.LegacyIntentionSet(1, &ixn))
|
||||
|
||||
createTime = ixn.CreatedAt
|
||||
|
||||
// Change a value and test updating
|
||||
ixnUpdate := ixn
|
||||
ixnUpdate.CreatedAt = createTime.Add(10 * time.Second)
|
||||
ixnUpdate.CreatedAt = testTimeB
|
||||
require.NoError(t, s.LegacyIntentionSet(2, &ixnUpdate))
|
||||
|
||||
id = ixn.ID
|
||||
|
@ -310,10 +855,12 @@ func TestStore_IntentionSet_updateCreatedAt(t *testing.T) {
|
|||
Name: "web",
|
||||
Sources: []*structs.SourceIntention{
|
||||
{
|
||||
LegacyID: id,
|
||||
Name: "*",
|
||||
Action: structs.IntentionActionAllow,
|
||||
LegacyMeta: map[string]string{},
|
||||
LegacyID: id,
|
||||
Name: "*",
|
||||
Action: structs.IntentionActionAllow,
|
||||
LegacyMeta: map[string]string{},
|
||||
LegacyCreateTime: &testTimeA,
|
||||
LegacyUpdateTime: &testTimeA,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
@ -321,15 +868,13 @@ func TestStore_IntentionSet_updateCreatedAt(t *testing.T) {
|
|||
require.NoError(t, conf.LegacyNormalize())
|
||||
require.NoError(t, conf.LegacyValidate())
|
||||
require.NoError(t, s.EnsureConfigEntry(1, conf.Clone(), nil))
|
||||
|
||||
createTime = *conf.Sources[0].LegacyCreateTime
|
||||
}
|
||||
|
||||
// Read it back and verify
|
||||
_, _, actual, err := s.IntentionGet(nil, id)
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, actual)
|
||||
require.Equal(t, createTime, actual.CreatedAt)
|
||||
require.Equal(t, testTimeA, actual.CreatedAt)
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -339,7 +884,14 @@ func TestStore_IntentionSet_metaNil(t *testing.T) {
|
|||
if legacy {
|
||||
// Build a valid intention
|
||||
ixn := &structs.Intention{
|
||||
ID: id,
|
||||
ID: id,
|
||||
SourceNS: "default",
|
||||
SourceName: "*",
|
||||
DestinationNS: "default",
|
||||
DestinationName: "web",
|
||||
Action: structs.IntentionActionAllow,
|
||||
CreatedAt: testTimeA,
|
||||
UpdatedAt: testTimeA,
|
||||
}
|
||||
|
||||
// Insert
|
||||
|
@ -351,9 +903,11 @@ func TestStore_IntentionSet_metaNil(t *testing.T) {
|
|||
Name: "web",
|
||||
Sources: []*structs.SourceIntention{
|
||||
{
|
||||
LegacyID: id,
|
||||
Name: "*",
|
||||
Action: structs.IntentionActionAllow,
|
||||
LegacyID: id,
|
||||
Name: "*",
|
||||
Action: structs.IntentionActionAllow,
|
||||
LegacyCreateTime: &testTimeA,
|
||||
LegacyUpdateTime: &testTimeA,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
@ -380,8 +934,15 @@ func TestStore_IntentionSet_metaSet(t *testing.T) {
|
|||
if legacy {
|
||||
// Build a valid intention
|
||||
ixn := structs.Intention{
|
||||
ID: id,
|
||||
Meta: expectMeta,
|
||||
ID: id,
|
||||
SourceNS: "default",
|
||||
SourceName: "*",
|
||||
DestinationNS: "default",
|
||||
DestinationName: "web",
|
||||
Action: structs.IntentionActionAllow,
|
||||
CreatedAt: testTimeA,
|
||||
UpdatedAt: testTimeA,
|
||||
Meta: expectMeta,
|
||||
}
|
||||
|
||||
// Insert
|
||||
|
@ -394,10 +955,12 @@ func TestStore_IntentionSet_metaSet(t *testing.T) {
|
|||
Name: "web",
|
||||
Sources: []*structs.SourceIntention{
|
||||
{
|
||||
LegacyID: id,
|
||||
Name: "*",
|
||||
Action: structs.IntentionActionAllow,
|
||||
LegacyMeta: expectMeta,
|
||||
LegacyID: id,
|
||||
Name: "*",
|
||||
Action: structs.IntentionActionAllow,
|
||||
LegacyCreateTime: &testTimeA,
|
||||
LegacyUpdateTime: &testTimeA,
|
||||
LegacyMeta: expectMeta,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
@ -428,7 +991,14 @@ func TestStore_IntentionDelete(t *testing.T) {
|
|||
// Create
|
||||
if legacy {
|
||||
ixn := &structs.Intention{
|
||||
ID: id,
|
||||
ID: id,
|
||||
SourceNS: "default",
|
||||
SourceName: "*",
|
||||
DestinationNS: "default",
|
||||
DestinationName: "web",
|
||||
Action: structs.IntentionActionAllow,
|
||||
CreatedAt: testTimeA,
|
||||
UpdatedAt: testTimeA,
|
||||
}
|
||||
lastIndex++
|
||||
require.NoError(t, s.LegacyIntentionSet(lastIndex, ixn))
|
||||
|
@ -442,9 +1012,11 @@ func TestStore_IntentionDelete(t *testing.T) {
|
|||
Name: "web",
|
||||
Sources: []*structs.SourceIntention{
|
||||
{
|
||||
LegacyID: id,
|
||||
Name: "*",
|
||||
Action: structs.IntentionActionAllow,
|
||||
LegacyID: id,
|
||||
Name: "*",
|
||||
Action: structs.IntentionActionAllow,
|
||||
LegacyCreateTime: &testTimeA,
|
||||
LegacyUpdateTime: &testTimeA,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
@ -519,6 +1091,8 @@ func TestStore_IntentionsList(t *testing.T) {
|
|||
SourceType: structs.IntentionSourceConsul,
|
||||
Action: structs.IntentionActionAllow,
|
||||
Meta: map[string]string{},
|
||||
CreatedAt: testTimeA,
|
||||
UpdatedAt: testTimeA,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -530,22 +1104,17 @@ func TestStore_IntentionsList(t *testing.T) {
|
|||
id := testUUID()
|
||||
for _, src := range srcs {
|
||||
conf.Sources = append(conf.Sources, &structs.SourceIntention{
|
||||
LegacyID: id,
|
||||
Name: src,
|
||||
Action: structs.IntentionActionAllow,
|
||||
LegacyID: id,
|
||||
Name: src,
|
||||
Action: structs.IntentionActionAllow,
|
||||
LegacyCreateTime: &testTimeA,
|
||||
LegacyUpdateTime: &testTimeA,
|
||||
})
|
||||
}
|
||||
return conf
|
||||
}
|
||||
|
||||
cmpIntention := func(ixn *structs.Intention, id string) *structs.Intention {
|
||||
ixn.ID = id
|
||||
//nolint:staticcheck
|
||||
ixn.UpdatePrecedence()
|
||||
return ixn
|
||||
}
|
||||
|
||||
clearIrrelevantFields := func(ixns []*structs.Intention) {
|
||||
clearIrrelevantFields := func(ixns ...*structs.Intention) {
|
||||
// Clear fields irrelevant for comparison.
|
||||
for _, ixn := range ixns {
|
||||
ixn.Hash = nil
|
||||
|
@ -556,6 +1125,15 @@ func TestStore_IntentionsList(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
cmpIntention := func(ixn *structs.Intention, id string) *structs.Intention {
|
||||
ixn2 := ixn.Clone()
|
||||
ixn2.ID = id
|
||||
clearIrrelevantFields(ixn2)
|
||||
//nolint:staticcheck
|
||||
ixn2.UpdatePrecedence()
|
||||
return ixn2
|
||||
}
|
||||
|
||||
var (
|
||||
expectIDs []string
|
||||
)
|
||||
|
@ -610,7 +1188,7 @@ func TestStore_IntentionsList(t *testing.T) {
|
|||
require.Equal(t, !legacy, fromConfig)
|
||||
require.Equal(t, lastIndex, idx)
|
||||
|
||||
clearIrrelevantFields(actual)
|
||||
clearIrrelevantFields(actual...)
|
||||
require.Equal(t, expected, actual)
|
||||
})
|
||||
}
|
||||
|
|
|
@ -69,7 +69,7 @@ func (s *Snapshot) Tombstones() (memdb.ResultIterator, error) {
|
|||
|
||||
// KVS is used when restoring from a snapshot. Use KVSSet for general inserts.
|
||||
func (s *Restore) KVS(entry *structs.DirEntry) error {
|
||||
if err := insertKVTxn(s.tx, entry, true); err != nil {
|
||||
if err := insertKVTxn(s.tx, entry, true, true); err != nil {
|
||||
return fmt.Errorf("failed inserting kvs entry: %s", err)
|
||||
}
|
||||
|
||||
|
@ -153,7 +153,7 @@ func kvsSetTxn(tx WriteTxn, idx uint64, entry *structs.DirEntry, updateSession b
|
|||
entry.ModifyIndex = idx
|
||||
|
||||
// Store the kv pair in the state store and update the index.
|
||||
if err := insertKVTxn(tx, entry, false); err != nil {
|
||||
if err := insertKVTxn(tx, entry, false, false); err != nil {
|
||||
return fmt.Errorf("failed inserting kvs entry: %s", err)
|
||||
}
|
||||
|
||||
|
|
|
@ -16,7 +16,7 @@ func kvsIndexer() *memdb.StringFieldIndex {
|
|||
}
|
||||
}
|
||||
|
||||
func insertKVTxn(tx WriteTxn, entry *structs.DirEntry, updateMax bool) error {
|
||||
func insertKVTxn(tx WriteTxn, entry *structs.DirEntry, updateMax bool, _ bool) error {
|
||||
if err := tx.Insert("kvs", entry); err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
|
@ -146,7 +146,7 @@ func (s *Snapshot) Sessions() (memdb.ResultIterator, error) {
|
|||
// Session is used when restoring from a snapshot. For general inserts, use
|
||||
// SessionCreate.
|
||||
func (s *Restore) Session(sess *structs.Session) error {
|
||||
if err := insertSessionTxn(s.tx, sess, sess.ModifyIndex, true); err != nil {
|
||||
if err := insertSessionTxn(s.tx, sess, sess.ModifyIndex, true, true); err != nil {
|
||||
return fmt.Errorf("failed inserting session: %s", err)
|
||||
}
|
||||
|
||||
|
@ -213,7 +213,7 @@ func sessionCreateTxn(tx *txn, idx uint64, sess *structs.Session) error {
|
|||
}
|
||||
|
||||
// Insert the session
|
||||
if err := insertSessionTxn(tx, sess, idx, false); err != nil {
|
||||
if err := insertSessionTxn(tx, sess, idx, false, false); err != nil {
|
||||
return fmt.Errorf("failed inserting session: %s", err)
|
||||
}
|
||||
|
||||
|
|
|
@ -48,7 +48,7 @@ func sessionDeleteWithSession(tx WriteTxn, session *structs.Session, idx uint64)
|
|||
return nil
|
||||
}
|
||||
|
||||
func insertSessionTxn(tx *txn, session *structs.Session, idx uint64, updateMax bool) error {
|
||||
func insertSessionTxn(tx *txn, session *structs.Session, idx uint64, updateMax bool, _ bool) error {
|
||||
if err := tx.Insert("sessions", session); err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
|
@ -61,7 +61,7 @@ func (c *ClientConnPool) ClientConn(datacenter string) (*grpc.ClientConn, error)
|
|||
grpc.WithInsecure(),
|
||||
grpc.WithContextDialer(c.dialer),
|
||||
grpc.WithDisableRetry(),
|
||||
grpc.WithStatsHandler(newStatsHandler(defaultMetrics)),
|
||||
grpc.WithStatsHandler(newStatsHandler(defaultMetrics())),
|
||||
// nolint:staticcheck // there is no other supported alternative to WithBalancerName
|
||||
grpc.WithBalancerName("pick_first"))
|
||||
if err != nil {
|
||||
|
|
|
@ -14,11 +14,12 @@ import (
|
|||
// The register function will be called with the grpc.Server to register
|
||||
// gRPC services with the server.
|
||||
func NewHandler(addr net.Addr, register func(server *grpc.Server)) *Handler {
|
||||
metrics := defaultMetrics()
|
||||
// We don't need to pass tls.Config to the server since it's multiplexed
|
||||
// behind the RPC listener, which already has TLS configured.
|
||||
srv := grpc.NewServer(
|
||||
grpc.StatsHandler(newStatsHandler(defaultMetrics)),
|
||||
grpc.StreamInterceptor((&activeStreamCounter{metrics: defaultMetrics}).Intercept),
|
||||
grpc.StatsHandler(newStatsHandler(metrics)),
|
||||
grpc.StreamInterceptor((&activeStreamCounter{metrics: metrics}).Intercept),
|
||||
)
|
||||
register(srv)
|
||||
|
||||
|
|
|
@ -10,7 +10,6 @@ import (
|
|||
"google.golang.org/grpc/stats"
|
||||
)
|
||||
|
||||
var defaultMetrics = metrics.Default()
|
||||
var StatsGauges = []prometheus.GaugeDefinition{
|
||||
{
|
||||
Name: []string{"grpc", "server", "connections"},
|
||||
|
@ -48,6 +47,8 @@ var StatsCounters = []prometheus.CounterDefinition{
|
|||
},
|
||||
}
|
||||
|
||||
var defaultMetrics = metrics.Default
|
||||
|
||||
// statsHandler is a grpc/stats.StatsHandler which emits connection and
|
||||
// request metrics to go-metrics.
|
||||
type statsHandler struct {
|
||||
|
|
|
@ -113,11 +113,14 @@ func patchGlobalMetrics(t *testing.T) (*fakeMetricsSink, func()) {
|
|||
FilterDefault: true,
|
||||
}
|
||||
var err error
|
||||
defaultMetrics, err = metrics.New(cfg, sink)
|
||||
defaultMetrics = func() *metrics.Metrics {
|
||||
m, _ := metrics.New(cfg, sink)
|
||||
return m
|
||||
}
|
||||
require.NoError(t, err)
|
||||
reset := func() {
|
||||
t.Helper()
|
||||
defaultMetrics, err = metrics.New(cfg, &metrics.BlackholeSink{})
|
||||
defaultMetrics = metrics.Default
|
||||
require.NoError(t, err, "failed to reset global metrics")
|
||||
}
|
||||
return sink, reset
|
||||
|
|
|
@ -365,6 +365,7 @@ func (s *HTTPHandlers) wrap(handler endpoint, methods []string) http.HandlerFunc
|
|||
return func(resp http.ResponseWriter, req *http.Request) {
|
||||
setHeaders(resp, s.agent.config.HTTPResponseHeaders)
|
||||
setTranslateAddr(resp, s.agent.config.TranslateWANAddrs)
|
||||
setACLDefaultPolicy(resp, s.agent.config.ACLDefaultPolicy)
|
||||
|
||||
// Obfuscate any tokens from appearing in the logs
|
||||
formVals, err := url.ParseQuery(req.URL.RawQuery)
|
||||
|
@ -705,6 +706,12 @@ func setConsistency(resp http.ResponseWriter, consistency string) {
|
|||
}
|
||||
}
|
||||
|
||||
func setACLDefaultPolicy(resp http.ResponseWriter, aclDefaultPolicy string) {
|
||||
if aclDefaultPolicy != "" {
|
||||
resp.Header().Set("X-Consul-Default-ACL-Policy", aclDefaultPolicy)
|
||||
}
|
||||
}
|
||||
|
||||
// setLastContact is used to set the last contact header
|
||||
func setLastContact(resp http.ResponseWriter, last time.Duration) {
|
||||
if last < 0 {
|
||||
|
|
|
@ -415,6 +415,54 @@ func TestHTTPAPI_TranslateAddrHeader(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestHTTPAPI_DefaultACLPolicy(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
type testcase struct {
|
||||
name string
|
||||
hcl string
|
||||
expect string
|
||||
}
|
||||
|
||||
cases := []testcase{
|
||||
{
|
||||
name: "default is allow",
|
||||
hcl: ``,
|
||||
expect: "allow",
|
||||
},
|
||||
{
|
||||
name: "explicit allow",
|
||||
hcl: `acl { default_policy = "allow" }`,
|
||||
expect: "allow",
|
||||
},
|
||||
{
|
||||
name: "explicit deny",
|
||||
hcl: `acl { default_policy = "deny" }`,
|
||||
expect: "deny",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range cases {
|
||||
tc := tc
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
a := NewTestAgent(t, tc.hcl)
|
||||
defer a.Shutdown()
|
||||
|
||||
resp := httptest.NewRecorder()
|
||||
handler := func(resp http.ResponseWriter, req *http.Request) (interface{}, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
req, _ := http.NewRequest("GET", "/v1/agent/self", nil)
|
||||
a.srv.wrap(handler, []string{"GET"})(resp, req)
|
||||
|
||||
require.Equal(t, tc.expect, resp.Header().Get("X-Consul-Default-ACL-Policy"))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestHTTPAPIResponseHeaders(t *testing.T) {
|
||||
t.Parallel()
|
||||
a := NewTestAgent(t, `
|
||||
|
|
|
@ -11,6 +11,8 @@ func autopilotToAPIServerEnterprise(_ *autopilot.ServerState, _ *api.AutopilotSe
|
|||
// noop in oss
|
||||
}
|
||||
|
||||
func autopilotToAPIStateEnterprise(_ *autopilot.State, _ *api.AutopilotState) {
|
||||
// noop in oss
|
||||
func autopilotToAPIStateEnterprise(state *autopilot.State, apiState *api.AutopilotState) {
|
||||
// without the enterprise features there is no different between these two and we don't want to
|
||||
// alarm anyone by leaving this as the zero value.
|
||||
apiState.OptimisticFailureTolerance = state.FailureTolerance
|
||||
}
|
||||
|
|
|
@ -656,9 +656,10 @@ func TestAutopilotStateToAPIConversion(t *testing.T) {
|
|||
}
|
||||
|
||||
expected := api.AutopilotState{
|
||||
Healthy: true,
|
||||
FailureTolerance: 1,
|
||||
Leader: string(leaderID),
|
||||
Healthy: true,
|
||||
FailureTolerance: 1,
|
||||
OptimisticFailureTolerance: 1,
|
||||
Leader: string(leaderID),
|
||||
Voters: []string{
|
||||
string(leaderID),
|
||||
string(follower1ID),
|
||||
|
|
|
@ -519,7 +519,7 @@ START:
|
|||
// Try to get a conn first
|
||||
conn, err := p.acquire(dc, nodeName, addr)
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("failed to get conn: %v", err)
|
||||
return nil, nil, fmt.Errorf("failed to get conn: %w", err)
|
||||
}
|
||||
|
||||
// Get a client
|
||||
|
@ -533,7 +533,7 @@ START:
|
|||
retries++
|
||||
goto START
|
||||
}
|
||||
return nil, nil, fmt.Errorf("failed to start stream: %v", err)
|
||||
return nil, nil, fmt.Errorf("failed to start stream: %w", err)
|
||||
}
|
||||
return conn, client, nil
|
||||
}
|
||||
|
@ -575,14 +575,14 @@ func (p *ConnPool) rpcInsecure(dc string, addr net.Addr, method string, args int
|
|||
var codec rpc.ClientCodec
|
||||
conn, _, err := p.dial(dc, addr, 0, RPCTLSInsecure)
|
||||
if err != nil {
|
||||
return fmt.Errorf("rpcinsecure error establishing connection: %v", err)
|
||||
return fmt.Errorf("rpcinsecure error establishing connection: %w", err)
|
||||
}
|
||||
codec = msgpackrpc.NewCodecFromHandle(true, true, conn, structs.MsgpackHandle)
|
||||
|
||||
// Make the RPC call
|
||||
err = msgpackrpc.CallWithCodec(codec, method, args, reply)
|
||||
if err != nil {
|
||||
return fmt.Errorf("rpcinsecure error making call: %v", err)
|
||||
return fmt.Errorf("rpcinsecure error making call: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
|
@ -594,7 +594,7 @@ func (p *ConnPool) rpc(dc string, nodeName string, addr net.Addr, method string,
|
|||
// Get a usable client
|
||||
conn, sc, err := p.getClient(dc, nodeName, addr)
|
||||
if err != nil {
|
||||
return fmt.Errorf("rpc error getting client: %v", err)
|
||||
return fmt.Errorf("rpc error getting client: %w", err)
|
||||
}
|
||||
|
||||
// Make the RPC call
|
||||
|
@ -611,7 +611,7 @@ func (p *ConnPool) rpc(dc string, nodeName string, addr net.Addr, method string,
|
|||
}
|
||||
|
||||
p.releaseConn(conn)
|
||||
return fmt.Errorf("rpc error making call: %v", err)
|
||||
return fmt.Errorf("rpc error making call: %w", err)
|
||||
}
|
||||
|
||||
// Done with the connection
|
||||
|
|
|
@ -59,6 +59,59 @@ func (e *ServiceIntentionsConfigEntry) DestinationServiceName() ServiceName {
|
|||
return NewServiceName(e.Name, &e.EnterpriseMeta)
|
||||
}
|
||||
|
||||
func (e *ServiceIntentionsConfigEntry) UpdateSourceByLegacyID(legacyID string, update *SourceIntention) bool {
|
||||
for i, src := range e.Sources {
|
||||
if src.LegacyID == legacyID {
|
||||
e.Sources[i] = update
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (e *ServiceIntentionsConfigEntry) UpsertSourceByName(sn ServiceName, upsert *SourceIntention) {
|
||||
for i, src := range e.Sources {
|
||||
if src.SourceServiceName() == sn {
|
||||
e.Sources[i] = upsert
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
e.Sources = append(e.Sources, upsert)
|
||||
}
|
||||
|
||||
func (e *ServiceIntentionsConfigEntry) DeleteSourceByLegacyID(legacyID string) bool {
|
||||
for i, src := range e.Sources {
|
||||
if src.LegacyID == legacyID {
|
||||
// Delete slice element: https://github.com/golang/go/wiki/SliceTricks#delete
|
||||
// a = append(a[:i], a[i+1:]...)
|
||||
e.Sources = append(e.Sources[:i], e.Sources[i+1:]...)
|
||||
|
||||
if len(e.Sources) == 0 {
|
||||
e.Sources = nil
|
||||
}
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (e *ServiceIntentionsConfigEntry) DeleteSourceByName(sn ServiceName) bool {
|
||||
for i, src := range e.Sources {
|
||||
if src.SourceServiceName() == sn {
|
||||
// Delete slice element: https://github.com/golang/go/wiki/SliceTricks#delete
|
||||
// a = append(a[:i], a[i+1:]...)
|
||||
e.Sources = append(e.Sources[:i], e.Sources[i+1:]...)
|
||||
|
||||
if len(e.Sources) == 0 {
|
||||
e.Sources = nil
|
||||
}
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (e *ServiceIntentionsConfigEntry) ToIntention(src *SourceIntention) *Intention {
|
||||
meta := e.Meta
|
||||
if src.LegacyID != "" {
|
||||
|
@ -352,6 +405,9 @@ func (e *ServiceIntentionsConfigEntry) normalize(legacyWrite bool) error {
|
|||
return fmt.Errorf("config entry is nil")
|
||||
}
|
||||
|
||||
// NOTE: this function must be deterministic so that the raft log doesn't
|
||||
// diverge. This means no ID assignments or time.Now() usage!
|
||||
|
||||
e.Kind = ServiceIntentions
|
||||
|
||||
e.EnterpriseMeta.Normalize()
|
||||
|
@ -377,11 +433,6 @@ func (e *ServiceIntentionsConfigEntry) normalize(legacyWrite bool) error {
|
|||
if src.LegacyMeta == nil {
|
||||
src.LegacyMeta = make(map[string]string)
|
||||
}
|
||||
// Set the created/updated times. If this is an update instead of an insert
|
||||
// the UpdateOver() will fix it up appropriately.
|
||||
now := time.Now().UTC()
|
||||
src.LegacyCreateTime = timePointer(now)
|
||||
src.LegacyUpdateTime = timePointer(now)
|
||||
} else {
|
||||
// Legacy fields are cleared, except LegacyMeta which we leave
|
||||
// populated so that we can later fail the write in Validate() and
|
||||
|
@ -541,11 +592,25 @@ func (e *ServiceIntentionsConfigEntry) validate(legacyWrite bool) error {
|
|||
)
|
||||
}
|
||||
}
|
||||
|
||||
if src.LegacyCreateTime == nil {
|
||||
return fmt.Errorf("Sources[%d].LegacyCreateTime must be set", i)
|
||||
}
|
||||
if src.LegacyUpdateTime == nil {
|
||||
return fmt.Errorf("Sources[%d].LegacyUpdateTime must be set", i)
|
||||
}
|
||||
} else {
|
||||
if len(src.LegacyMeta) > 0 {
|
||||
return fmt.Errorf("Sources[%d].LegacyMeta must be omitted", i)
|
||||
}
|
||||
src.LegacyMeta = nil // ensure it's completely unset
|
||||
|
||||
if src.LegacyCreateTime != nil {
|
||||
return fmt.Errorf("Sources[%d].LegacyCreateTime must be omitted", i)
|
||||
}
|
||||
if src.LegacyUpdateTime != nil {
|
||||
return fmt.Errorf("Sources[%d].LegacyUpdateTime must be omitted", i)
|
||||
}
|
||||
}
|
||||
|
||||
if legacyWrite {
|
||||
|
|
|
@ -21,6 +21,14 @@ func generateUUID() (ret string) {
|
|||
}
|
||||
|
||||
func TestServiceIntentionsConfigEntry(t *testing.T) {
|
||||
var (
|
||||
testLocation = time.FixedZone("UTC-8", -8*60*60)
|
||||
|
||||
testTimeA = time.Date(1955, 11, 5, 6, 15, 0, 0, testLocation)
|
||||
testTimeB = time.Date(1985, 10, 26, 1, 35, 0, 0, testLocation)
|
||||
testTimeC = time.Date(2015, 10, 21, 16, 29, 0, 0, testLocation)
|
||||
)
|
||||
|
||||
type testcase struct {
|
||||
entry *ServiceIntentionsConfigEntry
|
||||
legacy bool
|
||||
|
@ -126,9 +134,11 @@ func TestServiceIntentionsConfigEntry(t *testing.T) {
|
|||
Name: "test",
|
||||
Sources: []*SourceIntention{
|
||||
{
|
||||
LegacyID: legacyIDs[0],
|
||||
Name: "foo",
|
||||
Action: IntentionActionAllow,
|
||||
LegacyID: legacyIDs[0],
|
||||
Name: "foo",
|
||||
Action: IntentionActionAllow,
|
||||
LegacyCreateTime: &testTimeA,
|
||||
LegacyUpdateTime: &testTimeA,
|
||||
},
|
||||
},
|
||||
Meta: map[string]string{
|
||||
|
@ -198,10 +208,12 @@ func TestServiceIntentionsConfigEntry(t *testing.T) {
|
|||
Name: "test",
|
||||
Sources: []*SourceIntention{
|
||||
{
|
||||
LegacyID: legacyIDs[0],
|
||||
Name: "foo",
|
||||
Action: IntentionActionAllow,
|
||||
Description: strings.Repeat("x", 512),
|
||||
LegacyID: legacyIDs[0],
|
||||
Name: "foo",
|
||||
Action: IntentionActionAllow,
|
||||
Description: strings.Repeat("x", 512),
|
||||
LegacyCreateTime: &testTimeA,
|
||||
LegacyUpdateTime: &testTimeA,
|
||||
LegacyMeta: map[string]string{ // stray Meta will be dropped
|
||||
"old": "data",
|
||||
},
|
||||
|
@ -217,10 +229,12 @@ func TestServiceIntentionsConfigEntry(t *testing.T) {
|
|||
Name: "test",
|
||||
Sources: []*SourceIntention{
|
||||
{
|
||||
LegacyID: legacyIDs[0],
|
||||
Name: "foo",
|
||||
Action: IntentionActionAllow,
|
||||
LegacyMeta: makeStringMap(65, 5, 5),
|
||||
LegacyID: legacyIDs[0],
|
||||
Name: "foo",
|
||||
Action: IntentionActionAllow,
|
||||
LegacyCreateTime: &testTimeA,
|
||||
LegacyUpdateTime: &testTimeA,
|
||||
LegacyMeta: makeStringMap(65, 5, 5),
|
||||
},
|
||||
},
|
||||
},
|
||||
|
@ -233,10 +247,12 @@ func TestServiceIntentionsConfigEntry(t *testing.T) {
|
|||
Name: "test",
|
||||
Sources: []*SourceIntention{
|
||||
{
|
||||
LegacyID: legacyIDs[0],
|
||||
Name: "foo",
|
||||
Action: IntentionActionAllow,
|
||||
LegacyMeta: makeStringMap(64, 129, 5),
|
||||
LegacyID: legacyIDs[0],
|
||||
Name: "foo",
|
||||
Action: IntentionActionAllow,
|
||||
LegacyCreateTime: &testTimeA,
|
||||
LegacyUpdateTime: &testTimeA,
|
||||
LegacyMeta: makeStringMap(64, 129, 5),
|
||||
},
|
||||
},
|
||||
},
|
||||
|
@ -249,10 +265,12 @@ func TestServiceIntentionsConfigEntry(t *testing.T) {
|
|||
Name: "test",
|
||||
Sources: []*SourceIntention{
|
||||
{
|
||||
LegacyID: legacyIDs[0],
|
||||
Name: "foo",
|
||||
Action: IntentionActionAllow,
|
||||
LegacyMeta: makeStringMap(64, 128, 513),
|
||||
LegacyID: legacyIDs[0],
|
||||
Name: "foo",
|
||||
Action: IntentionActionAllow,
|
||||
LegacyCreateTime: &testTimeA,
|
||||
LegacyUpdateTime: &testTimeA,
|
||||
LegacyMeta: makeStringMap(64, 128, 513),
|
||||
},
|
||||
},
|
||||
},
|
||||
|
@ -265,10 +283,12 @@ func TestServiceIntentionsConfigEntry(t *testing.T) {
|
|||
Name: "test",
|
||||
Sources: []*SourceIntention{
|
||||
{
|
||||
LegacyID: legacyIDs[0],
|
||||
Name: "foo",
|
||||
Action: IntentionActionAllow,
|
||||
LegacyMeta: makeStringMap(64, 128, 512),
|
||||
LegacyID: legacyIDs[0],
|
||||
Name: "foo",
|
||||
Action: IntentionActionAllow,
|
||||
LegacyCreateTime: &testTimeA,
|
||||
LegacyUpdateTime: &testTimeA,
|
||||
LegacyMeta: makeStringMap(64, 128, 512),
|
||||
},
|
||||
},
|
||||
},
|
||||
|
@ -280,9 +300,11 @@ func TestServiceIntentionsConfigEntry(t *testing.T) {
|
|||
Name: "test",
|
||||
Sources: []*SourceIntention{
|
||||
{
|
||||
Name: "foo",
|
||||
Action: IntentionActionAllow,
|
||||
Description: strings.Repeat("x", 512),
|
||||
Name: "foo",
|
||||
Action: IntentionActionAllow,
|
||||
Description: strings.Repeat("x", 512),
|
||||
LegacyCreateTime: &testTimeA,
|
||||
LegacyUpdateTime: &testTimeA,
|
||||
},
|
||||
},
|
||||
},
|
||||
|
@ -1008,8 +1030,10 @@ func TestServiceIntentionsConfigEntry(t *testing.T) {
|
|||
Action: IntentionActionDeny,
|
||||
},
|
||||
{
|
||||
Name: "foo",
|
||||
Action: IntentionActionAllow,
|
||||
Name: "foo",
|
||||
Action: IntentionActionAllow,
|
||||
LegacyCreateTime: &testTimeA, // stray times will be dropped
|
||||
LegacyUpdateTime: &testTimeA,
|
||||
},
|
||||
{
|
||||
Name: "bar",
|
||||
|
@ -1059,9 +1083,11 @@ func TestServiceIntentionsConfigEntry(t *testing.T) {
|
|||
Name: "test",
|
||||
Sources: []*SourceIntention{
|
||||
{
|
||||
Name: WildcardSpecifier,
|
||||
Action: IntentionActionDeny,
|
||||
LegacyID: legacyIDs[0],
|
||||
Name: WildcardSpecifier,
|
||||
Action: IntentionActionDeny,
|
||||
LegacyID: legacyIDs[0],
|
||||
LegacyCreateTime: &testTimeA,
|
||||
LegacyUpdateTime: &testTimeA,
|
||||
},
|
||||
{
|
||||
Name: "foo",
|
||||
|
@ -1071,23 +1097,27 @@ func TestServiceIntentionsConfigEntry(t *testing.T) {
|
|||
"key1": "val1",
|
||||
"key2": "val2",
|
||||
},
|
||||
LegacyCreateTime: &testTimeB,
|
||||
LegacyUpdateTime: &testTimeB,
|
||||
},
|
||||
{
|
||||
Name: "bar",
|
||||
Action: IntentionActionDeny,
|
||||
LegacyID: legacyIDs[2],
|
||||
Name: "bar",
|
||||
Action: IntentionActionDeny,
|
||||
LegacyID: legacyIDs[2],
|
||||
LegacyCreateTime: &testTimeC,
|
||||
LegacyUpdateTime: &testTimeC,
|
||||
},
|
||||
},
|
||||
},
|
||||
check: func(t *testing.T, entry *ServiceIntentionsConfigEntry) {
|
||||
require.Len(t, entry.Sources, 3)
|
||||
|
||||
assert.False(t, entry.Sources[0].LegacyCreateTime.IsZero())
|
||||
assert.False(t, entry.Sources[0].LegacyUpdateTime.IsZero())
|
||||
assert.False(t, entry.Sources[1].LegacyCreateTime.IsZero())
|
||||
assert.False(t, entry.Sources[1].LegacyUpdateTime.IsZero())
|
||||
assert.False(t, entry.Sources[2].LegacyCreateTime.IsZero())
|
||||
assert.False(t, entry.Sources[2].LegacyUpdateTime.IsZero())
|
||||
// assert.False(t, entry.Sources[0].LegacyCreateTime.IsZero())
|
||||
// assert.False(t, entry.Sources[0].LegacyUpdateTime.IsZero())
|
||||
// assert.False(t, entry.Sources[1].LegacyCreateTime.IsZero())
|
||||
// assert.False(t, entry.Sources[1].LegacyUpdateTime.IsZero())
|
||||
// assert.False(t, entry.Sources[2].LegacyCreateTime.IsZero())
|
||||
// assert.False(t, entry.Sources[2].LegacyUpdateTime.IsZero())
|
||||
|
||||
assert.Equal(t, []*SourceIntention{
|
||||
{
|
||||
|
@ -1299,6 +1329,8 @@ func TestMigrateIntentions(t *testing.T) {
|
|||
LegacyMeta: map[string]string{
|
||||
"key1": "val1",
|
||||
},
|
||||
LegacyCreateTime: &anyTime,
|
||||
LegacyUpdateTime: &anyTime,
|
||||
},
|
||||
},
|
||||
},
|
||||
|
@ -1349,6 +1381,8 @@ func TestMigrateIntentions(t *testing.T) {
|
|||
LegacyMeta: map[string]string{
|
||||
"key1": "val1",
|
||||
},
|
||||
LegacyCreateTime: &anyTime,
|
||||
LegacyUpdateTime: &anyTime,
|
||||
},
|
||||
{
|
||||
LegacyID: legacyIDs[1],
|
||||
|
@ -1359,6 +1393,8 @@ func TestMigrateIntentions(t *testing.T) {
|
|||
LegacyMeta: map[string]string{
|
||||
"key2": "val2",
|
||||
},
|
||||
LegacyCreateTime: &anyTime,
|
||||
LegacyUpdateTime: &anyTime,
|
||||
},
|
||||
},
|
||||
},
|
||||
|
@ -1409,6 +1445,8 @@ func TestMigrateIntentions(t *testing.T) {
|
|||
LegacyMeta: map[string]string{
|
||||
"key1": "val1",
|
||||
},
|
||||
LegacyCreateTime: &anyTime,
|
||||
LegacyUpdateTime: &anyTime,
|
||||
},
|
||||
},
|
||||
},
|
||||
|
@ -1425,6 +1463,8 @@ func TestMigrateIntentions(t *testing.T) {
|
|||
LegacyMeta: map[string]string{
|
||||
"key2": "val2",
|
||||
},
|
||||
LegacyCreateTime: &anyTime,
|
||||
LegacyUpdateTime: &anyTime,
|
||||
},
|
||||
},
|
||||
},
|
||||
|
|
|
@ -440,6 +440,9 @@ func (x *Intention) ToConfigEntry(legacy bool) *ServiceIntentionsConfigEntry {
|
|||
}
|
||||
|
||||
func (x *Intention) ToSourceIntention(legacy bool) *SourceIntention {
|
||||
ct := x.CreatedAt // copy
|
||||
ut := x.UpdatedAt
|
||||
|
||||
src := &SourceIntention{
|
||||
Name: x.SourceName,
|
||||
EnterpriseMeta: *x.SourceEnterpriseMeta(),
|
||||
|
@ -450,8 +453,8 @@ func (x *Intention) ToSourceIntention(legacy bool) *SourceIntention {
|
|||
Type: x.SourceType,
|
||||
Description: x.Description,
|
||||
LegacyMeta: x.Meta,
|
||||
LegacyCreateTime: nil, // Ignore
|
||||
LegacyUpdateTime: nil, // Ignore
|
||||
LegacyCreateTime: &ct,
|
||||
LegacyUpdateTime: &ut,
|
||||
}
|
||||
if !legacy {
|
||||
src.Permissions = x.Permissions
|
||||
|
@ -522,13 +525,30 @@ type IntentionRequest struct {
|
|||
Op IntentionOp
|
||||
|
||||
// Intention is the intention.
|
||||
//
|
||||
// This is mutually exclusive with the Mutation field.
|
||||
Intention *Intention
|
||||
|
||||
// Mutation is a change to make to an Intention.
|
||||
//
|
||||
// This is mutually exclusive with the Intention field.
|
||||
//
|
||||
// This field is only set by the leader before writing to the raft log and
|
||||
// is not settable via the API or an RPC.
|
||||
Mutation *IntentionMutation
|
||||
|
||||
// WriteRequest is a common struct containing ACL tokens and other
|
||||
// write-related common elements for requests.
|
||||
WriteRequest
|
||||
}
|
||||
|
||||
type IntentionMutation struct {
|
||||
ID string
|
||||
Destination ServiceName
|
||||
Source ServiceName
|
||||
Value *SourceIntention
|
||||
}
|
||||
|
||||
// RequestDatacenter returns the datacenter for a given request.
|
||||
func (q *IntentionRequest) RequestDatacenter() string {
|
||||
return q.Datacenter
|
||||
|
|
File diff suppressed because one or more lines are too long
|
@ -158,15 +158,6 @@ const (
|
|||
// configured to use TLS. Any other value indicates that it was not setup in
|
||||
// that manner.
|
||||
MemberTagValueUseTLS = "1"
|
||||
|
||||
// MemberTagKeyReadReplica is the key used to indicate that the member is a read
|
||||
// replica server (will remain a Raft non-voter).
|
||||
// Read Replicas are a Consul Enterprise feature.
|
||||
MemberTagKeyReadReplica = "nonvoter"
|
||||
// MemberTagValueReadReplica is the value of the MemberTagKeyReadReplica key when
|
||||
// the member is in fact a read-replica. Any other value indicates that it is not.
|
||||
// Read Replicas are a Consul Enterprise feature.
|
||||
MemberTagValueReadReplica = "1"
|
||||
)
|
||||
|
||||
type MemberACLMode string
|
||||
|
|
11
api/api.go
11
api/api.go
|
@ -254,6 +254,11 @@ type QueryMeta struct {
|
|||
// CacheAge is set if request was ?cached and indicates how stale the cached
|
||||
// response is.
|
||||
CacheAge time.Duration
|
||||
|
||||
// DefaultACLPolicy is used to control the ACL interaction when there is no
|
||||
// defined policy. This can be "allow" which means ACLs are used to
|
||||
// deny-list, or "deny" which means ACLs are allow-lists.
|
||||
DefaultACLPolicy string
|
||||
}
|
||||
|
||||
// WriteMeta is used to return meta data about a write
|
||||
|
@ -962,6 +967,12 @@ func parseQueryMeta(resp *http.Response, q *QueryMeta) error {
|
|||
q.AddressTranslationEnabled = false
|
||||
}
|
||||
|
||||
// Parse X-Consul-Default-ACL-Policy
|
||||
switch v := header.Get("X-Consul-Default-ACL-Policy"); v {
|
||||
case "allow", "deny":
|
||||
q.DefaultACLPolicy = v
|
||||
}
|
||||
|
||||
// Parse Cache info
|
||||
if cacheStr := header.Get("X-Cache"); cacheStr != "" {
|
||||
q.CacheHit = strings.EqualFold(cacheStr, "HIT")
|
||||
|
|
|
@ -840,6 +840,7 @@ func TestAPI_ParseQueryMeta(t *testing.T) {
|
|||
resp.Header.Set("X-Consul-LastContact", "80")
|
||||
resp.Header.Set("X-Consul-KnownLeader", "true")
|
||||
resp.Header.Set("X-Consul-Translate-Addresses", "true")
|
||||
resp.Header.Set("X-Consul-Default-ACL-Policy", "deny")
|
||||
|
||||
qm := &QueryMeta{}
|
||||
if err := parseQueryMeta(resp, qm); err != nil {
|
||||
|
@ -858,6 +859,9 @@ func TestAPI_ParseQueryMeta(t *testing.T) {
|
|||
if !qm.AddressTranslationEnabled {
|
||||
t.Fatalf("Bad: %v", qm)
|
||||
}
|
||||
if qm.DefaultACLPolicy != "deny" {
|
||||
t.Fatalf("Bad: %v", qm)
|
||||
}
|
||||
}
|
||||
|
||||
func TestAPI_UnixSocket(t *testing.T) {
|
||||
|
|
|
@ -114,7 +114,7 @@ type OperatorHealthReply struct {
|
|||
type AutopilotState struct {
|
||||
Healthy bool
|
||||
FailureTolerance int
|
||||
OptimisitcFailureTolerance int
|
||||
OptimisticFailureTolerance int
|
||||
|
||||
Servers map[string]AutopilotServer
|
||||
Leader string
|
||||
|
@ -137,7 +137,7 @@ type AutopilotServer struct {
|
|||
StableSince time.Time
|
||||
RedundancyZone string `json:",omitempty"`
|
||||
UpgradeVersion string `json:",omitempty"`
|
||||
ReadReplica bool `json:",omitempty"`
|
||||
ReadReplica bool
|
||||
Status AutopilotServerStatus
|
||||
Meta map[string]string
|
||||
NodeType AutopilotServerType
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
ARG GOLANG_VERSION=1.14.11
|
||||
ARG GOLANG_VERSION=1.15.5
|
||||
FROM golang:${GOLANG_VERSION}
|
||||
|
||||
ARG GOTOOLS="github.com/elazarl/go-bindata-assetfs/... \
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
FROM travisci/ci-garnet:packer-1512502276-986baf0
|
||||
|
||||
ENV GOLANG_VERSION 1.14.11
|
||||
ENV GOLANG_VERSION 1.15.5
|
||||
|
||||
RUN mkdir -p /home/travis/go && chown -R travis /home/travis/go
|
||||
|
||||
|
|
|
@ -85,6 +85,7 @@ import (
|
|||
operauto "github.com/hashicorp/consul/command/operator/autopilot"
|
||||
operautoget "github.com/hashicorp/consul/command/operator/autopilot/get"
|
||||
operautoset "github.com/hashicorp/consul/command/operator/autopilot/set"
|
||||
operautostate "github.com/hashicorp/consul/command/operator/autopilot/state"
|
||||
operraft "github.com/hashicorp/consul/command/operator/raft"
|
||||
operraftlist "github.com/hashicorp/consul/command/operator/raft/listpeers"
|
||||
operraftremove "github.com/hashicorp/consul/command/operator/raft/removepeer"
|
||||
|
@ -202,6 +203,7 @@ func init() {
|
|||
Register("operator autopilot", func(cli.Ui) (cli.Command, error) { return operauto.New(), nil })
|
||||
Register("operator autopilot get-config", func(ui cli.Ui) (cli.Command, error) { return operautoget.New(ui), nil })
|
||||
Register("operator autopilot set-config", func(ui cli.Ui) (cli.Command, error) { return operautoset.New(ui), nil })
|
||||
Register("operator autopilot state", func(ui cli.Ui) (cli.Command, error) { return operautostate.New(ui), nil })
|
||||
Register("operator raft", func(cli.Ui) (cli.Command, error) { return operraft.New(), nil })
|
||||
Register("operator raft list-peers", func(ui cli.Ui) (cli.Command, error) { return operraftlist.New(ui), nil })
|
||||
Register("operator raft remove-peer", func(ui cli.Ui) (cli.Command, error) { return operraftremove.New(ui), nil })
|
||||
|
|
|
@ -44,7 +44,7 @@ func TestConfigUtil_Values(t *testing.T) {
|
|||
{
|
||||
`{ "duration": "nope" }`,
|
||||
"",
|
||||
"invalid duration nope",
|
||||
`invalid duration "nope"`,
|
||||
},
|
||||
{
|
||||
`{ "string": 123 }`,
|
||||
|
|
|
@ -0,0 +1,206 @@
|
|||
package state
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"sort"
|
||||
|
||||
"github.com/hashicorp/consul/api"
|
||||
)
|
||||
|
||||
const (
|
||||
PrettyFormat string = "pretty"
|
||||
JSONFormat string = "json"
|
||||
)
|
||||
|
||||
// Formatter defines methods provided by an autopilot state output formatter
|
||||
type Formatter interface {
|
||||
FormatState(state *api.AutopilotState) (string, error)
|
||||
}
|
||||
|
||||
// GetSupportedFormats returns supported formats
|
||||
func GetSupportedFormats() []string {
|
||||
return []string{PrettyFormat, JSONFormat}
|
||||
}
|
||||
|
||||
// NewFormatter returns Formatter implementation
|
||||
func NewFormatter(format string) (formatter Formatter, err error) {
|
||||
switch format {
|
||||
case PrettyFormat:
|
||||
formatter = newPrettyFormatter()
|
||||
case JSONFormat:
|
||||
formatter = newJSONFormatter()
|
||||
default:
|
||||
err = fmt.Errorf("Unknown format: %s", format)
|
||||
}
|
||||
|
||||
return formatter, err
|
||||
}
|
||||
|
||||
func newPrettyFormatter() Formatter {
|
||||
return &prettyFormatter{}
|
||||
}
|
||||
|
||||
type prettyFormatter struct {
|
||||
}
|
||||
|
||||
func outputStringSlice(buffer *bytes.Buffer, indent string, values []string) {
|
||||
for _, val := range values {
|
||||
buffer.WriteString(fmt.Sprintf("%s%s\n", indent, val))
|
||||
}
|
||||
}
|
||||
|
||||
type mapOutput struct {
|
||||
key string
|
||||
value string
|
||||
}
|
||||
|
||||
func formatZone(zoneName string, zone *api.AutopilotZone) string {
|
||||
var buffer bytes.Buffer
|
||||
|
||||
buffer.WriteString(fmt.Sprintf(" %s:\n", zoneName))
|
||||
buffer.WriteString(fmt.Sprintf(" Failure Tolerance: %d\n", zone.FailureTolerance))
|
||||
buffer.WriteString(" Voters:\n")
|
||||
outputStringSlice(&buffer, " ", zone.Voters)
|
||||
buffer.WriteString(" Servers:\n")
|
||||
outputStringSlice(&buffer, " ", zone.Servers)
|
||||
|
||||
return buffer.String()
|
||||
}
|
||||
|
||||
func formatServer(srv *api.AutopilotServer) string {
|
||||
var buffer bytes.Buffer
|
||||
|
||||
buffer.WriteString(fmt.Sprintf(" %s\n", srv.ID))
|
||||
buffer.WriteString(fmt.Sprintf(" Name: %s\n", srv.Name))
|
||||
buffer.WriteString(fmt.Sprintf(" Address: %s\n", srv.Address))
|
||||
buffer.WriteString(fmt.Sprintf(" Version: %s\n", srv.Version))
|
||||
buffer.WriteString(fmt.Sprintf(" Status: %s\n", srv.Status))
|
||||
buffer.WriteString(fmt.Sprintf(" Node Type: %s\n", srv.NodeType))
|
||||
buffer.WriteString(fmt.Sprintf(" Node Status: %s\n", srv.NodeStatus))
|
||||
buffer.WriteString(fmt.Sprintf(" Healthy: %t\n", srv.Healthy))
|
||||
buffer.WriteString(fmt.Sprintf(" Last Contact: %s\n", srv.LastContact.String()))
|
||||
buffer.WriteString(fmt.Sprintf(" Last Term: %d\n", srv.LastTerm))
|
||||
buffer.WriteString(fmt.Sprintf(" Last Index: %d\n", srv.LastIndex))
|
||||
if srv.RedundancyZone != "" {
|
||||
buffer.WriteString(fmt.Sprintf(" Redundancy Zone: %s\n", srv.RedundancyZone))
|
||||
}
|
||||
if srv.UpgradeVersion != "" {
|
||||
buffer.WriteString(fmt.Sprintf(" Upgrade Version: %s\n", srv.UpgradeVersion))
|
||||
}
|
||||
if srv.ReadReplica {
|
||||
buffer.WriteString(fmt.Sprintf(" Read Replica: %t\n", srv.ReadReplica))
|
||||
}
|
||||
if len(srv.Meta) > 0 {
|
||||
buffer.WriteString(fmt.Sprintf(" Meta\n"))
|
||||
var outputs []mapOutput
|
||||
for k, v := range srv.Meta {
|
||||
outputs = append(outputs, mapOutput{key: k, value: fmt.Sprintf(" %q: %q\n", k, v)})
|
||||
}
|
||||
|
||||
sort.Slice(outputs, func(i, j int) bool {
|
||||
return outputs[i].key < outputs[j].key
|
||||
})
|
||||
|
||||
for _, output := range outputs {
|
||||
buffer.WriteString(output.value)
|
||||
}
|
||||
}
|
||||
|
||||
return buffer.String()
|
||||
}
|
||||
|
||||
func (f *prettyFormatter) FormatState(state *api.AutopilotState) (string, error) {
|
||||
|
||||
var buffer bytes.Buffer
|
||||
|
||||
buffer.WriteString(fmt.Sprintf("Healthy: %t\n", state.Healthy))
|
||||
buffer.WriteString(fmt.Sprintf("Failure Tolerance: %d\n", state.FailureTolerance))
|
||||
buffer.WriteString(fmt.Sprintf("Optimistic Failure Tolerance: %d\n", state.OptimisticFailureTolerance))
|
||||
buffer.WriteString(fmt.Sprintf("Leader: %s\n", state.Leader))
|
||||
buffer.WriteString("Voters:\n")
|
||||
outputStringSlice(&buffer, " ", state.Voters)
|
||||
|
||||
if len(state.ReadReplicas) > 0 {
|
||||
buffer.WriteString("Read Replicas:\n")
|
||||
outputStringSlice(&buffer, " ", state.ReadReplicas)
|
||||
}
|
||||
|
||||
if len(state.RedundancyZones) > 0 {
|
||||
var outputs []mapOutput
|
||||
buffer.WriteString("Redundancy Zones:\n")
|
||||
for zoneName, zone := range state.RedundancyZones {
|
||||
outputs = append(outputs, mapOutput{key: zoneName, value: formatZone(zoneName, &zone)})
|
||||
}
|
||||
sort.Slice(outputs, func(i, j int) bool {
|
||||
return outputs[i].key < outputs[j].key
|
||||
})
|
||||
|
||||
for _, output := range outputs {
|
||||
buffer.WriteString(output.value)
|
||||
}
|
||||
}
|
||||
|
||||
if state.Upgrade != nil {
|
||||
u := state.Upgrade
|
||||
buffer.WriteString("Upgrade:\n")
|
||||
buffer.WriteString(fmt.Sprintf(" Status: %s\n", u.Status))
|
||||
buffer.WriteString(fmt.Sprintf(" Target Version: %s\n", u.TargetVersion))
|
||||
if len(u.TargetVersionVoters) > 0 {
|
||||
buffer.WriteString(" Target Version Voters:\n")
|
||||
outputStringSlice(&buffer, " ", u.TargetVersionVoters)
|
||||
}
|
||||
if len(u.TargetVersionNonVoters) > 0 {
|
||||
buffer.WriteString(" Target Version Non-Voters:\n")
|
||||
outputStringSlice(&buffer, " ", u.TargetVersionNonVoters)
|
||||
}
|
||||
if len(u.TargetVersionReadReplicas) > 0 {
|
||||
buffer.WriteString(" Target Version ReadReplicas:\n")
|
||||
outputStringSlice(&buffer, " ", u.TargetVersionReadReplicas)
|
||||
}
|
||||
if len(u.OtherVersionVoters) > 0 {
|
||||
buffer.WriteString(" Other Version Voters:\n")
|
||||
outputStringSlice(&buffer, " ", u.OtherVersionVoters)
|
||||
}
|
||||
if len(u.OtherVersionNonVoters) > 0 {
|
||||
buffer.WriteString(" Other Version Non-Voters:\n")
|
||||
outputStringSlice(&buffer, " ", u.OtherVersionNonVoters)
|
||||
}
|
||||
if len(u.OtherVersionReadReplicas) > 0 {
|
||||
buffer.WriteString(" Other Version ReadReplicas:\n")
|
||||
outputStringSlice(&buffer, " ", u.OtherVersionReadReplicas)
|
||||
}
|
||||
}
|
||||
|
||||
buffer.WriteString("Servers:\n")
|
||||
var outputs []mapOutput
|
||||
for id, srv := range state.Servers {
|
||||
outputs = append(outputs, mapOutput{key: id, value: formatServer(&srv)})
|
||||
}
|
||||
|
||||
sort.Slice(outputs, func(i, j int) bool {
|
||||
return outputs[i].key < outputs[j].key
|
||||
})
|
||||
|
||||
for _, output := range outputs {
|
||||
buffer.WriteString(output.value)
|
||||
}
|
||||
|
||||
return buffer.String(), nil
|
||||
}
|
||||
|
||||
func newJSONFormatter() Formatter {
|
||||
return &jsonFormatter{}
|
||||
}
|
||||
|
||||
type jsonFormatter struct {
|
||||
}
|
||||
|
||||
func (f *jsonFormatter) FormatState(state *api.AutopilotState) (string, error) {
|
||||
b, err := json.MarshalIndent(state, "", " ")
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("Failed to marshal token: %v", err)
|
||||
}
|
||||
return string(b), nil
|
||||
}
|
|
@ -0,0 +1,99 @@
|
|||
package state
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/hashicorp/consul/api"
|
||||
"github.com/hashicorp/consul/command/flags"
|
||||
"github.com/mitchellh/cli"
|
||||
)
|
||||
|
||||
func New(ui cli.Ui) *cmd {
|
||||
c := &cmd{UI: ui}
|
||||
c.init()
|
||||
return c
|
||||
}
|
||||
|
||||
type cmd struct {
|
||||
UI cli.Ui
|
||||
flags *flag.FlagSet
|
||||
http *flags.HTTPFlags
|
||||
help string
|
||||
|
||||
format string
|
||||
}
|
||||
|
||||
func (c *cmd) init() {
|
||||
c.flags = flag.NewFlagSet("", flag.ContinueOnError)
|
||||
c.flags.StringVar(
|
||||
&c.format,
|
||||
"format",
|
||||
PrettyFormat,
|
||||
fmt.Sprintf("Output format {%s}", strings.Join(GetSupportedFormats(), "|")),
|
||||
)
|
||||
c.http = &flags.HTTPFlags{}
|
||||
flags.Merge(c.flags, c.http.ClientFlags())
|
||||
flags.Merge(c.flags, c.http.ServerFlags())
|
||||
c.help = flags.Usage(help, c.flags)
|
||||
}
|
||||
|
||||
func (c *cmd) Run(args []string) int {
|
||||
if err := c.flags.Parse(args); err != nil {
|
||||
if err == flag.ErrHelp {
|
||||
return 0
|
||||
}
|
||||
c.UI.Error(fmt.Sprintf("Failed to parse args: %v", err))
|
||||
return 1
|
||||
}
|
||||
|
||||
// Set up a client.
|
||||
client, err := c.http.APIClient()
|
||||
if err != nil {
|
||||
c.UI.Error(fmt.Sprintf("Error initializing client: %s", err))
|
||||
return 1
|
||||
}
|
||||
|
||||
// Fetch the current configuration.
|
||||
opts := &api.QueryOptions{
|
||||
AllowStale: c.http.Stale(),
|
||||
}
|
||||
state, err := client.Operator().AutopilotState(opts)
|
||||
if err != nil {
|
||||
c.UI.Error(fmt.Sprintf("Error querying Autopilot state: %s", err))
|
||||
return 1
|
||||
}
|
||||
|
||||
formatter, err := NewFormatter(c.format)
|
||||
if err != nil {
|
||||
c.UI.Error(err.Error())
|
||||
return 1
|
||||
}
|
||||
|
||||
out, err := formatter.FormatState(state)
|
||||
if err != nil {
|
||||
c.UI.Error(err.Error())
|
||||
return 1
|
||||
}
|
||||
|
||||
if out != "" {
|
||||
c.UI.Info(out)
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (c *cmd) Synopsis() string {
|
||||
return synopsis
|
||||
}
|
||||
|
||||
func (c *cmd) Help() string {
|
||||
return c.help
|
||||
}
|
||||
|
||||
const synopsis = "Display the current Autopilot configuration"
|
||||
const help = `
|
||||
Usage: consul operator autopilot get-config [options]
|
||||
|
||||
Displays the current Autopilot configuration.
|
||||
`
|
|
@ -0,0 +1,126 @@
|
|||
package state
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"flag"
|
||||
"io/ioutil"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/hashicorp/consul/agent"
|
||||
"github.com/hashicorp/consul/api"
|
||||
"github.com/hashicorp/consul/testrpc"
|
||||
"github.com/mitchellh/cli"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
// update allows golden files to be updated based on the current output.
|
||||
var update = flag.Bool("update", false, "update golden files")
|
||||
|
||||
// golden reads and optionally writes the expected data to the golden file,
|
||||
// returning the contents as a string.
|
||||
func golden(t *testing.T, name, got string) string {
|
||||
t.Helper()
|
||||
|
||||
golden := filepath.Join("testdata", name+".golden")
|
||||
if *update && got != "" {
|
||||
err := ioutil.WriteFile(golden, []byte(got), 0644)
|
||||
require.NoError(t, err)
|
||||
}
|
||||
|
||||
expected, err := ioutil.ReadFile(golden)
|
||||
require.NoError(t, err)
|
||||
|
||||
return string(expected)
|
||||
}
|
||||
|
||||
func TestStateCommand_noTabs(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
if strings.ContainsRune(New(cli.NewMockUi()).Help(), '\t') {
|
||||
t.Fatal("help has tabs")
|
||||
}
|
||||
}
|
||||
|
||||
func TestStateCommand_Pretty(t *testing.T) {
|
||||
t.Parallel()
|
||||
a := agent.NewTestAgent(t, `
|
||||
node_id = "f0427127-7531-455a-b651-f1ea1d8451f0"
|
||||
`)
|
||||
|
||||
defer a.Shutdown()
|
||||
testrpc.WaitForLeader(t, a.RPC, "dc1")
|
||||
|
||||
ui := cli.NewMockUi()
|
||||
cmd := New(ui)
|
||||
|
||||
args := []string{
|
||||
"-http-addr=" + a.HTTPAddr(),
|
||||
}
|
||||
|
||||
code := cmd.Run(args)
|
||||
require.Empty(t, ui.ErrorWriter.String())
|
||||
require.Equal(t, code, 0)
|
||||
output := ui.OutputWriter.String()
|
||||
|
||||
// Just a few quick checks to ensure we got output
|
||||
// the output formatter will be tested in another test.
|
||||
require.Regexp(t, `^Healthy:`, output)
|
||||
require.Regexp(t, `(?m)^Leader:`, output)
|
||||
}
|
||||
|
||||
func TestStateCommand_JSON(t *testing.T) {
|
||||
t.Parallel()
|
||||
a := agent.NewTestAgent(t, "")
|
||||
|
||||
defer a.Shutdown()
|
||||
testrpc.WaitForLeader(t, a.RPC, "dc1")
|
||||
|
||||
ui := cli.NewMockUi()
|
||||
cmd := New(ui)
|
||||
|
||||
args := []string{
|
||||
"-http-addr=" + a.HTTPAddr(),
|
||||
"-format=json",
|
||||
}
|
||||
|
||||
code := cmd.Run(args)
|
||||
require.Empty(t, ui.ErrorWriter.String())
|
||||
require.Equal(t, code, 0)
|
||||
output := ui.OutputWriter.String()
|
||||
|
||||
var state api.AutopilotState
|
||||
require.NoError(t, json.Unmarshal([]byte(output), &state))
|
||||
}
|
||||
|
||||
func TestStateCommand_Formatter(t *testing.T) {
|
||||
cases := []string{
|
||||
"oss",
|
||||
"enterprise",
|
||||
}
|
||||
|
||||
for _, name := range cases {
|
||||
t.Run(name, func(t *testing.T) {
|
||||
statePath := filepath.Join("testdata", name, "state.json")
|
||||
input, err := ioutil.ReadFile(statePath)
|
||||
require.NoError(t, err)
|
||||
|
||||
var state api.AutopilotState
|
||||
require.NoError(t, json.Unmarshal(input, &state))
|
||||
|
||||
for _, format := range GetSupportedFormats() {
|
||||
t.Run(format, func(t *testing.T) {
|
||||
formatter, err := NewFormatter(format)
|
||||
require.NoError(t, err)
|
||||
|
||||
actual, err := formatter.FormatState(&state)
|
||||
require.NoError(t, err)
|
||||
|
||||
expected := golden(t, filepath.Join(name, format), actual)
|
||||
require.Equal(t, expected, actual)
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
|
@ -0,0 +1,401 @@
|
|||
{
|
||||
"Healthy": true,
|
||||
"FailureTolerance": 1,
|
||||
"OptimisticFailureTolerance": 0,
|
||||
"Servers": {
|
||||
"1b8044f6-1d25-4a83-9662-acdf404341d2": {
|
||||
"ID": "1b8044f6-1d25-4a83-9662-acdf404341d2",
|
||||
"Name": "node6.new",
|
||||
"Address": "198.18.1.6:8300",
|
||||
"NodeStatus": "alive",
|
||||
"Version": "1.9.0",
|
||||
"LastContact": "1ms",
|
||||
"LastTerm": 3,
|
||||
"LastIndex": 41,
|
||||
"Healthy": true,
|
||||
"StableSince": "2020-11-06T14:52:00Z",
|
||||
"RedundancyZone": "zone3",
|
||||
"UpgradeVersion": "2.0.0",
|
||||
"ReadReplica": false,
|
||||
"Status": "non-voter",
|
||||
"Meta": {
|
||||
"bar": "baz",
|
||||
"upgrade": "2.0.0",
|
||||
"zone": "zone3"
|
||||
},
|
||||
"NodeType": "zone-standby"
|
||||
},
|
||||
"1baeb453-ad9e-489a-bbfe-53bee097aec8": {
|
||||
"ID": "1baeb453-ad9e-489a-bbfe-53bee097aec8",
|
||||
"Name": "node4",
|
||||
"Address": "198.18.0.4:8300",
|
||||
"NodeStatus": "alive",
|
||||
"Version": "1.9.0",
|
||||
"LastContact": "1ms",
|
||||
"LastTerm": 3,
|
||||
"LastIndex": 41,
|
||||
"Healthy": true,
|
||||
"StableSince": "2020-11-06T14:52:00Z",
|
||||
"RedundancyZone": "zone2",
|
||||
"UpgradeVersion": "1.0.0",
|
||||
"ReadReplica": false,
|
||||
"Status": "non-voter",
|
||||
"Meta": {
|
||||
"bar": "baz",
|
||||
"upgrade": "1.0.0",
|
||||
"zone": "zone2"
|
||||
},
|
||||
"NodeType": "zone-standby"
|
||||
},
|
||||
"3044d109-f028-4489-b59c-f267afe408f2": {
|
||||
"ID": "3044d109-f028-4489-b59c-f267afe408f2",
|
||||
"Name": "node2.new",
|
||||
"Address": "198.18.1.2:8300",
|
||||
"NodeStatus": "alive",
|
||||
"Version": "1.9.0",
|
||||
"LastContact": "1ms",
|
||||
"LastTerm": 3,
|
||||
"LastIndex": 41,
|
||||
"Healthy": true,
|
||||
"StableSince": "2020-11-06T14:52:00Z",
|
||||
"RedundancyZone": "zone1",
|
||||
"UpgradeVersion": "2.0.0",
|
||||
"ReadReplica": false,
|
||||
"Status": "non-voter",
|
||||
"Meta": {
|
||||
"bar": "baz",
|
||||
"upgrade": "2.0.0",
|
||||
"zone": "zone1"
|
||||
},
|
||||
"NodeType": "zone-standby"
|
||||
},
|
||||
"4691e516-6989-4a16-8f55-12ff2226a3c9": {
|
||||
"ID": "4691e516-6989-4a16-8f55-12ff2226a3c9",
|
||||
"Name": "node4.new",
|
||||
"Address": "198.18.1.4:8300",
|
||||
"NodeStatus": "alive",
|
||||
"Version": "1.9.0",
|
||||
"LastContact": "1ms",
|
||||
"LastTerm": 3,
|
||||
"LastIndex": 41,
|
||||
"Healthy": true,
|
||||
"StableSince": "2020-11-06T14:52:00Z",
|
||||
"RedundancyZone": "zone2",
|
||||
"UpgradeVersion": "2.0.0",
|
||||
"ReadReplica": false,
|
||||
"Status": "non-voter",
|
||||
"Meta": {
|
||||
"bar": "baz",
|
||||
"upgrade": "2.0.0",
|
||||
"zone": "zone2"
|
||||
},
|
||||
"NodeType": "zone-standby"
|
||||
},
|
||||
"4c42fe86-321d-4e8f-be0d-c261e6cfc453": {
|
||||
"ID": "4c42fe86-321d-4e8f-be0d-c261e6cfc453",
|
||||
"Name": "node5",
|
||||
"Address": "198.18.0.5:8300",
|
||||
"NodeStatus": "alive",
|
||||
"Version": "1.9.0",
|
||||
"LastContact": "1ms",
|
||||
"LastTerm": 3,
|
||||
"LastIndex": 42,
|
||||
"Healthy": true,
|
||||
"StableSince": "2020-11-06T14:51:00Z",
|
||||
"RedundancyZone": "zone3",
|
||||
"UpgradeVersion": "1.0.0",
|
||||
"ReadReplica": false,
|
||||
"Status": "voter",
|
||||
"Meta": {
|
||||
"foo": "bar",
|
||||
"upgrade": "1.0.0",
|
||||
"zone": "zone3"
|
||||
},
|
||||
"NodeType": "zone-voter"
|
||||
},
|
||||
"6ca5a41c-6162-41c1-b4eb-09082efe206f": {
|
||||
"ID": "6ca5a41c-6162-41c1-b4eb-09082efe206f",
|
||||
"Name": "node2",
|
||||
"Address": "198.18.0.2:8300",
|
||||
"NodeStatus": "alive",
|
||||
"Version": "1.9.0",
|
||||
"LastContact": "1ms",
|
||||
"LastTerm": 3,
|
||||
"LastIndex": 41,
|
||||
"Healthy": true,
|
||||
"StableSince": "2020-11-06T14:52:00Z",
|
||||
"RedundancyZone": "zone1",
|
||||
"UpgradeVersion": "1.0.0",
|
||||
"ReadReplica": false,
|
||||
"Status": "non-voter",
|
||||
"Meta": {
|
||||
"bar": "baz",
|
||||
"upgrade": "1.0.0",
|
||||
"zone": "zone1"
|
||||
},
|
||||
"NodeType": "zone-standby"
|
||||
},
|
||||
"6e8a37e5-a1c4-4212-a75e-91487d8cda6d": {
|
||||
"ID": "6e8a37e5-a1c4-4212-a75e-91487d8cda6d",
|
||||
"Name": "node3",
|
||||
"Address": "198.18.0.3:8300",
|
||||
"NodeStatus": "alive",
|
||||
"Version": "1.9.0",
|
||||
"LastContact": "1ms",
|
||||
"LastTerm": 3,
|
||||
"LastIndex": 42,
|
||||
"Healthy": true,
|
||||
"StableSince": "2020-11-06T14:51:00Z",
|
||||
"RedundancyZone": "zone2",
|
||||
"UpgradeVersion": "1.0.0",
|
||||
"ReadReplica": false,
|
||||
"Status": "voter",
|
||||
"Meta": {
|
||||
"foo": "bar",
|
||||
"upgrade": "1.0.0",
|
||||
"zone": "zone2"
|
||||
},
|
||||
"NodeType": "zone-voter"
|
||||
},
|
||||
"746b8782-ce77-41fd-bce0-cce62cca62b4": {
|
||||
"ID": "746b8782-ce77-41fd-bce0-cce62cca62b4",
|
||||
"Name": "node6",
|
||||
"Address": "198.18.0.6:8300",
|
||||
"NodeStatus": "alive",
|
||||
"Version": "1.9.0",
|
||||
"LastContact": "1ms",
|
||||
"LastTerm": 3,
|
||||
"LastIndex": 41,
|
||||
"Healthy": true,
|
||||
"StableSince": "2020-11-06T14:52:00Z",
|
||||
"RedundancyZone": "zone3",
|
||||
"UpgradeVersion": "1.0.0",
|
||||
"ReadReplica": false,
|
||||
"Status": "non-voter",
|
||||
"Meta": {
|
||||
"bar": "baz",
|
||||
"upgrade": "1.0.0",
|
||||
"zone": "zone3"
|
||||
},
|
||||
"NodeType": "zone-standby"
|
||||
},
|
||||
"79324811-9588-4311-b208-f272e38aaabf": {
|
||||
"ID": "79324811-9588-4311-b208-f272e38aaabf",
|
||||
"Name": "node1",
|
||||
"Address": "198.18.0.1:8300",
|
||||
"NodeStatus": "alive",
|
||||
"Version": "1.9.0",
|
||||
"LastContact": "0s",
|
||||
"LastTerm": 3,
|
||||
"LastIndex": 42,
|
||||
"Healthy": true,
|
||||
"StableSince": "2020-11-06T14:51:00Z",
|
||||
"RedundancyZone": "zone1",
|
||||
"UpgradeVersion": "1.0.0",
|
||||
"ReadReplica": false,
|
||||
"Status": "leader",
|
||||
"Meta": {
|
||||
"foo": "bar",
|
||||
"upgrade": "1.0.0",
|
||||
"zone": "zone1"
|
||||
},
|
||||
"NodeType": "zone-voter"
|
||||
},
|
||||
"7b02a615-ccce-4251-bda8-b89e0bd4f7c7": {
|
||||
"ID": "7b02a615-ccce-4251-bda8-b89e0bd4f7c7",
|
||||
"Name": "read-replica",
|
||||
"Address": "198.18.0.7:8300",
|
||||
"NodeStatus": "alive",
|
||||
"Version": "1.9.0",
|
||||
"LastContact": "2ms",
|
||||
"LastTerm": 3,
|
||||
"LastIndex": 39,
|
||||
"Healthy": true,
|
||||
"StableSince": "2020-11-06T14:53:00Z",
|
||||
"UpgradeVersion": "1.0.0",
|
||||
"ReadReplica": true,
|
||||
"Status": "non-voter",
|
||||
"Meta": {
|
||||
"baz": "foo",
|
||||
"version": "1.0.0"
|
||||
},
|
||||
"NodeType": "read-replica"
|
||||
},
|
||||
"98dfd0fd-504e-4280-8e73-6983a6af1b8c": {
|
||||
"ID": "98dfd0fd-504e-4280-8e73-6983a6af1b8c",
|
||||
"Name": "node5.new",
|
||||
"Address": "198.18.1.5:8300",
|
||||
"NodeStatus": "alive",
|
||||
"Version": "1.9.0",
|
||||
"LastContact": "1ms",
|
||||
"LastTerm": 3,
|
||||
"LastIndex": 42,
|
||||
"Healthy": true,
|
||||
"StableSince": "2020-11-06T14:51:00Z",
|
||||
"RedundancyZone": "zone3",
|
||||
"UpgradeVersion": "2.0.0",
|
||||
"ReadReplica": false,
|
||||
"Status": "non-voter",
|
||||
"Meta": {
|
||||
"foo": "bar",
|
||||
"upgrade": "2.0.0",
|
||||
"zone": "zone3"
|
||||
},
|
||||
"NodeType": "zone-standby"
|
||||
},
|
||||
"997b0851-37c5-4d65-a477-a8b3a56eea42": {
|
||||
"ID": "997b0851-37c5-4d65-a477-a8b3a56eea42",
|
||||
"Name": "node3.new",
|
||||
"Address": "198.18.1.3:8300",
|
||||
"NodeStatus": "alive",
|
||||
"Version": "1.9.0",
|
||||
"LastContact": "1ms",
|
||||
"LastTerm": 3,
|
||||
"LastIndex": 42,
|
||||
"Healthy": true,
|
||||
"StableSince": "2020-11-06T14:51:00Z",
|
||||
"RedundancyZone": "zone2",
|
||||
"UpgradeVersion": "2.0.0",
|
||||
"ReadReplica": false,
|
||||
"Status": "non-voter",
|
||||
"Meta": {
|
||||
"foo": "bar",
|
||||
"upgrade": "2.0.0",
|
||||
"zone": "zone2"
|
||||
},
|
||||
"NodeType": "zone-standby"
|
||||
},
|
||||
"de95799e-15a4-4c86-b508-78840554b7cb": {
|
||||
"ID": "de95799e-15a4-4c86-b508-78840554b7cb",
|
||||
"Name": "node1.new",
|
||||
"Address": "198.18.1.1:8300",
|
||||
"NodeStatus": "alive",
|
||||
"Version": "1.9.0",
|
||||
"LastContact": "0s",
|
||||
"LastTerm": 3,
|
||||
"LastIndex": 42,
|
||||
"Healthy": true,
|
||||
"StableSince": "2020-11-06T14:51:00Z",
|
||||
"RedundancyZone": "zone1",
|
||||
"UpgradeVersion": "2.0.0",
|
||||
"ReadReplica": false,
|
||||
"Status": "non-voter",
|
||||
"Meta": {
|
||||
"foo": "bar",
|
||||
"upgrade": "2.0.0",
|
||||
"zone": "zone1"
|
||||
},
|
||||
"NodeType": "zone-standby"
|
||||
}
|
||||
},
|
||||
"Leader": "79324811-9588-4311-b208-f272e38aaabf",
|
||||
"Voters": [
|
||||
"79324811-9588-4311-b208-f272e38aaabf",
|
||||
"6e8a37e5-a1c4-4212-a75e-91487d8cda6d",
|
||||
"4c42fe86-321d-4e8f-be0d-c261e6cfc453"
|
||||
],
|
||||
"ReadReplicas": [
|
||||
"7b02a615-ccce-4251-bda8-b89e0bd4f7c7"
|
||||
],
|
||||
"RedundancyZones": {
|
||||
"zone1": {
|
||||
"Servers": [
|
||||
"79324811-9588-4311-b208-f272e38aaabf",
|
||||
"6ca5a41c-6162-41c1-b4eb-09082efe206f",
|
||||
"de95799e-15a4-4c86-b508-78840554b7cb",
|
||||
"3044d109-f028-4489-b59c-f267afe408f2"
|
||||
],
|
||||
"Voters": [
|
||||
"79324811-9588-4311-b208-f272e38aaabf"
|
||||
],
|
||||
"FailureTolerance": 3
|
||||
},
|
||||
"zone2": {
|
||||
"Servers": [
|
||||
"6e8a37e5-a1c4-4212-a75e-91487d8cda6d",
|
||||
"1baeb453-ad9e-489a-bbfe-53bee097aec8",
|
||||
"997b0851-37c5-4d65-a477-a8b3a56eea42",
|
||||
"4691e516-6989-4a16-8f55-12ff2226a3c9"
|
||||
],
|
||||
"Voters": [
|
||||
"6e8a37e5-a1c4-4212-a75e-91487d8cda6d"
|
||||
],
|
||||
"FailureTolerance": 3
|
||||
},
|
||||
"zone3": {
|
||||
"Servers": [
|
||||
"4c42fe86-321d-4e8f-be0d-c261e6cfc453",
|
||||
"746b8782-ce77-41fd-bce0-cce62cca62b4",
|
||||
"98dfd0fd-504e-4280-8e73-6983a6af1b8c",
|
||||
"1b8044f6-1d25-4a83-9662-acdf404341d2"
|
||||
],
|
||||
"Voters": [
|
||||
"4c42fe86-321d-4e8f-be0d-c261e6cfc453"
|
||||
],
|
||||
"FailureTolerance": 3
|
||||
}
|
||||
},
|
||||
"Upgrade": {
|
||||
"Status": "promoting",
|
||||
"TargetVersion": "2.0.0",
|
||||
"TargetVersionNonVoters": [
|
||||
"de95799e-15a4-4c86-b508-78840554b7cb",
|
||||
"3044d109-f028-4489-b59c-f267afe408f2",
|
||||
"997b0851-37c5-4d65-a477-a8b3a56eea42",
|
||||
"4691e516-6989-4a16-8f55-12ff2226a3c9",
|
||||
"98dfd0fd-504e-4280-8e73-6983a6af1b8c",
|
||||
"1b8044f6-1d25-4a83-9662-acdf404341d2"
|
||||
],
|
||||
"OtherVersionVoters": [
|
||||
"79324811-9588-4311-b208-f272e38aaabf",
|
||||
"6e8a37e5-a1c4-4212-a75e-91487d8cda6d",
|
||||
"4c42fe86-321d-4e8f-be0d-c261e6cfc453"
|
||||
],
|
||||
"OtherVersionNonVoters": [
|
||||
"6ca5a41c-6162-41c1-b4eb-09082efe206f",
|
||||
"1baeb453-ad9e-489a-bbfe-53bee097aec8",
|
||||
"746b8782-ce77-41fd-bce0-cce62cca62b4"
|
||||
],
|
||||
"OtherVersionReadReplicas": [
|
||||
"7b02a615-ccce-4251-bda8-b89e0bd4f7c7"
|
||||
],
|
||||
"RedundancyZones": {
|
||||
"zone1": {
|
||||
"TargetVersionNonVoters": [
|
||||
"de95799e-15a4-4c86-b508-78840554b7cb",
|
||||
"3044d109-f028-4489-b59c-f267afe408f2"
|
||||
],
|
||||
"OtherVersionVoters": [
|
||||
"79324811-9588-4311-b208-f272e38aaabf"
|
||||
],
|
||||
"OtherVersionNonVoters": [
|
||||
"6ca5a41c-6162-41c1-b4eb-09082efe206f"
|
||||
]
|
||||
},
|
||||
"zone2": {
|
||||
"TargetVersionNonVoters": [
|
||||
"997b0851-37c5-4d65-a477-a8b3a56eea42",
|
||||
"4691e516-6989-4a16-8f55-12ff2226a3c9"
|
||||
],
|
||||
"OtherVersionVoters": [
|
||||
"6e8a37e5-a1c4-4212-a75e-91487d8cda6d"
|
||||
],
|
||||
"OtherVersionNonVoters": [
|
||||
"1baeb453-ad9e-489a-bbfe-53bee097aec8"
|
||||
]
|
||||
},
|
||||
"zone3": {
|
||||
"TargetVersionNonVoters": [
|
||||
"98dfd0fd-504e-4280-8e73-6983a6af1b8c",
|
||||
"1b8044f6-1d25-4a83-9662-acdf404341d2"
|
||||
],
|
||||
"OtherVersionVoters": [
|
||||
"4c42fe86-321d-4e8f-be0d-c261e6cfc453"
|
||||
],
|
||||
"OtherVersionNonVoters": [
|
||||
"746b8782-ce77-41fd-bce0-cce62cca62b4"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,279 @@
|
|||
Healthy: true
|
||||
Failure Tolerance: 1
|
||||
Optimistic Failure Tolerance: 0
|
||||
Leader: 79324811-9588-4311-b208-f272e38aaabf
|
||||
Voters:
|
||||
79324811-9588-4311-b208-f272e38aaabf
|
||||
6e8a37e5-a1c4-4212-a75e-91487d8cda6d
|
||||
4c42fe86-321d-4e8f-be0d-c261e6cfc453
|
||||
Read Replicas:
|
||||
7b02a615-ccce-4251-bda8-b89e0bd4f7c7
|
||||
Redundancy Zones:
|
||||
zone1:
|
||||
Failure Tolerance: 3
|
||||
Voters:
|
||||
79324811-9588-4311-b208-f272e38aaabf
|
||||
Servers:
|
||||
79324811-9588-4311-b208-f272e38aaabf
|
||||
6ca5a41c-6162-41c1-b4eb-09082efe206f
|
||||
de95799e-15a4-4c86-b508-78840554b7cb
|
||||
3044d109-f028-4489-b59c-f267afe408f2
|
||||
zone2:
|
||||
Failure Tolerance: 3
|
||||
Voters:
|
||||
6e8a37e5-a1c4-4212-a75e-91487d8cda6d
|
||||
Servers:
|
||||
6e8a37e5-a1c4-4212-a75e-91487d8cda6d
|
||||
1baeb453-ad9e-489a-bbfe-53bee097aec8
|
||||
997b0851-37c5-4d65-a477-a8b3a56eea42
|
||||
4691e516-6989-4a16-8f55-12ff2226a3c9
|
||||
zone3:
|
||||
Failure Tolerance: 3
|
||||
Voters:
|
||||
4c42fe86-321d-4e8f-be0d-c261e6cfc453
|
||||
Servers:
|
||||
4c42fe86-321d-4e8f-be0d-c261e6cfc453
|
||||
746b8782-ce77-41fd-bce0-cce62cca62b4
|
||||
98dfd0fd-504e-4280-8e73-6983a6af1b8c
|
||||
1b8044f6-1d25-4a83-9662-acdf404341d2
|
||||
Upgrade:
|
||||
Status: promoting
|
||||
Target Version: 2.0.0
|
||||
Target Version Non-Voters:
|
||||
de95799e-15a4-4c86-b508-78840554b7cb
|
||||
3044d109-f028-4489-b59c-f267afe408f2
|
||||
997b0851-37c5-4d65-a477-a8b3a56eea42
|
||||
4691e516-6989-4a16-8f55-12ff2226a3c9
|
||||
98dfd0fd-504e-4280-8e73-6983a6af1b8c
|
||||
1b8044f6-1d25-4a83-9662-acdf404341d2
|
||||
Other Version Voters:
|
||||
79324811-9588-4311-b208-f272e38aaabf
|
||||
6e8a37e5-a1c4-4212-a75e-91487d8cda6d
|
||||
4c42fe86-321d-4e8f-be0d-c261e6cfc453
|
||||
Other Version Non-Voters:
|
||||
6ca5a41c-6162-41c1-b4eb-09082efe206f
|
||||
1baeb453-ad9e-489a-bbfe-53bee097aec8
|
||||
746b8782-ce77-41fd-bce0-cce62cca62b4
|
||||
Other Version ReadReplicas:
|
||||
7b02a615-ccce-4251-bda8-b89e0bd4f7c7
|
||||
Servers:
|
||||
1b8044f6-1d25-4a83-9662-acdf404341d2
|
||||
Name: node6.new
|
||||
Address: 198.18.1.6:8300
|
||||
Version: 1.9.0
|
||||
Status: non-voter
|
||||
Node Type: zone-standby
|
||||
Node Status: alive
|
||||
Healthy: true
|
||||
Last Contact: 1ms
|
||||
Last Term: 3
|
||||
Last Index: 41
|
||||
Redundancy Zone: zone3
|
||||
Upgrade Version: 2.0.0
|
||||
Meta
|
||||
"bar": "baz"
|
||||
"upgrade": "2.0.0"
|
||||
"zone": "zone3"
|
||||
1baeb453-ad9e-489a-bbfe-53bee097aec8
|
||||
Name: node4
|
||||
Address: 198.18.0.4:8300
|
||||
Version: 1.9.0
|
||||
Status: non-voter
|
||||
Node Type: zone-standby
|
||||
Node Status: alive
|
||||
Healthy: true
|
||||
Last Contact: 1ms
|
||||
Last Term: 3
|
||||
Last Index: 41
|
||||
Redundancy Zone: zone2
|
||||
Upgrade Version: 1.0.0
|
||||
Meta
|
||||
"bar": "baz"
|
||||
"upgrade": "1.0.0"
|
||||
"zone": "zone2"
|
||||
3044d109-f028-4489-b59c-f267afe408f2
|
||||
Name: node2.new
|
||||
Address: 198.18.1.2:8300
|
||||
Version: 1.9.0
|
||||
Status: non-voter
|
||||
Node Type: zone-standby
|
||||
Node Status: alive
|
||||
Healthy: true
|
||||
Last Contact: 1ms
|
||||
Last Term: 3
|
||||
Last Index: 41
|
||||
Redundancy Zone: zone1
|
||||
Upgrade Version: 2.0.0
|
||||
Meta
|
||||
"bar": "baz"
|
||||
"upgrade": "2.0.0"
|
||||
"zone": "zone1"
|
||||
4691e516-6989-4a16-8f55-12ff2226a3c9
|
||||
Name: node4.new
|
||||
Address: 198.18.1.4:8300
|
||||
Version: 1.9.0
|
||||
Status: non-voter
|
||||
Node Type: zone-standby
|
||||
Node Status: alive
|
||||
Healthy: true
|
||||
Last Contact: 1ms
|
||||
Last Term: 3
|
||||
Last Index: 41
|
||||
Redundancy Zone: zone2
|
||||
Upgrade Version: 2.0.0
|
||||
Meta
|
||||
"bar": "baz"
|
||||
"upgrade": "2.0.0"
|
||||
"zone": "zone2"
|
||||
4c42fe86-321d-4e8f-be0d-c261e6cfc453
|
||||
Name: node5
|
||||
Address: 198.18.0.5:8300
|
||||
Version: 1.9.0
|
||||
Status: voter
|
||||
Node Type: zone-voter
|
||||
Node Status: alive
|
||||
Healthy: true
|
||||
Last Contact: 1ms
|
||||
Last Term: 3
|
||||
Last Index: 42
|
||||
Redundancy Zone: zone3
|
||||
Upgrade Version: 1.0.0
|
||||
Meta
|
||||
"foo": "bar"
|
||||
"upgrade": "1.0.0"
|
||||
"zone": "zone3"
|
||||
6ca5a41c-6162-41c1-b4eb-09082efe206f
|
||||
Name: node2
|
||||
Address: 198.18.0.2:8300
|
||||
Version: 1.9.0
|
||||
Status: non-voter
|
||||
Node Type: zone-standby
|
||||
Node Status: alive
|
||||
Healthy: true
|
||||
Last Contact: 1ms
|
||||
Last Term: 3
|
||||
Last Index: 41
|
||||
Redundancy Zone: zone1
|
||||
Upgrade Version: 1.0.0
|
||||
Meta
|
||||
"bar": "baz"
|
||||
"upgrade": "1.0.0"
|
||||
"zone": "zone1"
|
||||
6e8a37e5-a1c4-4212-a75e-91487d8cda6d
|
||||
Name: node3
|
||||
Address: 198.18.0.3:8300
|
||||
Version: 1.9.0
|
||||
Status: voter
|
||||
Node Type: zone-voter
|
||||
Node Status: alive
|
||||
Healthy: true
|
||||
Last Contact: 1ms
|
||||
Last Term: 3
|
||||
Last Index: 42
|
||||
Redundancy Zone: zone2
|
||||
Upgrade Version: 1.0.0
|
||||
Meta
|
||||
"foo": "bar"
|
||||
"upgrade": "1.0.0"
|
||||
"zone": "zone2"
|
||||
746b8782-ce77-41fd-bce0-cce62cca62b4
|
||||
Name: node6
|
||||
Address: 198.18.0.6:8300
|
||||
Version: 1.9.0
|
||||
Status: non-voter
|
||||
Node Type: zone-standby
|
||||
Node Status: alive
|
||||
Healthy: true
|
||||
Last Contact: 1ms
|
||||
Last Term: 3
|
||||
Last Index: 41
|
||||
Redundancy Zone: zone3
|
||||
Upgrade Version: 1.0.0
|
||||
Meta
|
||||
"bar": "baz"
|
||||
"upgrade": "1.0.0"
|
||||
"zone": "zone3"
|
||||
79324811-9588-4311-b208-f272e38aaabf
|
||||
Name: node1
|
||||
Address: 198.18.0.1:8300
|
||||
Version: 1.9.0
|
||||
Status: leader
|
||||
Node Type: zone-voter
|
||||
Node Status: alive
|
||||
Healthy: true
|
||||
Last Contact: 0s
|
||||
Last Term: 3
|
||||
Last Index: 42
|
||||
Redundancy Zone: zone1
|
||||
Upgrade Version: 1.0.0
|
||||
Meta
|
||||
"foo": "bar"
|
||||
"upgrade": "1.0.0"
|
||||
"zone": "zone1"
|
||||
7b02a615-ccce-4251-bda8-b89e0bd4f7c7
|
||||
Name: read-replica
|
||||
Address: 198.18.0.7:8300
|
||||
Version: 1.9.0
|
||||
Status: non-voter
|
||||
Node Type: read-replica
|
||||
Node Status: alive
|
||||
Healthy: true
|
||||
Last Contact: 2ms
|
||||
Last Term: 3
|
||||
Last Index: 39
|
||||
Upgrade Version: 1.0.0
|
||||
Read Replica: true
|
||||
Meta
|
||||
"baz": "foo"
|
||||
"version": "1.0.0"
|
||||
98dfd0fd-504e-4280-8e73-6983a6af1b8c
|
||||
Name: node5.new
|
||||
Address: 198.18.1.5:8300
|
||||
Version: 1.9.0
|
||||
Status: non-voter
|
||||
Node Type: zone-standby
|
||||
Node Status: alive
|
||||
Healthy: true
|
||||
Last Contact: 1ms
|
||||
Last Term: 3
|
||||
Last Index: 42
|
||||
Redundancy Zone: zone3
|
||||
Upgrade Version: 2.0.0
|
||||
Meta
|
||||
"foo": "bar"
|
||||
"upgrade": "2.0.0"
|
||||
"zone": "zone3"
|
||||
997b0851-37c5-4d65-a477-a8b3a56eea42
|
||||
Name: node3.new
|
||||
Address: 198.18.1.3:8300
|
||||
Version: 1.9.0
|
||||
Status: non-voter
|
||||
Node Type: zone-standby
|
||||
Node Status: alive
|
||||
Healthy: true
|
||||
Last Contact: 1ms
|
||||
Last Term: 3
|
||||
Last Index: 42
|
||||
Redundancy Zone: zone2
|
||||
Upgrade Version: 2.0.0
|
||||
Meta
|
||||
"foo": "bar"
|
||||
"upgrade": "2.0.0"
|
||||
"zone": "zone2"
|
||||
de95799e-15a4-4c86-b508-78840554b7cb
|
||||
Name: node1.new
|
||||
Address: 198.18.1.1:8300
|
||||
Version: 1.9.0
|
||||
Status: non-voter
|
||||
Node Type: zone-standby
|
||||
Node Status: alive
|
||||
Healthy: true
|
||||
Last Contact: 0s
|
||||
Last Term: 3
|
||||
Last Index: 42
|
||||
Redundancy Zone: zone1
|
||||
Upgrade Version: 2.0.0
|
||||
Meta
|
||||
"foo": "bar"
|
||||
"upgrade": "2.0.0"
|
||||
"zone": "zone1"
|
|
@ -0,0 +1,389 @@
|
|||
{
|
||||
"Healthy": true,
|
||||
"FailureTolerance": 1,
|
||||
"OptimisitcFailureTolerance": 10,
|
||||
"Servers": {
|
||||
"1b8044f6-1d25-4a83-9662-acdf404341d2": {
|
||||
"ID": "1b8044f6-1d25-4a83-9662-acdf404341d2",
|
||||
"Name": "node6.new",
|
||||
"Address": "198.18.1.6:8300",
|
||||
"NodeStatus": "alive",
|
||||
"Version": "1.9.0",
|
||||
"LastContact": "1ms",
|
||||
"LastTerm": 3,
|
||||
"LastIndex": 41,
|
||||
"Healthy": true,
|
||||
"StableSince": "2020-11-06T14:52:00Z",
|
||||
"RedundancyZone": "zone3",
|
||||
"UpgradeVersion": "2.0.0",
|
||||
"Status": "non-voter",
|
||||
"Meta": {
|
||||
"bar": "baz",
|
||||
"upgrade": "2.0.0",
|
||||
"zone": "zone3"
|
||||
},
|
||||
"NodeType": "zone-standby"
|
||||
},
|
||||
"1baeb453-ad9e-489a-bbfe-53bee097aec8": {
|
||||
"ID": "1baeb453-ad9e-489a-bbfe-53bee097aec8",
|
||||
"Name": "node4",
|
||||
"Address": "198.18.0.4:8300",
|
||||
"NodeStatus": "alive",
|
||||
"Version": "1.9.0",
|
||||
"LastContact": "1ms",
|
||||
"LastTerm": 3,
|
||||
"LastIndex": 41,
|
||||
"Healthy": true,
|
||||
"StableSince": "2020-11-06T14:52:00Z",
|
||||
"RedundancyZone": "zone2",
|
||||
"UpgradeVersion": "1.0.0",
|
||||
"Status": "non-voter",
|
||||
"Meta": {
|
||||
"bar": "baz",
|
||||
"upgrade": "1.0.0",
|
||||
"zone": "zone2"
|
||||
},
|
||||
"NodeType": "zone-standby"
|
||||
},
|
||||
"3044d109-f028-4489-b59c-f267afe408f2": {
|
||||
"ID": "3044d109-f028-4489-b59c-f267afe408f2",
|
||||
"Name": "node2.new",
|
||||
"Address": "198.18.1.2:8300",
|
||||
"NodeStatus": "alive",
|
||||
"Version": "1.9.0",
|
||||
"LastContact": "1ms",
|
||||
"LastTerm": 3,
|
||||
"LastIndex": 41,
|
||||
"Healthy": true,
|
||||
"StableSince": "2020-11-06T14:52:00Z",
|
||||
"RedundancyZone": "zone1",
|
||||
"UpgradeVersion": "2.0.0",
|
||||
"Status": "non-voter",
|
||||
"Meta": {
|
||||
"bar": "baz",
|
||||
"upgrade": "2.0.0",
|
||||
"zone": "zone1"
|
||||
},
|
||||
"NodeType": "zone-standby"
|
||||
},
|
||||
"4691e516-6989-4a16-8f55-12ff2226a3c9": {
|
||||
"ID": "4691e516-6989-4a16-8f55-12ff2226a3c9",
|
||||
"Name": "node4.new",
|
||||
"Address": "198.18.1.4:8300",
|
||||
"NodeStatus": "alive",
|
||||
"Version": "1.9.0",
|
||||
"LastContact": "1ms",
|
||||
"LastTerm": 3,
|
||||
"LastIndex": 41,
|
||||
"Healthy": true,
|
||||
"StableSince": "2020-11-06T14:52:00Z",
|
||||
"RedundancyZone": "zone2",
|
||||
"UpgradeVersion": "2.0.0",
|
||||
"Status": "non-voter",
|
||||
"Meta": {
|
||||
"bar": "baz",
|
||||
"upgrade": "2.0.0",
|
||||
"zone": "zone2"
|
||||
},
|
||||
"NodeType": "zone-standby"
|
||||
},
|
||||
"4c42fe86-321d-4e8f-be0d-c261e6cfc453": {
|
||||
"ID": "4c42fe86-321d-4e8f-be0d-c261e6cfc453",
|
||||
"Name": "node5",
|
||||
"Address": "198.18.0.5:8300",
|
||||
"NodeStatus": "alive",
|
||||
"Version": "1.9.0",
|
||||
"LastContact": "1ms",
|
||||
"LastTerm": 3,
|
||||
"LastIndex": 42,
|
||||
"Healthy": true,
|
||||
"StableSince": "2020-11-06T14:51:00Z",
|
||||
"RedundancyZone": "zone3",
|
||||
"UpgradeVersion": "1.0.0",
|
||||
"Status": "voter",
|
||||
"Meta": {
|
||||
"foo": "bar",
|
||||
"upgrade": "1.0.0",
|
||||
"zone": "zone3"
|
||||
},
|
||||
"NodeType": "zone-voter"
|
||||
},
|
||||
"6ca5a41c-6162-41c1-b4eb-09082efe206f": {
|
||||
"ID": "6ca5a41c-6162-41c1-b4eb-09082efe206f",
|
||||
"Name": "node2",
|
||||
"Address": "198.18.0.2:8300",
|
||||
"NodeStatus": "alive",
|
||||
"Version": "1.9.0",
|
||||
"LastContact": "1ms",
|
||||
"LastTerm": 3,
|
||||
"LastIndex": 41,
|
||||
"Healthy": true,
|
||||
"StableSince": "2020-11-06T14:52:00Z",
|
||||
"RedundancyZone": "zone1",
|
||||
"UpgradeVersion": "1.0.0",
|
||||
"Status": "non-voter",
|
||||
"Meta": {
|
||||
"bar": "baz",
|
||||
"upgrade": "1.0.0",
|
||||
"zone": "zone1"
|
||||
},
|
||||
"NodeType": "zone-standby"
|
||||
},
|
||||
"6e8a37e5-a1c4-4212-a75e-91487d8cda6d": {
|
||||
"ID": "6e8a37e5-a1c4-4212-a75e-91487d8cda6d",
|
||||
"Name": "node3",
|
||||
"Address": "198.18.0.3:8300",
|
||||
"NodeStatus": "alive",
|
||||
"Version": "1.9.0",
|
||||
"LastContact": "1ms",
|
||||
"LastTerm": 3,
|
||||
"LastIndex": 42,
|
||||
"Healthy": true,
|
||||
"StableSince": "2020-11-06T14:51:00Z",
|
||||
"RedundancyZone": "zone2",
|
||||
"UpgradeVersion": "1.0.0",
|
||||
"Status": "voter",
|
||||
"Meta": {
|
||||
"foo": "bar",
|
||||
"upgrade": "1.0.0",
|
||||
"zone": "zone2"
|
||||
},
|
||||
"NodeType": "zone-voter"
|
||||
},
|
||||
"746b8782-ce77-41fd-bce0-cce62cca62b4": {
|
||||
"ID": "746b8782-ce77-41fd-bce0-cce62cca62b4",
|
||||
"Name": "node6",
|
||||
"Address": "198.18.0.6:8300",
|
||||
"NodeStatus": "alive",
|
||||
"Version": "1.9.0",
|
||||
"LastContact": "1ms",
|
||||
"LastTerm": 3,
|
||||
"LastIndex": 41,
|
||||
"Healthy": true,
|
||||
"StableSince": "2020-11-06T14:52:00Z",
|
||||
"RedundancyZone": "zone3",
|
||||
"UpgradeVersion": "1.0.0",
|
||||
"Status": "non-voter",
|
||||
"Meta": {
|
||||
"bar": "baz",
|
||||
"upgrade": "1.0.0",
|
||||
"zone": "zone3"
|
||||
},
|
||||
"NodeType": "zone-standby"
|
||||
},
|
||||
"79324811-9588-4311-b208-f272e38aaabf": {
|
||||
"ID": "79324811-9588-4311-b208-f272e38aaabf",
|
||||
"Name": "node1",
|
||||
"Address": "198.18.0.1:8300",
|
||||
"NodeStatus": "alive",
|
||||
"Version": "1.9.0",
|
||||
"LastContact": "0s",
|
||||
"LastTerm": 3,
|
||||
"LastIndex": 42,
|
||||
"Healthy": true,
|
||||
"StableSince": "2020-11-06T14:51:00Z",
|
||||
"RedundancyZone": "zone1",
|
||||
"UpgradeVersion": "1.0.0",
|
||||
"Status": "leader",
|
||||
"Meta": {
|
||||
"foo": "bar",
|
||||
"upgrade": "1.0.0",
|
||||
"zone": "zone1"
|
||||
},
|
||||
"NodeType": "zone-voter"
|
||||
},
|
||||
"7b02a615-ccce-4251-bda8-b89e0bd4f7c7": {
|
||||
"ID": "7b02a615-ccce-4251-bda8-b89e0bd4f7c7",
|
||||
"Name": "read-replica",
|
||||
"Address": "198.18.0.7:8300",
|
||||
"NodeStatus": "alive",
|
||||
"Version": "1.9.0",
|
||||
"LastContact": "2ms",
|
||||
"LastTerm": 3,
|
||||
"LastIndex": 39,
|
||||
"Healthy": true,
|
||||
"StableSince": "2020-11-06T14:53:00Z",
|
||||
"UpgradeVersion": "1.0.0",
|
||||
"ReadReplica": true,
|
||||
"Status": "non-voter",
|
||||
"Meta": {
|
||||
"baz": "foo",
|
||||
"version": "1.0.0"
|
||||
},
|
||||
"NodeType": "read-replica"
|
||||
},
|
||||
"98dfd0fd-504e-4280-8e73-6983a6af1b8c": {
|
||||
"ID": "98dfd0fd-504e-4280-8e73-6983a6af1b8c",
|
||||
"Name": "node5.new",
|
||||
"Address": "198.18.1.5:8300",
|
||||
"NodeStatus": "alive",
|
||||
"Version": "1.9.0",
|
||||
"LastContact": "1ms",
|
||||
"LastTerm": 3,
|
||||
"LastIndex": 42,
|
||||
"Healthy": true,
|
||||
"StableSince": "2020-11-06T14:51:00Z",
|
||||
"RedundancyZone": "zone3",
|
||||
"UpgradeVersion": "2.0.0",
|
||||
"Status": "non-voter",
|
||||
"Meta": {
|
||||
"foo": "bar",
|
||||
"upgrade": "2.0.0",
|
||||
"zone": "zone3"
|
||||
},
|
||||
"NodeType": "zone-standby"
|
||||
},
|
||||
"997b0851-37c5-4d65-a477-a8b3a56eea42": {
|
||||
"ID": "997b0851-37c5-4d65-a477-a8b3a56eea42",
|
||||
"Name": "node3.new",
|
||||
"Address": "198.18.1.3:8300",
|
||||
"NodeStatus": "alive",
|
||||
"Version": "1.9.0",
|
||||
"LastContact": "1ms",
|
||||
"LastTerm": 3,
|
||||
"LastIndex": 42,
|
||||
"Healthy": true,
|
||||
"StableSince": "2020-11-06T14:51:00Z",
|
||||
"RedundancyZone": "zone2",
|
||||
"UpgradeVersion": "2.0.0",
|
||||
"Status": "non-voter",
|
||||
"Meta": {
|
||||
"foo": "bar",
|
||||
"upgrade": "2.0.0",
|
||||
"zone": "zone2"
|
||||
},
|
||||
"NodeType": "zone-standby"
|
||||
},
|
||||
"de95799e-15a4-4c86-b508-78840554b7cb": {
|
||||
"ID": "de95799e-15a4-4c86-b508-78840554b7cb",
|
||||
"Name": "node1.new",
|
||||
"Address": "198.18.1.1:8300",
|
||||
"NodeStatus": "alive",
|
||||
"Version": "1.9.0",
|
||||
"LastContact": "0s",
|
||||
"LastTerm": 3,
|
||||
"LastIndex": 42,
|
||||
"Healthy": true,
|
||||
"StableSince": "2020-11-06T14:51:00Z",
|
||||
"RedundancyZone": "zone1",
|
||||
"UpgradeVersion": "2.0.0",
|
||||
"Status": "non-voter",
|
||||
"Meta": {
|
||||
"foo": "bar",
|
||||
"upgrade": "2.0.0",
|
||||
"zone": "zone1"
|
||||
},
|
||||
"NodeType": "zone-standby"
|
||||
}
|
||||
},
|
||||
"Leader": "79324811-9588-4311-b208-f272e38aaabf",
|
||||
"Voters": [
|
||||
"79324811-9588-4311-b208-f272e38aaabf",
|
||||
"6e8a37e5-a1c4-4212-a75e-91487d8cda6d",
|
||||
"4c42fe86-321d-4e8f-be0d-c261e6cfc453"
|
||||
],
|
||||
"ReadReplicas": [
|
||||
"7b02a615-ccce-4251-bda8-b89e0bd4f7c7"
|
||||
],
|
||||
"RedundancyZones": {
|
||||
"zone1": {
|
||||
"Servers": [
|
||||
"79324811-9588-4311-b208-f272e38aaabf",
|
||||
"6ca5a41c-6162-41c1-b4eb-09082efe206f",
|
||||
"de95799e-15a4-4c86-b508-78840554b7cb",
|
||||
"3044d109-f028-4489-b59c-f267afe408f2"
|
||||
],
|
||||
"Voters": [
|
||||
"79324811-9588-4311-b208-f272e38aaabf"
|
||||
],
|
||||
"FailureTolerance": 3
|
||||
},
|
||||
"zone2": {
|
||||
"Servers": [
|
||||
"6e8a37e5-a1c4-4212-a75e-91487d8cda6d",
|
||||
"1baeb453-ad9e-489a-bbfe-53bee097aec8",
|
||||
"997b0851-37c5-4d65-a477-a8b3a56eea42",
|
||||
"4691e516-6989-4a16-8f55-12ff2226a3c9"
|
||||
],
|
||||
"Voters": [
|
||||
"6e8a37e5-a1c4-4212-a75e-91487d8cda6d"
|
||||
],
|
||||
"FailureTolerance": 3
|
||||
},
|
||||
"zone3": {
|
||||
"Servers": [
|
||||
"4c42fe86-321d-4e8f-be0d-c261e6cfc453",
|
||||
"746b8782-ce77-41fd-bce0-cce62cca62b4",
|
||||
"98dfd0fd-504e-4280-8e73-6983a6af1b8c",
|
||||
"1b8044f6-1d25-4a83-9662-acdf404341d2"
|
||||
],
|
||||
"Voters": [
|
||||
"4c42fe86-321d-4e8f-be0d-c261e6cfc453"
|
||||
],
|
||||
"FailureTolerance": 3
|
||||
}
|
||||
},
|
||||
"Upgrade": {
|
||||
"Status": "promoting",
|
||||
"TargetVersion": "2.0.0",
|
||||
"TargetVersionNonVoters": [
|
||||
"de95799e-15a4-4c86-b508-78840554b7cb",
|
||||
"3044d109-f028-4489-b59c-f267afe408f2",
|
||||
"997b0851-37c5-4d65-a477-a8b3a56eea42",
|
||||
"4691e516-6989-4a16-8f55-12ff2226a3c9",
|
||||
"98dfd0fd-504e-4280-8e73-6983a6af1b8c",
|
||||
"1b8044f6-1d25-4a83-9662-acdf404341d2"
|
||||
],
|
||||
"OtherVersionVoters": [
|
||||
"79324811-9588-4311-b208-f272e38aaabf",
|
||||
"6e8a37e5-a1c4-4212-a75e-91487d8cda6d",
|
||||
"4c42fe86-321d-4e8f-be0d-c261e6cfc453"
|
||||
],
|
||||
"OtherVersionNonVoters": [
|
||||
"6ca5a41c-6162-41c1-b4eb-09082efe206f",
|
||||
"1baeb453-ad9e-489a-bbfe-53bee097aec8",
|
||||
"746b8782-ce77-41fd-bce0-cce62cca62b4"
|
||||
],
|
||||
"OtherVersionReadReplicas": [
|
||||
"7b02a615-ccce-4251-bda8-b89e0bd4f7c7"
|
||||
],
|
||||
"RedundancyZones": {
|
||||
"zone1": {
|
||||
"TargetVersionNonVoters": [
|
||||
"de95799e-15a4-4c86-b508-78840554b7cb",
|
||||
"3044d109-f028-4489-b59c-f267afe408f2"
|
||||
],
|
||||
"OtherVersionVoters": [
|
||||
"79324811-9588-4311-b208-f272e38aaabf"
|
||||
],
|
||||
"OtherVersionNonVoters": [
|
||||
"6ca5a41c-6162-41c1-b4eb-09082efe206f"
|
||||
]
|
||||
},
|
||||
"zone2": {
|
||||
"TargetVersionNonVoters": [
|
||||
"997b0851-37c5-4d65-a477-a8b3a56eea42",
|
||||
"4691e516-6989-4a16-8f55-12ff2226a3c9"
|
||||
],
|
||||
"OtherVersionVoters": [
|
||||
"6e8a37e5-a1c4-4212-a75e-91487d8cda6d"
|
||||
],
|
||||
"OtherVersionNonVoters": [
|
||||
"1baeb453-ad9e-489a-bbfe-53bee097aec8"
|
||||
]
|
||||
},
|
||||
"zone3": {
|
||||
"TargetVersionNonVoters": [
|
||||
"98dfd0fd-504e-4280-8e73-6983a6af1b8c",
|
||||
"1b8044f6-1d25-4a83-9662-acdf404341d2"
|
||||
],
|
||||
"OtherVersionVoters": [
|
||||
"4c42fe86-321d-4e8f-be0d-c261e6cfc453"
|
||||
],
|
||||
"OtherVersionNonVoters": [
|
||||
"746b8782-ce77-41fd-bce0-cce62cca62b4"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,67 @@
|
|||
{
|
||||
"Healthy": true,
|
||||
"FailureTolerance": 1,
|
||||
"OptimisticFailureTolerance": 0,
|
||||
"Servers": {
|
||||
"79324811-9588-4311-b208-f272e38aaabf": {
|
||||
"ID": "79324811-9588-4311-b208-f272e38aaabf",
|
||||
"Name": "node1",
|
||||
"Address": "198.18.0.1:8300",
|
||||
"NodeStatus": "alive",
|
||||
"Version": "1.9.0",
|
||||
"LastContact": "0s",
|
||||
"LastTerm": 3,
|
||||
"LastIndex": 42,
|
||||
"Healthy": true,
|
||||
"StableSince": "2020-11-06T14:51:00Z",
|
||||
"ReadReplica": false,
|
||||
"Status": "leader",
|
||||
"Meta": {
|
||||
"foo": "bar"
|
||||
},
|
||||
"NodeType": "voter"
|
||||
},
|
||||
"ae84aefb-a303-4734-8739-5c102d4ee2d9": {
|
||||
"ID": "ae84aefb-a303-4734-8739-5c102d4ee2d9",
|
||||
"Name": "node3",
|
||||
"Address": "198.18.0.3:8300",
|
||||
"NodeStatus": "alive",
|
||||
"Version": "1.9.0",
|
||||
"LastContact": "2ms",
|
||||
"LastTerm": 3,
|
||||
"LastIndex": 39,
|
||||
"Healthy": true,
|
||||
"StableSince": "2020-11-06T14:53:00Z",
|
||||
"ReadReplica": false,
|
||||
"Status": "voter",
|
||||
"Meta": {
|
||||
"baz": "foo"
|
||||
},
|
||||
"NodeType": "voter"
|
||||
},
|
||||
"ef8aee9a-f9d6-4ec4-b383-aac956bdb80f": {
|
||||
"ID": "ef8aee9a-f9d6-4ec4-b383-aac956bdb80f",
|
||||
"Name": "node2",
|
||||
"Address": "198.18.0.2:8300",
|
||||
"NodeStatus": "alive",
|
||||
"Version": "1.9.0",
|
||||
"LastContact": "1ms",
|
||||
"LastTerm": 3,
|
||||
"LastIndex": 41,
|
||||
"Healthy": true,
|
||||
"StableSince": "2020-11-06T14:52:00Z",
|
||||
"ReadReplica": false,
|
||||
"Status": "voter",
|
||||
"Meta": {
|
||||
"bar": "baz"
|
||||
},
|
||||
"NodeType": "voter"
|
||||
}
|
||||
},
|
||||
"Leader": "79324811-9588-4311-b208-f272e38aaabf",
|
||||
"Voters": [
|
||||
"79324811-9588-4311-b208-f272e38aaabf",
|
||||
"ef8aee9a-f9d6-4ec4-b383-aac956bdb80f",
|
||||
"ae84aefb-a303-4734-8739-5c102d4ee2d9"
|
||||
]
|
||||
}
|
|
@ -0,0 +1,48 @@
|
|||
Healthy: true
|
||||
Failure Tolerance: 1
|
||||
Optimistic Failure Tolerance: 0
|
||||
Leader: 79324811-9588-4311-b208-f272e38aaabf
|
||||
Voters:
|
||||
79324811-9588-4311-b208-f272e38aaabf
|
||||
ef8aee9a-f9d6-4ec4-b383-aac956bdb80f
|
||||
ae84aefb-a303-4734-8739-5c102d4ee2d9
|
||||
Servers:
|
||||
79324811-9588-4311-b208-f272e38aaabf
|
||||
Name: node1
|
||||
Address: 198.18.0.1:8300
|
||||
Version: 1.9.0
|
||||
Status: leader
|
||||
Node Type: voter
|
||||
Node Status: alive
|
||||
Healthy: true
|
||||
Last Contact: 0s
|
||||
Last Term: 3
|
||||
Last Index: 42
|
||||
Meta
|
||||
"foo": "bar"
|
||||
ae84aefb-a303-4734-8739-5c102d4ee2d9
|
||||
Name: node3
|
||||
Address: 198.18.0.3:8300
|
||||
Version: 1.9.0
|
||||
Status: voter
|
||||
Node Type: voter
|
||||
Node Status: alive
|
||||
Healthy: true
|
||||
Last Contact: 2ms
|
||||
Last Term: 3
|
||||
Last Index: 39
|
||||
Meta
|
||||
"baz": "foo"
|
||||
ef8aee9a-f9d6-4ec4-b383-aac956bdb80f
|
||||
Name: node2
|
||||
Address: 198.18.0.2:8300
|
||||
Version: 1.9.0
|
||||
Status: voter
|
||||
Node Type: voter
|
||||
Node Status: alive
|
||||
Healthy: true
|
||||
Last Contact: 1ms
|
||||
Last Term: 3
|
||||
Last Index: 41
|
||||
Meta
|
||||
"bar": "baz"
|
|
@ -0,0 +1,64 @@
|
|||
{
|
||||
"Healthy": true,
|
||||
"FailureTolerance": 1,
|
||||
"OptimisitcFailureTolerance": 0,
|
||||
"Servers": {
|
||||
"79324811-9588-4311-b208-f272e38aaabf": {
|
||||
"ID": "79324811-9588-4311-b208-f272e38aaabf",
|
||||
"Name": "node1",
|
||||
"Address": "198.18.0.1:8300",
|
||||
"NodeStatus": "alive",
|
||||
"Version": "1.9.0",
|
||||
"LastContact": "0s",
|
||||
"LastTerm": 3,
|
||||
"LastIndex": 42,
|
||||
"Healthy": true,
|
||||
"StableSince": "2020-11-06T14:51:00Z",
|
||||
"Status": "leader",
|
||||
"Meta": {
|
||||
"foo": "bar"
|
||||
},
|
||||
"NodeType": "voter"
|
||||
},
|
||||
"ae84aefb-a303-4734-8739-5c102d4ee2d9": {
|
||||
"ID": "ae84aefb-a303-4734-8739-5c102d4ee2d9",
|
||||
"Name": "node3",
|
||||
"Address": "198.18.0.3:8300",
|
||||
"NodeStatus": "alive",
|
||||
"Version": "1.9.0",
|
||||
"LastContact": "2ms",
|
||||
"LastTerm": 3,
|
||||
"LastIndex": 39,
|
||||
"Healthy": true,
|
||||
"StableSince": "2020-11-06T14:53:00Z",
|
||||
"Status": "voter",
|
||||
"Meta": {
|
||||
"baz": "foo"
|
||||
},
|
||||
"NodeType": "voter"
|
||||
},
|
||||
"ef8aee9a-f9d6-4ec4-b383-aac956bdb80f": {
|
||||
"ID": "ef8aee9a-f9d6-4ec4-b383-aac956bdb80f",
|
||||
"Name": "node2",
|
||||
"Address": "198.18.0.2:8300",
|
||||
"NodeStatus": "alive",
|
||||
"Version": "1.9.0",
|
||||
"LastContact": "1ms",
|
||||
"LastTerm": 3,
|
||||
"LastIndex": 41,
|
||||
"Healthy": true,
|
||||
"StableSince": "2020-11-06T14:52:00Z",
|
||||
"Status": "voter",
|
||||
"Meta": {
|
||||
"bar": "baz"
|
||||
},
|
||||
"NodeType": "voter"
|
||||
}
|
||||
},
|
||||
"Leader": "79324811-9588-4311-b208-f272e38aaabf",
|
||||
"Voters": [
|
||||
"79324811-9588-4311-b208-f272e38aaabf",
|
||||
"ef8aee9a-f9d6-4ec4-b383-aac956bdb80f",
|
||||
"ae84aefb-a303-4734-8739-5c102d4ee2d9"
|
||||
]
|
||||
}
|
|
@ -48,18 +48,31 @@ func (_ *prettyFormatter) Format(info *OutputFormat) (string, error) {
|
|||
fmt.Fprintf(tw, "\n Term\t%d", info.Meta.Term)
|
||||
fmt.Fprintf(tw, "\n Version\t%d", info.Meta.Version)
|
||||
fmt.Fprintf(tw, "\n")
|
||||
fmt.Fprintln(tw, "\n Type\tCount\tSize\t")
|
||||
fmt.Fprintf(tw, " %s\t%s\t%s\t", "----", "----", "----")
|
||||
fmt.Fprintln(tw, "\n Type\tCount\tSize")
|
||||
fmt.Fprintf(tw, " %s\t%s\t%s", "----", "----", "----")
|
||||
// For each different type generate new output
|
||||
for _, s := range info.Stats {
|
||||
fmt.Fprintf(tw, "\n %s\t%d\t%s\t", s.Name, s.Count, ByteSize(uint64(s.Sum)))
|
||||
fmt.Fprintf(tw, "\n %s\t%d\t%s", s.Name, s.Count, ByteSize(uint64(s.Sum)))
|
||||
}
|
||||
fmt.Fprintf(tw, "\n %s\t%s\t%s", "----", "----", "----")
|
||||
fmt.Fprintf(tw, "\n Total\t\t%s", ByteSize(uint64(info.TotalSize)))
|
||||
|
||||
if info.StatsKV != nil {
|
||||
fmt.Fprintf(tw, "\n")
|
||||
fmt.Fprintln(tw, "\n Key Name\tCount\tSize")
|
||||
fmt.Fprintf(tw, " %s\t%s\t%s", "----", "----", "----")
|
||||
// For each different type generate new output
|
||||
for _, s := range info.StatsKV {
|
||||
fmt.Fprintf(tw, "\n %s\t%d\t%s", s.Name, s.Count, ByteSize(uint64(s.Sum)))
|
||||
}
|
||||
fmt.Fprintf(tw, "\n %s\t%s\t%s", "----", "----", "----")
|
||||
fmt.Fprintf(tw, "\n Total\t\t%s", ByteSize(uint64(info.TotalSizeKV)))
|
||||
}
|
||||
fmt.Fprintf(tw, "\n %s\t%s\t%s\t", "----", "----", "----")
|
||||
fmt.Fprintf(tw, "\n Total\t\t%s\t", ByteSize(uint64(info.TotalSize)))
|
||||
|
||||
if err := tw.Flush(); err != nil {
|
||||
return b.String(), err
|
||||
}
|
||||
|
||||
return b.String(), nil
|
||||
}
|
||||
|
||||
|
|
|
@ -13,6 +13,11 @@ func TestFormat(t *testing.T) {
|
|||
Sum: 1,
|
||||
Count: 2,
|
||||
}}
|
||||
mkv := []typeStats{{
|
||||
Name: "msgKV",
|
||||
Sum: 1,
|
||||
Count: 2,
|
||||
}}
|
||||
info := OutputFormat{
|
||||
Meta: &MetadataInfo{
|
||||
ID: "one",
|
||||
|
@ -21,8 +26,10 @@ func TestFormat(t *testing.T) {
|
|||
Term: 4,
|
||||
Version: 1,
|
||||
},
|
||||
Stats: m,
|
||||
TotalSize: 1,
|
||||
Stats: m,
|
||||
StatsKV: mkv,
|
||||
TotalSize: 1,
|
||||
TotalSizeKV: 1,
|
||||
}
|
||||
|
||||
formatters := map[string]Formatter{
|
||||
|
|
|
@ -29,10 +29,21 @@ type cmd struct {
|
|||
flags *flag.FlagSet
|
||||
help string
|
||||
format string
|
||||
|
||||
// flags
|
||||
kvDetails bool
|
||||
kvDepth int
|
||||
kvFilter string
|
||||
}
|
||||
|
||||
func (c *cmd) init() {
|
||||
c.flags = flag.NewFlagSet("", flag.ContinueOnError)
|
||||
c.flags.BoolVar(&c.kvDetails, "kvdetails", false,
|
||||
"Provides a detailed KV space usage breakdown for any KV data that's been stored.")
|
||||
c.flags.IntVar(&c.kvDepth, "kvdepth", 2,
|
||||
"Can only be used with -kvdetails. The key prefix depth used to breakdown KV store data. Defaults to 2.")
|
||||
c.flags.StringVar(&c.kvFilter, "kvfilter", "",
|
||||
"Can only be used with -kvdetails. Limits KV key breakdown using this prefix filter.")
|
||||
c.flags.StringVar(
|
||||
&c.format,
|
||||
"format",
|
||||
|
@ -52,12 +63,24 @@ type MetadataInfo struct {
|
|||
Version raft.SnapshotVersion
|
||||
}
|
||||
|
||||
// SnapshotInfo is used for passing snapshot stat
|
||||
// information between functions
|
||||
type SnapshotInfo struct {
|
||||
Meta MetadataInfo
|
||||
Stats map[structs.MessageType]typeStats
|
||||
StatsKV map[string]typeStats
|
||||
TotalSize int
|
||||
TotalSizeKV int
|
||||
}
|
||||
|
||||
// OutputFormat is used for passing information
|
||||
// through the formatter
|
||||
type OutputFormat struct {
|
||||
Meta *MetadataInfo
|
||||
Stats []typeStats
|
||||
TotalSize int
|
||||
Meta *MetadataInfo
|
||||
Stats []typeStats
|
||||
StatsKV []typeStats
|
||||
TotalSize int
|
||||
TotalSizeKV int
|
||||
}
|
||||
|
||||
func (c *cmd) Run(args []string) int {
|
||||
|
@ -101,7 +124,7 @@ func (c *cmd) Run(args []string) int {
|
|||
}
|
||||
}()
|
||||
|
||||
stats, totalSize, err := enhance(readFile)
|
||||
info, err := c.enhance(readFile)
|
||||
if err != nil {
|
||||
c.UI.Error(fmt.Sprintf("Error extracting snapshot data: %s", err))
|
||||
return 1
|
||||
|
@ -122,13 +145,17 @@ func (c *cmd) Run(args []string) int {
|
|||
}
|
||||
|
||||
//Restructures stats given above to be human readable
|
||||
formattedStats := generatetypeStats(stats)
|
||||
formattedStats := generateStats(info)
|
||||
formattedStatsKV := generateKVStats(info)
|
||||
|
||||
in := &OutputFormat{
|
||||
Meta: metaformat,
|
||||
Stats: formattedStats,
|
||||
TotalSize: totalSize,
|
||||
Meta: metaformat,
|
||||
Stats: formattedStats,
|
||||
StatsKV: formattedStatsKV,
|
||||
TotalSize: info.TotalSize,
|
||||
TotalSizeKV: info.TotalSizeKV,
|
||||
}
|
||||
|
||||
out, err := formatter.Format(in)
|
||||
if err != nil {
|
||||
c.UI.Error(err.Error())
|
||||
|
@ -145,19 +172,55 @@ type typeStats struct {
|
|||
Count int
|
||||
}
|
||||
|
||||
func generatetypeStats(info map[structs.MessageType]typeStats) []typeStats {
|
||||
ss := make([]typeStats, 0, len(info))
|
||||
// generateStats formats the stats for the output struct
|
||||
// that's used to produce the printed output the user sees.
|
||||
func generateStats(info SnapshotInfo) []typeStats {
|
||||
ss := make([]typeStats, 0, len(info.Stats))
|
||||
|
||||
for _, s := range info {
|
||||
for _, s := range info.Stats {
|
||||
ss = append(ss, s)
|
||||
}
|
||||
|
||||
// Sort the stat slice
|
||||
sort.Slice(ss, func(i, j int) bool { return ss[i].Sum > ss[j].Sum })
|
||||
ss = sortTypeStats(ss)
|
||||
|
||||
return ss
|
||||
}
|
||||
|
||||
// generateKVStats reformats the KV stats to work with
|
||||
// the output struct that's used to produce the printed
|
||||
// output the user sees.
|
||||
func generateKVStats(info SnapshotInfo) []typeStats {
|
||||
kvLen := len(info.StatsKV)
|
||||
if kvLen > 0 {
|
||||
ks := make([]typeStats, 0, kvLen)
|
||||
|
||||
for _, s := range info.StatsKV {
|
||||
ks = append(ks, s)
|
||||
}
|
||||
|
||||
ks = sortTypeStats(ks)
|
||||
|
||||
return ks
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// sortTypeStats sorts the stat slice by size and then
|
||||
// alphabetically in the case the size is identical
|
||||
func sortTypeStats(stats []typeStats) []typeStats {
|
||||
sort.Slice(stats, func(i, j int) bool {
|
||||
// sort alphabetically if size is equal
|
||||
if stats[i].Sum == stats[j].Sum {
|
||||
return stats[i].Name < stats[j].Name
|
||||
}
|
||||
|
||||
return stats[i].Sum > stats[j].Sum
|
||||
})
|
||||
|
||||
return stats
|
||||
}
|
||||
|
||||
// countingReader helps keep track of the bytes we have read
|
||||
// when reading snapshots
|
||||
type countingReader struct {
|
||||
|
@ -175,36 +238,89 @@ func (r *countingReader) Read(p []byte) (n int, err error) {
|
|||
|
||||
// enhance utilizes ReadSnapshot to populate the struct with
|
||||
// all of the snapshot's itemized data
|
||||
func enhance(file io.Reader) (map[structs.MessageType]typeStats, int, error) {
|
||||
stats := make(map[structs.MessageType]typeStats)
|
||||
func (c *cmd) enhance(file io.Reader) (SnapshotInfo, error) {
|
||||
info := SnapshotInfo{
|
||||
Stats: make(map[structs.MessageType]typeStats),
|
||||
StatsKV: make(map[string]typeStats),
|
||||
TotalSize: 0,
|
||||
TotalSizeKV: 0,
|
||||
}
|
||||
cr := &countingReader{wrappedReader: file}
|
||||
totalSize := 0
|
||||
handler := func(header *fsm.SnapshotHeader, msg structs.MessageType, dec *codec.Decoder) error {
|
||||
name := structs.MessageType.String(msg)
|
||||
s := stats[msg]
|
||||
s := info.Stats[msg]
|
||||
if s.Name == "" {
|
||||
s.Name = name
|
||||
}
|
||||
|
||||
var val interface{}
|
||||
err := dec.Decode(&val)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to decode msg type %v, error %v", name, err)
|
||||
}
|
||||
|
||||
size := cr.read - totalSize
|
||||
size := cr.read - info.TotalSize
|
||||
s.Sum += size
|
||||
s.Count++
|
||||
totalSize = cr.read
|
||||
stats[msg] = s
|
||||
info.TotalSize = cr.read
|
||||
info.Stats[msg] = s
|
||||
|
||||
c.kvEnhance(s.Name, val, size, &info)
|
||||
|
||||
return nil
|
||||
}
|
||||
if err := fsm.ReadSnapshot(cr, handler); err != nil {
|
||||
return nil, 0, err
|
||||
return info, err
|
||||
}
|
||||
return stats, totalSize, nil
|
||||
return info, nil
|
||||
|
||||
}
|
||||
|
||||
// kvEnhance populates the struct with all of the snapshot's
|
||||
// size information for KV data stored in it
|
||||
func (c *cmd) kvEnhance(keyType string, val interface{}, size int, info *SnapshotInfo) {
|
||||
if c.kvDetails {
|
||||
if keyType != "KVS" {
|
||||
return
|
||||
}
|
||||
|
||||
// have to coerce this into a usable type here or this won't work
|
||||
keyVal := val.(map[string]interface{})
|
||||
for k, v := range keyVal {
|
||||
// we only care about the entry on the key specifically
|
||||
// related to the key name, so skip all others
|
||||
if k != "Key" {
|
||||
continue
|
||||
}
|
||||
|
||||
// check for whether a filter is specified. if it is, skip
|
||||
// any keys that don't match.
|
||||
if len(c.kvFilter) > 0 && !strings.HasPrefix(v.(string), c.kvFilter) {
|
||||
break
|
||||
}
|
||||
|
||||
split := strings.Split(v.(string), "/")
|
||||
|
||||
// handle the situation where the key is shorter than
|
||||
// the specified depth.
|
||||
actualDepth := c.kvDepth
|
||||
if c.kvDepth > len(split) {
|
||||
actualDepth = len(split)
|
||||
}
|
||||
prefix := strings.Join(split[0:actualDepth], "/")
|
||||
kvs := info.StatsKV[prefix]
|
||||
if kvs.Name == "" {
|
||||
kvs.Name = prefix
|
||||
}
|
||||
|
||||
kvs.Sum += size
|
||||
kvs.Count++
|
||||
info.TotalSizeKV += size
|
||||
info.StatsKV[prefix] = kvs
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (c *cmd) Synopsis() string {
|
||||
return synopsis
|
||||
}
|
||||
|
|
|
@ -95,3 +95,57 @@ func TestSnapshotInspectCommand(t *testing.T) {
|
|||
want := golden(t, t.Name(), ui.OutputWriter.String())
|
||||
require.Equal(t, want, ui.OutputWriter.String())
|
||||
}
|
||||
|
||||
func TestSnapshotInspectKVDetailsCommand(t *testing.T) {
|
||||
|
||||
filepath := "./testdata/backupWithKV.snap"
|
||||
|
||||
// Inspect the snapshot
|
||||
ui := cli.NewMockUi()
|
||||
c := New(ui)
|
||||
args := []string{"-kvdetails", filepath}
|
||||
|
||||
code := c.Run(args)
|
||||
if code != 0 {
|
||||
t.Fatalf("bad: %d. %#v", code, ui.ErrorWriter.String())
|
||||
}
|
||||
|
||||
want := golden(t, t.Name(), ui.OutputWriter.String())
|
||||
require.Equal(t, want, ui.OutputWriter.String())
|
||||
}
|
||||
|
||||
func TestSnapshotInspectKVDetailsDepthCommand(t *testing.T) {
|
||||
|
||||
filepath := "./testdata/backupWithKV.snap"
|
||||
|
||||
// Inspect the snapshot
|
||||
ui := cli.NewMockUi()
|
||||
c := New(ui)
|
||||
args := []string{"-kvdetails", "-kvdepth", "3", filepath}
|
||||
|
||||
code := c.Run(args)
|
||||
if code != 0 {
|
||||
t.Fatalf("bad: %d. %#v", code, ui.ErrorWriter.String())
|
||||
}
|
||||
|
||||
want := golden(t, t.Name(), ui.OutputWriter.String())
|
||||
require.Equal(t, want, ui.OutputWriter.String())
|
||||
}
|
||||
|
||||
func TestSnapshotInspectKVDetailsDepthFilterCommand(t *testing.T) {
|
||||
|
||||
filepath := "./testdata/backupWithKV.snap"
|
||||
|
||||
// Inspect the snapshot
|
||||
ui := cli.NewMockUi()
|
||||
c := New(ui)
|
||||
args := []string{"-kvdetails", "-kvdepth", "3", "-kvfilter", "vault/logical", filepath}
|
||||
|
||||
code := c.Run(args)
|
||||
if code != 0 {
|
||||
t.Fatalf("bad: %d. %#v", code, ui.ErrorWriter.String())
|
||||
}
|
||||
|
||||
want := golden(t, t.Name(), ui.OutputWriter.String())
|
||||
require.Equal(t, want, ui.OutputWriter.String())
|
||||
}
|
||||
|
|
|
@ -4,16 +4,16 @@
|
|||
Term 2
|
||||
Version 1
|
||||
|
||||
Type Count Size
|
||||
---- ---- ----
|
||||
Register 3 1.7KB
|
||||
ConnectCA 1 1.2KB
|
||||
ConnectCAProviderState 1 1.1KB
|
||||
Index 12 344B
|
||||
Autopilot 1 199B
|
||||
ConnectCAConfig 1 197B
|
||||
FederationState 1 139B
|
||||
SystemMetadata 1 68B
|
||||
ChunkingState 1 12B
|
||||
---- ---- ----
|
||||
Type Count Size
|
||||
---- ---- ----
|
||||
Register 3 1.7KB
|
||||
ConnectCA 1 1.2KB
|
||||
ConnectCAProviderState 1 1.1KB
|
||||
Index 12 344B
|
||||
Autopilot 1 199B
|
||||
ConnectCAConfig 1 197B
|
||||
FederationState 1 139B
|
||||
SystemMetadata 1 68B
|
||||
ChunkingState 1 12B
|
||||
---- ---- ----
|
||||
Total 5KB
|
||||
|
|
|
@ -4,15 +4,15 @@
|
|||
Term 2
|
||||
Version 1
|
||||
|
||||
Type Count Size
|
||||
---- ---- ----
|
||||
Register 3 1.8KB
|
||||
ConnectCA 1 1.2KB
|
||||
ConnectCAProviderState 1 1.1KB
|
||||
Index 11 313B
|
||||
ConnectCAConfig 1 247B
|
||||
Autopilot 1 199B
|
||||
SystemMetadata 1 68B
|
||||
ChunkingState 1 12B
|
||||
---- ---- ----
|
||||
Type Count Size
|
||||
---- ---- ----
|
||||
Register 3 1.8KB
|
||||
ConnectCA 1 1.2KB
|
||||
ConnectCAProviderState 1 1.1KB
|
||||
Index 11 313B
|
||||
ConnectCAConfig 1 247B
|
||||
Autopilot 1 199B
|
||||
SystemMetadata 1 68B
|
||||
ChunkingState 1 12B
|
||||
---- ---- ----
|
||||
Total 5KB
|
||||
|
|
27
command/snapshot/inspect/testdata/TestSnapshotInspectKVDetailsCommand.golden
vendored
Normal file
27
command/snapshot/inspect/testdata/TestSnapshotInspectKVDetailsCommand.golden
vendored
Normal file
|
@ -0,0 +1,27 @@
|
|||
ID 2-12426-1604593650375
|
||||
Size 17228
|
||||
Index 12426
|
||||
Term 2
|
||||
Version 1
|
||||
|
||||
Type Count Size
|
||||
---- ---- ----
|
||||
KVS 27 12.3KB
|
||||
Register 5 3.4KB
|
||||
Index 11 285B
|
||||
Autopilot 1 199B
|
||||
Session 1 199B
|
||||
CoordinateBatchUpdate 1 166B
|
||||
Tombstone 2 146B
|
||||
FederationState 1 139B
|
||||
ChunkingState 1 12B
|
||||
---- ---- ----
|
||||
Total 16.8KB
|
||||
|
||||
Key Name Count Size
|
||||
---- ---- ----
|
||||
vault/core 16 5.9KB
|
||||
vault/sys 7 4.4KB
|
||||
vault/logical 4 2KB
|
||||
---- ---- ----
|
||||
Total 12.3KB
|
44
command/snapshot/inspect/testdata/TestSnapshotInspectKVDetailsDepthCommand.golden
vendored
Normal file
44
command/snapshot/inspect/testdata/TestSnapshotInspectKVDetailsDepthCommand.golden
vendored
Normal file
|
@ -0,0 +1,44 @@
|
|||
ID 2-12426-1604593650375
|
||||
Size 17228
|
||||
Index 12426
|
||||
Term 2
|
||||
Version 1
|
||||
|
||||
Type Count Size
|
||||
---- ---- ----
|
||||
KVS 27 12.3KB
|
||||
Register 5 3.4KB
|
||||
Index 11 285B
|
||||
Autopilot 1 199B
|
||||
Session 1 199B
|
||||
CoordinateBatchUpdate 1 166B
|
||||
Tombstone 2 146B
|
||||
FederationState 1 139B
|
||||
ChunkingState 1 12B
|
||||
---- ---- ----
|
||||
Total 16.8KB
|
||||
|
||||
Key Name Count Size
|
||||
---- ---- ----
|
||||
vault/sys/policy 3 3.3KB
|
||||
vault/logical/0989e79e-06cd-5374-c8c0-4c6d675bc1c9 3 1.8KB
|
||||
vault/core/leader 1 1.6KB
|
||||
vault/sys/token 3 1KB
|
||||
vault/core/mounts 1 675B
|
||||
vault/core/wrapping 1 633B
|
||||
vault/core/local-mounts 1 450B
|
||||
vault/core/auth 1 423B
|
||||
vault/core/cluster 2 388B
|
||||
vault/core/keyring 1 320B
|
||||
vault/core/master 1 237B
|
||||
vault/core/seal-config 1 211B
|
||||
vault/logical/5c018b68-3573-41d3-0c33-04bce60cd6b0 1 210B
|
||||
vault/core/hsm 1 189B
|
||||
vault/core/local-audit 1 185B
|
||||
vault/core/local-auth 1 183B
|
||||
vault/core/audit 1 179B
|
||||
vault/core/lock 1 170B
|
||||
vault/core/shamir-kek 1 159B
|
||||
vault/sys/counters 1 155B
|
||||
---- ---- ----
|
||||
Total 12.3KB
|
26
command/snapshot/inspect/testdata/TestSnapshotInspectKVDetailsDepthFilterCommand.golden
vendored
Normal file
26
command/snapshot/inspect/testdata/TestSnapshotInspectKVDetailsDepthFilterCommand.golden
vendored
Normal file
|
@ -0,0 +1,26 @@
|
|||
ID 2-12426-1604593650375
|
||||
Size 17228
|
||||
Index 12426
|
||||
Term 2
|
||||
Version 1
|
||||
|
||||
Type Count Size
|
||||
---- ---- ----
|
||||
KVS 27 12.3KB
|
||||
Register 5 3.4KB
|
||||
Index 11 285B
|
||||
Autopilot 1 199B
|
||||
Session 1 199B
|
||||
CoordinateBatchUpdate 1 166B
|
||||
Tombstone 2 146B
|
||||
FederationState 1 139B
|
||||
ChunkingState 1 12B
|
||||
---- ---- ----
|
||||
Total 16.8KB
|
||||
|
||||
Key Name Count Size
|
||||
---- ---- ----
|
||||
vault/logical/0989e79e-06cd-5374-c8c0-4c6d675bc1c9 3 1.8KB
|
||||
vault/logical/5c018b68-3573-41d3-0c33-04bce60cd6b0 1 210B
|
||||
---- ---- ----
|
||||
Total 2KB
|
Binary file not shown.
|
@ -13,5 +13,13 @@
|
|||
"Count": 2
|
||||
}
|
||||
],
|
||||
"TotalSize": 1
|
||||
"StatsKV": [
|
||||
{
|
||||
"Name": "msgKV",
|
||||
"Sum": 1,
|
||||
"Count": 2
|
||||
}
|
||||
],
|
||||
"TotalSize": 1,
|
||||
"TotalSizeKV": 1
|
||||
}
|
|
@ -4,8 +4,14 @@
|
|||
Term 4
|
||||
Version 1
|
||||
|
||||
Type Count Size
|
||||
---- ---- ----
|
||||
msg 2 1B
|
||||
---- ---- ----
|
||||
Total 1B
|
||||
Type Count Size
|
||||
---- ---- ----
|
||||
msg 2 1B
|
||||
---- ---- ----
|
||||
Total 1B
|
||||
|
||||
Key Name Count Size
|
||||
---- ---- ----
|
||||
msgKV 2 1B
|
||||
---- ---- ----
|
||||
Total 1B
|
|
@ -1,29 +1,16 @@
|
|||
-----BEGIN CERTIFICATE-----
|
||||
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
|
||||
-----END CERTIFICATE-----
|
||||
|
|
|
@ -1,27 +0,0 @@
|
|||
-----BEGIN CERTIFICATE REQUEST-----
|
||||
MIIEhDCCAmwCAQAwPzELMAkGA1UEBhMCQVUxEzARBgNVBAgMClNvbWUtU3RhdGUx
|
||||
GzAZBgNVBAoMEkNvbnN1bCBUZXN0IENsaWVudDCCAiIwDQYJKoZIhvcNAQEBBQAD
|
||||
ggIPADCCAgoCggIBAOyJwuXCN+8sOnFmWDx6w5Xs+ADd905EKkfrdLc0qIcrndZw
|
||||
dCx9zOeArLhUS1aoh0ctwz5xwBsmUgwIQwr/V6Q1+sAEDmPmnuAiR/m3lxE+ZPr1
|
||||
CyNhrrTqs7jXkRRDNtZevw6dmTE0FC4Tho016NVXckVZRRJL8Svfk2GvZbZ+1HAI
|
||||
i1a/Du7VQPbd1HOLKPkpb0JUAksdAaks/avzKhRUoHQBR/T4S9GK95WrCqJIZG6i
|
||||
OVK2cymfZ9t/qYdm5czdq8hokWNqOtVb+yBx4/zs8JZI3/TZ/K4nSFcpZNZmPI7x
|
||||
sBL0/GG7nqWyAFWFsn/+4q0aFIBVUe+ydONKoXKZI080b9CLkNXIp1P8rJsodjKZ
|
||||
O/xr+/JCeo6D7ZAJymFe1QXJaGt7ac08s9scU97n0iuCRn6RXLdii9pbiAlArmAE
|
||||
1zXQKA/hcyVtjfUdeq0kA1RE308gUab552XstQUHH0/+JcoPhn1UN+D6UOw7CxVQ
|
||||
rq6FBDeDt7S/WtJNGM3GYrQWtbvTQhDW0jBp0Tr+BwIDXYN5QLM6rcnB2GR3+aYV
|
||||
uvre6DrvisA6yKO+0F29n2LMDdcUu8ZGzoEyFOZAgcw943VlU8bSxWkANoO0e3gK
|
||||
ub4AvOaRqvVKyeFuFnwzKjSEjiZTHVWs/1XVTnK51b9H33uK976O0q9qGlERAgMB
|
||||
AAGgADANBgkqhkiG9w0BAQsFAAOCAgEARWD26zR/JuyER2SmCq0GYc4i5bTmQOZw
|
||||
BcToQsYQcfTrdZspzKhb4EM560tVqE8V6Ckkl2V97ZbbD7KIRgY6LEsCcr6VCfU5
|
||||
rSiyHo1M/PDnK6FqYE0qTqqu3jOGmh97RAZi8REy8gfT/PrE8kDaEVfm0nCB8aaW
|
||||
8wg6ncmStRLKJB4QP1YDpcQH568MMY/0E7xUixp1KjaA4VwE4QEFNoxmpp/Dwpzw
|
||||
BB6kjlKM2tsjkbm0EHCPU5creAoqPNVQNCIVTDBUgkwhUrU2ljAjlC24tL66L0FP
|
||||
RKmyk8yWR2PQkkkWlRwarypGmOAE0GMVMICNh5WTaPaa1XTTpZme3ctpZQZO3LRT
|
||||
Bao4tXM3pA8vApCbkqExR1ow86HvOHv50Y+LQpD1fk7cj18npCR831f1cr/jQw6V
|
||||
p4Z6bdftF38ZCsCdjUHXbtc3tA0220I+0e8ng1znAfMPQC+KVrHaapt1OFWGHPFk
|
||||
KDLyxtZN1IOEOkjtI23KOUlEtG4jnm5UjFG43EXA3qmbUrHNCsHhRpHhMBqu4a0Z
|
||||
ifPQ5+YFyUCmgVUSGeGmpnyxWalmwOT2Ygd/uhn7TnZ0GAv+x1tLHNe78Q8YKi9a
|
||||
gw4zZcDFJmCPnVkfQ+lVSSY5GjSPI8DZCfr+IJBIPOiknTGP9g2dEbUX8xVyEfK6
|
||||
18IjYxoATJE=
|
||||
-----END CERTIFICATE REQUEST-----
|
|
@ -1,51 +1,5 @@
|
|||
-----BEGIN RSA PRIVATE KEY-----
|
||||
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
|
||||
-----END RSA PRIVATE KEY-----
|
||||
-----BEGIN EC PRIVATE KEY-----
|
||||
MHcCAQEEIDxDVYnUL3LCN7kSKF/ShH1c8HacmeUyU/2qJ/fo+5kDoAoGCCqGSM49
|
||||
AwEHoUQDQgAEMtVdDd8tDZBaOaDFFzWD0hTxO7soxUuz1dWaO8FGhIS07dfSBjYu
|
||||
mEOgfNtfOzAILvkBd4gS8DrQZ2Rbks86iA==
|
||||
-----END EC PRIVATE KEY-----
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
-----BEGIN EC PRIVATE KEY-----
|
||||
MHcCAQEEINtFYGWAzcVyRRQKjadE83olH8xAwZYe5sEn4rfPtI8xoAoGCCqGSM49
|
||||
AwEHoUQDQgAErHueX3t67iU5Bj7Nh53zhggnF4pLwjuDbmTDSYIe/Tbeixc2M2Nb
|
||||
7cGr9/Bk9cH8exB/o2KzbQ2nxPZ+ftBTAQ==
|
||||
-----END EC PRIVATE KEY-----
|
|
@ -0,0 +1,18 @@
|
|||
-----BEGIN CERTIFICATE-----
|
||||
MIIC7zCCApSgAwIBAgIRAIubxOonau4Z6UJRYv5KBDMwCgYIKoZIzj0EAwIwgbkx
|
||||
CzAJBgNVBAYTAlVTMQswCQYDVQQIEwJDQTEWMBQGA1UEBxMNU2FuIEZyYW5jaXNj
|
||||
bzEaMBgGA1UECRMRMTAxIFNlY29uZCBTdHJlZXQxDjAMBgNVBBETBTk0MTA1MRcw
|
||||
FQYDVQQKEw5IYXNoaUNvcnAgSW5jLjFAMD4GA1UEAxM3Q29uc3VsIEFnZW50IENB
|
||||
IDE4NTU3MTQ5MTMzMTA0NzczNDYwMjQyMDcxODI5NjUzMzQzNTQ0MzAeFw0yMDEw
|
||||
MjgyMjI3NTZaFw0yNTEwMjcyMjI3NTZaMIG5MQswCQYDVQQGEwJVUzELMAkGA1UE
|
||||
CBMCQ0ExFjAUBgNVBAcTDVNhbiBGcmFuY2lzY28xGjAYBgNVBAkTETEwMSBTZWNv
|
||||
bmQgU3RyZWV0MQ4wDAYDVQQREwU5NDEwNTEXMBUGA1UEChMOSGFzaGlDb3JwIElu
|
||||
Yy4xQDA+BgNVBAMTN0NvbnN1bCBBZ2VudCBDQSAxODU1NzE0OTEzMzEwNDc3MzQ2
|
||||
MDI0MjA3MTgyOTY1MzM0MzU0NDMwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAASs
|
||||
e55fe3ruJTkGPs2HnfOGCCcXikvCO4NuZMNJgh79Nt6LFzYzY1vtwav38GT1wfx7
|
||||
EH+jYrNtDafE9n5+0FMBo3sweTAOBgNVHQ8BAf8EBAMCAYYwDwYDVR0TAQH/BAUw
|
||||
AwEB/zApBgNVHQ4EIgQgC7613rQP8SvteO3KDQHpd+V8piYh1eM9+IXLxIZ4pLYw
|
||||
KwYDVR0jBCQwIoAgC7613rQP8SvteO3KDQHpd+V8piYh1eM9+IXLxIZ4pLYwCgYI
|
||||
KoZIzj0EAwIDSQAwRgIhALoE4RO8DHR4AkxmO5ostQxAYMIpiSTC9VZsWva3hHj4
|
||||
AiEAijGw7bHPearXh9I2ghGE4jGJbGK4R9JHcLOq3+GE2Ng=
|
||||
-----END CERTIFICATE-----
|
|
@ -0,0 +1,5 @@
|
|||
-----BEGIN EC PRIVATE KEY-----
|
||||
MHcCAQEEIDxDVYnUL3LCN7kSKF/ShH1c8HacmeUyU/2qJ/fo+5kDoAoGCCqGSM49
|
||||
AwEHoUQDQgAEMtVdDd8tDZBaOaDFFzWD0hTxO7soxUuz1dWaO8FGhIS07dfSBjYu
|
||||
mEOgfNtfOzAILvkBd4gS8DrQZ2Rbks86iA==
|
||||
-----END EC PRIVATE KEY-----
|
|
@ -0,0 +1,16 @@
|
|||
-----BEGIN CERTIFICATE-----
|
||||
MIICnDCCAkOgAwIBAgIRAOnKNzSoGq53Rq/G5tbm85swCgYIKoZIzj0EAwIwgbkx
|
||||
CzAJBgNVBAYTAlVTMQswCQYDVQQIEwJDQTEWMBQGA1UEBxMNU2FuIEZyYW5jaXNj
|
||||
bzEaMBgGA1UECRMRMTAxIFNlY29uZCBTdHJlZXQxDjAMBgNVBBETBTk0MTA1MRcw
|
||||
FQYDVQQKEw5IYXNoaUNvcnAgSW5jLjFAMD4GA1UEAxM3Q29uc3VsIEFnZW50IENB
|
||||
IDE4NTU3MTQ5MTMzMTA0NzczNDYwMjQyMDcxODI5NjUzMzQzNTQ0MzAeFw0yMDEw
|
||||
MjgyMjI3NTZaFw0yMTEwMjgyMjI3NTZaMBwxGjAYBgNVBAMTEWNsaWVudC5kYzEu
|
||||
Y29uc3VsMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEMtVdDd8tDZBaOaDFFzWD
|
||||
0hTxO7soxUuz1dWaO8FGhIS07dfSBjYumEOgfNtfOzAILvkBd4gS8DrQZ2Rbks86
|
||||
iKOBxzCBxDAOBgNVHQ8BAf8EBAMCBaAwHQYDVR0lBBYwFAYIKwYBBQUHAwIGCCsG
|
||||
AQUFBwMBMAwGA1UdEwEB/wQCMAAwKQYDVR0OBCIEIEJWUjlDw7H2fbRpGG8fpqCq
|
||||
GEX80iDpQqXOU0wg6fEPMCsGA1UdIwQkMCKAIAu+td60D/Er7Xjtyg0B6XflfKYm
|
||||
IdXjPfiFy8SGeKS2MC0GA1UdEQQmMCSCEWNsaWVudC5kYzEuY29uc3Vsgglsb2Nh
|
||||
bGhvc3SHBH8AAAEwCgYIKoZIzj0EAwIDRwAwRAIgYAZTf8VcZ4nQl4lbm579BfXy
|
||||
6YpYz/DdfkEODUBxUyYCIDXhfmxtL/gTSkIh1E+fV7H7ZmqPKgTDH1XBV2zYnj/C
|
||||
-----END CERTIFICATE-----
|
|
@ -0,0 +1,5 @@
|
|||
-----BEGIN EC PRIVATE KEY-----
|
||||
MHcCAQEEICYdaRvHDtbGbReTekgKf9uyKFEnR7kr7VU3kw3uGzAhoAoGCCqGSM49
|
||||
AwEHoUQDQgAE0etZvg/aUTU+HPwDHtEwZslBuEshwHl7AcERHQeFTuhtfjpwHQw+
|
||||
uTunFkmQoqNmE+n7P4v7fe771lpxif8VwA==
|
||||
-----END EC PRIVATE KEY-----
|
|
@ -0,0 +1,17 @@
|
|||
-----BEGIN CERTIFICATE-----
|
||||
MIICxjCCAmugAwIBAgIRAOKZmO0GuFJUOfJ7Ycf0WOEwCgYIKoZIzj0EAwIwgbkx
|
||||
CzAJBgNVBAYTAlVTMQswCQYDVQQIEwJDQTEWMBQGA1UEBxMNU2FuIEZyYW5jaXNj
|
||||
bzEaMBgGA1UECRMRMTAxIFNlY29uZCBTdHJlZXQxDjAMBgNVBBETBTk0MTA1MRcw
|
||||
FQYDVQQKEw5IYXNoaUNvcnAgSW5jLjFAMD4GA1UEAxM3Q29uc3VsIEFnZW50IENB
|
||||
IDE4NTU3MTQ5MTMzMTA0NzczNDYwMjQyMDcxODI5NjUzMzQzNTQ0MzAeFw0yMDEw
|
||||
MjgyMjI3NTZaFw0yMTEwMjgyMjI3NTZaMBwxGjAYBgNVBAMTEXNlcnZlci5kYzEu
|
||||
Y29uc3VsMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE0etZvg/aUTU+HPwDHtEw
|
||||
ZslBuEshwHl7AcERHQeFTuhtfjpwHQw+uTunFkmQoqNmE+n7P4v7fe771lpxif8V
|
||||
wKOB7zCB7DAOBgNVHQ8BAf8EBAMCBaAwHQYDVR0lBBYwFAYIKwYBBQUHAwEGCCsG
|
||||
AQUFBwMCMAwGA1UdEwEB/wQCMAAwKQYDVR0OBCIEIEA1xxAYluRqg6wFwGu75o/5
|
||||
8Ty6FWR9RgIYvZzCM2N9MCsGA1UdIwQkMCKAIAu+td60D/Er7Xjtyg0B6XflfKYm
|
||||
IdXjPfiFy8SGeKS2MFUGA1UdEQROMEyCC2NvbnN1bC50ZXN0ghlzZXJ2ZXIwLnNl
|
||||
cnZlci5kYzEuY29uc3VsghFzZXJ2ZXIuZGMxLmNvbnN1bIIJbG9jYWxob3N0hwR/
|
||||
AAABMAoGCCqGSM49BAMCA0kAMEYCIQDz9YnCvKkgGqw5M0HLDI82rqwQsH2SRQUs
|
||||
kogKi3oGmQIhAPBA5AgF3y1E94PbeYfvoDBJy1JiY3KsckY2Gz+M8Iyc
|
||||
-----END CERTIFICATE-----
|
|
@ -0,0 +1,33 @@
|
|||
#!/bin/bash
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
cd "$(dirname "$0")"
|
||||
|
||||
if [[ ! -f consul-agent-ca-key.pem ]] || [[ ! -f consul-agent-ca.pem ]]; then
|
||||
echo "Regenerating CA..."
|
||||
rm -f consul-agent-ca-key.pem consul-agent-ca.pem
|
||||
consul tls ca create
|
||||
fi
|
||||
rm -f rootca.crt rootca.key path/rootca.crt
|
||||
cp consul-agent-ca.pem rootca.crt
|
||||
cp consul-agent-ca-key.pem rootca.key
|
||||
cp rootca.crt path
|
||||
|
||||
if [[ ! -f dc1-server-consul-0.pem ]] || [[ ! -f dc1-server-consul-0-key.pem ]]; then
|
||||
echo "Regenerating server..."
|
||||
rm -f dc1-server-consul-0.pem dc1-server-consul-0-key.pem
|
||||
consul tls cert create -server -node=server0 -additional-dnsname=consul.test
|
||||
fi
|
||||
rm -f server.crt server.key
|
||||
cp dc1-server-consul-0.pem server.crt
|
||||
cp dc1-server-consul-0-key.pem server.key
|
||||
|
||||
if [[ ! -f dc1-client-consul-0.pem ]] || [[ ! -f dc1-client-consul-0-key.pem ]]; then
|
||||
echo "Regenerating client..."
|
||||
rm -f dc1-client-consul-0.pem dc1-client-consul-0-key.pem
|
||||
consul tls cert create -client
|
||||
fi
|
||||
rm -f client.crt client.key
|
||||
cp dc1-client-consul-0.pem client.crt
|
||||
cp dc1-client-consul-0-key.pem client.key
|
|
@ -1,31 +1,18 @@
|
|||
-----BEGIN CERTIFICATE-----
|
||||
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
|
||||
-----END CERTIFICATE-----
|
||||
|
|
|
@ -1,31 +1,18 @@
|
|||
-----BEGIN CERTIFICATE-----
|
||||
MIIFXTCCA0WgAwIBAgIJAKkYXwqUpHWIMA0GCSqGSIb3DQEBCwUAMEUxCzAJBgNV
|
||||
BAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEwHwYDVQQKDBhJbnRlcm5ldCBX
|
||||
aWRnaXRzIFB0eSBMdGQwHhcNMTcwNDE0MTkwNjIwWhcNMjIwNDE0MTkwNjIwWjBF
|
||||
MQswCQYDVQQGEwJBVTETMBEGA1UECAwKU29tZS1TdGF0ZTEhMB8GA1UECgwYSW50
|
||||
ZXJuZXQgV2lkZ2l0cyBQdHkgTHRkMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIIC
|
||||
CgKCAgEA0pMZRBhBHaYG1FttDzt+rctrM8NtslfbAbHflM4ly7mibCmrnh/sGYSP
|
||||
Y/QcrGnCWrjXspUM0dxtmXGmXKj4yRpKhVYw16saDcn2t55dxMw23uEhgfrigtYS
|
||||
/MoC78OaQryRGvYdxF5unrybBrXDrVfxA8ycYBOV04piS7NlN1/m3TkiNrqkl1up
|
||||
paPscfFaY59yz3Lgq2vs8U1SLtph8ALwjJcz8O3BbBUQuiZYNuiLLnkRroxT3oSp
|
||||
Zbdna2aXtdD/H9JJUoJLjoZp6x5fNOtc+5vF8QZ/jKeJDsxqwf4VT4Z6t8sUMFtO
|
||||
YAR3QEV7wtOhWWfFVzdlCsKZhD8ryemlYSMJ7wfmXUeoiElSPRfvIHUTcJFx37VR
|
||||
V6LmOzs1gjMpGZxGLk/GtaCbDlyCaT/hae8bqtRhngBy5bvGxmdSBQd0fjYRr2CP
|
||||
Pz+Gzx3yQx8yb9VV7dTI3H4LWaTjuy2WooITzy3xY4bOvp4UwusFgbIdywwCpDFJ
|
||||
zzy3FaqoMx06v5D3I9JCanlXk5FErA0GX45pPM4x3FWZfVwrXZtomBFDC6qlRMpH
|
||||
FH8A/KrbpolwiDklSWUxHbfz32gpzf3kLW2nOVpnZPyAgSYudjSFGW00jRomCXOy
|
||||
EOgQj/w+M3hKosvVqIE8IQgfMPNRCK9hn11gVqUvkdcYvo/2jUMCAwEAAaNQME4w
|
||||
HQYDVR0OBBYEFMnRHZ6zGzu/N5MdNv4HKPP5rMqOMB8GA1UdIwQYMBaAFMnRHZ6z
|
||||
Gzu/N5MdNv4HKPP5rMqOMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQELBQADggIB
|
||||
AFPWJZiQTupGr5B14PGPkiesyL1VCkO9D7nr6dXgQwAUTIdDuo9+nZg1HzOd1evY
|
||||
tRWGTAYOsyGNLBIjdhvTZcZz625i1zXFRzkpu0iOUoaH77/XVGkAKyaeYc3oIe9i
|
||||
bRke37/NQokmM3Eko+M/KoAk31osDxlmdQTOVqqoZN+ag6xsrpN6XUmhu3DNfw1j
|
||||
xud6RMH6J+SamG/vUg+GyqKZ9WpKNcLliVPJgOfX1XI3wstOIqkhlw/qveIgIfPv
|
||||
aUVf+rykdgqYMMQvR6qx0k5iHeqj+F1cZRp3P0Uao0hoxi+udgj0X3F7/s/Cbr9c
|
||||
TPZrIlhicZgli9UOwrjZ4B4mEZ4aD8yDbYO3TZe2DnuhPI7uDFG8lvCuLYu8V/lA
|
||||
0yRoBaJf5Z1IXI4190Ww6bmxYV/n5EAFi6o46hWRUiYROtKEcP9Rmabodgp+Jxw6
|
||||
fwzcYqUXTOXR8/gAFPxt5oEfhZ8VJ4nlB9PFTjDi7Gbz+2MnmJokqkxLAR7//FEz
|
||||
Rdmyfl+CvnPZ4TXLk77tuhf3Os9zHSTLobBdDivrTOpc0LdYw5l9yN9s93bG2TBr
|
||||
2T0aKInqAduReLE2nhkYCdlY+dbjbELEiLicqSaEiwr9WbaWuLMoy4tDY52pYgTR
|
||||
lEclafi+O3Y35hEE6VAfZvM1TeR3gvnnQf4ThqIxkRVl
|
||||
MIIC7zCCApSgAwIBAgIRAIubxOonau4Z6UJRYv5KBDMwCgYIKoZIzj0EAwIwgbkx
|
||||
CzAJBgNVBAYTAlVTMQswCQYDVQQIEwJDQTEWMBQGA1UEBxMNU2FuIEZyYW5jaXNj
|
||||
bzEaMBgGA1UECRMRMTAxIFNlY29uZCBTdHJlZXQxDjAMBgNVBBETBTk0MTA1MRcw
|
||||
FQYDVQQKEw5IYXNoaUNvcnAgSW5jLjFAMD4GA1UEAxM3Q29uc3VsIEFnZW50IENB
|
||||
IDE4NTU3MTQ5MTMzMTA0NzczNDYwMjQyMDcxODI5NjUzMzQzNTQ0MzAeFw0yMDEw
|
||||
MjgyMjI3NTZaFw0yNTEwMjcyMjI3NTZaMIG5MQswCQYDVQQGEwJVUzELMAkGA1UE
|
||||
CBMCQ0ExFjAUBgNVBAcTDVNhbiBGcmFuY2lzY28xGjAYBgNVBAkTETEwMSBTZWNv
|
||||
bmQgU3RyZWV0MQ4wDAYDVQQREwU5NDEwNTEXMBUGA1UEChMOSGFzaGlDb3JwIElu
|
||||
Yy4xQDA+BgNVBAMTN0NvbnN1bCBBZ2VudCBDQSAxODU1NzE0OTEzMzEwNDc3MzQ2
|
||||
MDI0MjA3MTgyOTY1MzM0MzU0NDMwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAASs
|
||||
e55fe3ruJTkGPs2HnfOGCCcXikvCO4NuZMNJgh79Nt6LFzYzY1vtwav38GT1wfx7
|
||||
EH+jYrNtDafE9n5+0FMBo3sweTAOBgNVHQ8BAf8EBAMCAYYwDwYDVR0TAQH/BAUw
|
||||
AwEB/zApBgNVHQ4EIgQgC7613rQP8SvteO3KDQHpd+V8piYh1eM9+IXLxIZ4pLYw
|
||||
KwYDVR0jBCQwIoAgC7613rQP8SvteO3KDQHpd+V8piYh1eM9+IXLxIZ4pLYwCgYI
|
||||
KoZIzj0EAwIDSQAwRgIhALoE4RO8DHR4AkxmO5ostQxAYMIpiSTC9VZsWva3hHj4
|
||||
AiEAijGw7bHPearXh9I2ghGE4jGJbGK4R9JHcLOq3+GE2Ng=
|
||||
-----END CERTIFICATE-----
|
||||
|
|
|
@ -1,51 +1,5 @@
|
|||
-----BEGIN RSA PRIVATE KEY-----
|
||||
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
|
||||
-----END RSA PRIVATE KEY-----
|
||||
-----BEGIN EC PRIVATE KEY-----
|
||||
MHcCAQEEINtFYGWAzcVyRRQKjadE83olH8xAwZYe5sEn4rfPtI8xoAoGCCqGSM49
|
||||
AwEHoUQDQgAErHueX3t67iU5Bj7Nh53zhggnF4pLwjuDbmTDSYIe/Tbeixc2M2Nb
|
||||
7cGr9/Bk9cH8exB/o2KzbQ2nxPZ+ftBTAQ==
|
||||
-----END EC PRIVATE KEY-----
|
||||
|
|
|
@ -1 +0,0 @@
|
|||
8F3D34AED9A76443
|
|
@ -1,30 +1,17 @@
|
|||
-----BEGIN CERTIFICATE-----
|
||||
MIIFHDCCAwQCCQCPPTSu2adkQjANBgkqhkiG9w0BAQsFADBFMQswCQYDVQQGEwJB
|
||||
VTETMBEGA1UECAwKU29tZS1TdGF0ZTEhMB8GA1UECgwYSW50ZXJuZXQgV2lkZ2l0
|
||||
cyBQdHkgTHRkMB4XDTE3MDQxNDE5MDkzOFoXDTIyMDMxOTE5MDkzOFowWzELMAkG
|
||||
A1UEBhMCQVUxEzARBgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoMGEludGVybmV0
|
||||
IFdpZGdpdHMgUHR5IEx0ZDEUMBIGA1UEAwwLY29uc3VsLnRlc3QwggIiMA0GCSqG
|
||||
SIb3DQEBAQUAA4ICDwAwggIKAoICAQDI57K1LPlNXpK4ud6XbqhQAM9w6mym5d+5
|
||||
utGlq+ojNrnKdhaokzDOcEpELLguQlFevDrTGuBJLxInmDvRN8BmYshZAuwW+odw
|
||||
iGa4IiT9rN0JeQIQr9DvNjRe7Rhzb3v0CIFIwaBWZyRSdEKuZORyDOvSudATI2p9
|
||||
Ux9HpWJKG9clHEiAjWeseLfEttkAZgojPgdz/7Nq2CT877QtdbbKO8h+IIKevhVM
|
||||
JLIyWhwGMy5orlVP42om7wgAIonm8LFCHe84JUVlOjuJgMzfUvz/UuKJFbAku7Dt
|
||||
UXKL+gZsJg5YHhtMMHQO40UDNTfxNEiIRNyOHaQv4ptRUJq7vIbBng+m1uyYFkJp
|
||||
NvTf4+OApXzsfhb8bjt8QRn5fsCsYU94oUVy+DSw10EW+IR1F7pGJZX5dYZgm6H7
|
||||
E+cj0pYV1dKbdP/5L3mv6KJUTaGDRanLiNPQFOyhs6y6Qf1Y7a270oge9UpHAQ6D
|
||||
KI5jvCuwzW2wDE7KEpo5SGRIjoK9KfouyhlYAaIiJNnVyjVXDXGhzJgH+/2cGW/n
|
||||
TQAqKl4OtmzyNpihgv9Mhm86nro0XLnZHruJZ/SvZgpmSs8luo70CFK990j65JiM
|
||||
UTvBdKADfjCZB4NxVo3yUC0UVaMsoMg8mbOGJPErY8WaFCqwsBHLPmbvbZvp8BYs
|
||||
ve3dDhh+OQIDAQABMA0GCSqGSIb3DQEBCwUAA4ICAQABCbceu3dDQ+NSRN45vEiB
|
||||
wiyq7NLVHoSw7UWN0RPg7SVOPNt7lOqQ5ZtYNJQ0XAuBdHzNAZ/sfUgfURJbYhs5
|
||||
MYalkkmMkpNWZQoMYxdj1sLX6KzLFFOtcACUSuZyOSGCBmuEDuDUTsQp1drZjNqb
|
||||
fOtCWAuvSSbw8SnO6eTjUCwjcbe482kR5vnoaoRetmtTpIFdxwOXnegDXgkBnMMr
|
||||
JSx+xIRVn2JzxJwaBmtn16P0/ksEMSPh0rKRvzE9zC7Jm2DvNOgEkdK8ae6DHFYY
|
||||
zSonWjX3SlT+gpvuCwXC4ZmO2nV4CH34JDUUNRPub7o+t5XTGUxPGo0c/tdQYWZc
|
||||
k0XTRzK6pLAq4X3NAF83nZ8pEi5z1u0Y+Dcx3AqBfhJSc2wg5++GZbELZGAmgiH2
|
||||
WZa2/9zkCUGPke4GoctYNNhTmquk6ysJjDO+xOtaEQNwk04mqBwvb7j85iRNBkYX
|
||||
+9PDF4sumEkkuNFB9vRSEJVrn3onQuZ3n2k1IujR6S6Vojhvideo5aT1NVq+1SQ5
|
||||
bDe5E4IyrsFuxjYNIyA1hZmX2/qXYJ9U05QbyMs170lNhoKkdOf1voCxG3+sRTfz
|
||||
yhGNJ2ks0MuEEiEjOFh9Za9lnk4/OHUG4yjWWdE5S/WH1xyyfokIM7xbo3Le8XgJ
|
||||
ddL0E6Ag/Ow1ey1H7gsNOA==
|
||||
MIICxjCCAmugAwIBAgIRAOKZmO0GuFJUOfJ7Ycf0WOEwCgYIKoZIzj0EAwIwgbkx
|
||||
CzAJBgNVBAYTAlVTMQswCQYDVQQIEwJDQTEWMBQGA1UEBxMNU2FuIEZyYW5jaXNj
|
||||
bzEaMBgGA1UECRMRMTAxIFNlY29uZCBTdHJlZXQxDjAMBgNVBBETBTk0MTA1MRcw
|
||||
FQYDVQQKEw5IYXNoaUNvcnAgSW5jLjFAMD4GA1UEAxM3Q29uc3VsIEFnZW50IENB
|
||||
IDE4NTU3MTQ5MTMzMTA0NzczNDYwMjQyMDcxODI5NjUzMzQzNTQ0MzAeFw0yMDEw
|
||||
MjgyMjI3NTZaFw0yMTEwMjgyMjI3NTZaMBwxGjAYBgNVBAMTEXNlcnZlci5kYzEu
|
||||
Y29uc3VsMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE0etZvg/aUTU+HPwDHtEw
|
||||
ZslBuEshwHl7AcERHQeFTuhtfjpwHQw+uTunFkmQoqNmE+n7P4v7fe771lpxif8V
|
||||
wKOB7zCB7DAOBgNVHQ8BAf8EBAMCBaAwHQYDVR0lBBYwFAYIKwYBBQUHAwEGCCsG
|
||||
AQUFBwMCMAwGA1UdEwEB/wQCMAAwKQYDVR0OBCIEIEA1xxAYluRqg6wFwGu75o/5
|
||||
8Ty6FWR9RgIYvZzCM2N9MCsGA1UdIwQkMCKAIAu+td60D/Er7Xjtyg0B6XflfKYm
|
||||
IdXjPfiFy8SGeKS2MFUGA1UdEQROMEyCC2NvbnN1bC50ZXN0ghlzZXJ2ZXIwLnNl
|
||||
cnZlci5kYzEuY29uc3VsghFzZXJ2ZXIuZGMxLmNvbnN1bIIJbG9jYWxob3N0hwR/
|
||||
AAABMAoGCCqGSM49BAMCA0kAMEYCIQDz9YnCvKkgGqw5M0HLDI82rqwQsH2SRQUs
|
||||
kogKi3oGmQIhAPBA5AgF3y1E94PbeYfvoDBJy1JiY3KsckY2Gz+M8Iyc
|
||||
-----END CERTIFICATE-----
|
||||
|
|
|
@ -1,27 +0,0 @@
|
|||
-----BEGIN CERTIFICATE REQUEST-----
|
||||
MIIEoDCCAogCAQAwWzELMAkGA1UEBhMCQVUxEzARBgNVBAgMClNvbWUtU3RhdGUx
|
||||
ITAfBgNVBAoMGEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDEUMBIGA1UEAwwLY29u
|
||||
c3VsLnRlc3QwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDI57K1LPlN
|
||||
XpK4ud6XbqhQAM9w6mym5d+5utGlq+ojNrnKdhaokzDOcEpELLguQlFevDrTGuBJ
|
||||
LxInmDvRN8BmYshZAuwW+odwiGa4IiT9rN0JeQIQr9DvNjRe7Rhzb3v0CIFIwaBW
|
||||
ZyRSdEKuZORyDOvSudATI2p9Ux9HpWJKG9clHEiAjWeseLfEttkAZgojPgdz/7Nq
|
||||
2CT877QtdbbKO8h+IIKevhVMJLIyWhwGMy5orlVP42om7wgAIonm8LFCHe84JUVl
|
||||
OjuJgMzfUvz/UuKJFbAku7DtUXKL+gZsJg5YHhtMMHQO40UDNTfxNEiIRNyOHaQv
|
||||
4ptRUJq7vIbBng+m1uyYFkJpNvTf4+OApXzsfhb8bjt8QRn5fsCsYU94oUVy+DSw
|
||||
10EW+IR1F7pGJZX5dYZgm6H7E+cj0pYV1dKbdP/5L3mv6KJUTaGDRanLiNPQFOyh
|
||||
s6y6Qf1Y7a270oge9UpHAQ6DKI5jvCuwzW2wDE7KEpo5SGRIjoK9KfouyhlYAaIi
|
||||
JNnVyjVXDXGhzJgH+/2cGW/nTQAqKl4OtmzyNpihgv9Mhm86nro0XLnZHruJZ/Sv
|
||||
ZgpmSs8luo70CFK990j65JiMUTvBdKADfjCZB4NxVo3yUC0UVaMsoMg8mbOGJPEr
|
||||
Y8WaFCqwsBHLPmbvbZvp8BYsve3dDhh+OQIDAQABoAAwDQYJKoZIhvcNAQELBQAD
|
||||
ggIBAIq+JNjO/pixXFRwYUISRib68ya39C0VKj2T8L4HsAtpP2K7gkKGEoQ5AFhi
|
||||
wxe0424myEBBZ4kS6y5BCETtKFb3C9K4c2l1Wp5GIR9QUrj2qrorGnZNJSEBrtHm
|
||||
G4neTxPyepZ+n9C9K5RWC/S/l8nge4+oWU2F0nWIAFK+4GGGTyXFjbfGKtET8CKd
|
||||
NQ4jcrabkRd49qPrXhEMTWzPkKdtZ4Gh7eFVAi5IrgJJlJPhb5/XnUQ+6L7NrIWG
|
||||
D1nHCq8XNDm8Gg9KiMuB4/A7dcqyaoyYofojrnZ2HryGTeLbezpwQmMfVy1dM0ht
|
||||
vueKQwOS7xjiglsV48G5mmql+0auYoBLes2yYGBJ4gLC+Gwokav+FtRhyyRUVZ3+
|
||||
lta2a8XpzdMpmESQrwvz+v37fC/eBsplhk8tTy0IQUzUp0KVCRkR40UHeH9YKsu1
|
||||
cvPwfzk3DPt2wHWjBw7b8RKSXQFyszP9QWODqxnOtE7BoNdHiyxF/W4RaysIVrAG
|
||||
Vs2iRNVSPexiT30BgnfQN1i1HU4eXC/QuTWilo86vwDOjpHhr9FdjPghLk2TbV+g
|
||||
iO3TX0zy0t6Un5rLGMt066pAFCIqynT1sf3CTVXbaeWpkHj1wjVmYOoG75xxWMsv
|
||||
GKmSoKRex/SDko3s2pmfQ+vmkCTKQqabayUH5mh1Ron9B043
|
||||
-----END CERTIFICATE REQUEST-----
|
|
@ -1,51 +1,5 @@
|
|||
-----BEGIN RSA PRIVATE KEY-----
|
||||
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
|
||||
-----END RSA PRIVATE KEY-----
|
||||
-----BEGIN EC PRIVATE KEY-----
|
||||
MHcCAQEEICYdaRvHDtbGbReTekgKf9uyKFEnR7kr7VU3kw3uGzAhoAoGCCqGSM49
|
||||
AwEHoUQDQgAE0etZvg/aUTU+HPwDHtEwZslBuEshwHl7AcERHQeFTuhtfjpwHQw+
|
||||
uTunFkmQoqNmE+n7P4v7fe771lpxif8VwA==
|
||||
-----END EC PRIVATE KEY-----
|
||||
|
|
|
@ -920,11 +920,6 @@ func (c *Configurator) wrapALPNTLSClient(dc, nodeName, alpnProto string, conn ne
|
|||
return nil, err
|
||||
}
|
||||
|
||||
if cs := tlsConn.ConnectionState(); !cs.NegotiatedProtocolIsMutual {
|
||||
tlsConn.Close()
|
||||
return nil, fmt.Errorf("could not negotiate ALPN protocol %q with %q", alpnProto, config.ServerName)
|
||||
}
|
||||
|
||||
return tlsConn, nil
|
||||
}
|
||||
|
||||
|
|
|
@ -215,7 +215,6 @@ func TestConfigurator_outgoingWrapperALPN_OK(t *testing.T) {
|
|||
tlsConn := tlsClient.(*tls.Conn)
|
||||
cs := tlsConn.ConnectionState()
|
||||
require.Equal(t, "foo", cs.NegotiatedProtocol)
|
||||
require.True(t, cs.NegotiatedProtocolIsMutual)
|
||||
|
||||
err = <-errc
|
||||
require.NoError(t, err)
|
||||
|
|
|
@ -5,8 +5,8 @@ module.exports = {
|
|||
ecmaVersion: 2018,
|
||||
sourceType: 'module',
|
||||
ecmaFeatures: {
|
||||
legacyDecorators: true
|
||||
}
|
||||
legacyDecorators: true,
|
||||
},
|
||||
},
|
||||
plugins: ['ember'],
|
||||
extends: ['eslint:recommended', 'plugin:ember/recommended'],
|
||||
|
@ -17,7 +17,7 @@ module.exports = {
|
|||
'no-unused-vars': ['error', { args: 'none' }],
|
||||
'ember/no-new-mixins': ['warn'],
|
||||
'ember/no-jquery': 'warn',
|
||||
'ember/no-global-jquery': 'warn'
|
||||
'ember/no-global-jquery': 'warn',
|
||||
},
|
||||
overrides: [
|
||||
// node files
|
||||
|
@ -31,14 +31,14 @@ module.exports = {
|
|||
'blueprints/*/index.js',
|
||||
'config/**/*.js',
|
||||
'lib/*/index.js',
|
||||
'server/**/*.js'
|
||||
'server/**/*.js',
|
||||
],
|
||||
parserOptions: {
|
||||
sourceType: 'script'
|
||||
sourceType: 'script',
|
||||
},
|
||||
env: {
|
||||
browser: false,
|
||||
node: true
|
||||
node: true,
|
||||
},
|
||||
plugins: ['node'],
|
||||
rules: Object.assign({}, require('eslint-plugin-node').configs.recommended.rules, {
|
||||
|
@ -46,8 +46,8 @@ module.exports = {
|
|||
|
||||
// this can be removed once the following is fixed
|
||||
// https://github.com/mysticatea/eslint-plugin-node/issues/77
|
||||
'node/no-unpublished-require': 'off'
|
||||
})
|
||||
}
|
||||
]
|
||||
'node/no-unpublished-require': 'off',
|
||||
}),
|
||||
},
|
||||
],
|
||||
};
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue