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:
|
references:
|
||||||
images:
|
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
|
ember: &EMBER_IMAGE docker.mirror.hashicorp.services/circleci/node:12-browsers
|
||||||
|
|
||||||
paths:
|
paths:
|
||||||
|
@ -243,7 +243,7 @@ jobs:
|
||||||
-tags="$GOTAGS" -p 2 \
|
-tags="$GOTAGS" -p 2 \
|
||||||
-race -gcflags=all=-d=checkptr=0 \
|
-race -gcflags=all=-d=checkptr=0 \
|
||||||
./agent/{ae,cache,cache-types,checks,config,pool,proxycfg,router}/... \
|
./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}/... \
|
./agent/{grpc,rpc,rpcclient,submatview}/... \
|
||||||
./snapshot
|
./snapshot
|
||||||
|
|
||||||
|
@ -344,7 +344,7 @@ jobs:
|
||||||
<<: *build-distros
|
<<: *build-distros
|
||||||
environment:
|
environment:
|
||||||
<<: *build-env
|
<<: *build-env
|
||||||
XC_OS: "darwin freebsd linux windows"
|
XC_OS: "freebsd linux windows"
|
||||||
XC_ARCH: "386"
|
XC_ARCH: "386"
|
||||||
|
|
||||||
# build all amd64 architecture supported OS binaries
|
# 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
|
# only runs on master: checks latest commit to see if the PR associated has a backport/* or docs* label to cherry-pick
|
||||||
cherry-picker:
|
cherry-picker:
|
||||||
docker:
|
docker:
|
||||||
- image: docker.mirror.hashicorp.services/alpine:3.11
|
- image: docker.mirror.hashicorp.services/alpine:3.12
|
||||||
steps:
|
steps:
|
||||||
- run: apk add --no-cache --no-progress git bash curl ncurses jq openssh-client
|
- run: apk add --no-cache --no-progress git bash curl ncurses jq openssh-client
|
||||||
- checkout
|
- checkout
|
||||||
|
@ -833,7 +833,7 @@ jobs:
|
||||||
|
|
||||||
trigger-oss-merge:
|
trigger-oss-merge:
|
||||||
docker:
|
docker:
|
||||||
- image: docker.mirror.hashicorp.services/alpine:3.11
|
- image: docker.mirror.hashicorp.services/alpine:3.12
|
||||||
steps:
|
steps:
|
||||||
- run: apk add --no-cache --no-progress curl jq
|
- run: apk add --no-cache --no-progress curl jq
|
||||||
- run:
|
- run:
|
||||||
|
@ -894,6 +894,7 @@ workflows:
|
||||||
branches:
|
branches:
|
||||||
only:
|
only:
|
||||||
- master
|
- master
|
||||||
|
- /release\/\d+\.\d+\.x$/
|
||||||
requires:
|
requires:
|
||||||
- build-static-assets
|
- build-static-assets
|
||||||
- dev-build:
|
- 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
|
# 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
|
# 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}" \
|
curl -f -s -H "Authorization: token ${GITHUB_TOKEN}" \
|
||||||
-X POST \
|
-X POST \
|
||||||
-d "{ \"body\": \"${github_message}\"}" \
|
-d "{ \"body\": \"${github_message}\"}" \
|
||||||
|
|
20
CHANGELOG.md
20
CHANGELOG.md
|
@ -1,5 +1,25 @@
|
||||||
## UNRELEASED
|
## 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)
|
## 1.9.0-beta2 (November 07, 2020)
|
||||||
|
|
||||||
BREAKING CHANGES:
|
BREAKING CHANGES:
|
||||||
|
|
|
@ -77,8 +77,8 @@ type RuntimeConfig struct {
|
||||||
|
|
||||||
// ACLDefaultPolicy is used to control the ACL interaction when
|
// ACLDefaultPolicy is used to control the ACL interaction when
|
||||||
// there is no defined policy. This can be "allow" which means
|
// there is no defined policy. This can be "allow" which means
|
||||||
// ACLs are used to black-list, or "deny" which means ACLs are
|
// ACLs are used to deny-list, or "deny" which means ACLs are
|
||||||
// white-lists.
|
// allow-lists.
|
||||||
//
|
//
|
||||||
// hcl: acl.default_policy = ("allow"|"deny")
|
// hcl: acl.default_policy = ("allow"|"deny")
|
||||||
ACLDefaultPolicy string
|
ACLDefaultPolicy string
|
||||||
|
|
|
@ -268,8 +268,8 @@ type Config struct {
|
||||||
|
|
||||||
// ACLDefaultPolicy is used to control the ACL interaction when
|
// ACLDefaultPolicy is used to control the ACL interaction when
|
||||||
// there is no defined policy. This can be "allow" which means
|
// there is no defined policy. This can be "allow" which means
|
||||||
// ACLs are used to black-list, or "deny" which means ACLs are
|
// ACLs are used to deny-list, or "deny" which means ACLs are
|
||||||
// white-lists.
|
// allow-lists.
|
||||||
ACLDefaultPolicy string
|
ACLDefaultPolicy string
|
||||||
|
|
||||||
// ACLDownPolicy controls the behavior of ACLs if the ACLDatacenter
|
// 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.
|
// Apply does an upsert of the given config entry.
|
||||||
func (c *ConfigEntry) Apply(args *structs.ConfigEntryRequest, reply *bool) error {
|
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 {
|
if err := c.srv.validateEnterpriseRequest(args.Entry.GetEnterpriseMeta(), true); err != nil {
|
||||||
return err
|
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.
|
// Normalize and validate the incoming config entry as if it came from a user.
|
||||||
if normalizeAndValidateFn == nil {
|
if err := args.Entry.Normalize(); err != nil {
|
||||||
if err := args.Entry.Normalize(); err != nil {
|
return err
|
||||||
return err
|
}
|
||||||
}
|
if err := args.Entry.Validate(); err != nil {
|
||||||
if err := args.Entry.Validate(); err != nil {
|
return err
|
||||||
return err
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if err := normalizeAndValidateFn(args.Entry); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if authz != nil && !args.Entry.CanWrite(authz) {
|
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 {
|
func (c *ConfigEntry) preflightCheck(kind string) error {
|
||||||
switch kind {
|
switch kind {
|
||||||
case structs.ServiceIntentions:
|
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()
|
usingConfigEntries, err := c.srv.fsm.State().AreIntentionsInConfigEntries()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("system metadata lookup failed: %v", err)
|
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)}})
|
[]metrics.Label{{Name: "op", Value: string(req.Op)}})
|
||||||
defer metrics.MeasureSinceWithLabels([]string{"fsm", "intention"}, time.Now(),
|
defer metrics.MeasureSinceWithLabels([]string{"fsm", "intention"}, time.Now(),
|
||||||
[]metrics.Label{{Name: "op", Value: string(req.Op)}})
|
[]metrics.Label{{Name: "op", Value: string(req.Op)}})
|
||||||
|
|
||||||
|
if req.Mutation != nil {
|
||||||
|
return c.state.IntentionMutation(index, req.Op, req.Mutation)
|
||||||
|
}
|
||||||
|
|
||||||
switch req.Op {
|
switch req.Op {
|
||||||
case structs.IntentionOpCreate, structs.IntentionOpUpdate:
|
case structs.IntentionOpCreate, structs.IntentionOpUpdate:
|
||||||
//nolint:staticcheck
|
//nolint:staticcheck
|
||||||
|
|
|
@ -33,22 +33,11 @@ var (
|
||||||
ErrIntentionNotFound = errors.New("Intention not found")
|
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.
|
// Intention manages the Connect intentions.
|
||||||
type Intention struct {
|
type Intention struct {
|
||||||
// srv is a pointer back to the server.
|
// srv is a pointer back to the server.
|
||||||
srv *Server
|
srv *Server
|
||||||
logger hclog.Logger
|
logger hclog.Logger
|
||||||
|
|
||||||
configEntryEndpoint *ConfigEntry
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Intention) checkIntentionID(id string) (bool, error) {
|
func (s *Intention) checkIntentionID(id string) (bool, error) {
|
||||||
|
@ -62,25 +51,139 @@ func (s *Intention) checkIntentionID(id string) (bool, error) {
|
||||||
return true, nil
|
return true, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// prepareApplyCreate validates that the requester has permissions to create
|
var ErrIntentionsNotUpgradedYet = errors.New("Intentions are read only while being upgraded to config entries")
|
||||||
// the new intention, generates a new uuid for the intention and generally
|
|
||||||
// validates that the request is well-formed
|
// 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
|
||||||
// Returns an existing service-intentions config entry for this destination if
|
// optimization and the actual real enforcement happens in the FSM. It would be
|
||||||
// one exists.
|
// wasteful to round trip all the way through raft to have it fail for
|
||||||
func (s *Intention) prepareApplyCreate(
|
// known-up-front reasons, hence why we check it twice.
|
||||||
ident structs.ACLIdentity,
|
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,
|
authz acl.Authorizer,
|
||||||
entMeta *structs.EnterpriseMeta,
|
entMeta *structs.EnterpriseMeta,
|
||||||
args *structs.IntentionRequest,
|
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) {
|
if !args.Intention.CanWrite(authz) {
|
||||||
var accessorID string
|
sn := args.Intention.SourceServiceName()
|
||||||
if ident != nil {
|
dn := args.Intention.DestinationServiceName()
|
||||||
accessorID = ident.ID()
|
|
||||||
}
|
|
||||||
// todo(kit) Migrate intention access denial logging over to audit logging when we implement it
|
// 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
|
return nil, acl.ErrPermissionDenied
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -106,8 +209,6 @@ func (s *Intention) prepareApplyCreate(
|
||||||
args.Intention.SourceType = structs.IntentionSourceConsul
|
args.Intention.SourceType = structs.IntentionSourceConsul
|
||||||
}
|
}
|
||||||
|
|
||||||
args.Intention.DefaultNamespaces(entMeta)
|
|
||||||
|
|
||||||
if err := s.validateEnterpriseIntention(args.Intention); err != nil {
|
if err := s.validateEnterpriseIntention(args.Intention); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -117,74 +218,59 @@ func (s *Intention) prepareApplyCreate(
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
_, configEntry, err := s.srv.fsm.State().ConfigEntry(nil, structs.ServiceIntentions, args.Intention.DestinationName, args.Intention.DestinationEnterpriseMeta())
|
// NOTE: if the append of this source causes a duplicate source name the
|
||||||
if err != nil {
|
// config entry validation will fail so we don't have to check that
|
||||||
return nil, fmt.Errorf("service-intentions config entry lookup failed: %v", err)
|
// explicitly here.
|
||||||
} else if configEntry == nil {
|
|
||||||
return nil, nil
|
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
|
func (s *Intention) computeApplyChangesLegacyUpdate(
|
||||||
// intention as well as generally validating that the request is well-formed
|
accessorID string,
|
||||||
//
|
|
||||||
// Returns an existing service-intentions config entry for this destination if
|
|
||||||
// one exists.
|
|
||||||
func (s *Intention) prepareApplyUpdateLegacy(
|
|
||||||
ident structs.ACLIdentity,
|
|
||||||
authz acl.Authorizer,
|
authz acl.Authorizer,
|
||||||
entMeta *structs.EnterpriseMeta,
|
entMeta *structs.EnterpriseMeta,
|
||||||
args *structs.IntentionRequest,
|
args *structs.IntentionRequest,
|
||||||
) (*structs.ServiceIntentionsConfigEntry, error) {
|
) (*structs.IntentionMutation, error) {
|
||||||
if !args.Intention.CanWrite(authz) {
|
// This variant is just for legacy UUID-based intentions.
|
||||||
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
|
|
||||||
}
|
|
||||||
|
|
||||||
_, 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 {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("Intention lookup failed: %v", err)
|
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)
|
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) {
|
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
|
// 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)
|
s.logger.Warn("Update operation on intention denied due to ACLs", "intention", args.Intention.ID, "accessorID", accessorID)
|
||||||
return nil, acl.ErrPermissionDenied
|
return nil, acl.ErrPermissionDenied
|
||||||
}
|
}
|
||||||
|
|
||||||
|
args.Intention.DefaultNamespaces(entMeta)
|
||||||
|
|
||||||
// Prior to v1.9.0 renames of the destination side of an intention were
|
// Prior to v1.9.0 renames of the destination side of an intention were
|
||||||
// allowed, but that behavior doesn't work anymore.
|
// allowed, but that behavior doesn't work anymore.
|
||||||
if ixn.DestinationServiceName() != args.Intention.DestinationServiceName() {
|
if ixn.DestinationServiceName() != args.Intention.DestinationServiceName() {
|
||||||
return nil, fmt.Errorf("Cannot modify DestinationNS or DestinationName for an intention once it exists.")
|
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
|
// Default source type
|
||||||
if args.Intention.SourceType == "" {
|
if args.Intention.SourceType == "" {
|
||||||
args.Intention.SourceType = structs.IntentionSourceConsul
|
args.Intention.SourceType = structs.IntentionSourceConsul
|
||||||
}
|
}
|
||||||
|
|
||||||
args.Intention.DefaultNamespaces(entMeta)
|
|
||||||
|
|
||||||
if err := s.validateEnterpriseIntention(args.Intention); err != nil {
|
if err := s.validateEnterpriseIntention(args.Intention); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -196,329 +282,149 @@ func (s *Intention) prepareApplyUpdateLegacy(
|
||||||
return nil, err
|
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
|
func (s *Intention) computeApplyChangesUpsert(
|
||||||
// and that the requester is authorized to delete it
|
accessorID string,
|
||||||
//
|
|
||||||
// Returns an existing service-intentions config entry for this destination if
|
|
||||||
// one exists.
|
|
||||||
func (s *Intention) prepareApplyDeleteLegacy(
|
|
||||||
ident structs.ACLIdentity,
|
|
||||||
authz acl.Authorizer,
|
authz acl.Authorizer,
|
||||||
|
entMeta *structs.EnterpriseMeta,
|
||||||
args *structs.IntentionRequest,
|
args *structs.IntentionRequest,
|
||||||
) (*structs.ServiceIntentionsConfigEntry, error) {
|
) (*structs.IntentionMutation, error) {
|
||||||
// If this is not a create, then we have to verify the ID.
|
// This variant is just for config-entry based intentions.
|
||||||
_, configEntry, ixn, err := s.srv.fsm.State().IntentionGet(nil, args.Intention.ID)
|
|
||||||
|
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 {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("Intention lookup failed: %v", err)
|
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)
|
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) {
|
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
|
// 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)
|
s.logger.Warn("Deletion operation on intention denied due to ACLs", "intention", args.Intention.ID, "accessorID", accessorID)
|
||||||
return nil, acl.ErrPermissionDenied
|
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")
|
func (s *Intention) computeApplyChangesDelete(
|
||||||
|
accessorID string,
|
||||||
// legacyUpgradeCheck fast fails a write request using the legacy intention
|
authz acl.Authorizer,
|
||||||
// RPCs if the system is known to be mid-upgrade. This is purely a perf
|
entMeta *structs.EnterpriseMeta,
|
||||||
// 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,
|
args *structs.IntentionRequest,
|
||||||
reply *string) error {
|
) (*structs.IntentionMutation, error) {
|
||||||
|
args.Intention.DefaultNamespaces(entMeta)
|
||||||
// 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
|
|
||||||
}
|
|
||||||
|
|
||||||
|
if !args.Intention.CanWrite(authz) {
|
||||||
sn := args.Intention.SourceServiceName()
|
sn := args.Intention.SourceServiceName()
|
||||||
|
dn := args.Intention.DestinationServiceName()
|
||||||
// TODO(intentions): have service-intentions validation functions
|
// todo(kit) Migrate intention access denial logging over to audit logging when we implement it
|
||||||
// return structured errors so that we can rewrite the field prefix
|
s.logger.Warn("Intention delete denied due to ACLs",
|
||||||
// here so that the validation errors are not misleading.
|
"source", sn.String(),
|
||||||
if prevEntry == nil {
|
"destination", dn.String(),
|
||||||
// Meta is NOT permitted here, as it would need to be persisted on
|
"accessorID", accessorID)
|
||||||
// the enclosing config entry.
|
return nil, acl.ErrPermissionDenied
|
||||||
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)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if !noop && prevEntry != nil && legacyWrite && !prevEntry.LegacyIDFieldsAreAllSet() {
|
// Pre-flight to avoid pointless raft operations.
|
||||||
sn := prevEntry.DestinationServiceName()
|
_, _, ixn, err := s.srv.fsm.State().IntentionGetExact(nil, args.Intention.ToExact())
|
||||||
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())
|
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
|
return &structs.IntentionMutation{
|
||||||
if legacyWrite {
|
Destination: args.Intention.DestinationServiceName(),
|
||||||
*reply = args.Intention.ID
|
Source: args.Intention.SourceServiceName(),
|
||||||
} else {
|
}, nil
|
||||||
*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
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get returns a single intention by ID.
|
// Get returns a single intention by ID.
|
||||||
func (s *Intention) Get(
|
func (s *Intention) Get(args *structs.IntentionQueryRequest, reply *structs.IndexedIntentions) error {
|
||||||
args *structs.IntentionQueryRequest,
|
// Exit early if Connect hasn't been enabled.
|
||||||
reply *structs.IndexedIntentions) error {
|
if !s.srv.config.ConnectEnabled {
|
||||||
|
return ErrConnectNotEnabled
|
||||||
|
}
|
||||||
|
|
||||||
// Forward if necessary
|
// Forward if necessary
|
||||||
if done, err := s.srv.ForwardRPC("Intention.Get", args, args, reply); done {
|
if done, err := s.srv.ForwardRPC("Intention.Get", args, args, reply); done {
|
||||||
return err
|
return err
|
||||||
|
@ -591,9 +497,12 @@ func (s *Intention) Get(
|
||||||
}
|
}
|
||||||
|
|
||||||
// List returns all the intentions.
|
// List returns all the intentions.
|
||||||
func (s *Intention) List(
|
func (s *Intention) List(args *structs.IntentionListRequest, reply *structs.IndexedIntentions) error {
|
||||||
args *structs.IntentionListRequest,
|
// Exit early if Connect hasn't been enabled.
|
||||||
reply *structs.IndexedIntentions) error {
|
if !s.srv.config.ConnectEnabled {
|
||||||
|
return ErrConnectNotEnabled
|
||||||
|
}
|
||||||
|
|
||||||
// Forward if necessary
|
// Forward if necessary
|
||||||
if done, err := s.srv.ForwardRPC("Intention.List", args, args, reply); done {
|
if done, err := s.srv.ForwardRPC("Intention.List", args, args, reply); done {
|
||||||
return err
|
return err
|
||||||
|
@ -658,9 +567,12 @@ func (s *Intention) List(
|
||||||
}
|
}
|
||||||
|
|
||||||
// Match returns the set of intentions that match the given source/destination.
|
// Match returns the set of intentions that match the given source/destination.
|
||||||
func (s *Intention) Match(
|
func (s *Intention) Match(args *structs.IntentionQueryRequest, reply *structs.IndexedIntentionMatches) error {
|
||||||
args *structs.IntentionQueryRequest,
|
// Exit early if Connect hasn't been enabled.
|
||||||
reply *structs.IndexedIntentionMatches) error {
|
if !s.srv.config.ConnectEnabled {
|
||||||
|
return ErrConnectNotEnabled
|
||||||
|
}
|
||||||
|
|
||||||
// Forward if necessary
|
// Forward if necessary
|
||||||
if done, err := s.srv.ForwardRPC("Intention.Match", args, args, reply); done {
|
if done, err := s.srv.ForwardRPC("Intention.Match", args, args, reply); done {
|
||||||
return err
|
return err
|
||||||
|
@ -729,9 +641,12 @@ func (s *Intention) Match(
|
||||||
// Note: Whenever the logic for this method is changed, you should take
|
// Note: Whenever the logic for this method is changed, you should take
|
||||||
// a look at the agent authorize endpoint (agent/agent_endpoint.go) since
|
// a look at the agent authorize endpoint (agent/agent_endpoint.go) since
|
||||||
// the logic there is similar.
|
// the logic there is similar.
|
||||||
func (s *Intention) Check(
|
func (s *Intention) Check(args *structs.IntentionQueryRequest, reply *structs.IntentionQueryCheckResponse) error {
|
||||||
args *structs.IntentionQueryRequest,
|
// Exit early if Connect hasn't been enabled.
|
||||||
reply *structs.IntentionQueryCheckResponse) error {
|
if !s.srv.config.ConnectEnabled {
|
||||||
|
return ErrConnectNotEnabled
|
||||||
|
}
|
||||||
|
|
||||||
// Forward maybe
|
// Forward maybe
|
||||||
if done, err := s.srv.ForwardRPC("Intention.Check", args, args, reply); done {
|
if done, err := s.srv.ForwardRPC("Intention.Check", args, args, reply); done {
|
||||||
return err
|
return err
|
||||||
|
@ -851,23 +766,6 @@ func (s *Intention) validateEnterpriseIntention(ixn *structs.Intention) error {
|
||||||
return nil
|
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 {
|
func equalStringMaps(a, b map[string]string) bool {
|
||||||
if len(a) != len(b) {
|
if len(a) != len(b) {
|
||||||
return false
|
return false
|
||||||
|
|
|
@ -1033,18 +1033,32 @@ func (s *Server) bootstrapConfigEntries(entries []structs.ConfigEntry) error {
|
||||||
|
|
||||||
state := s.fsm.State()
|
state := s.fsm.State()
|
||||||
|
|
||||||
// Do a quick preflight check to see if someone is trying to upgrade from
|
// Do some quick preflight checks to see if someone is doing something
|
||||||
// an older pre-1.9.0 version of consul with intentions AND are trying to
|
// that's not allowed at this time:
|
||||||
// bootstrap a service-intentions config entry at the same 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()
|
usingConfigEntries, err := s.fsm.State().AreIntentionsInConfigEntries()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("Failed to determine if we are migrating intentions yet: %v", err)
|
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 {
|
for _, entry := range entries {
|
||||||
if entry.GetKind() == structs.ServiceIntentions {
|
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",
|
if !s.config.ConnectEnabled {
|
||||||
entry.GetKind(), entry.GetName(), err)
|
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
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
entries, err = s.filterMigratedLegacyIntentions(entries)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
// Totally cheat and repurpose one part of config entry replication
|
// Totally cheat and repurpose one part of config entry replication
|
||||||
// here so we automatically get our writes rate limited.
|
// here so we automatically get our writes rate limited.
|
||||||
_, err = s.reconcileLocalConfig(ctx, entries, structs.ConfigEntryUpsert)
|
_, err = s.reconcileLocalConfig(ctx, entries, structs.ConfigEntryUpsert)
|
||||||
|
|
|
@ -84,3 +84,7 @@ func migrateIntentionsToConfigEntries(ixns structs.Intentions) []*structs.Servic
|
||||||
|
|
||||||
return structs.MigrateIntentions(output)
|
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"
|
"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) {
|
func TestMigrateIntentionsToConfigEntries(t *testing.T) {
|
||||||
compare := func(t *testing.T, got structs.Intentions, expect [][]string) {
|
compare := func(t *testing.T, got structs.Intentions, expect [][]string) {
|
||||||
t.Helper()
|
t.Helper()
|
||||||
|
|
|
@ -425,6 +425,11 @@ func TestLeader_LegacyIntentionMigration(t *testing.T) {
|
||||||
makeIxn("contractor", "*", false),
|
makeIxn("contractor", "*", false),
|
||||||
makeIxn("*", "*", true),
|
makeIxn("*", "*", true),
|
||||||
}
|
}
|
||||||
|
ixns = appendLegacyIntentionsForMigrationTestEnterprise(t, s1pre, ixns)
|
||||||
|
|
||||||
|
testLeader_LegacyIntentionMigrationHookEnterprise(t, s1pre, true)
|
||||||
|
|
||||||
|
var retained []*structs.Intention
|
||||||
for _, ixn := range ixns {
|
for _, ixn := range ixns {
|
||||||
ixn2 := *ixn
|
ixn2 := *ixn
|
||||||
resp, err := s1pre.raftApply(structs.IntentionRequestType, &structs.IntentionRequest{
|
resp, err := s1pre.raftApply(structs.IntentionRequestType, &structs.IntentionRequest{
|
||||||
|
@ -435,6 +440,10 @@ func TestLeader_LegacyIntentionMigration(t *testing.T) {
|
||||||
if respErr, ok := resp.(error); ok {
|
if respErr, ok := resp.(error); ok {
|
||||||
t.Fatalf("respErr: %v", respErr)
|
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 {
|
mapify := func(ixns []*structs.Intention) map[string]*structs.Intention {
|
||||||
|
@ -465,7 +474,7 @@ func TestLeader_LegacyIntentionMigration(t *testing.T) {
|
||||||
for k, expectV := range expect {
|
for k, expectV := range expect {
|
||||||
gotV, ok := gotM[k]
|
gotV, ok := gotM[k]
|
||||||
if !ok {
|
if !ok {
|
||||||
r.Errorf("results are missing key %q", k)
|
r.Errorf("results are missing key %q: %v", k, expectV)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -483,8 +492,14 @@ func TestLeader_LegacyIntentionMigration(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
expectM := mapify(ixns)
|
expectM := mapify(ixns)
|
||||||
checkIntentions(t, s1pre, false, expectM)
|
expectRetainedM := mapify(retained)
|
||||||
checkIntentions(t, s1pre, true, expectM)
|
|
||||||
|
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.
|
// Shutdown s1pre and restart it to trigger migration.
|
||||||
s1pre.Shutdown()
|
s1pre.Shutdown()
|
||||||
|
@ -500,8 +515,7 @@ func TestLeader_LegacyIntentionMigration(t *testing.T) {
|
||||||
|
|
||||||
testrpc.WaitForLeader(t, s1.RPC, "dc1")
|
testrpc.WaitForLeader(t, s1.RPC, "dc1")
|
||||||
|
|
||||||
// check that all 7 intentions are present before migration
|
testLeader_LegacyIntentionMigrationHookEnterprise(t, s1, false)
|
||||||
checkIntentions(t, s1, false, expectM)
|
|
||||||
|
|
||||||
// Wait until the migration routine is complete.
|
// Wait until the migration routine is complete.
|
||||||
retry.Run(t, func(r *retry.R) {
|
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
|
// check that all 7 intentions are present the general way after migration
|
||||||
checkIntentions(t, s1, false, expectM)
|
require.True(t, t.Run("check migrated intentions", func(t *testing.T) {
|
||||||
// check that no intentions exist in the legacy table
|
checkIntentions(t, s1, false, expectRetainedM)
|
||||||
checkIntentions(t, s1, true, map[string]*structs.Intention{})
|
}))
|
||||||
|
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 {
|
mapifyConfigs := func(entries interface{}) map[structs.ConfigEntryKindName]*structs.ServiceIntentionsConfigEntry {
|
||||||
m := make(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)
|
require.NoError(t, err)
|
||||||
gotConfigsM := mapifyConfigs(gotConfigs)
|
gotConfigsM := mapifyConfigs(gotConfigs)
|
||||||
|
|
||||||
expectConfigs := structs.MigrateIntentions(ixns)
|
expectConfigs := structs.MigrateIntentions(retained)
|
||||||
for _, entry := range expectConfigs {
|
for _, entry := range expectConfigs {
|
||||||
require.NoError(t, entry.LegacyNormalize()) // tidy them up the same way the write would
|
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 func(ctx context.Context) error
|
||||||
|
|
||||||
type leaderRoutine struct {
|
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 {
|
type LeaderRoutineManager struct {
|
||||||
|
@ -41,7 +50,7 @@ func (m *LeaderRoutineManager) IsRunning(name string) bool {
|
||||||
defer m.lock.Unlock()
|
defer m.lock.Unlock()
|
||||||
|
|
||||||
if routine, ok := m.routines[name]; ok {
|
if routine, ok := m.routines[name]; ok {
|
||||||
return routine.running
|
return routine.running()
|
||||||
}
|
}
|
||||||
|
|
||||||
return false
|
return false
|
||||||
|
@ -55,7 +64,7 @@ func (m *LeaderRoutineManager) StartWithContext(parentCtx context.Context, name
|
||||||
m.lock.Lock()
|
m.lock.Lock()
|
||||||
defer m.lock.Unlock()
|
defer m.lock.Unlock()
|
||||||
|
|
||||||
if instance, ok := m.routines[name]; ok && instance.running {
|
if instance, ok := m.routines[name]; ok && instance.running() {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -65,11 +74,15 @@ func (m *LeaderRoutineManager) StartWithContext(parentCtx context.Context, name
|
||||||
|
|
||||||
ctx, cancel := context.WithCancel(parentCtx)
|
ctx, cancel := context.WithCancel(parentCtx)
|
||||||
instance := &leaderRoutine{
|
instance := &leaderRoutine{
|
||||||
running: true,
|
cancel: cancel,
|
||||||
cancel: cancel,
|
stoppedCh: make(chan struct{}),
|
||||||
}
|
}
|
||||||
|
|
||||||
go func() {
|
go func() {
|
||||||
|
defer func() {
|
||||||
|
close(instance.stoppedCh)
|
||||||
|
}()
|
||||||
|
|
||||||
err := routine(ctx)
|
err := routine(ctx)
|
||||||
if err != nil && err != context.DeadlineExceeded && err != context.Canceled {
|
if err != nil && err != context.DeadlineExceeded && err != context.Canceled {
|
||||||
m.logger.Error("routine exited with error",
|
m.logger.Error("routine exited with error",
|
||||||
|
@ -79,10 +92,6 @@ func (m *LeaderRoutineManager) StartWithContext(parentCtx context.Context, name
|
||||||
} else {
|
} else {
|
||||||
m.logger.Debug("stopped routine", "routine", name)
|
m.logger.Debug("stopped routine", "routine", name)
|
||||||
}
|
}
|
||||||
|
|
||||||
m.lock.Lock()
|
|
||||||
instance.running = false
|
|
||||||
m.lock.Unlock()
|
|
||||||
}()
|
}()
|
||||||
|
|
||||||
m.routines[name] = instance
|
m.routines[name] = instance
|
||||||
|
@ -90,7 +99,19 @@ func (m *LeaderRoutineManager) StartWithContext(parentCtx context.Context, name
|
||||||
return nil
|
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()
|
m.lock.Lock()
|
||||||
defer m.lock.Unlock()
|
defer m.lock.Unlock()
|
||||||
|
|
||||||
|
@ -100,15 +121,16 @@ func (m *LeaderRoutineManager) Stop(name string) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
if !instance.running {
|
if !instance.running() {
|
||||||
return nil
|
return instance
|
||||||
}
|
}
|
||||||
|
|
||||||
m.logger.Debug("stopping routine", "routine", name)
|
m.logger.Debug("stopping routine", "routine", name)
|
||||||
instance.cancel()
|
instance.cancel()
|
||||||
|
|
||||||
delete(m.routines, name)
|
delete(m.routines, name)
|
||||||
return nil
|
|
||||||
|
return instance
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *LeaderRoutineManager) StopAll() {
|
func (m *LeaderRoutineManager) StopAll() {
|
||||||
|
@ -116,7 +138,7 @@ func (m *LeaderRoutineManager) StopAll() {
|
||||||
defer m.lock.Unlock()
|
defer m.lock.Unlock()
|
||||||
|
|
||||||
for name, routine := range m.routines {
|
for name, routine := range m.routines {
|
||||||
if !routine.running {
|
if !routine.running() {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
m.logger.Debug("stopping routine", "routine", name)
|
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(&runs))
|
||||||
require.Equal(r, uint32(1), atomic.LoadUint32(&running))
|
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
|
// ensure the background go routine was actually cancelled
|
||||||
retry.Run(t, func(r *retry.R) {
|
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.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) {
|
retry.Run(t, func(r *retry.R) {
|
||||||
require.Equal(r, uint32(0), atomic.LoadUint32(&running))
|
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) {
|
func TestLeader_ConfigEntryBootstrap_Fail(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
pr, pw := io.Pipe()
|
type testcase struct {
|
||||||
defer pw.Close()
|
name string
|
||||||
|
entries []structs.ConfigEntry
|
||||||
|
serverCB func(c *Config)
|
||||||
|
expectMessage string
|
||||||
|
}
|
||||||
|
|
||||||
ch := make(chan string, 1)
|
cases := []testcase{
|
||||||
go func() {
|
{
|
||||||
defer pr.Close()
|
name: "service-splitter without L7 protocol",
|
||||||
scan := bufio.NewScanner(pr)
|
entries: []structs.ConfigEntry{
|
||||||
for scan.Scan() {
|
&structs.ServiceSplitterConfigEntry{
|
||||||
line := scan.Text()
|
Kind: structs.ServiceSplitter,
|
||||||
|
Name: "web",
|
||||||
if strings.Contains(line, "failed to establish leadership") {
|
Splits: []structs.ServiceSplit{
|
||||||
ch <- ""
|
{Weight: 100, Service: "web"},
|
||||||
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"},
|
|
||||||
},
|
},
|
||||||
|
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{
|
for _, tc := range cases {
|
||||||
Name: config.NodeName,
|
tc := tc
|
||||||
Level: hclog.Debug,
|
t.Run(tc.name, func(t *testing.T) {
|
||||||
Output: io.MultiWriter(pw, testutil.NewLogBuffer(t)),
|
pr, pw := io.Pipe()
|
||||||
})
|
defer pw.Close()
|
||||||
|
|
||||||
deps := newDefaultDeps(t, config)
|
var (
|
||||||
deps.Logger = logger
|
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)
|
if strings.Contains(line, "failed to establish leadership") {
|
||||||
require.NoError(t, err)
|
applyErrorLine = line
|
||||||
defer srv.Shutdown()
|
ch <- ""
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if strings.Contains(line, "successfully established leadership") {
|
||||||
|
ch <- "leadership should not have gotten here if config entries properly failed"
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
select {
|
if scan.Err() != nil {
|
||||||
case result := <-ch:
|
ch <- fmt.Sprintf("ERROR: %v", scan.Err())
|
||||||
require.Empty(t, result)
|
} else {
|
||||||
case <-time.After(time.Second):
|
ch <- "should not get here"
|
||||||
t.Fatal("timeout waiting for a result from tailing logs")
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
_, 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 &FederationState{s} })
|
||||||
registerEndpoint(func(s *Server) interface{} { return &DiscoveryChain{s} })
|
registerEndpoint(func(s *Server) interface{} { return &DiscoveryChain{s} })
|
||||||
registerEndpoint(func(s *Server) interface{} { return &Health{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 &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 &KVS{s, s.loggers.Named(logging.KV)} })
|
||||||
registerEndpoint(func(s *Server) interface{} { return &Operator{s, s.loggers.Named(logging.Operator)} })
|
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 {
|
if err == nil {
|
||||||
t.Fatal("expected error")
|
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())
|
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
|
// performed within a single transaction to avoid race conditions on state
|
||||||
// updates.
|
// updates.
|
||||||
func (s *Restore) Registration(idx uint64, req *structs.RegisterRequest) error {
|
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
|
// 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)
|
tx := s.db.WriteTxn(idx)
|
||||||
defer tx.Abort()
|
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
|
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
|
// ensureRegistrationTxn is used to make sure a node, service, and check
|
||||||
// registration is performed within a single transaction to avoid race
|
// registration is performed within a single transaction to avoid race
|
||||||
// conditions on state updates.
|
// conditions on state updates.
|
||||||
func (s *Store) ensureRegistrationTxn(tx WriteTxn, idx uint64, preserveIndexes bool, req *structs.RegisterRequest) error {
|
func (s *Store) ensureRegistrationTxn(tx WriteTxn, idx uint64, preserveIndexes bool, req *structs.RegisterRequest, restore bool) error {
|
||||||
if _, err := validateRegisterRequestTxn(tx, req); err != nil {
|
if _, err := validateRegisterRequestTxn(tx, req, restore); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -40,7 +40,7 @@ func TestServiceHealthEventsFromChanges(t *testing.T) {
|
||||||
Name: "service reg, new node",
|
Name: "service reg, new node",
|
||||||
Mutate: func(s *Store, tx *txn) error {
|
Mutate: func(s *Store, tx *txn) error {
|
||||||
return s.ensureRegistrationTxn(tx, tx.Index, false,
|
return s.ensureRegistrationTxn(tx, tx.Index, false,
|
||||||
testServiceRegistration(t, "web"))
|
testServiceRegistration(t, "web"), false)
|
||||||
},
|
},
|
||||||
WantEvents: []stream.Event{
|
WantEvents: []stream.Event{
|
||||||
testServiceHealthEvent(t, "web"),
|
testServiceHealthEvent(t, "web"),
|
||||||
|
@ -51,11 +51,11 @@ func TestServiceHealthEventsFromChanges(t *testing.T) {
|
||||||
Name: "service reg, existing node",
|
Name: "service reg, existing node",
|
||||||
Setup: func(s *Store, tx *txn) error {
|
Setup: func(s *Store, tx *txn) error {
|
||||||
return s.ensureRegistrationTxn(tx, tx.Index, false,
|
return s.ensureRegistrationTxn(tx, tx.Index, false,
|
||||||
testServiceRegistration(t, "db"))
|
testServiceRegistration(t, "db"), false)
|
||||||
},
|
},
|
||||||
Mutate: func(s *Store, tx *txn) error {
|
Mutate: func(s *Store, tx *txn) error {
|
||||||
return s.ensureRegistrationTxn(tx, tx.Index, false,
|
return s.ensureRegistrationTxn(tx, tx.Index, false,
|
||||||
testServiceRegistration(t, "web"))
|
testServiceRegistration(t, "web"), false)
|
||||||
},
|
},
|
||||||
WantEvents: []stream.Event{
|
WantEvents: []stream.Event{
|
||||||
// Should only publish new service
|
// Should only publish new service
|
||||||
|
@ -67,11 +67,11 @@ func TestServiceHealthEventsFromChanges(t *testing.T) {
|
||||||
Name: "service dereg, existing node",
|
Name: "service dereg, existing node",
|
||||||
Setup: func(s *Store, tx *txn) error {
|
Setup: func(s *Store, tx *txn) error {
|
||||||
if err := s.ensureRegistrationTxn(tx, tx.Index, false,
|
if err := s.ensureRegistrationTxn(tx, tx.Index, false,
|
||||||
testServiceRegistration(t, "db")); err != nil {
|
testServiceRegistration(t, "db"), false); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if err := s.ensureRegistrationTxn(tx, tx.Index, false,
|
if err := s.ensureRegistrationTxn(tx, tx.Index, false,
|
||||||
testServiceRegistration(t, "web")); err != nil {
|
testServiceRegistration(t, "web"), false); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
|
@ -88,10 +88,10 @@ func TestServiceHealthEventsFromChanges(t *testing.T) {
|
||||||
{
|
{
|
||||||
Name: "node dereg",
|
Name: "node dereg",
|
||||||
Setup: func(s *Store, tx *txn) error {
|
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 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 err
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
|
@ -109,7 +109,7 @@ func TestServiceHealthEventsFromChanges(t *testing.T) {
|
||||||
{
|
{
|
||||||
Name: "connect native reg, new node",
|
Name: "connect native reg, new node",
|
||||||
Mutate: func(s *Store, tx *txn) error {
|
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{
|
WantEvents: []stream.Event{
|
||||||
// We should see both a regular service health event as well as a connect
|
// 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",
|
Name: "connect native reg, existing node",
|
||||||
Setup: func(s *Store, tx *txn) error {
|
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 {
|
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{
|
WantEvents: []stream.Event{
|
||||||
// We should see both a regular service health event as well as a connect
|
// 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",
|
Name: "connect native dereg, existing node",
|
||||||
Setup: func(s *Store, tx *txn) error {
|
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 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 {
|
Mutate: func(s *Store, tx *txn) error {
|
||||||
return s.deleteServiceTxn(tx, tx.Index, "node1", "web", nil)
|
return s.deleteServiceTxn(tx, tx.Index, "node1", "web", nil)
|
||||||
|
@ -162,10 +162,10 @@ func TestServiceHealthEventsFromChanges(t *testing.T) {
|
||||||
{
|
{
|
||||||
Name: "connect sidecar reg, new node",
|
Name: "connect sidecar reg, new node",
|
||||||
Mutate: func(s *Store, tx *txn) error {
|
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 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{
|
WantEvents: []stream.Event{
|
||||||
// We should see both a regular service health event for the web service
|
// 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",
|
Name: "connect sidecar reg, existing node",
|
||||||
Setup: func(s *Store, tx *txn) error {
|
Setup: func(s *Store, tx *txn) error {
|
||||||
if err := s.ensureRegistrationTxn(tx, tx.Index, false,
|
if err := s.ensureRegistrationTxn(tx, tx.Index, false,
|
||||||
testServiceRegistration(t, "db")); err != nil {
|
testServiceRegistration(t, "db"), false); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return s.ensureRegistrationTxn(tx, tx.Index, false,
|
return s.ensureRegistrationTxn(tx, tx.Index, false,
|
||||||
testServiceRegistration(t, "web"))
|
testServiceRegistration(t, "web"), false)
|
||||||
},
|
},
|
||||||
Mutate: func(s *Store, tx *txn) error {
|
Mutate: func(s *Store, tx *txn) error {
|
||||||
return s.ensureRegistrationTxn(tx, tx.Index, false,
|
return s.ensureRegistrationTxn(tx, tx.Index, false,
|
||||||
testServiceRegistration(t, "web", regSidecar))
|
testServiceRegistration(t, "web", regSidecar), false)
|
||||||
},
|
},
|
||||||
WantEvents: []stream.Event{
|
WantEvents: []stream.Event{
|
||||||
// We should see both a regular service health event for the proxy
|
// 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",
|
Name: "connect sidecar dereg, existing node",
|
||||||
Setup: func(s *Store, tx *txn) error {
|
Setup: func(s *Store, tx *txn) error {
|
||||||
if err := s.ensureRegistrationTxn(tx, tx.Index, false,
|
if err := s.ensureRegistrationTxn(tx, tx.Index, false,
|
||||||
testServiceRegistration(t, "db")); err != nil {
|
testServiceRegistration(t, "db"), false); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if err := s.ensureRegistrationTxn(tx, tx.Index, false,
|
if err := s.ensureRegistrationTxn(tx, tx.Index, false,
|
||||||
testServiceRegistration(t, "web")); err != nil {
|
testServiceRegistration(t, "web"), false); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return s.ensureRegistrationTxn(tx, tx.Index, false,
|
return s.ensureRegistrationTxn(tx, tx.Index, false,
|
||||||
testServiceRegistration(t, "web", regSidecar))
|
testServiceRegistration(t, "web", regSidecar), false)
|
||||||
},
|
},
|
||||||
Mutate: func(s *Store, tx *txn) error {
|
Mutate: func(s *Store, tx *txn) error {
|
||||||
// Delete only the sidecar
|
// Delete only the sidecar
|
||||||
|
@ -227,20 +227,20 @@ func TestServiceHealthEventsFromChanges(t *testing.T) {
|
||||||
Name: "connect sidecar mutate svc",
|
Name: "connect sidecar mutate svc",
|
||||||
Setup: func(s *Store, tx *txn) error {
|
Setup: func(s *Store, tx *txn) error {
|
||||||
if err := s.ensureRegistrationTxn(tx, tx.Index, false,
|
if err := s.ensureRegistrationTxn(tx, tx.Index, false,
|
||||||
testServiceRegistration(t, "db")); err != nil {
|
testServiceRegistration(t, "db"), false); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if err := s.ensureRegistrationTxn(tx, tx.Index, false,
|
if err := s.ensureRegistrationTxn(tx, tx.Index, false,
|
||||||
testServiceRegistration(t, "web")); err != nil {
|
testServiceRegistration(t, "web"), false); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return s.ensureRegistrationTxn(tx, tx.Index, false,
|
return s.ensureRegistrationTxn(tx, tx.Index, false,
|
||||||
testServiceRegistration(t, "web", regSidecar))
|
testServiceRegistration(t, "web", regSidecar), false)
|
||||||
},
|
},
|
||||||
Mutate: func(s *Store, tx *txn) error {
|
Mutate: func(s *Store, tx *txn) error {
|
||||||
// Change port of the target service instance
|
// Change port of the target service instance
|
||||||
return s.ensureRegistrationTxn(tx, tx.Index, false,
|
return s.ensureRegistrationTxn(tx, tx.Index, false,
|
||||||
testServiceRegistration(t, "web", regMutatePort))
|
testServiceRegistration(t, "web", regMutatePort), false)
|
||||||
},
|
},
|
||||||
WantEvents: []stream.Event{
|
WantEvents: []stream.Event{
|
||||||
// We should see the service topic update but not connect since proxy
|
// 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",
|
Name: "connect sidecar mutate sidecar",
|
||||||
Setup: func(s *Store, tx *txn) error {
|
Setup: func(s *Store, tx *txn) error {
|
||||||
if err := s.ensureRegistrationTxn(tx, tx.Index, false,
|
if err := s.ensureRegistrationTxn(tx, tx.Index, false,
|
||||||
testServiceRegistration(t, "db")); err != nil {
|
testServiceRegistration(t, "db"), false); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if err := s.ensureRegistrationTxn(tx, tx.Index, false,
|
if err := s.ensureRegistrationTxn(tx, tx.Index, false,
|
||||||
testServiceRegistration(t, "web")); err != nil {
|
testServiceRegistration(t, "web"), false); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return s.ensureRegistrationTxn(tx, tx.Index, false,
|
return s.ensureRegistrationTxn(tx, tx.Index, false,
|
||||||
testServiceRegistration(t, "web", regSidecar))
|
testServiceRegistration(t, "web", regSidecar), false)
|
||||||
},
|
},
|
||||||
Mutate: func(s *Store, tx *txn) error {
|
Mutate: func(s *Store, tx *txn) error {
|
||||||
// Change port of the sidecar service instance
|
// Change port of the sidecar service instance
|
||||||
return s.ensureRegistrationTxn(tx, tx.Index, false,
|
return s.ensureRegistrationTxn(tx, tx.Index, false,
|
||||||
testServiceRegistration(t, "web", regSidecar, regMutatePort))
|
testServiceRegistration(t, "web", regSidecar, regMutatePort), false)
|
||||||
},
|
},
|
||||||
WantEvents: []stream.Event{
|
WantEvents: []stream.Event{
|
||||||
// We should see the proxy service topic update and a connect update
|
// 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",
|
Name: "connect sidecar rename service",
|
||||||
Setup: func(s *Store, tx *txn) error {
|
Setup: func(s *Store, tx *txn) error {
|
||||||
if err := s.ensureRegistrationTxn(tx, tx.Index, false,
|
if err := s.ensureRegistrationTxn(tx, tx.Index, false,
|
||||||
testServiceRegistration(t, "db")); err != nil {
|
testServiceRegistration(t, "db"), false); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if err := s.ensureRegistrationTxn(tx, tx.Index, false,
|
if err := s.ensureRegistrationTxn(tx, tx.Index, false,
|
||||||
testServiceRegistration(t, "web")); err != nil {
|
testServiceRegistration(t, "web"), false); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return s.ensureRegistrationTxn(tx, tx.Index, false,
|
return s.ensureRegistrationTxn(tx, tx.Index, false,
|
||||||
testServiceRegistration(t, "web", regSidecar))
|
testServiceRegistration(t, "web", regSidecar), false)
|
||||||
},
|
},
|
||||||
Mutate: func(s *Store, tx *txn) error {
|
Mutate: func(s *Store, tx *txn) error {
|
||||||
// Change service name but not ID, update proxy too
|
// Change service name but not ID, update proxy too
|
||||||
if err := s.ensureRegistrationTxn(tx, tx.Index, false,
|
if err := s.ensureRegistrationTxn(tx, tx.Index, false,
|
||||||
testServiceRegistration(t, "web", regRenameService)); err != nil {
|
testServiceRegistration(t, "web", regRenameService), false); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return s.ensureRegistrationTxn(tx, tx.Index, false,
|
return s.ensureRegistrationTxn(tx, tx.Index, false,
|
||||||
testServiceRegistration(t, "web", regSidecar, regRenameService))
|
testServiceRegistration(t, "web", regSidecar, regRenameService), false)
|
||||||
},
|
},
|
||||||
WantEvents: []stream.Event{
|
WantEvents: []stream.Event{
|
||||||
// We should see events to deregister the old service instance and the
|
// 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 {
|
Setup: func(s *Store, tx *txn) error {
|
||||||
// Register a web_changed service
|
// Register a web_changed service
|
||||||
if err := s.ensureRegistrationTxn(tx, tx.Index, false,
|
if err := s.ensureRegistrationTxn(tx, tx.Index, false,
|
||||||
testServiceRegistration(t, "web_changed")); err != nil {
|
testServiceRegistration(t, "web_changed"), false); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
// Also a web
|
// Also a web
|
||||||
if err := s.ensureRegistrationTxn(tx, tx.Index, false,
|
if err := s.ensureRegistrationTxn(tx, tx.Index, false,
|
||||||
testServiceRegistration(t, "web")); err != nil {
|
testServiceRegistration(t, "web"), false); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
// And a sidecar initially for web, will be moved to target web_changed
|
// And a sidecar initially for web, will be moved to target web_changed
|
||||||
// in Mutate.
|
// in Mutate.
|
||||||
return s.ensureRegistrationTxn(tx, tx.Index, false,
|
return s.ensureRegistrationTxn(tx, tx.Index, false,
|
||||||
testServiceRegistration(t, "web", regSidecar))
|
testServiceRegistration(t, "web", regSidecar), false)
|
||||||
},
|
},
|
||||||
Mutate: func(s *Store, tx *txn) error {
|
Mutate: func(s *Store, tx *txn) error {
|
||||||
// Change only the destination service of the proxy without a service
|
// Change only the destination service of the proxy without a service
|
||||||
// rename or deleting and recreating the proxy. This is far fetched but
|
// rename or deleting and recreating the proxy. This is far fetched but
|
||||||
// still valid.
|
// still valid.
|
||||||
return s.ensureRegistrationTxn(tx, tx.Index, false,
|
return s.ensureRegistrationTxn(tx, tx.Index, false,
|
||||||
testServiceRegistration(t, "web", regSidecar, regRenameService))
|
testServiceRegistration(t, "web", regSidecar, regRenameService), false)
|
||||||
},
|
},
|
||||||
WantEvents: []stream.Event{
|
WantEvents: []stream.Event{
|
||||||
// We should only see service health events for the sidecar service
|
// 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 {
|
Setup: func(s *Store, tx *txn) error {
|
||||||
// Register a db service
|
// Register a db service
|
||||||
if err := s.ensureRegistrationTxn(tx, tx.Index, false,
|
if err := s.ensureRegistrationTxn(tx, tx.Index, false,
|
||||||
testServiceRegistration(t, "db")); err != nil {
|
testServiceRegistration(t, "db"), false); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
// Also a web
|
// Also a web
|
||||||
if err := s.ensureRegistrationTxn(tx, tx.Index, false,
|
if err := s.ensureRegistrationTxn(tx, tx.Index, false,
|
||||||
testServiceRegistration(t, "web")); err != nil {
|
testServiceRegistration(t, "web"), false); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
// With a connect sidecar
|
// With a connect sidecar
|
||||||
if err := s.ensureRegistrationTxn(tx, tx.Index, false,
|
if err := s.ensureRegistrationTxn(tx, tx.Index, false,
|
||||||
testServiceRegistration(t, "web", regSidecar)); err != nil {
|
testServiceRegistration(t, "web", regSidecar), false); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
|
@ -423,7 +423,7 @@ func TestServiceHealthEventsFromChanges(t *testing.T) {
|
||||||
Mutate: func(s *Store, tx *txn) error {
|
Mutate: func(s *Store, tx *txn) error {
|
||||||
// Change only the node meta.
|
// Change only the node meta.
|
||||||
return s.ensureRegistrationTxn(tx, tx.Index, false,
|
return s.ensureRegistrationTxn(tx, tx.Index, false,
|
||||||
testNodeRegistration(t, regNodeMeta))
|
testNodeRegistration(t, regNodeMeta), false)
|
||||||
},
|
},
|
||||||
WantEvents: []stream.Event{
|
WantEvents: []stream.Event{
|
||||||
// We should see updates for all services and a connect update for the
|
// 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 {
|
Setup: func(s *Store, tx *txn) error {
|
||||||
// Register a db service
|
// Register a db service
|
||||||
if err := s.ensureRegistrationTxn(tx, tx.Index, false,
|
if err := s.ensureRegistrationTxn(tx, tx.Index, false,
|
||||||
testServiceRegistration(t, "db")); err != nil {
|
testServiceRegistration(t, "db"), false); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
// Also a web
|
// Also a web
|
||||||
if err := s.ensureRegistrationTxn(tx, tx.Index, false,
|
if err := s.ensureRegistrationTxn(tx, tx.Index, false,
|
||||||
testServiceRegistration(t, "web")); err != nil {
|
testServiceRegistration(t, "web"), false); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
// With a connect sidecar
|
// With a connect sidecar
|
||||||
if err := s.ensureRegistrationTxn(tx, tx.Index, false,
|
if err := s.ensureRegistrationTxn(tx, tx.Index, false,
|
||||||
testServiceRegistration(t, "web", regSidecar)); err != nil {
|
testServiceRegistration(t, "web", regSidecar), false); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
|
@ -485,17 +485,17 @@ func TestServiceHealthEventsFromChanges(t *testing.T) {
|
||||||
// services registered afterwards.
|
// services registered afterwards.
|
||||||
// Register a db service
|
// Register a db service
|
||||||
if err := s.ensureRegistrationTxn(tx, tx.Index, false,
|
if err := s.ensureRegistrationTxn(tx, tx.Index, false,
|
||||||
testServiceRegistration(t, "db", regRenameNode)); err != nil {
|
testServiceRegistration(t, "db", regRenameNode), false); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
// Also a web
|
// Also a web
|
||||||
if err := s.ensureRegistrationTxn(tx, tx.Index, false,
|
if err := s.ensureRegistrationTxn(tx, tx.Index, false,
|
||||||
testServiceRegistration(t, "web", regRenameNode)); err != nil {
|
testServiceRegistration(t, "web", regRenameNode), false); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
// With a connect sidecar
|
// With a connect sidecar
|
||||||
if err := s.ensureRegistrationTxn(tx, tx.Index, false,
|
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 err
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
|
@ -540,17 +540,17 @@ func TestServiceHealthEventsFromChanges(t *testing.T) {
|
||||||
Setup: func(s *Store, tx *txn) error {
|
Setup: func(s *Store, tx *txn) error {
|
||||||
// Register a db service
|
// Register a db service
|
||||||
if err := s.ensureRegistrationTxn(tx, tx.Index, false,
|
if err := s.ensureRegistrationTxn(tx, tx.Index, false,
|
||||||
testServiceRegistration(t, "db")); err != nil {
|
testServiceRegistration(t, "db"), false); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
// Also a web
|
// Also a web
|
||||||
if err := s.ensureRegistrationTxn(tx, tx.Index, false,
|
if err := s.ensureRegistrationTxn(tx, tx.Index, false,
|
||||||
testServiceRegistration(t, "web")); err != nil {
|
testServiceRegistration(t, "web"), false); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
// With a connect sidecar
|
// With a connect sidecar
|
||||||
if err := s.ensureRegistrationTxn(tx, tx.Index, false,
|
if err := s.ensureRegistrationTxn(tx, tx.Index, false,
|
||||||
testServiceRegistration(t, "web", regSidecar)); err != nil {
|
testServiceRegistration(t, "web", regSidecar), false); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
|
@ -558,7 +558,7 @@ func TestServiceHealthEventsFromChanges(t *testing.T) {
|
||||||
Mutate: func(s *Store, tx *txn) error {
|
Mutate: func(s *Store, tx *txn) error {
|
||||||
// Change only the node-level check status
|
// Change only the node-level check status
|
||||||
if err := s.ensureRegistrationTxn(tx, tx.Index, false,
|
if err := s.ensureRegistrationTxn(tx, tx.Index, false,
|
||||||
testServiceRegistration(t, "web", regNodeCheckFail)); err != nil {
|
testServiceRegistration(t, "web", regNodeCheckFail), false); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
|
@ -600,17 +600,17 @@ func TestServiceHealthEventsFromChanges(t *testing.T) {
|
||||||
Setup: func(s *Store, tx *txn) error {
|
Setup: func(s *Store, tx *txn) error {
|
||||||
// Register a db service
|
// Register a db service
|
||||||
if err := s.ensureRegistrationTxn(tx, tx.Index, false,
|
if err := s.ensureRegistrationTxn(tx, tx.Index, false,
|
||||||
testServiceRegistration(t, "db")); err != nil {
|
testServiceRegistration(t, "db"), false); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
// Also a web
|
// Also a web
|
||||||
if err := s.ensureRegistrationTxn(tx, tx.Index, false,
|
if err := s.ensureRegistrationTxn(tx, tx.Index, false,
|
||||||
testServiceRegistration(t, "web")); err != nil {
|
testServiceRegistration(t, "web"), false); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
// With a connect sidecar
|
// With a connect sidecar
|
||||||
if err := s.ensureRegistrationTxn(tx, tx.Index, false,
|
if err := s.ensureRegistrationTxn(tx, tx.Index, false,
|
||||||
testServiceRegistration(t, "web", regSidecar)); err != nil {
|
testServiceRegistration(t, "web", regSidecar), false); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
|
@ -618,7 +618,7 @@ func TestServiceHealthEventsFromChanges(t *testing.T) {
|
||||||
Mutate: func(s *Store, tx *txn) error {
|
Mutate: func(s *Store, tx *txn) error {
|
||||||
// Change the service-level check status
|
// Change the service-level check status
|
||||||
if err := s.ensureRegistrationTxn(tx, tx.Index, false,
|
if err := s.ensureRegistrationTxn(tx, tx.Index, false,
|
||||||
testServiceRegistration(t, "web", regServiceCheckFail)); err != nil {
|
testServiceRegistration(t, "web", regServiceCheckFail), false); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
// Also change the service-level check status for the proxy. This is
|
// 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
|
// - the proxies check would get updated at roughly the same time as the
|
||||||
// target service check updates.
|
// target service check updates.
|
||||||
if err := s.ensureRegistrationTxn(tx, tx.Index, false,
|
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 err
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
|
@ -663,17 +663,17 @@ func TestServiceHealthEventsFromChanges(t *testing.T) {
|
||||||
Setup: func(s *Store, tx *txn) error {
|
Setup: func(s *Store, tx *txn) error {
|
||||||
// Register a db service
|
// Register a db service
|
||||||
if err := s.ensureRegistrationTxn(tx, tx.Index, false,
|
if err := s.ensureRegistrationTxn(tx, tx.Index, false,
|
||||||
testServiceRegistration(t, "db")); err != nil {
|
testServiceRegistration(t, "db"), false); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
// Also a web
|
// Also a web
|
||||||
if err := s.ensureRegistrationTxn(tx, tx.Index, false,
|
if err := s.ensureRegistrationTxn(tx, tx.Index, false,
|
||||||
testServiceRegistration(t, "web")); err != nil {
|
testServiceRegistration(t, "web"), false); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
// With a connect sidecar
|
// With a connect sidecar
|
||||||
if err := s.ensureRegistrationTxn(tx, tx.Index, false,
|
if err := s.ensureRegistrationTxn(tx, tx.Index, false,
|
||||||
testServiceRegistration(t, "web", regSidecar)); err != nil {
|
testServiceRegistration(t, "web", regSidecar), false); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
|
@ -717,17 +717,17 @@ func TestServiceHealthEventsFromChanges(t *testing.T) {
|
||||||
Setup: func(s *Store, tx *txn) error {
|
Setup: func(s *Store, tx *txn) error {
|
||||||
// Register a db service
|
// Register a db service
|
||||||
if err := s.ensureRegistrationTxn(tx, tx.Index, false,
|
if err := s.ensureRegistrationTxn(tx, tx.Index, false,
|
||||||
testServiceRegistration(t, "db")); err != nil {
|
testServiceRegistration(t, "db"), false); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
// Also a web
|
// Also a web
|
||||||
if err := s.ensureRegistrationTxn(tx, tx.Index, false,
|
if err := s.ensureRegistrationTxn(tx, tx.Index, false,
|
||||||
testServiceRegistration(t, "web")); err != nil {
|
testServiceRegistration(t, "web"), false); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
// With a connect sidecar
|
// With a connect sidecar
|
||||||
if err := s.ensureRegistrationTxn(tx, tx.Index, false,
|
if err := s.ensureRegistrationTxn(tx, tx.Index, false,
|
||||||
testServiceRegistration(t, "web", regSidecar)); err != nil {
|
testServiceRegistration(t, "web", regSidecar), false); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
|
@ -777,19 +777,19 @@ func TestServiceHealthEventsFromChanges(t *testing.T) {
|
||||||
|
|
||||||
// Register a db service
|
// Register a db service
|
||||||
if err := s.ensureRegistrationTxn(tx, tx.Index, false,
|
if err := s.ensureRegistrationTxn(tx, tx.Index, false,
|
||||||
testServiceRegistration(t, "db")); err != nil {
|
testServiceRegistration(t, "db"), false); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Node2
|
// Node2
|
||||||
// Also a web
|
// Also a web
|
||||||
if err := s.ensureRegistrationTxn(tx, tx.Index, false,
|
if err := s.ensureRegistrationTxn(tx, tx.Index, false,
|
||||||
testServiceRegistration(t, "web", regNode2)); err != nil {
|
testServiceRegistration(t, "web", regNode2), false); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
// With a connect sidecar
|
// With a connect sidecar
|
||||||
if err := s.ensureRegistrationTxn(tx, tx.Index, false,
|
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
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -808,17 +808,17 @@ func TestServiceHealthEventsFromChanges(t *testing.T) {
|
||||||
|
|
||||||
// Register those on node1
|
// Register those on node1
|
||||||
if err := s.ensureRegistrationTxn(tx, tx.Index, false,
|
if err := s.ensureRegistrationTxn(tx, tx.Index, false,
|
||||||
testServiceRegistration(t, "web")); err != nil {
|
testServiceRegistration(t, "web"), false); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if err := s.ensureRegistrationTxn(tx, tx.Index, false,
|
if err := s.ensureRegistrationTxn(tx, tx.Index, false,
|
||||||
testServiceRegistration(t, "web", regSidecar)); err != nil {
|
testServiceRegistration(t, "web", regSidecar), false); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// And for good measure, add a new connect-native service to node2
|
// And for good measure, add a new connect-native service to node2
|
||||||
if err := s.ensureRegistrationTxn(tx, tx.Index, false,
|
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
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -323,7 +323,7 @@ func catalogChecksForNodeService(tx ReadTxn, node string, service string, entMet
|
||||||
return tx.Get("checks", "node_service", node, service)
|
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
|
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.
|
// 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.
|
// Check for existing configuration.
|
||||||
existing, err := firstConfigEntryWithTxn(tx, conf.GetKind(), conf.GetName(), entMeta)
|
existing, err := firstConfigEntryWithTxn(tx, conf.GetKind(), conf.GetName(), entMeta)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -254,6 +254,14 @@ func (s *Store) DeleteConfigEntry(idx uint64, kind, name string, entMeta *struct
|
||||||
tx := s.db.WriteTxn(idx)
|
tx := s.db.WriteTxn(idx)
|
||||||
defer tx.Abort()
|
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.
|
// Try to retrieve the existing config entry.
|
||||||
existing, err := firstConfigEntryWithTxn(tx, kind, name, entMeta)
|
existing, err := firstConfigEntryWithTxn(tx, kind, name, entMeta)
|
||||||
if err != nil {
|
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 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 {
|
if conf == nil {
|
||||||
return fmt.Errorf("cannot insert nil config entry")
|
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.")
|
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.
|
// LegacyIntentionSet creates or updates an intention.
|
||||||
//
|
//
|
||||||
// Deprecated: Edit service-intentions config entries directly.
|
// Deprecated: Edit service-intentions config entries directly.
|
||||||
|
|
|
@ -13,6 +13,14 @@ import (
|
||||||
"github.com/stretchr/testify/require"
|
"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)) {
|
func testBothIntentionFormats(t *testing.T, f func(t *testing.T, s *Store, legacy bool)) {
|
||||||
t.Helper()
|
t.Helper()
|
||||||
|
|
||||||
|
@ -72,6 +80,8 @@ func TestStore_IntentionSetGet_basic(t *testing.T) {
|
||||||
DestinationNS: "default",
|
DestinationNS: "default",
|
||||||
DestinationName: "web",
|
DestinationName: "web",
|
||||||
Meta: map[string]string{},
|
Meta: map[string]string{},
|
||||||
|
CreatedAt: testTimeA,
|
||||||
|
UpdatedAt: testTimeA,
|
||||||
}
|
}
|
||||||
|
|
||||||
// Inserting a with empty ID is disallowed.
|
// Inserting a with empty ID is disallowed.
|
||||||
|
@ -89,6 +99,8 @@ func TestStore_IntentionSetGet_basic(t *testing.T) {
|
||||||
DestinationNS: "default",
|
DestinationNS: "default",
|
||||||
DestinationName: "web",
|
DestinationName: "web",
|
||||||
Meta: map[string]string{},
|
Meta: map[string]string{},
|
||||||
|
CreatedAt: testTimeA,
|
||||||
|
UpdatedAt: testTimeA,
|
||||||
RaftIndex: structs.RaftIndex{
|
RaftIndex: structs.RaftIndex{
|
||||||
CreateIndex: lastIndex,
|
CreateIndex: lastIndex,
|
||||||
ModifyIndex: lastIndex,
|
ModifyIndex: lastIndex,
|
||||||
|
@ -103,10 +115,12 @@ func TestStore_IntentionSetGet_basic(t *testing.T) {
|
||||||
Name: "web",
|
Name: "web",
|
||||||
Sources: []*structs.SourceIntention{
|
Sources: []*structs.SourceIntention{
|
||||||
{
|
{
|
||||||
LegacyID: srcID,
|
LegacyID: srcID,
|
||||||
Name: "*",
|
Name: "*",
|
||||||
Action: structs.IntentionActionAllow,
|
Action: structs.IntentionActionAllow,
|
||||||
LegacyMeta: map[string]string{},
|
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())
|
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) {
|
func TestStore_LegacyIntentionSet_emptyId(t *testing.T) {
|
||||||
// note: irrelevant test for config entries variant
|
// note: irrelevant test for config entries variant
|
||||||
s := testStateStore(t)
|
s := testStateStore(t)
|
||||||
|
@ -282,24 +824,27 @@ func TestStore_IntentionSet_updateCreatedAt(t *testing.T) {
|
||||||
testBothIntentionFormats(t, func(t *testing.T, s *Store, legacy bool) {
|
testBothIntentionFormats(t, func(t *testing.T, s *Store, legacy bool) {
|
||||||
// Build a valid intention
|
// Build a valid intention
|
||||||
var (
|
var (
|
||||||
id = testUUID()
|
id = testUUID()
|
||||||
createTime time.Time
|
|
||||||
)
|
)
|
||||||
|
|
||||||
if legacy {
|
if legacy {
|
||||||
ixn := structs.Intention{
|
ixn := structs.Intention{
|
||||||
ID: id,
|
ID: id,
|
||||||
CreatedAt: time.Now().UTC(),
|
SourceNS: "default",
|
||||||
|
SourceName: "*",
|
||||||
|
DestinationNS: "default",
|
||||||
|
DestinationName: "web",
|
||||||
|
Action: structs.IntentionActionAllow,
|
||||||
|
CreatedAt: testTimeA,
|
||||||
|
UpdatedAt: testTimeA,
|
||||||
}
|
}
|
||||||
|
|
||||||
// Insert
|
// Insert
|
||||||
require.NoError(t, s.LegacyIntentionSet(1, &ixn))
|
require.NoError(t, s.LegacyIntentionSet(1, &ixn))
|
||||||
|
|
||||||
createTime = ixn.CreatedAt
|
|
||||||
|
|
||||||
// Change a value and test updating
|
// Change a value and test updating
|
||||||
ixnUpdate := ixn
|
ixnUpdate := ixn
|
||||||
ixnUpdate.CreatedAt = createTime.Add(10 * time.Second)
|
ixnUpdate.CreatedAt = testTimeB
|
||||||
require.NoError(t, s.LegacyIntentionSet(2, &ixnUpdate))
|
require.NoError(t, s.LegacyIntentionSet(2, &ixnUpdate))
|
||||||
|
|
||||||
id = ixn.ID
|
id = ixn.ID
|
||||||
|
@ -310,10 +855,12 @@ func TestStore_IntentionSet_updateCreatedAt(t *testing.T) {
|
||||||
Name: "web",
|
Name: "web",
|
||||||
Sources: []*structs.SourceIntention{
|
Sources: []*structs.SourceIntention{
|
||||||
{
|
{
|
||||||
LegacyID: id,
|
LegacyID: id,
|
||||||
Name: "*",
|
Name: "*",
|
||||||
Action: structs.IntentionActionAllow,
|
Action: structs.IntentionActionAllow,
|
||||||
LegacyMeta: map[string]string{},
|
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.LegacyNormalize())
|
||||||
require.NoError(t, conf.LegacyValidate())
|
require.NoError(t, conf.LegacyValidate())
|
||||||
require.NoError(t, s.EnsureConfigEntry(1, conf.Clone(), nil))
|
require.NoError(t, s.EnsureConfigEntry(1, conf.Clone(), nil))
|
||||||
|
|
||||||
createTime = *conf.Sources[0].LegacyCreateTime
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Read it back and verify
|
// Read it back and verify
|
||||||
_, _, actual, err := s.IntentionGet(nil, id)
|
_, _, actual, err := s.IntentionGet(nil, id)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.NotNil(t, actual)
|
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 {
|
if legacy {
|
||||||
// Build a valid intention
|
// Build a valid intention
|
||||||
ixn := &structs.Intention{
|
ixn := &structs.Intention{
|
||||||
ID: id,
|
ID: id,
|
||||||
|
SourceNS: "default",
|
||||||
|
SourceName: "*",
|
||||||
|
DestinationNS: "default",
|
||||||
|
DestinationName: "web",
|
||||||
|
Action: structs.IntentionActionAllow,
|
||||||
|
CreatedAt: testTimeA,
|
||||||
|
UpdatedAt: testTimeA,
|
||||||
}
|
}
|
||||||
|
|
||||||
// Insert
|
// Insert
|
||||||
|
@ -351,9 +903,11 @@ func TestStore_IntentionSet_metaNil(t *testing.T) {
|
||||||
Name: "web",
|
Name: "web",
|
||||||
Sources: []*structs.SourceIntention{
|
Sources: []*structs.SourceIntention{
|
||||||
{
|
{
|
||||||
LegacyID: id,
|
LegacyID: id,
|
||||||
Name: "*",
|
Name: "*",
|
||||||
Action: structs.IntentionActionAllow,
|
Action: structs.IntentionActionAllow,
|
||||||
|
LegacyCreateTime: &testTimeA,
|
||||||
|
LegacyUpdateTime: &testTimeA,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
@ -380,8 +934,15 @@ func TestStore_IntentionSet_metaSet(t *testing.T) {
|
||||||
if legacy {
|
if legacy {
|
||||||
// Build a valid intention
|
// Build a valid intention
|
||||||
ixn := structs.Intention{
|
ixn := structs.Intention{
|
||||||
ID: id,
|
ID: id,
|
||||||
Meta: expectMeta,
|
SourceNS: "default",
|
||||||
|
SourceName: "*",
|
||||||
|
DestinationNS: "default",
|
||||||
|
DestinationName: "web",
|
||||||
|
Action: structs.IntentionActionAllow,
|
||||||
|
CreatedAt: testTimeA,
|
||||||
|
UpdatedAt: testTimeA,
|
||||||
|
Meta: expectMeta,
|
||||||
}
|
}
|
||||||
|
|
||||||
// Insert
|
// Insert
|
||||||
|
@ -394,10 +955,12 @@ func TestStore_IntentionSet_metaSet(t *testing.T) {
|
||||||
Name: "web",
|
Name: "web",
|
||||||
Sources: []*structs.SourceIntention{
|
Sources: []*structs.SourceIntention{
|
||||||
{
|
{
|
||||||
LegacyID: id,
|
LegacyID: id,
|
||||||
Name: "*",
|
Name: "*",
|
||||||
Action: structs.IntentionActionAllow,
|
Action: structs.IntentionActionAllow,
|
||||||
LegacyMeta: expectMeta,
|
LegacyCreateTime: &testTimeA,
|
||||||
|
LegacyUpdateTime: &testTimeA,
|
||||||
|
LegacyMeta: expectMeta,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
@ -428,7 +991,14 @@ func TestStore_IntentionDelete(t *testing.T) {
|
||||||
// Create
|
// Create
|
||||||
if legacy {
|
if legacy {
|
||||||
ixn := &structs.Intention{
|
ixn := &structs.Intention{
|
||||||
ID: id,
|
ID: id,
|
||||||
|
SourceNS: "default",
|
||||||
|
SourceName: "*",
|
||||||
|
DestinationNS: "default",
|
||||||
|
DestinationName: "web",
|
||||||
|
Action: structs.IntentionActionAllow,
|
||||||
|
CreatedAt: testTimeA,
|
||||||
|
UpdatedAt: testTimeA,
|
||||||
}
|
}
|
||||||
lastIndex++
|
lastIndex++
|
||||||
require.NoError(t, s.LegacyIntentionSet(lastIndex, ixn))
|
require.NoError(t, s.LegacyIntentionSet(lastIndex, ixn))
|
||||||
|
@ -442,9 +1012,11 @@ func TestStore_IntentionDelete(t *testing.T) {
|
||||||
Name: "web",
|
Name: "web",
|
||||||
Sources: []*structs.SourceIntention{
|
Sources: []*structs.SourceIntention{
|
||||||
{
|
{
|
||||||
LegacyID: id,
|
LegacyID: id,
|
||||||
Name: "*",
|
Name: "*",
|
||||||
Action: structs.IntentionActionAllow,
|
Action: structs.IntentionActionAllow,
|
||||||
|
LegacyCreateTime: &testTimeA,
|
||||||
|
LegacyUpdateTime: &testTimeA,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
@ -519,6 +1091,8 @@ func TestStore_IntentionsList(t *testing.T) {
|
||||||
SourceType: structs.IntentionSourceConsul,
|
SourceType: structs.IntentionSourceConsul,
|
||||||
Action: structs.IntentionActionAllow,
|
Action: structs.IntentionActionAllow,
|
||||||
Meta: map[string]string{},
|
Meta: map[string]string{},
|
||||||
|
CreatedAt: testTimeA,
|
||||||
|
UpdatedAt: testTimeA,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -530,22 +1104,17 @@ func TestStore_IntentionsList(t *testing.T) {
|
||||||
id := testUUID()
|
id := testUUID()
|
||||||
for _, src := range srcs {
|
for _, src := range srcs {
|
||||||
conf.Sources = append(conf.Sources, &structs.SourceIntention{
|
conf.Sources = append(conf.Sources, &structs.SourceIntention{
|
||||||
LegacyID: id,
|
LegacyID: id,
|
||||||
Name: src,
|
Name: src,
|
||||||
Action: structs.IntentionActionAllow,
|
Action: structs.IntentionActionAllow,
|
||||||
|
LegacyCreateTime: &testTimeA,
|
||||||
|
LegacyUpdateTime: &testTimeA,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
return conf
|
return conf
|
||||||
}
|
}
|
||||||
|
|
||||||
cmpIntention := func(ixn *structs.Intention, id string) *structs.Intention {
|
clearIrrelevantFields := func(ixns ...*structs.Intention) {
|
||||||
ixn.ID = id
|
|
||||||
//nolint:staticcheck
|
|
||||||
ixn.UpdatePrecedence()
|
|
||||||
return ixn
|
|
||||||
}
|
|
||||||
|
|
||||||
clearIrrelevantFields := func(ixns []*structs.Intention) {
|
|
||||||
// Clear fields irrelevant for comparison.
|
// Clear fields irrelevant for comparison.
|
||||||
for _, ixn := range ixns {
|
for _, ixn := range ixns {
|
||||||
ixn.Hash = nil
|
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 (
|
var (
|
||||||
expectIDs []string
|
expectIDs []string
|
||||||
)
|
)
|
||||||
|
@ -610,7 +1188,7 @@ func TestStore_IntentionsList(t *testing.T) {
|
||||||
require.Equal(t, !legacy, fromConfig)
|
require.Equal(t, !legacy, fromConfig)
|
||||||
require.Equal(t, lastIndex, idx)
|
require.Equal(t, lastIndex, idx)
|
||||||
|
|
||||||
clearIrrelevantFields(actual)
|
clearIrrelevantFields(actual...)
|
||||||
require.Equal(t, expected, 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.
|
// KVS is used when restoring from a snapshot. Use KVSSet for general inserts.
|
||||||
func (s *Restore) KVS(entry *structs.DirEntry) error {
|
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)
|
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
|
entry.ModifyIndex = idx
|
||||||
|
|
||||||
// Store the kv pair in the state store and update the index.
|
// 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)
|
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 {
|
if err := tx.Insert("kvs", entry); err != nil {
|
||||||
return err
|
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
|
// Session is used when restoring from a snapshot. For general inserts, use
|
||||||
// SessionCreate.
|
// SessionCreate.
|
||||||
func (s *Restore) Session(sess *structs.Session) error {
|
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)
|
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
|
// 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)
|
return fmt.Errorf("failed inserting session: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -48,7 +48,7 @@ func sessionDeleteWithSession(tx WriteTxn, session *structs.Session, idx uint64)
|
||||||
return nil
|
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 {
|
if err := tx.Insert("sessions", session); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
|
@ -61,7 +61,7 @@ func (c *ClientConnPool) ClientConn(datacenter string) (*grpc.ClientConn, error)
|
||||||
grpc.WithInsecure(),
|
grpc.WithInsecure(),
|
||||||
grpc.WithContextDialer(c.dialer),
|
grpc.WithContextDialer(c.dialer),
|
||||||
grpc.WithDisableRetry(),
|
grpc.WithDisableRetry(),
|
||||||
grpc.WithStatsHandler(newStatsHandler(defaultMetrics)),
|
grpc.WithStatsHandler(newStatsHandler(defaultMetrics())),
|
||||||
// nolint:staticcheck // there is no other supported alternative to WithBalancerName
|
// nolint:staticcheck // there is no other supported alternative to WithBalancerName
|
||||||
grpc.WithBalancerName("pick_first"))
|
grpc.WithBalancerName("pick_first"))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
@ -14,11 +14,12 @@ import (
|
||||||
// The register function will be called with the grpc.Server to register
|
// The register function will be called with the grpc.Server to register
|
||||||
// gRPC services with the server.
|
// gRPC services with the server.
|
||||||
func NewHandler(addr net.Addr, register func(server *grpc.Server)) *Handler {
|
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
|
// We don't need to pass tls.Config to the server since it's multiplexed
|
||||||
// behind the RPC listener, which already has TLS configured.
|
// behind the RPC listener, which already has TLS configured.
|
||||||
srv := grpc.NewServer(
|
srv := grpc.NewServer(
|
||||||
grpc.StatsHandler(newStatsHandler(defaultMetrics)),
|
grpc.StatsHandler(newStatsHandler(metrics)),
|
||||||
grpc.StreamInterceptor((&activeStreamCounter{metrics: defaultMetrics}).Intercept),
|
grpc.StreamInterceptor((&activeStreamCounter{metrics: metrics}).Intercept),
|
||||||
)
|
)
|
||||||
register(srv)
|
register(srv)
|
||||||
|
|
||||||
|
|
|
@ -10,7 +10,6 @@ import (
|
||||||
"google.golang.org/grpc/stats"
|
"google.golang.org/grpc/stats"
|
||||||
)
|
)
|
||||||
|
|
||||||
var defaultMetrics = metrics.Default()
|
|
||||||
var StatsGauges = []prometheus.GaugeDefinition{
|
var StatsGauges = []prometheus.GaugeDefinition{
|
||||||
{
|
{
|
||||||
Name: []string{"grpc", "server", "connections"},
|
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
|
// statsHandler is a grpc/stats.StatsHandler which emits connection and
|
||||||
// request metrics to go-metrics.
|
// request metrics to go-metrics.
|
||||||
type statsHandler struct {
|
type statsHandler struct {
|
||||||
|
|
|
@ -113,11 +113,14 @@ func patchGlobalMetrics(t *testing.T) (*fakeMetricsSink, func()) {
|
||||||
FilterDefault: true,
|
FilterDefault: true,
|
||||||
}
|
}
|
||||||
var err error
|
var err error
|
||||||
defaultMetrics, err = metrics.New(cfg, sink)
|
defaultMetrics = func() *metrics.Metrics {
|
||||||
|
m, _ := metrics.New(cfg, sink)
|
||||||
|
return m
|
||||||
|
}
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
reset := func() {
|
reset := func() {
|
||||||
t.Helper()
|
t.Helper()
|
||||||
defaultMetrics, err = metrics.New(cfg, &metrics.BlackholeSink{})
|
defaultMetrics = metrics.Default
|
||||||
require.NoError(t, err, "failed to reset global metrics")
|
require.NoError(t, err, "failed to reset global metrics")
|
||||||
}
|
}
|
||||||
return sink, reset
|
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) {
|
return func(resp http.ResponseWriter, req *http.Request) {
|
||||||
setHeaders(resp, s.agent.config.HTTPResponseHeaders)
|
setHeaders(resp, s.agent.config.HTTPResponseHeaders)
|
||||||
setTranslateAddr(resp, s.agent.config.TranslateWANAddrs)
|
setTranslateAddr(resp, s.agent.config.TranslateWANAddrs)
|
||||||
|
setACLDefaultPolicy(resp, s.agent.config.ACLDefaultPolicy)
|
||||||
|
|
||||||
// Obfuscate any tokens from appearing in the logs
|
// Obfuscate any tokens from appearing in the logs
|
||||||
formVals, err := url.ParseQuery(req.URL.RawQuery)
|
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
|
// setLastContact is used to set the last contact header
|
||||||
func setLastContact(resp http.ResponseWriter, last time.Duration) {
|
func setLastContact(resp http.ResponseWriter, last time.Duration) {
|
||||||
if last < 0 {
|
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) {
|
func TestHTTPAPIResponseHeaders(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
a := NewTestAgent(t, `
|
a := NewTestAgent(t, `
|
||||||
|
|
|
@ -11,6 +11,8 @@ func autopilotToAPIServerEnterprise(_ *autopilot.ServerState, _ *api.AutopilotSe
|
||||||
// noop in oss
|
// noop in oss
|
||||||
}
|
}
|
||||||
|
|
||||||
func autopilotToAPIStateEnterprise(_ *autopilot.State, _ *api.AutopilotState) {
|
func autopilotToAPIStateEnterprise(state *autopilot.State, apiState *api.AutopilotState) {
|
||||||
// noop in oss
|
// 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{
|
expected := api.AutopilotState{
|
||||||
Healthy: true,
|
Healthy: true,
|
||||||
FailureTolerance: 1,
|
FailureTolerance: 1,
|
||||||
Leader: string(leaderID),
|
OptimisticFailureTolerance: 1,
|
||||||
|
Leader: string(leaderID),
|
||||||
Voters: []string{
|
Voters: []string{
|
||||||
string(leaderID),
|
string(leaderID),
|
||||||
string(follower1ID),
|
string(follower1ID),
|
||||||
|
|
|
@ -519,7 +519,7 @@ START:
|
||||||
// Try to get a conn first
|
// Try to get a conn first
|
||||||
conn, err := p.acquire(dc, nodeName, addr)
|
conn, err := p.acquire(dc, nodeName, addr)
|
||||||
if err != nil {
|
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
|
// Get a client
|
||||||
|
@ -533,7 +533,7 @@ START:
|
||||||
retries++
|
retries++
|
||||||
goto START
|
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
|
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
|
var codec rpc.ClientCodec
|
||||||
conn, _, err := p.dial(dc, addr, 0, RPCTLSInsecure)
|
conn, _, err := p.dial(dc, addr, 0, RPCTLSInsecure)
|
||||||
if err != nil {
|
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)
|
codec = msgpackrpc.NewCodecFromHandle(true, true, conn, structs.MsgpackHandle)
|
||||||
|
|
||||||
// Make the RPC call
|
// Make the RPC call
|
||||||
err = msgpackrpc.CallWithCodec(codec, method, args, reply)
|
err = msgpackrpc.CallWithCodec(codec, method, args, reply)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("rpcinsecure error making call: %v", err)
|
return fmt.Errorf("rpcinsecure error making call: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
@ -594,7 +594,7 @@ func (p *ConnPool) rpc(dc string, nodeName string, addr net.Addr, method string,
|
||||||
// Get a usable client
|
// Get a usable client
|
||||||
conn, sc, err := p.getClient(dc, nodeName, addr)
|
conn, sc, err := p.getClient(dc, nodeName, addr)
|
||||||
if err != nil {
|
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
|
// Make the RPC call
|
||||||
|
@ -611,7 +611,7 @@ func (p *ConnPool) rpc(dc string, nodeName string, addr net.Addr, method string,
|
||||||
}
|
}
|
||||||
|
|
||||||
p.releaseConn(conn)
|
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
|
// Done with the connection
|
||||||
|
|
|
@ -59,6 +59,59 @@ func (e *ServiceIntentionsConfigEntry) DestinationServiceName() ServiceName {
|
||||||
return NewServiceName(e.Name, &e.EnterpriseMeta)
|
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 {
|
func (e *ServiceIntentionsConfigEntry) ToIntention(src *SourceIntention) *Intention {
|
||||||
meta := e.Meta
|
meta := e.Meta
|
||||||
if src.LegacyID != "" {
|
if src.LegacyID != "" {
|
||||||
|
@ -352,6 +405,9 @@ func (e *ServiceIntentionsConfigEntry) normalize(legacyWrite bool) error {
|
||||||
return fmt.Errorf("config entry is nil")
|
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.Kind = ServiceIntentions
|
||||||
|
|
||||||
e.EnterpriseMeta.Normalize()
|
e.EnterpriseMeta.Normalize()
|
||||||
|
@ -377,11 +433,6 @@ func (e *ServiceIntentionsConfigEntry) normalize(legacyWrite bool) error {
|
||||||
if src.LegacyMeta == nil {
|
if src.LegacyMeta == nil {
|
||||||
src.LegacyMeta = make(map[string]string)
|
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 {
|
} else {
|
||||||
// Legacy fields are cleared, except LegacyMeta which we leave
|
// Legacy fields are cleared, except LegacyMeta which we leave
|
||||||
// populated so that we can later fail the write in Validate() and
|
// 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 {
|
} else {
|
||||||
if len(src.LegacyMeta) > 0 {
|
if len(src.LegacyMeta) > 0 {
|
||||||
return fmt.Errorf("Sources[%d].LegacyMeta must be omitted", i)
|
return fmt.Errorf("Sources[%d].LegacyMeta must be omitted", i)
|
||||||
}
|
}
|
||||||
src.LegacyMeta = nil // ensure it's completely unset
|
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 {
|
if legacyWrite {
|
||||||
|
|
|
@ -21,6 +21,14 @@ func generateUUID() (ret string) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestServiceIntentionsConfigEntry(t *testing.T) {
|
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 {
|
type testcase struct {
|
||||||
entry *ServiceIntentionsConfigEntry
|
entry *ServiceIntentionsConfigEntry
|
||||||
legacy bool
|
legacy bool
|
||||||
|
@ -126,9 +134,11 @@ func TestServiceIntentionsConfigEntry(t *testing.T) {
|
||||||
Name: "test",
|
Name: "test",
|
||||||
Sources: []*SourceIntention{
|
Sources: []*SourceIntention{
|
||||||
{
|
{
|
||||||
LegacyID: legacyIDs[0],
|
LegacyID: legacyIDs[0],
|
||||||
Name: "foo",
|
Name: "foo",
|
||||||
Action: IntentionActionAllow,
|
Action: IntentionActionAllow,
|
||||||
|
LegacyCreateTime: &testTimeA,
|
||||||
|
LegacyUpdateTime: &testTimeA,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
Meta: map[string]string{
|
Meta: map[string]string{
|
||||||
|
@ -198,10 +208,12 @@ func TestServiceIntentionsConfigEntry(t *testing.T) {
|
||||||
Name: "test",
|
Name: "test",
|
||||||
Sources: []*SourceIntention{
|
Sources: []*SourceIntention{
|
||||||
{
|
{
|
||||||
LegacyID: legacyIDs[0],
|
LegacyID: legacyIDs[0],
|
||||||
Name: "foo",
|
Name: "foo",
|
||||||
Action: IntentionActionAllow,
|
Action: IntentionActionAllow,
|
||||||
Description: strings.Repeat("x", 512),
|
Description: strings.Repeat("x", 512),
|
||||||
|
LegacyCreateTime: &testTimeA,
|
||||||
|
LegacyUpdateTime: &testTimeA,
|
||||||
LegacyMeta: map[string]string{ // stray Meta will be dropped
|
LegacyMeta: map[string]string{ // stray Meta will be dropped
|
||||||
"old": "data",
|
"old": "data",
|
||||||
},
|
},
|
||||||
|
@ -217,10 +229,12 @@ func TestServiceIntentionsConfigEntry(t *testing.T) {
|
||||||
Name: "test",
|
Name: "test",
|
||||||
Sources: []*SourceIntention{
|
Sources: []*SourceIntention{
|
||||||
{
|
{
|
||||||
LegacyID: legacyIDs[0],
|
LegacyID: legacyIDs[0],
|
||||||
Name: "foo",
|
Name: "foo",
|
||||||
Action: IntentionActionAllow,
|
Action: IntentionActionAllow,
|
||||||
LegacyMeta: makeStringMap(65, 5, 5),
|
LegacyCreateTime: &testTimeA,
|
||||||
|
LegacyUpdateTime: &testTimeA,
|
||||||
|
LegacyMeta: makeStringMap(65, 5, 5),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -233,10 +247,12 @@ func TestServiceIntentionsConfigEntry(t *testing.T) {
|
||||||
Name: "test",
|
Name: "test",
|
||||||
Sources: []*SourceIntention{
|
Sources: []*SourceIntention{
|
||||||
{
|
{
|
||||||
LegacyID: legacyIDs[0],
|
LegacyID: legacyIDs[0],
|
||||||
Name: "foo",
|
Name: "foo",
|
||||||
Action: IntentionActionAllow,
|
Action: IntentionActionAllow,
|
||||||
LegacyMeta: makeStringMap(64, 129, 5),
|
LegacyCreateTime: &testTimeA,
|
||||||
|
LegacyUpdateTime: &testTimeA,
|
||||||
|
LegacyMeta: makeStringMap(64, 129, 5),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -249,10 +265,12 @@ func TestServiceIntentionsConfigEntry(t *testing.T) {
|
||||||
Name: "test",
|
Name: "test",
|
||||||
Sources: []*SourceIntention{
|
Sources: []*SourceIntention{
|
||||||
{
|
{
|
||||||
LegacyID: legacyIDs[0],
|
LegacyID: legacyIDs[0],
|
||||||
Name: "foo",
|
Name: "foo",
|
||||||
Action: IntentionActionAllow,
|
Action: IntentionActionAllow,
|
||||||
LegacyMeta: makeStringMap(64, 128, 513),
|
LegacyCreateTime: &testTimeA,
|
||||||
|
LegacyUpdateTime: &testTimeA,
|
||||||
|
LegacyMeta: makeStringMap(64, 128, 513),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -265,10 +283,12 @@ func TestServiceIntentionsConfigEntry(t *testing.T) {
|
||||||
Name: "test",
|
Name: "test",
|
||||||
Sources: []*SourceIntention{
|
Sources: []*SourceIntention{
|
||||||
{
|
{
|
||||||
LegacyID: legacyIDs[0],
|
LegacyID: legacyIDs[0],
|
||||||
Name: "foo",
|
Name: "foo",
|
||||||
Action: IntentionActionAllow,
|
Action: IntentionActionAllow,
|
||||||
LegacyMeta: makeStringMap(64, 128, 512),
|
LegacyCreateTime: &testTimeA,
|
||||||
|
LegacyUpdateTime: &testTimeA,
|
||||||
|
LegacyMeta: makeStringMap(64, 128, 512),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -280,9 +300,11 @@ func TestServiceIntentionsConfigEntry(t *testing.T) {
|
||||||
Name: "test",
|
Name: "test",
|
||||||
Sources: []*SourceIntention{
|
Sources: []*SourceIntention{
|
||||||
{
|
{
|
||||||
Name: "foo",
|
Name: "foo",
|
||||||
Action: IntentionActionAllow,
|
Action: IntentionActionAllow,
|
||||||
Description: strings.Repeat("x", 512),
|
Description: strings.Repeat("x", 512),
|
||||||
|
LegacyCreateTime: &testTimeA,
|
||||||
|
LegacyUpdateTime: &testTimeA,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -1008,8 +1030,10 @@ func TestServiceIntentionsConfigEntry(t *testing.T) {
|
||||||
Action: IntentionActionDeny,
|
Action: IntentionActionDeny,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Name: "foo",
|
Name: "foo",
|
||||||
Action: IntentionActionAllow,
|
Action: IntentionActionAllow,
|
||||||
|
LegacyCreateTime: &testTimeA, // stray times will be dropped
|
||||||
|
LegacyUpdateTime: &testTimeA,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Name: "bar",
|
Name: "bar",
|
||||||
|
@ -1059,9 +1083,11 @@ func TestServiceIntentionsConfigEntry(t *testing.T) {
|
||||||
Name: "test",
|
Name: "test",
|
||||||
Sources: []*SourceIntention{
|
Sources: []*SourceIntention{
|
||||||
{
|
{
|
||||||
Name: WildcardSpecifier,
|
Name: WildcardSpecifier,
|
||||||
Action: IntentionActionDeny,
|
Action: IntentionActionDeny,
|
||||||
LegacyID: legacyIDs[0],
|
LegacyID: legacyIDs[0],
|
||||||
|
LegacyCreateTime: &testTimeA,
|
||||||
|
LegacyUpdateTime: &testTimeA,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Name: "foo",
|
Name: "foo",
|
||||||
|
@ -1071,23 +1097,27 @@ func TestServiceIntentionsConfigEntry(t *testing.T) {
|
||||||
"key1": "val1",
|
"key1": "val1",
|
||||||
"key2": "val2",
|
"key2": "val2",
|
||||||
},
|
},
|
||||||
|
LegacyCreateTime: &testTimeB,
|
||||||
|
LegacyUpdateTime: &testTimeB,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Name: "bar",
|
Name: "bar",
|
||||||
Action: IntentionActionDeny,
|
Action: IntentionActionDeny,
|
||||||
LegacyID: legacyIDs[2],
|
LegacyID: legacyIDs[2],
|
||||||
|
LegacyCreateTime: &testTimeC,
|
||||||
|
LegacyUpdateTime: &testTimeC,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
check: func(t *testing.T, entry *ServiceIntentionsConfigEntry) {
|
check: func(t *testing.T, entry *ServiceIntentionsConfigEntry) {
|
||||||
require.Len(t, entry.Sources, 3)
|
require.Len(t, entry.Sources, 3)
|
||||||
|
|
||||||
assert.False(t, entry.Sources[0].LegacyCreateTime.IsZero())
|
// assert.False(t, entry.Sources[0].LegacyCreateTime.IsZero())
|
||||||
assert.False(t, entry.Sources[0].LegacyUpdateTime.IsZero())
|
// assert.False(t, entry.Sources[0].LegacyUpdateTime.IsZero())
|
||||||
assert.False(t, entry.Sources[1].LegacyCreateTime.IsZero())
|
// assert.False(t, entry.Sources[1].LegacyCreateTime.IsZero())
|
||||||
assert.False(t, entry.Sources[1].LegacyUpdateTime.IsZero())
|
// assert.False(t, entry.Sources[1].LegacyUpdateTime.IsZero())
|
||||||
assert.False(t, entry.Sources[2].LegacyCreateTime.IsZero())
|
// assert.False(t, entry.Sources[2].LegacyCreateTime.IsZero())
|
||||||
assert.False(t, entry.Sources[2].LegacyUpdateTime.IsZero())
|
// assert.False(t, entry.Sources[2].LegacyUpdateTime.IsZero())
|
||||||
|
|
||||||
assert.Equal(t, []*SourceIntention{
|
assert.Equal(t, []*SourceIntention{
|
||||||
{
|
{
|
||||||
|
@ -1299,6 +1329,8 @@ func TestMigrateIntentions(t *testing.T) {
|
||||||
LegacyMeta: map[string]string{
|
LegacyMeta: map[string]string{
|
||||||
"key1": "val1",
|
"key1": "val1",
|
||||||
},
|
},
|
||||||
|
LegacyCreateTime: &anyTime,
|
||||||
|
LegacyUpdateTime: &anyTime,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -1349,6 +1381,8 @@ func TestMigrateIntentions(t *testing.T) {
|
||||||
LegacyMeta: map[string]string{
|
LegacyMeta: map[string]string{
|
||||||
"key1": "val1",
|
"key1": "val1",
|
||||||
},
|
},
|
||||||
|
LegacyCreateTime: &anyTime,
|
||||||
|
LegacyUpdateTime: &anyTime,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
LegacyID: legacyIDs[1],
|
LegacyID: legacyIDs[1],
|
||||||
|
@ -1359,6 +1393,8 @@ func TestMigrateIntentions(t *testing.T) {
|
||||||
LegacyMeta: map[string]string{
|
LegacyMeta: map[string]string{
|
||||||
"key2": "val2",
|
"key2": "val2",
|
||||||
},
|
},
|
||||||
|
LegacyCreateTime: &anyTime,
|
||||||
|
LegacyUpdateTime: &anyTime,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -1409,6 +1445,8 @@ func TestMigrateIntentions(t *testing.T) {
|
||||||
LegacyMeta: map[string]string{
|
LegacyMeta: map[string]string{
|
||||||
"key1": "val1",
|
"key1": "val1",
|
||||||
},
|
},
|
||||||
|
LegacyCreateTime: &anyTime,
|
||||||
|
LegacyUpdateTime: &anyTime,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -1425,6 +1463,8 @@ func TestMigrateIntentions(t *testing.T) {
|
||||||
LegacyMeta: map[string]string{
|
LegacyMeta: map[string]string{
|
||||||
"key2": "val2",
|
"key2": "val2",
|
||||||
},
|
},
|
||||||
|
LegacyCreateTime: &anyTime,
|
||||||
|
LegacyUpdateTime: &anyTime,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
|
@ -440,6 +440,9 @@ func (x *Intention) ToConfigEntry(legacy bool) *ServiceIntentionsConfigEntry {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (x *Intention) ToSourceIntention(legacy bool) *SourceIntention {
|
func (x *Intention) ToSourceIntention(legacy bool) *SourceIntention {
|
||||||
|
ct := x.CreatedAt // copy
|
||||||
|
ut := x.UpdatedAt
|
||||||
|
|
||||||
src := &SourceIntention{
|
src := &SourceIntention{
|
||||||
Name: x.SourceName,
|
Name: x.SourceName,
|
||||||
EnterpriseMeta: *x.SourceEnterpriseMeta(),
|
EnterpriseMeta: *x.SourceEnterpriseMeta(),
|
||||||
|
@ -450,8 +453,8 @@ func (x *Intention) ToSourceIntention(legacy bool) *SourceIntention {
|
||||||
Type: x.SourceType,
|
Type: x.SourceType,
|
||||||
Description: x.Description,
|
Description: x.Description,
|
||||||
LegacyMeta: x.Meta,
|
LegacyMeta: x.Meta,
|
||||||
LegacyCreateTime: nil, // Ignore
|
LegacyCreateTime: &ct,
|
||||||
LegacyUpdateTime: nil, // Ignore
|
LegacyUpdateTime: &ut,
|
||||||
}
|
}
|
||||||
if !legacy {
|
if !legacy {
|
||||||
src.Permissions = x.Permissions
|
src.Permissions = x.Permissions
|
||||||
|
@ -522,13 +525,30 @@ type IntentionRequest struct {
|
||||||
Op IntentionOp
|
Op IntentionOp
|
||||||
|
|
||||||
// Intention is the intention.
|
// Intention is the intention.
|
||||||
|
//
|
||||||
|
// This is mutually exclusive with the Mutation field.
|
||||||
Intention *Intention
|
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
|
// WriteRequest is a common struct containing ACL tokens and other
|
||||||
// write-related common elements for requests.
|
// write-related common elements for requests.
|
||||||
WriteRequest
|
WriteRequest
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type IntentionMutation struct {
|
||||||
|
ID string
|
||||||
|
Destination ServiceName
|
||||||
|
Source ServiceName
|
||||||
|
Value *SourceIntention
|
||||||
|
}
|
||||||
|
|
||||||
// RequestDatacenter returns the datacenter for a given request.
|
// RequestDatacenter returns the datacenter for a given request.
|
||||||
func (q *IntentionRequest) RequestDatacenter() string {
|
func (q *IntentionRequest) RequestDatacenter() string {
|
||||||
return q.Datacenter
|
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
|
// configured to use TLS. Any other value indicates that it was not setup in
|
||||||
// that manner.
|
// that manner.
|
||||||
MemberTagValueUseTLS = "1"
|
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
|
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
|
// CacheAge is set if request was ?cached and indicates how stale the cached
|
||||||
// response is.
|
// response is.
|
||||||
CacheAge time.Duration
|
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
|
// 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
|
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
|
// Parse Cache info
|
||||||
if cacheStr := header.Get("X-Cache"); cacheStr != "" {
|
if cacheStr := header.Get("X-Cache"); cacheStr != "" {
|
||||||
q.CacheHit = strings.EqualFold(cacheStr, "HIT")
|
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-LastContact", "80")
|
||||||
resp.Header.Set("X-Consul-KnownLeader", "true")
|
resp.Header.Set("X-Consul-KnownLeader", "true")
|
||||||
resp.Header.Set("X-Consul-Translate-Addresses", "true")
|
resp.Header.Set("X-Consul-Translate-Addresses", "true")
|
||||||
|
resp.Header.Set("X-Consul-Default-ACL-Policy", "deny")
|
||||||
|
|
||||||
qm := &QueryMeta{}
|
qm := &QueryMeta{}
|
||||||
if err := parseQueryMeta(resp, qm); err != nil {
|
if err := parseQueryMeta(resp, qm); err != nil {
|
||||||
|
@ -858,6 +859,9 @@ func TestAPI_ParseQueryMeta(t *testing.T) {
|
||||||
if !qm.AddressTranslationEnabled {
|
if !qm.AddressTranslationEnabled {
|
||||||
t.Fatalf("Bad: %v", qm)
|
t.Fatalf("Bad: %v", qm)
|
||||||
}
|
}
|
||||||
|
if qm.DefaultACLPolicy != "deny" {
|
||||||
|
t.Fatalf("Bad: %v", qm)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestAPI_UnixSocket(t *testing.T) {
|
func TestAPI_UnixSocket(t *testing.T) {
|
||||||
|
|
|
@ -114,7 +114,7 @@ type OperatorHealthReply struct {
|
||||||
type AutopilotState struct {
|
type AutopilotState struct {
|
||||||
Healthy bool
|
Healthy bool
|
||||||
FailureTolerance int
|
FailureTolerance int
|
||||||
OptimisitcFailureTolerance int
|
OptimisticFailureTolerance int
|
||||||
|
|
||||||
Servers map[string]AutopilotServer
|
Servers map[string]AutopilotServer
|
||||||
Leader string
|
Leader string
|
||||||
|
@ -137,7 +137,7 @@ type AutopilotServer struct {
|
||||||
StableSince time.Time
|
StableSince time.Time
|
||||||
RedundancyZone string `json:",omitempty"`
|
RedundancyZone string `json:",omitempty"`
|
||||||
UpgradeVersion string `json:",omitempty"`
|
UpgradeVersion string `json:",omitempty"`
|
||||||
ReadReplica bool `json:",omitempty"`
|
ReadReplica bool
|
||||||
Status AutopilotServerStatus
|
Status AutopilotServerStatus
|
||||||
Meta map[string]string
|
Meta map[string]string
|
||||||
NodeType AutopilotServerType
|
NodeType AutopilotServerType
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
ARG GOLANG_VERSION=1.14.11
|
ARG GOLANG_VERSION=1.15.5
|
||||||
FROM golang:${GOLANG_VERSION}
|
FROM golang:${GOLANG_VERSION}
|
||||||
|
|
||||||
ARG GOTOOLS="github.com/elazarl/go-bindata-assetfs/... \
|
ARG GOTOOLS="github.com/elazarl/go-bindata-assetfs/... \
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
FROM travisci/ci-garnet:packer-1512502276-986baf0
|
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
|
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"
|
operauto "github.com/hashicorp/consul/command/operator/autopilot"
|
||||||
operautoget "github.com/hashicorp/consul/command/operator/autopilot/get"
|
operautoget "github.com/hashicorp/consul/command/operator/autopilot/get"
|
||||||
operautoset "github.com/hashicorp/consul/command/operator/autopilot/set"
|
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"
|
operraft "github.com/hashicorp/consul/command/operator/raft"
|
||||||
operraftlist "github.com/hashicorp/consul/command/operator/raft/listpeers"
|
operraftlist "github.com/hashicorp/consul/command/operator/raft/listpeers"
|
||||||
operraftremove "github.com/hashicorp/consul/command/operator/raft/removepeer"
|
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", 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 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 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", 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 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 })
|
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" }`,
|
`{ "duration": "nope" }`,
|
||||||
"",
|
"",
|
||||||
"invalid duration nope",
|
`invalid duration "nope"`,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
`{ "string": 123 }`,
|
`{ "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 Term\t%d", info.Meta.Term)
|
||||||
fmt.Fprintf(tw, "\n Version\t%d", info.Meta.Version)
|
fmt.Fprintf(tw, "\n Version\t%d", info.Meta.Version)
|
||||||
fmt.Fprintf(tw, "\n")
|
fmt.Fprintf(tw, "\n")
|
||||||
fmt.Fprintln(tw, "\n Type\tCount\tSize\t")
|
fmt.Fprintln(tw, "\n Type\tCount\tSize")
|
||||||
fmt.Fprintf(tw, " %s\t%s\t%s\t", "----", "----", "----")
|
fmt.Fprintf(tw, " %s\t%s\t%s", "----", "----", "----")
|
||||||
// For each different type generate new output
|
// For each different type generate new output
|
||||||
for _, s := range info.Stats {
|
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 {
|
if err := tw.Flush(); err != nil {
|
||||||
return b.String(), err
|
return b.String(), err
|
||||||
}
|
}
|
||||||
|
|
||||||
return b.String(), nil
|
return b.String(), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -13,6 +13,11 @@ func TestFormat(t *testing.T) {
|
||||||
Sum: 1,
|
Sum: 1,
|
||||||
Count: 2,
|
Count: 2,
|
||||||
}}
|
}}
|
||||||
|
mkv := []typeStats{{
|
||||||
|
Name: "msgKV",
|
||||||
|
Sum: 1,
|
||||||
|
Count: 2,
|
||||||
|
}}
|
||||||
info := OutputFormat{
|
info := OutputFormat{
|
||||||
Meta: &MetadataInfo{
|
Meta: &MetadataInfo{
|
||||||
ID: "one",
|
ID: "one",
|
||||||
|
@ -21,8 +26,10 @@ func TestFormat(t *testing.T) {
|
||||||
Term: 4,
|
Term: 4,
|
||||||
Version: 1,
|
Version: 1,
|
||||||
},
|
},
|
||||||
Stats: m,
|
Stats: m,
|
||||||
TotalSize: 1,
|
StatsKV: mkv,
|
||||||
|
TotalSize: 1,
|
||||||
|
TotalSizeKV: 1,
|
||||||
}
|
}
|
||||||
|
|
||||||
formatters := map[string]Formatter{
|
formatters := map[string]Formatter{
|
||||||
|
|
|
@ -29,10 +29,21 @@ type cmd struct {
|
||||||
flags *flag.FlagSet
|
flags *flag.FlagSet
|
||||||
help string
|
help string
|
||||||
format string
|
format string
|
||||||
|
|
||||||
|
// flags
|
||||||
|
kvDetails bool
|
||||||
|
kvDepth int
|
||||||
|
kvFilter string
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *cmd) init() {
|
func (c *cmd) init() {
|
||||||
c.flags = flag.NewFlagSet("", flag.ContinueOnError)
|
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.flags.StringVar(
|
||||||
&c.format,
|
&c.format,
|
||||||
"format",
|
"format",
|
||||||
|
@ -52,12 +63,24 @@ type MetadataInfo struct {
|
||||||
Version raft.SnapshotVersion
|
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
|
// OutputFormat is used for passing information
|
||||||
// through the formatter
|
// through the formatter
|
||||||
type OutputFormat struct {
|
type OutputFormat struct {
|
||||||
Meta *MetadataInfo
|
Meta *MetadataInfo
|
||||||
Stats []typeStats
|
Stats []typeStats
|
||||||
TotalSize int
|
StatsKV []typeStats
|
||||||
|
TotalSize int
|
||||||
|
TotalSizeKV int
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *cmd) Run(args []string) 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 {
|
if err != nil {
|
||||||
c.UI.Error(fmt.Sprintf("Error extracting snapshot data: %s", err))
|
c.UI.Error(fmt.Sprintf("Error extracting snapshot data: %s", err))
|
||||||
return 1
|
return 1
|
||||||
|
@ -122,13 +145,17 @@ func (c *cmd) Run(args []string) int {
|
||||||
}
|
}
|
||||||
|
|
||||||
//Restructures stats given above to be human readable
|
//Restructures stats given above to be human readable
|
||||||
formattedStats := generatetypeStats(stats)
|
formattedStats := generateStats(info)
|
||||||
|
formattedStatsKV := generateKVStats(info)
|
||||||
|
|
||||||
in := &OutputFormat{
|
in := &OutputFormat{
|
||||||
Meta: metaformat,
|
Meta: metaformat,
|
||||||
Stats: formattedStats,
|
Stats: formattedStats,
|
||||||
TotalSize: totalSize,
|
StatsKV: formattedStatsKV,
|
||||||
|
TotalSize: info.TotalSize,
|
||||||
|
TotalSizeKV: info.TotalSizeKV,
|
||||||
}
|
}
|
||||||
|
|
||||||
out, err := formatter.Format(in)
|
out, err := formatter.Format(in)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.UI.Error(err.Error())
|
c.UI.Error(err.Error())
|
||||||
|
@ -145,19 +172,55 @@ type typeStats struct {
|
||||||
Count int
|
Count int
|
||||||
}
|
}
|
||||||
|
|
||||||
func generatetypeStats(info map[structs.MessageType]typeStats) []typeStats {
|
// generateStats formats the stats for the output struct
|
||||||
ss := make([]typeStats, 0, len(info))
|
// 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)
|
ss = append(ss, s)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Sort the stat slice
|
ss = sortTypeStats(ss)
|
||||||
sort.Slice(ss, func(i, j int) bool { return ss[i].Sum > ss[j].Sum })
|
|
||||||
|
|
||||||
return 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
|
// countingReader helps keep track of the bytes we have read
|
||||||
// when reading snapshots
|
// when reading snapshots
|
||||||
type countingReader struct {
|
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
|
// enhance utilizes ReadSnapshot to populate the struct with
|
||||||
// all of the snapshot's itemized data
|
// all of the snapshot's itemized data
|
||||||
func enhance(file io.Reader) (map[structs.MessageType]typeStats, int, error) {
|
func (c *cmd) enhance(file io.Reader) (SnapshotInfo, error) {
|
||||||
stats := make(map[structs.MessageType]typeStats)
|
info := SnapshotInfo{
|
||||||
|
Stats: make(map[structs.MessageType]typeStats),
|
||||||
|
StatsKV: make(map[string]typeStats),
|
||||||
|
TotalSize: 0,
|
||||||
|
TotalSizeKV: 0,
|
||||||
|
}
|
||||||
cr := &countingReader{wrappedReader: file}
|
cr := &countingReader{wrappedReader: file}
|
||||||
totalSize := 0
|
|
||||||
handler := func(header *fsm.SnapshotHeader, msg structs.MessageType, dec *codec.Decoder) error {
|
handler := func(header *fsm.SnapshotHeader, msg structs.MessageType, dec *codec.Decoder) error {
|
||||||
name := structs.MessageType.String(msg)
|
name := structs.MessageType.String(msg)
|
||||||
s := stats[msg]
|
s := info.Stats[msg]
|
||||||
if s.Name == "" {
|
if s.Name == "" {
|
||||||
s.Name = name
|
s.Name = name
|
||||||
}
|
}
|
||||||
|
|
||||||
var val interface{}
|
var val interface{}
|
||||||
err := dec.Decode(&val)
|
err := dec.Decode(&val)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("failed to decode msg type %v, error %v", name, err)
|
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.Sum += size
|
||||||
s.Count++
|
s.Count++
|
||||||
totalSize = cr.read
|
info.TotalSize = cr.read
|
||||||
stats[msg] = s
|
info.Stats[msg] = s
|
||||||
|
|
||||||
|
c.kvEnhance(s.Name, val, size, &info)
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
if err := fsm.ReadSnapshot(cr, handler); err != 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 {
|
func (c *cmd) Synopsis() string {
|
||||||
return synopsis
|
return synopsis
|
||||||
}
|
}
|
||||||
|
|
|
@ -95,3 +95,57 @@ func TestSnapshotInspectCommand(t *testing.T) {
|
||||||
want := golden(t, t.Name(), ui.OutputWriter.String())
|
want := golden(t, t.Name(), ui.OutputWriter.String())
|
||||||
require.Equal(t, want, 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
|
Term 2
|
||||||
Version 1
|
Version 1
|
||||||
|
|
||||||
Type Count Size
|
Type Count Size
|
||||||
---- ---- ----
|
---- ---- ----
|
||||||
Register 3 1.7KB
|
Register 3 1.7KB
|
||||||
ConnectCA 1 1.2KB
|
ConnectCA 1 1.2KB
|
||||||
ConnectCAProviderState 1 1.1KB
|
ConnectCAProviderState 1 1.1KB
|
||||||
Index 12 344B
|
Index 12 344B
|
||||||
Autopilot 1 199B
|
Autopilot 1 199B
|
||||||
ConnectCAConfig 1 197B
|
ConnectCAConfig 1 197B
|
||||||
FederationState 1 139B
|
FederationState 1 139B
|
||||||
SystemMetadata 1 68B
|
SystemMetadata 1 68B
|
||||||
ChunkingState 1 12B
|
ChunkingState 1 12B
|
||||||
---- ---- ----
|
---- ---- ----
|
||||||
Total 5KB
|
Total 5KB
|
||||||
|
|
|
@ -4,15 +4,15 @@
|
||||||
Term 2
|
Term 2
|
||||||
Version 1
|
Version 1
|
||||||
|
|
||||||
Type Count Size
|
Type Count Size
|
||||||
---- ---- ----
|
---- ---- ----
|
||||||
Register 3 1.8KB
|
Register 3 1.8KB
|
||||||
ConnectCA 1 1.2KB
|
ConnectCA 1 1.2KB
|
||||||
ConnectCAProviderState 1 1.1KB
|
ConnectCAProviderState 1 1.1KB
|
||||||
Index 11 313B
|
Index 11 313B
|
||||||
ConnectCAConfig 1 247B
|
ConnectCAConfig 1 247B
|
||||||
Autopilot 1 199B
|
Autopilot 1 199B
|
||||||
SystemMetadata 1 68B
|
SystemMetadata 1 68B
|
||||||
ChunkingState 1 12B
|
ChunkingState 1 12B
|
||||||
---- ---- ----
|
---- ---- ----
|
||||||
Total 5KB
|
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
|
"Count": 2
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"TotalSize": 1
|
"StatsKV": [
|
||||||
|
{
|
||||||
|
"Name": "msgKV",
|
||||||
|
"Sum": 1,
|
||||||
|
"Count": 2
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"TotalSize": 1,
|
||||||
|
"TotalSizeKV": 1
|
||||||
}
|
}
|
|
@ -4,8 +4,14 @@
|
||||||
Term 4
|
Term 4
|
||||||
Version 1
|
Version 1
|
||||||
|
|
||||||
Type Count Size
|
Type Count Size
|
||||||
---- ---- ----
|
---- ---- ----
|
||||||
msg 2 1B
|
msg 2 1B
|
||||||
---- ---- ----
|
---- ---- ----
|
||||||
Total 1B
|
Total 1B
|
||||||
|
|
||||||
|
Key Name Count Size
|
||||||
|
---- ---- ----
|
||||||
|
msgKV 2 1B
|
||||||
|
---- ---- ----
|
||||||
|
Total 1B
|
|
@ -1,29 +1,16 @@
|
||||||
-----BEGIN CERTIFICATE-----
|
-----BEGIN CERTIFICATE-----
|
||||||
MIIFADCCAugCCQCPPTSu2adkQzANBgkqhkiG9w0BAQsFADBFMQswCQYDVQQGEwJB
|
MIICnDCCAkOgAwIBAgIRAOnKNzSoGq53Rq/G5tbm85swCgYIKoZIzj0EAwIwgbkx
|
||||||
VTETMBEGA1UECAwKU29tZS1TdGF0ZTEhMB8GA1UECgwYSW50ZXJuZXQgV2lkZ2l0
|
CzAJBgNVBAYTAlVTMQswCQYDVQQIEwJDQTEWMBQGA1UEBxMNU2FuIEZyYW5jaXNj
|
||||||
cyBQdHkgTHRkMB4XDTE3MDQxNDE5MTE0MVoXDTIyMDMxOTE5MTE0MVowPzELMAkG
|
bzEaMBgGA1UECRMRMTAxIFNlY29uZCBTdHJlZXQxDjAMBgNVBBETBTk0MTA1MRcw
|
||||||
A1UEBhMCQVUxEzARBgNVBAgMClNvbWUtU3RhdGUxGzAZBgNVBAoMEkNvbnN1bCBU
|
FQYDVQQKEw5IYXNoaUNvcnAgSW5jLjFAMD4GA1UEAxM3Q29uc3VsIEFnZW50IENB
|
||||||
ZXN0IENsaWVudDCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAOyJwuXC
|
IDE4NTU3MTQ5MTMzMTA0NzczNDYwMjQyMDcxODI5NjUzMzQzNTQ0MzAeFw0yMDEw
|
||||||
N+8sOnFmWDx6w5Xs+ADd905EKkfrdLc0qIcrndZwdCx9zOeArLhUS1aoh0ctwz5x
|
MjgyMjI3NTZaFw0yMTEwMjgyMjI3NTZaMBwxGjAYBgNVBAMTEWNsaWVudC5kYzEu
|
||||||
wBsmUgwIQwr/V6Q1+sAEDmPmnuAiR/m3lxE+ZPr1CyNhrrTqs7jXkRRDNtZevw6d
|
Y29uc3VsMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEMtVdDd8tDZBaOaDFFzWD
|
||||||
mTE0FC4Tho016NVXckVZRRJL8Svfk2GvZbZ+1HAIi1a/Du7VQPbd1HOLKPkpb0JU
|
0hTxO7soxUuz1dWaO8FGhIS07dfSBjYumEOgfNtfOzAILvkBd4gS8DrQZ2Rbks86
|
||||||
AksdAaks/avzKhRUoHQBR/T4S9GK95WrCqJIZG6iOVK2cymfZ9t/qYdm5czdq8ho
|
iKOBxzCBxDAOBgNVHQ8BAf8EBAMCBaAwHQYDVR0lBBYwFAYIKwYBBQUHAwIGCCsG
|
||||||
kWNqOtVb+yBx4/zs8JZI3/TZ/K4nSFcpZNZmPI7xsBL0/GG7nqWyAFWFsn/+4q0a
|
AQUFBwMBMAwGA1UdEwEB/wQCMAAwKQYDVR0OBCIEIEJWUjlDw7H2fbRpGG8fpqCq
|
||||||
FIBVUe+ydONKoXKZI080b9CLkNXIp1P8rJsodjKZO/xr+/JCeo6D7ZAJymFe1QXJ
|
GEX80iDpQqXOU0wg6fEPMCsGA1UdIwQkMCKAIAu+td60D/Er7Xjtyg0B6XflfKYm
|
||||||
aGt7ac08s9scU97n0iuCRn6RXLdii9pbiAlArmAE1zXQKA/hcyVtjfUdeq0kA1RE
|
IdXjPfiFy8SGeKS2MC0GA1UdEQQmMCSCEWNsaWVudC5kYzEuY29uc3Vsgglsb2Nh
|
||||||
308gUab552XstQUHH0/+JcoPhn1UN+D6UOw7CxVQrq6FBDeDt7S/WtJNGM3GYrQW
|
bGhvc3SHBH8AAAEwCgYIKoZIzj0EAwIDRwAwRAIgYAZTf8VcZ4nQl4lbm579BfXy
|
||||||
tbvTQhDW0jBp0Tr+BwIDXYN5QLM6rcnB2GR3+aYVuvre6DrvisA6yKO+0F29n2LM
|
6YpYz/DdfkEODUBxUyYCIDXhfmxtL/gTSkIh1E+fV7H7ZmqPKgTDH1XBV2zYnj/C
|
||||||
DdcUu8ZGzoEyFOZAgcw943VlU8bSxWkANoO0e3gKub4AvOaRqvVKyeFuFnwzKjSE
|
|
||||||
jiZTHVWs/1XVTnK51b9H33uK976O0q9qGlERAgMBAAEwDQYJKoZIhvcNAQELBQAD
|
|
||||||
ggIBAAGb44gmBikP4KJOLDDQWUX+l6kfIEobKzulmUcZ5puNS68fnQrWYUXTk5MK
|
|
||||||
IKBfuQSrxux/CEx1nBIZS/IcAnxG6GpxIcVdtzrZ/2yhDsmT3ZlgMo0+kNgqPnB2
|
|
||||||
hVe19HO44KAkjKpbjzqya2YX5CY83WBbB0kLvbRtBraU74LDyPD8Nb2YgCKkKZuC
|
|
||||||
WHtvMw/3bGQ74FXRIjLBv1moOiWaOvaMpztn4GzChZuU8Ikyps3YiRKES2gSvPIj
|
|
||||||
tynzyoED/ddNA6Q/NeLYaCqwzIVScuqBsDCNTvFqZztS4J7VJxxnRztXb/og2yE4
|
|
||||||
xqXezVV/O+6HhRPnxInYNiMRS++C/cyeKt150t/YS718KlebL4Pd+fQ4fDQzMyYl
|
|
||||||
ixEOVFLZiT07heUBkNA//Y0thBr/VbMvvcnMKmm3wLKsL/VGznI5akZhBa0DRj/0
|
|
||||||
y+dSNJ2CL4I11SQ5yIaVgggqVEJ6wUWpE5woLQuvg+P+QVgkBtGR7H5dmREMq06L
|
|
||||||
dPWFoBNBil5MNDLAt2eoOauoVdba1XWWW6HZJP9lR/qgFeE9yWAFUB0BwonkYJYM
|
|
||||||
0Tm9+Jv87nJAy+a1RDszjAMV/N1RvCOb9+2g4NnHSXNVqH+gP0BsVYP5e2A31GX0
|
|
||||||
aftVp7tPN1bSFt7nFZNbbsFhRD/fQvOpCOC/7pTnXx8zhl/2
|
|
||||||
-----END CERTIFICATE-----
|
-----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-----
|
-----BEGIN EC PRIVATE KEY-----
|
||||||
MIIJKAIBAAKCAgEA7InC5cI37yw6cWZYPHrDlez4AN33TkQqR+t0tzSohyud1nB0
|
MHcCAQEEIDxDVYnUL3LCN7kSKF/ShH1c8HacmeUyU/2qJ/fo+5kDoAoGCCqGSM49
|
||||||
LH3M54CsuFRLVqiHRy3DPnHAGyZSDAhDCv9XpDX6wAQOY+ae4CJH+beXET5k+vUL
|
AwEHoUQDQgAEMtVdDd8tDZBaOaDFFzWD0hTxO7soxUuz1dWaO8FGhIS07dfSBjYu
|
||||||
I2GutOqzuNeRFEM21l6/Dp2ZMTQULhOGjTXo1VdyRVlFEkvxK9+TYa9ltn7UcAiL
|
mEOgfNtfOzAILvkBd4gS8DrQZ2Rbks86iA==
|
||||||
Vr8O7tVA9t3Uc4so+SlvQlQCSx0BqSz9q/MqFFSgdAFH9PhL0Yr3lasKokhkbqI5
|
-----END EC PRIVATE KEY-----
|
||||||
UrZzKZ9n23+ph2blzN2ryGiRY2o61Vv7IHHj/Ozwlkjf9Nn8ridIVylk1mY8jvGw
|
|
||||||
EvT8YbuepbIAVYWyf/7irRoUgFVR77J040qhcpkjTzRv0IuQ1cinU/ysmyh2Mpk7
|
|
||||||
/Gv78kJ6joPtkAnKYV7VBcloa3tpzTyz2xxT3ufSK4JGfpFct2KL2luICUCuYATX
|
|
||||||
NdAoD+FzJW2N9R16rSQDVETfTyBRpvnnZey1BQcfT/4lyg+GfVQ34PpQ7DsLFVCu
|
|
||||||
roUEN4O3tL9a0k0YzcZitBa1u9NCENbSMGnROv4HAgNdg3lAszqtycHYZHf5phW6
|
|
||||||
+t7oOu+KwDrIo77QXb2fYswN1xS7xkbOgTIU5kCBzD3jdWVTxtLFaQA2g7R7eAq5
|
|
||||||
vgC85pGq9UrJ4W4WfDMqNISOJlMdVaz/VdVOcrnVv0ffe4r3vo7Sr2oaURECAwEA
|
|
||||||
AQKCAgBm8FkSNmCzRJM2kKyrvV1q3NLdRbv/oqin3e9QX6lMEg5BqXTVe/X1dck0
|
|
||||||
+vJCh1s//clvXn+VESs5s0rB+XfBrgAvGlTM4yuXLTQXl+81gOrfUE8Fmdg3QcDv
|
|
||||||
G1k28T1nM5qAGNP3VsvFdZfj0mc+mSzQw1XM7aHKTyVLqNJiBnYbP4ysNr+f7syz
|
|
||||||
4rw3gINXU9HokrjgyYHUhxDqiQtyB5ZAheIz2O7eBVVUHDingUu73fuGZIJfxdCj
|
|
||||||
9L3pgD1X18yPjfpfwnZSLhJu/0GR6+eT76kPXOKbQ9s2m3wX1ixapRHUXiuLuOQF
|
|
||||||
Bh8hGOsiyuEJJkVvyDG9V/OIiw0RBQBOlVHhLoAGvFMVNjAo7Ei/zCU5FH1G9Lh8
|
|
||||||
XiIF0PVbO94jcfgfxtKCaj7xO9aoDV/pkp/2vrmWxfewf/5h7aZN8ny7/hSrvwCo
|
|
||||||
Q/XQ8bUxBnsfqVxiWqZIktnak2WNzLk614Y+aPkkie5x0FZe7pq71N0bnofv6Rh6
|
|
||||||
xp8A6cRvYmx7//lJZkmwk/gpZI1u6Kz6nOIfzKsGPRi12VlJpDq+zJAOBiiiFn1m
|
|
||||||
i/HV3ieqpCSIB2LgZYerG+jb5liBSjXR36puvJJQcqDb9S+4jRkMW9kbvt+k6VAI
|
|
||||||
aT0Sq6OIAj10eGH70z+z3SdrUs8wZ81/79/F8wBTys1o9HIYAQKCAQEA/4Wkox8B
|
|
||||||
cfgX5z1i/Jd95NEMnjjJZhm6q6GnzZ13bfdfO50uL1rdVMLZO8SjN/kc33TsKxPl
|
|
||||||
VkQhjjTjcZKtkYvy4citpWWVevQQtaFaHudz8J3XUlWW9R7O/gtpUhHR+fp5yqLK
|
|
||||||
wKLrv0upS9Nm4S6eEUPXoKzv+khQTVUm/z8KRD2yMM+4cNMtVf02HIclZNbbxCPf
|
|
||||||
PXYR+0FbCfXy2KpoZxxE/NX8zrwN5KKbtvuIF7BkNvNxOzP3/dWPaykiryOgRyRX
|
|
||||||
E4aY5BWT415xg86AAY+dpYlGp9xK3VdO6hNKBJF4F5q37imFh+b3Mk1GACZMY3h9
|
|
||||||
swIfE5u1bYHBgQKCAQEA7PsHGlrLap6lTTA7HIYJ09qblTw4hxsMVa2+nIx31WTN
|
|
||||||
HYGTcDGIYUegp/sLT5ucAHKnwHPZgCvOmwrxo5IoZ6PKLrE7kaA4CTKogAo3tTt1
|
|
||||||
xc8dUH8l/zF8gDaYEobTNG5O0h+ALCEoBCR9RfS8Ub217f0x9s7nzyftiFQ/c9MF
|
|
||||||
gAJtvRnIU8il98qGz/PJVrIwVklVi1GuZXIYw922shxGnolcVEYjiBjWNblxfOO3
|
|
||||||
5RzsHGNK6NoHGx16Ux62zoc8Rw9E44GBARo+qw0aSbjLgfB10wsp2rzC65ARWM9Y
|
|
||||||
gst6pKIJUwOQjWaiffc1qAfc+W0kQNSqYHcFdYk3kQKCAQBUyhEWu+wr2Gp+JiWZ
|
|
||||||
sd9ptWDdg/R4t+L0nwDivvTpfaORUZgIyLsXLE0PgzGyGizVjaPsq353gMYtvSkX
|
|
||||||
/9cuq+TdvUy5zJqsoR6GVtNj2+PiHU5dGN+t2RpQvJKnVBh8Pfx6HEjxYV6fLMkx
|
|
||||||
yyWhZWm4Su3beGdtgt96ud3l5xJOELb3cYY/kiPCG/L/xmzHKHDmhgzHBU30NPyz
|
|
||||||
snRyJyHbzUqrJ4rrQwXNL5RCRPck/ThT77ZMMfOBvIMJyS2kNksyMEHgzdIgJXTc
|
|
||||||
hvNeDID3g6OJUaMrgnMpPZaHH/14xJi4JHQSSJ7xuNegTnoDBLJmc44qf3K2e/3Z
|
|
||||||
J6yBAoIBADHh6S3X/Md1m2/y/g5T/I+WjXdNVMzDmcYTK3NCchr9+9sBImrUUlO/
|
|
||||||
wwZ45nmcVKsXd04gVKERF401MYXvxweBx5YqglJ1+jWdbzB8dht056Z6oT4HdZUQ
|
|
||||||
8pb+ZuZHcP+xVHAQZ2dil0y/7YqjKFzAZSIyUKkWBl9plStEKJMV0SuP10+dtLhG
|
|
||||||
HQFapSPyuefA3EHdb99Ck0YRTTs1WTaGkyrd2Qx4MxR7veNTJJtYR6Y3f0++as82
|
|
||||||
zZYcj1odtfclKj/+685DvUbhIl3ZBTaNanDwj6ybxfSgFRuGmNAr3QKzGB69aN8L
|
|
||||||
egr5lqyTM70p4o6yNZZb7X0esIx8FLECggEBAKqeqOipGQopRptxQXQQuFA9CDff
|
|
||||||
waZEEt7usmFpwWPa/IEERIxfFwA/d9qnUYlhbawMakbZmaMR/3o4Wt/G09YOvmX+
|
|
||||||
Q5ci57XQ6jg85Ym0kmCOFK0d8heG+Wm5LoSP4BjwSziGbo+uz0ouzQ/e8o84wXK/
|
|
||||||
OIAgja327BsEVhYAl8OgKU9ZCDwGp9xo7vVn6ZYnQEKZrF1cS9jPfNX+A615AvxB
|
|
||||||
yGgzkm7PXObcdYoh6vKpDu6E+mFeSns+cYBhG698TGhR4k4A/MtfBIl1XTQMgoft
|
|
||||||
ichLZuS7o0NsVouhsFEKIaxwmIdgQ7vr9f+QPLBWoCEBcezm7du2VB9hsMY=
|
|
||||||
-----END RSA 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-----
|
-----BEGIN CERTIFICATE-----
|
||||||
MIIFXTCCA0WgAwIBAgIJAKkYXwqUpHWIMA0GCSqGSIb3DQEBCwUAMEUxCzAJBgNV
|
MIIC7zCCApSgAwIBAgIRAIubxOonau4Z6UJRYv5KBDMwCgYIKoZIzj0EAwIwgbkx
|
||||||
BAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEwHwYDVQQKDBhJbnRlcm5ldCBX
|
CzAJBgNVBAYTAlVTMQswCQYDVQQIEwJDQTEWMBQGA1UEBxMNU2FuIEZyYW5jaXNj
|
||||||
aWRnaXRzIFB0eSBMdGQwHhcNMTcwNDE0MTkwNjIwWhcNMjIwNDE0MTkwNjIwWjBF
|
bzEaMBgGA1UECRMRMTAxIFNlY29uZCBTdHJlZXQxDjAMBgNVBBETBTk0MTA1MRcw
|
||||||
MQswCQYDVQQGEwJBVTETMBEGA1UECAwKU29tZS1TdGF0ZTEhMB8GA1UECgwYSW50
|
FQYDVQQKEw5IYXNoaUNvcnAgSW5jLjFAMD4GA1UEAxM3Q29uc3VsIEFnZW50IENB
|
||||||
ZXJuZXQgV2lkZ2l0cyBQdHkgTHRkMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIIC
|
IDE4NTU3MTQ5MTMzMTA0NzczNDYwMjQyMDcxODI5NjUzMzQzNTQ0MzAeFw0yMDEw
|
||||||
CgKCAgEA0pMZRBhBHaYG1FttDzt+rctrM8NtslfbAbHflM4ly7mibCmrnh/sGYSP
|
MjgyMjI3NTZaFw0yNTEwMjcyMjI3NTZaMIG5MQswCQYDVQQGEwJVUzELMAkGA1UE
|
||||||
Y/QcrGnCWrjXspUM0dxtmXGmXKj4yRpKhVYw16saDcn2t55dxMw23uEhgfrigtYS
|
CBMCQ0ExFjAUBgNVBAcTDVNhbiBGcmFuY2lzY28xGjAYBgNVBAkTETEwMSBTZWNv
|
||||||
/MoC78OaQryRGvYdxF5unrybBrXDrVfxA8ycYBOV04piS7NlN1/m3TkiNrqkl1up
|
bmQgU3RyZWV0MQ4wDAYDVQQREwU5NDEwNTEXMBUGA1UEChMOSGFzaGlDb3JwIElu
|
||||||
paPscfFaY59yz3Lgq2vs8U1SLtph8ALwjJcz8O3BbBUQuiZYNuiLLnkRroxT3oSp
|
Yy4xQDA+BgNVBAMTN0NvbnN1bCBBZ2VudCBDQSAxODU1NzE0OTEzMzEwNDc3MzQ2
|
||||||
Zbdna2aXtdD/H9JJUoJLjoZp6x5fNOtc+5vF8QZ/jKeJDsxqwf4VT4Z6t8sUMFtO
|
MDI0MjA3MTgyOTY1MzM0MzU0NDMwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAASs
|
||||||
YAR3QEV7wtOhWWfFVzdlCsKZhD8ryemlYSMJ7wfmXUeoiElSPRfvIHUTcJFx37VR
|
e55fe3ruJTkGPs2HnfOGCCcXikvCO4NuZMNJgh79Nt6LFzYzY1vtwav38GT1wfx7
|
||||||
V6LmOzs1gjMpGZxGLk/GtaCbDlyCaT/hae8bqtRhngBy5bvGxmdSBQd0fjYRr2CP
|
EH+jYrNtDafE9n5+0FMBo3sweTAOBgNVHQ8BAf8EBAMCAYYwDwYDVR0TAQH/BAUw
|
||||||
Pz+Gzx3yQx8yb9VV7dTI3H4LWaTjuy2WooITzy3xY4bOvp4UwusFgbIdywwCpDFJ
|
AwEB/zApBgNVHQ4EIgQgC7613rQP8SvteO3KDQHpd+V8piYh1eM9+IXLxIZ4pLYw
|
||||||
zzy3FaqoMx06v5D3I9JCanlXk5FErA0GX45pPM4x3FWZfVwrXZtomBFDC6qlRMpH
|
KwYDVR0jBCQwIoAgC7613rQP8SvteO3KDQHpd+V8piYh1eM9+IXLxIZ4pLYwCgYI
|
||||||
FH8A/KrbpolwiDklSWUxHbfz32gpzf3kLW2nOVpnZPyAgSYudjSFGW00jRomCXOy
|
KoZIzj0EAwIDSQAwRgIhALoE4RO8DHR4AkxmO5ostQxAYMIpiSTC9VZsWva3hHj4
|
||||||
EOgQj/w+M3hKosvVqIE8IQgfMPNRCK9hn11gVqUvkdcYvo/2jUMCAwEAAaNQME4w
|
AiEAijGw7bHPearXh9I2ghGE4jGJbGK4R9JHcLOq3+GE2Ng=
|
||||||
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
|
|
||||||
-----END CERTIFICATE-----
|
-----END CERTIFICATE-----
|
||||||
|
|
|
@ -1,31 +1,18 @@
|
||||||
-----BEGIN CERTIFICATE-----
|
-----BEGIN CERTIFICATE-----
|
||||||
MIIFXTCCA0WgAwIBAgIJAKkYXwqUpHWIMA0GCSqGSIb3DQEBCwUAMEUxCzAJBgNV
|
MIIC7zCCApSgAwIBAgIRAIubxOonau4Z6UJRYv5KBDMwCgYIKoZIzj0EAwIwgbkx
|
||||||
BAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEwHwYDVQQKDBhJbnRlcm5ldCBX
|
CzAJBgNVBAYTAlVTMQswCQYDVQQIEwJDQTEWMBQGA1UEBxMNU2FuIEZyYW5jaXNj
|
||||||
aWRnaXRzIFB0eSBMdGQwHhcNMTcwNDE0MTkwNjIwWhcNMjIwNDE0MTkwNjIwWjBF
|
bzEaMBgGA1UECRMRMTAxIFNlY29uZCBTdHJlZXQxDjAMBgNVBBETBTk0MTA1MRcw
|
||||||
MQswCQYDVQQGEwJBVTETMBEGA1UECAwKU29tZS1TdGF0ZTEhMB8GA1UECgwYSW50
|
FQYDVQQKEw5IYXNoaUNvcnAgSW5jLjFAMD4GA1UEAxM3Q29uc3VsIEFnZW50IENB
|
||||||
ZXJuZXQgV2lkZ2l0cyBQdHkgTHRkMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIIC
|
IDE4NTU3MTQ5MTMzMTA0NzczNDYwMjQyMDcxODI5NjUzMzQzNTQ0MzAeFw0yMDEw
|
||||||
CgKCAgEA0pMZRBhBHaYG1FttDzt+rctrM8NtslfbAbHflM4ly7mibCmrnh/sGYSP
|
MjgyMjI3NTZaFw0yNTEwMjcyMjI3NTZaMIG5MQswCQYDVQQGEwJVUzELMAkGA1UE
|
||||||
Y/QcrGnCWrjXspUM0dxtmXGmXKj4yRpKhVYw16saDcn2t55dxMw23uEhgfrigtYS
|
CBMCQ0ExFjAUBgNVBAcTDVNhbiBGcmFuY2lzY28xGjAYBgNVBAkTETEwMSBTZWNv
|
||||||
/MoC78OaQryRGvYdxF5unrybBrXDrVfxA8ycYBOV04piS7NlN1/m3TkiNrqkl1up
|
bmQgU3RyZWV0MQ4wDAYDVQQREwU5NDEwNTEXMBUGA1UEChMOSGFzaGlDb3JwIElu
|
||||||
paPscfFaY59yz3Lgq2vs8U1SLtph8ALwjJcz8O3BbBUQuiZYNuiLLnkRroxT3oSp
|
Yy4xQDA+BgNVBAMTN0NvbnN1bCBBZ2VudCBDQSAxODU1NzE0OTEzMzEwNDc3MzQ2
|
||||||
Zbdna2aXtdD/H9JJUoJLjoZp6x5fNOtc+5vF8QZ/jKeJDsxqwf4VT4Z6t8sUMFtO
|
MDI0MjA3MTgyOTY1MzM0MzU0NDMwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAASs
|
||||||
YAR3QEV7wtOhWWfFVzdlCsKZhD8ryemlYSMJ7wfmXUeoiElSPRfvIHUTcJFx37VR
|
e55fe3ruJTkGPs2HnfOGCCcXikvCO4NuZMNJgh79Nt6LFzYzY1vtwav38GT1wfx7
|
||||||
V6LmOzs1gjMpGZxGLk/GtaCbDlyCaT/hae8bqtRhngBy5bvGxmdSBQd0fjYRr2CP
|
EH+jYrNtDafE9n5+0FMBo3sweTAOBgNVHQ8BAf8EBAMCAYYwDwYDVR0TAQH/BAUw
|
||||||
Pz+Gzx3yQx8yb9VV7dTI3H4LWaTjuy2WooITzy3xY4bOvp4UwusFgbIdywwCpDFJ
|
AwEB/zApBgNVHQ4EIgQgC7613rQP8SvteO3KDQHpd+V8piYh1eM9+IXLxIZ4pLYw
|
||||||
zzy3FaqoMx06v5D3I9JCanlXk5FErA0GX45pPM4x3FWZfVwrXZtomBFDC6qlRMpH
|
KwYDVR0jBCQwIoAgC7613rQP8SvteO3KDQHpd+V8piYh1eM9+IXLxIZ4pLYwCgYI
|
||||||
FH8A/KrbpolwiDklSWUxHbfz32gpzf3kLW2nOVpnZPyAgSYudjSFGW00jRomCXOy
|
KoZIzj0EAwIDSQAwRgIhALoE4RO8DHR4AkxmO5ostQxAYMIpiSTC9VZsWva3hHj4
|
||||||
EOgQj/w+M3hKosvVqIE8IQgfMPNRCK9hn11gVqUvkdcYvo/2jUMCAwEAAaNQME4w
|
AiEAijGw7bHPearXh9I2ghGE4jGJbGK4R9JHcLOq3+GE2Ng=
|
||||||
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
|
|
||||||
-----END CERTIFICATE-----
|
-----END CERTIFICATE-----
|
||||||
|
|
|
@ -1,51 +1,5 @@
|
||||||
-----BEGIN RSA PRIVATE KEY-----
|
-----BEGIN EC PRIVATE KEY-----
|
||||||
MIIJKAIBAAKCAgEA0pMZRBhBHaYG1FttDzt+rctrM8NtslfbAbHflM4ly7mibCmr
|
MHcCAQEEINtFYGWAzcVyRRQKjadE83olH8xAwZYe5sEn4rfPtI8xoAoGCCqGSM49
|
||||||
nh/sGYSPY/QcrGnCWrjXspUM0dxtmXGmXKj4yRpKhVYw16saDcn2t55dxMw23uEh
|
AwEHoUQDQgAErHueX3t67iU5Bj7Nh53zhggnF4pLwjuDbmTDSYIe/Tbeixc2M2Nb
|
||||||
gfrigtYS/MoC78OaQryRGvYdxF5unrybBrXDrVfxA8ycYBOV04piS7NlN1/m3Tki
|
7cGr9/Bk9cH8exB/o2KzbQ2nxPZ+ftBTAQ==
|
||||||
Nrqkl1uppaPscfFaY59yz3Lgq2vs8U1SLtph8ALwjJcz8O3BbBUQuiZYNuiLLnkR
|
-----END EC PRIVATE KEY-----
|
||||||
roxT3oSpZbdna2aXtdD/H9JJUoJLjoZp6x5fNOtc+5vF8QZ/jKeJDsxqwf4VT4Z6
|
|
||||||
t8sUMFtOYAR3QEV7wtOhWWfFVzdlCsKZhD8ryemlYSMJ7wfmXUeoiElSPRfvIHUT
|
|
||||||
cJFx37VRV6LmOzs1gjMpGZxGLk/GtaCbDlyCaT/hae8bqtRhngBy5bvGxmdSBQd0
|
|
||||||
fjYRr2CPPz+Gzx3yQx8yb9VV7dTI3H4LWaTjuy2WooITzy3xY4bOvp4UwusFgbId
|
|
||||||
ywwCpDFJzzy3FaqoMx06v5D3I9JCanlXk5FErA0GX45pPM4x3FWZfVwrXZtomBFD
|
|
||||||
C6qlRMpHFH8A/KrbpolwiDklSWUxHbfz32gpzf3kLW2nOVpnZPyAgSYudjSFGW00
|
|
||||||
jRomCXOyEOgQj/w+M3hKosvVqIE8IQgfMPNRCK9hn11gVqUvkdcYvo/2jUMCAwEA
|
|
||||||
AQKCAgEAw54ncJzfkP11hr1QOUBZ1HYOps28EFuRdqeZPpGrhvBytOyZI5IgMSx2
|
|
||||||
ULKsGHc/OCxTJPFWMXcG0e9ETvwh8iBcbjW9tfybfYfLjJSwI2xa5P5btHYvCsB4
|
|
||||||
byHzTG131tt6KYPN72iSdyBbHAarO1Ng8NiZxJ8tJpF98zk6pBCRVZ4H7LPCx2E9
|
|
||||||
3kTEGK3P/JBZheIAWP8v5JKh8Cirpt30PYcRl5Yng5KmMWgBtzCca5XJGU//cc3n
|
|
||||||
2Dhi+OEbuqnm99bQirfEHSk9KFDUvUKQ5KS3Y8tXnoDc0ESSQJkbjv8s7aTYonuP
|
|
||||||
+Z7scWabLEiRsY63QuiRE0foeXR95audkWzVBe+H8LUI1MIRUnzxfcTfggj+PzSz
|
|
||||||
5coIRTrJrQknI5IXvimS2uY8v0Y6u3tE4u/o9Ua3oWIyyw6NFMslNrJbeMVGpxTa
|
|
||||||
+0+LShfJiiASuGn5NPC/16rN/VMqBt7LlYK7NpUPPwRNO/qPbTC8G8O/AzZN1Jz2
|
|
||||||
im9Lv+xlAKOdwtjEpgY0IDnrydrjq8kscjGlxR2/Hn7eegWFZHtBF79Yvu0rUH4G
|
|
||||||
fox+xKhKx/tb1B6pGz1dPZBZe9f3OPP485PFbb9cv2eGsS4MHvm2EXJs+x4dcsU5
|
|
||||||
+7AsTPRLT3aGGXLfC5xYAf+zoCLJg38SeI1fuJvZXtNB5QF8WRECggEBAOtxIEPA
|
|
||||||
q6kZjubiccRglZ00mdg3HRQGBZ6v00G982YXHVTGcBnArFUQ9BFpCPNNdSZbxITZ
|
|
||||||
muLi8+FQ5ykxFuM0036YC1VxzVTB3lum1KbND/BOBpRvIflkXss1ah7zrHO46dGf
|
|
||||||
ATjZhfaflPVDnfLbdeggR0dhepggdgkZuHIET5MGpK84/A7kYFKXwqUHM2fOIjFc
|
|
||||||
nW49Rd8z27+sAsEC9i8VmJsc9kAF8GL0CRtQ/QtSy5SpDDiDrdyrg8GbSjDRVm4d
|
|
||||||
AFHiDGQ3N29k1/SiPhaoGec6ECZsZJBhZEuZlUDte+AQPpTC5ePMHwkJ/XYDD3gG
|
|
||||||
Oy3SQTZIO90gwl8CggEBAOT2HBP6w4G53/m299qN8kQOowLDn3yDCWFwZv8Mpj5u
|
|
||||||
Am32sftN/oE6OU8daTabCCPY/nIA+6Kf7K1Bk3X7km8wzjwLzrd8lf/I1icma22z
|
|
||||||
55krrEjO6tSEvZFOv74mpRJGhZ/LKIdhgCZqC91dBAlKE5gGYgf8qAJ9AJsjMO9j
|
|
||||||
QX+7VMnyCBA5Gja1+kmOZ0roJfV3kM2GSPNCon6NjxMfTl+MJEMVPPdu/y2p1Ggc
|
|
||||||
Ymee+JoJCOXVAlaw1L6NtCKTVW0/sIYGe3xQr35eqL7eT4hxD722l/tZmbKRa7gT
|
|
||||||
e/j9qxV7Mi/NpXEdeb+D/wk1BQs4Qh59o5iC1lxpR50CggEACLyp9dmwhRXtt6Ov
|
|
||||||
lRoAc1UAYIWrDpMqojjkHgxue9tfu0Wh41LDEmUOqZa8PkshjcraABQTK1hAtJvL
|
|
||||||
+DtaHhRXxNrfkMwoUnzfQ4dtXMM/VCuREvEM0bRn0CKrTXq9a43xH1ZHNVTdI8nI
|
|
||||||
PVHFCr4aIgMQohV79yk9OBk8Pv7p9QrKEbaLpAHVkTsQfg9GWRPNMQe+z9h2P1It
|
|
||||||
VW+Mqpzxhc3HW/o3KSkPQpzLubfHrCPmah3b1j0MtqOmwAiDOEyMaImq+V7qFs31
|
|
||||||
wKx9VxauNykFzQ7aipJ7KOB0WFnasA4gCrCPofWZklqAzFUSks6KRGn2yDyFLv5/
|
|
||||||
OjV9AQKCAQATrtynEw2vn00T6JjSHxXOp/t3h120lIc/6yvPjUTVZRusXGLcmc3h
|
|
||||||
SiIXHQ4odZdzjXoCTvdS+bCdDGAi6meiS23PV6yDtaAnhxpx7ymZGrg0QL7k23Tw
|
|
||||||
pCCv1zdAn43dTla6b/qh+M3Nf5xZgV+RdN7OWO4ghaXj4N8mdxYD3mKJGo+ldLsg
|
|
||||||
uef5ABfuLuHOXLq2qXq3UG4BC59whbbhC/Xu3NtZMQA2vUIOqOTrtlT3V4FDrLcp
|
|
||||||
GvDChx0i7Iep2USkya7hNrly7HTJxlV3YyEvN5kE1CeoogFGip3aC0LDGvuUMy0T
|
|
||||||
UviACuqmfjB0mCxA1KtKd76So9zNwPc5AoIBADPzlGeAzIK4NTfJzgpUqMn+3+Kc
|
|
||||||
PCcFWki3R86dSST6q3buUOu3Hr8rT4025XOlumhMo+OKUiC/gHNYyXgYW8UIa0uT
|
|
||||||
PJgzJ/18az0Ci6YF0Mf8KZhtBMZp9RKvJOshkcjUsqagnUhpktVW/eb5BFHWb5N+
|
|
||||||
+Ln1XLUSqUEKsHOL9nKkFMtddXOcfG5VHtqgl2rMPfbkrFthTXf355PF3pfG4j6L
|
|
||||||
KSrvQXl7FhQhFG/46p7CGugmaLu2glMloHFWjmcWOGcO9TMFTkAMitIq3LLyzCV/
|
|
||||||
6ShoSNJ76RROeeegU3u3AKVfckYkkai+kyGjw0mY1zOV/5/+bL7qrGjKZSg=
|
|
||||||
-----END RSA PRIVATE KEY-----
|
|
||||||
|
|
|
@ -1 +0,0 @@
|
||||||
8F3D34AED9A76443
|
|
|
@ -1,30 +1,17 @@
|
||||||
-----BEGIN CERTIFICATE-----
|
-----BEGIN CERTIFICATE-----
|
||||||
MIIFHDCCAwQCCQCPPTSu2adkQjANBgkqhkiG9w0BAQsFADBFMQswCQYDVQQGEwJB
|
MIICxjCCAmugAwIBAgIRAOKZmO0GuFJUOfJ7Ycf0WOEwCgYIKoZIzj0EAwIwgbkx
|
||||||
VTETMBEGA1UECAwKU29tZS1TdGF0ZTEhMB8GA1UECgwYSW50ZXJuZXQgV2lkZ2l0
|
CzAJBgNVBAYTAlVTMQswCQYDVQQIEwJDQTEWMBQGA1UEBxMNU2FuIEZyYW5jaXNj
|
||||||
cyBQdHkgTHRkMB4XDTE3MDQxNDE5MDkzOFoXDTIyMDMxOTE5MDkzOFowWzELMAkG
|
bzEaMBgGA1UECRMRMTAxIFNlY29uZCBTdHJlZXQxDjAMBgNVBBETBTk0MTA1MRcw
|
||||||
A1UEBhMCQVUxEzARBgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoMGEludGVybmV0
|
FQYDVQQKEw5IYXNoaUNvcnAgSW5jLjFAMD4GA1UEAxM3Q29uc3VsIEFnZW50IENB
|
||||||
IFdpZGdpdHMgUHR5IEx0ZDEUMBIGA1UEAwwLY29uc3VsLnRlc3QwggIiMA0GCSqG
|
IDE4NTU3MTQ5MTMzMTA0NzczNDYwMjQyMDcxODI5NjUzMzQzNTQ0MzAeFw0yMDEw
|
||||||
SIb3DQEBAQUAA4ICDwAwggIKAoICAQDI57K1LPlNXpK4ud6XbqhQAM9w6mym5d+5
|
MjgyMjI3NTZaFw0yMTEwMjgyMjI3NTZaMBwxGjAYBgNVBAMTEXNlcnZlci5kYzEu
|
||||||
utGlq+ojNrnKdhaokzDOcEpELLguQlFevDrTGuBJLxInmDvRN8BmYshZAuwW+odw
|
Y29uc3VsMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE0etZvg/aUTU+HPwDHtEw
|
||||||
iGa4IiT9rN0JeQIQr9DvNjRe7Rhzb3v0CIFIwaBWZyRSdEKuZORyDOvSudATI2p9
|
ZslBuEshwHl7AcERHQeFTuhtfjpwHQw+uTunFkmQoqNmE+n7P4v7fe771lpxif8V
|
||||||
Ux9HpWJKG9clHEiAjWeseLfEttkAZgojPgdz/7Nq2CT877QtdbbKO8h+IIKevhVM
|
wKOB7zCB7DAOBgNVHQ8BAf8EBAMCBaAwHQYDVR0lBBYwFAYIKwYBBQUHAwEGCCsG
|
||||||
JLIyWhwGMy5orlVP42om7wgAIonm8LFCHe84JUVlOjuJgMzfUvz/UuKJFbAku7Dt
|
AQUFBwMCMAwGA1UdEwEB/wQCMAAwKQYDVR0OBCIEIEA1xxAYluRqg6wFwGu75o/5
|
||||||
UXKL+gZsJg5YHhtMMHQO40UDNTfxNEiIRNyOHaQv4ptRUJq7vIbBng+m1uyYFkJp
|
8Ty6FWR9RgIYvZzCM2N9MCsGA1UdIwQkMCKAIAu+td60D/Er7Xjtyg0B6XflfKYm
|
||||||
NvTf4+OApXzsfhb8bjt8QRn5fsCsYU94oUVy+DSw10EW+IR1F7pGJZX5dYZgm6H7
|
IdXjPfiFy8SGeKS2MFUGA1UdEQROMEyCC2NvbnN1bC50ZXN0ghlzZXJ2ZXIwLnNl
|
||||||
E+cj0pYV1dKbdP/5L3mv6KJUTaGDRanLiNPQFOyhs6y6Qf1Y7a270oge9UpHAQ6D
|
cnZlci5kYzEuY29uc3VsghFzZXJ2ZXIuZGMxLmNvbnN1bIIJbG9jYWxob3N0hwR/
|
||||||
KI5jvCuwzW2wDE7KEpo5SGRIjoK9KfouyhlYAaIiJNnVyjVXDXGhzJgH+/2cGW/n
|
AAABMAoGCCqGSM49BAMCA0kAMEYCIQDz9YnCvKkgGqw5M0HLDI82rqwQsH2SRQUs
|
||||||
TQAqKl4OtmzyNpihgv9Mhm86nro0XLnZHruJZ/SvZgpmSs8luo70CFK990j65JiM
|
kogKi3oGmQIhAPBA5AgF3y1E94PbeYfvoDBJy1JiY3KsckY2Gz+M8Iyc
|
||||||
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==
|
|
||||||
-----END CERTIFICATE-----
|
-----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-----
|
-----BEGIN EC PRIVATE KEY-----
|
||||||
MIIJKAIBAAKCAgEAyOeytSz5TV6SuLnel26oUADPcOpspuXfubrRpavqIza5ynYW
|
MHcCAQEEICYdaRvHDtbGbReTekgKf9uyKFEnR7kr7VU3kw3uGzAhoAoGCCqGSM49
|
||||||
qJMwznBKRCy4LkJRXrw60xrgSS8SJ5g70TfAZmLIWQLsFvqHcIhmuCIk/azdCXkC
|
AwEHoUQDQgAE0etZvg/aUTU+HPwDHtEwZslBuEshwHl7AcERHQeFTuhtfjpwHQw+
|
||||||
EK/Q7zY0Xu0Yc2979AiBSMGgVmckUnRCrmTkcgzr0rnQEyNqfVMfR6ViShvXJRxI
|
uTunFkmQoqNmE+n7P4v7fe771lpxif8VwA==
|
||||||
gI1nrHi3xLbZAGYKIz4Hc/+zatgk/O+0LXW2yjvIfiCCnr4VTCSyMlocBjMuaK5V
|
-----END EC PRIVATE KEY-----
|
||||||
T+NqJu8IACKJ5vCxQh3vOCVFZTo7iYDM31L8/1LiiRWwJLuw7VFyi/oGbCYOWB4b
|
|
||||||
TDB0DuNFAzU38TRIiETcjh2kL+KbUVCau7yGwZ4PptbsmBZCaTb03+PjgKV87H4W
|
|
||||||
/G47fEEZ+X7ArGFPeKFFcvg0sNdBFviEdRe6RiWV+XWGYJuh+xPnI9KWFdXSm3T/
|
|
||||||
+S95r+iiVE2hg0Wpy4jT0BTsobOsukH9WO2tu9KIHvVKRwEOgyiOY7wrsM1tsAxO
|
|
||||||
yhKaOUhkSI6CvSn6LsoZWAGiIiTZ1co1Vw1xocyYB/v9nBlv500AKipeDrZs8jaY
|
|
||||||
oYL/TIZvOp66NFy52R67iWf0r2YKZkrPJbqO9AhSvfdI+uSYjFE7wXSgA34wmQeD
|
|
||||||
cVaN8lAtFFWjLKDIPJmzhiTxK2PFmhQqsLARyz5m722b6fAWLL3t3Q4YfjkCAwEA
|
|
||||||
AQKCAgBHT2ZxRHtg6PavNto5af+4FfGLpMnYG7PjmtobMgAza5Nat7unLkeenuDd
|
|
||||||
ffoKAWQcejdvAxUlJN4Oy8w/oMhcDygJ4C2oolg8q026gfQbTqZOXHNNNPq2Tckd
|
|
||||||
AI8zOhkHL5WkG4Yr5QRReA7LE+i6SrfR3j5q7KE5xq1NovhWUbd15qodZxOrdlXU
|
|
||||||
LwqrR4zFoZjHpbUrcXj/hp2vnR66fanWiveSHOo2UrglgzJ7SONqKKcDajcdhq6S
|
|
||||||
TbAhFsH0M+fbR+9v1NGZJuyRQEWo4uShv977ytssAULlSGPLM17YDCeoTXKEbkrq
|
|
||||||
rpMivGoaZEbc8sx6araykCe8B1jU8vMZJCr+3dFd6cDFUypfeXtR/tgsQx+8xGj0
|
|
||||||
EvJoTRjIiDzP2+ZDblMzSUM9m28falxtmqPx3xmgZcIob3pgjeQq9SDwJfQEz+Ll
|
|
||||||
ZN4j7ROm9FB7/sqHZmWd17BSaghdjFPvkY4ibk/N9tO1sslw3DoA/jMEOmqa+CKx
|
|
||||||
bKE2KeNmqCiMiENuNVhpAQ9SvJw7MfNi2UNpl58Oeu4H3zc58JlFj5Xxo9TBGRDR
|
|
||||||
rVkzfqPxcNaJgy7WAgCGQM+pdcYq0ByGW1RSCo7zfYIaIilofySiZf5XWqFkfklT
|
|
||||||
SiCorumhB8rjVRnE4N/rcL6uq2ZWMMxmBPw5lFgsLWka8jlcmQKCAQEA+NnweOLC
|
|
||||||
WibA3UbdUagdcM/u2zk9SwewkLoTe9gLi2uwj757UqXSN9R6r7v/bmYRMGNob37i
|
|
||||||
kxdAidnFiYHAWItp27qphLry8pyWRzxNYnutHu0ZKeR3A+FWSxsw08Y0jC2Ry0Jw
|
|
||||||
a+CHeR8xKh93IR4RG1Yp2REfEeZFklmb5CZ37i1SFCmYuVnXZrbGQ1d7san3p2YB
|
|
||||||
it5y4ezk3AIaVKo0T6iZzuwGohpZMf7JeD5UPH7N02+VoJKEbjgarnDhXLph1DX+
|
|
||||||
J+JRmw/grHYHT7odPTpY+F1CInwaoXj4lqMUOhx+BmTYvb3PTPbbEtAppB4yrrNZ
|
|
||||||
a6VVs/7WZVE3RwKCAQEAzq0pEf3qgOBMmYYMvZhlNdVimEyqY8ISCCHTFW+aTc+l
|
|
||||||
dRB2tBVb5eTP6j5hYfbJVXi8N7uvCdiVhmR11x+8HqYGr8nMWDopoEvO+bNcGwvq
|
|
||||||
KXYx/BsjK8nY3i+s3MuwsLZg/tP14FHVqrStg4IueTzru4IpnknwpkkVBM28MKh1
|
|
||||||
VveorwdKFpP5nkenCt4KkYlFQff/rqE8I3LaGlvxIFVOPDXa08P7dKGIokuNfoQD
|
|
||||||
nAwQf5Wd/8RuRY6tIuFL5MNGSFRzzWgXwSMOUWb9bhTq6ZkLECfwE9TVqDxlkTIq
|
|
||||||
lK3o7lsWHRMUcFO7y6SHAH4lm92Y5Z5JAw1rCG5efwKCAQEA1o1Qv2kCmCeBcUZ/
|
|
||||||
2r9PYsxj667WIbJnkOBdnBERIwueFtDsEr3VGT2g6ZL1D4IAn++VQ0vqnVcW1cNk
|
|
||||||
hMHRzIWmp0OwlDd676ICDzj2n0pyYI+benr4AehuNiMjXfMtqw4+/TgzJU9Yfh1e
|
|
||||||
jirC01LQ/Pi06+nPF+epZBzOQ07HaBq7AZc7jdLf3DcJiVYL14nrc/CGs+xGsHNG
|
|
||||||
fklx2j1FDMkYk0b8ERcWf/xkR7+1cNMDMqCqKN8qPr0wg+Xe58vqPMSwdEK0iTSP
|
|
||||||
SSIZ+6tDOl7sBnahZooJi954tae08MVQAsM/+5eC+6B6ESZYQJ+ooucO2biaw62b
|
|
||||||
u47iUQKCAQBKWIH4peVwfL8xTsZQgXyO8/amoJV+kzZXVIuRH3dbXEHBra11tGU9
|
|
||||||
eqTMN6piShs8stTKG6qomQ+Yq5S0UQcj40dufuISLsIAlqSasEmGtS+DwK/UZ5Ks
|
|
||||||
Usy/iFjfiCpENycHJApDqkx5PstYDkFXfXGzHuyHs0NtHccA1l1HB6JGKYq1g6LE
|
|
||||||
InDd3hqZzyvwFHgkdei00e1HNy574u0HW4hsIldYbByNZPo4n4MDqst9m91nd9PB
|
|
||||||
SND+FofzjyX04cXriO2rSzGYWVryL2Ek80NZyqLvKd6z05EUFr3WkDw/BZxP+dYn
|
|
||||||
mVB147kfVUz3AWnX+svgdaMABRimjMVTAoIBAGNVJuNpGUo3ynvUf6hu5x8+e4Zx
|
|
||||||
CeSmeeNxMsbth8UyNpBWGgJiZPtIIHCKMoNgXAvyNV46aqc/IfOuBMcn4YEewk6g
|
|
||||||
0/Ww6/vB01gpa0y1isizPYvVL0Ur6VCyMzPI+I6auXAXIQGr9qjqtHC8TbDL7wlv
|
|
||||||
svH9ddYbpmG5nf0T32n6+A8Yenrlp4aDnZCt7u0Lwfx+GWowXMetY6wYcwCN9KLQ
|
|
||||||
6WnzO+BtmaIDFyTYuMmLw+wcECulh5DeZ64qzXf+aZQTK9ClJB7wOTRMawY5FOSs
|
|
||||||
E5SmBs3FquiNVuzcn7WPTqWT34y0XefMFZEvsNJ6QesAA7qm2YWEHrbdVW0=
|
|
||||||
-----END RSA PRIVATE KEY-----
|
|
||||||
|
|
|
@ -920,11 +920,6 @@ func (c *Configurator) wrapALPNTLSClient(dc, nodeName, alpnProto string, conn ne
|
||||||
return nil, err
|
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
|
return tlsConn, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -215,7 +215,6 @@ func TestConfigurator_outgoingWrapperALPN_OK(t *testing.T) {
|
||||||
tlsConn := tlsClient.(*tls.Conn)
|
tlsConn := tlsClient.(*tls.Conn)
|
||||||
cs := tlsConn.ConnectionState()
|
cs := tlsConn.ConnectionState()
|
||||||
require.Equal(t, "foo", cs.NegotiatedProtocol)
|
require.Equal(t, "foo", cs.NegotiatedProtocol)
|
||||||
require.True(t, cs.NegotiatedProtocolIsMutual)
|
|
||||||
|
|
||||||
err = <-errc
|
err = <-errc
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
|
@ -5,8 +5,8 @@ module.exports = {
|
||||||
ecmaVersion: 2018,
|
ecmaVersion: 2018,
|
||||||
sourceType: 'module',
|
sourceType: 'module',
|
||||||
ecmaFeatures: {
|
ecmaFeatures: {
|
||||||
legacyDecorators: true
|
legacyDecorators: true,
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
plugins: ['ember'],
|
plugins: ['ember'],
|
||||||
extends: ['eslint:recommended', 'plugin:ember/recommended'],
|
extends: ['eslint:recommended', 'plugin:ember/recommended'],
|
||||||
|
@ -17,7 +17,7 @@ module.exports = {
|
||||||
'no-unused-vars': ['error', { args: 'none' }],
|
'no-unused-vars': ['error', { args: 'none' }],
|
||||||
'ember/no-new-mixins': ['warn'],
|
'ember/no-new-mixins': ['warn'],
|
||||||
'ember/no-jquery': 'warn',
|
'ember/no-jquery': 'warn',
|
||||||
'ember/no-global-jquery': 'warn'
|
'ember/no-global-jquery': 'warn',
|
||||||
},
|
},
|
||||||
overrides: [
|
overrides: [
|
||||||
// node files
|
// node files
|
||||||
|
@ -31,14 +31,14 @@ module.exports = {
|
||||||
'blueprints/*/index.js',
|
'blueprints/*/index.js',
|
||||||
'config/**/*.js',
|
'config/**/*.js',
|
||||||
'lib/*/index.js',
|
'lib/*/index.js',
|
||||||
'server/**/*.js'
|
'server/**/*.js',
|
||||||
],
|
],
|
||||||
parserOptions: {
|
parserOptions: {
|
||||||
sourceType: 'script'
|
sourceType: 'script',
|
||||||
},
|
},
|
||||||
env: {
|
env: {
|
||||||
browser: false,
|
browser: false,
|
||||||
node: true
|
node: true,
|
||||||
},
|
},
|
||||||
plugins: ['node'],
|
plugins: ['node'],
|
||||||
rules: Object.assign({}, require('eslint-plugin-node').configs.recommended.rules, {
|
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
|
// this can be removed once the following is fixed
|
||||||
// https://github.com/mysticatea/eslint-plugin-node/issues/77
|
// 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