Skip to content

Commit a2a8e9c

Browse files
authored
connect: intentions are now managed as a new config entry kind "service-intentions" (hashicorp#8834)
- Upgrade the ConfigEntry.ListAll RPC to be kind-aware so that older copies of consul will not see new config entries it doesn't understand replicate down. - Add shim conversion code so that the old API/CLI method of interacting with intentions will continue to work so long as none of these are edited via config entry endpoints. Almost all of the read-only APIs will continue to function indefinitely. - Add new APIs that operate on individual intentions without IDs so that the UI doesn't need to implement CAS operations. - Add a new serf feature flag indicating support for intentions-as-config-entries. - The old line-item intentions way of interacting with the state store will transparently flip between the legacy memdb table and the config entry representations so that readers will never see a hiccup during migration where the results are incomplete. It uses a piece of system metadata to control the flip. - The primary datacenter will begin migrating intentions into config entries on startup once all servers in the datacenter are on a version of Consul with the intentions-as-config-entries feature flag. When it is complete the old state store representations will be cleared. We also record a piece of system metadata indicating this has occurred. We use this metadata to skip ALL of this code the next time the leader starts up. - The secondary datacenters continue to run the old intentions replicator until all servers in the secondary DC and primary DC support intentions-as-config-entries (via serf flag). Once this condition it met the old intentions replicator ceases. - The secondary datacenters replicate the new config entries as they are migrated in the primary. When they detect that the primary has zeroed it's old state store table it waits until all config entries up to that point are replicated and then zeroes its own copy of the old state store table. We also record a piece of system metadata indicating this has occurred. We use this metadata to skip ALL of this code the next time the leader starts up.
1 parent e657341 commit a2a8e9c

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

71 files changed

+7348
-1915
lines changed

.changelog/8834.txt

+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
```release-note:feature
2+
connect: intentions are now managed as a new config entry kind "service-intentions"
3+
```
4+
```release-note:breaking-change
5+
connect: intention destinations can no longer be renamed
6+
```

agent/config/runtime_test.go

+190
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ type configTest struct {
3939
privatev4 func() ([]*net.IPAddr, error)
4040
publicv6 func() ([]*net.IPAddr, error)
4141
patch func(rt *RuntimeConfig)
42+
patchActual func(rt *RuntimeConfig)
4243
err string
4344
warns []string
4445
hostname func() (string, error)
@@ -3814,6 +3815,192 @@ func TestBuilder_BuildAndValide_ConfigFlagsAndEdgecases(t *testing.T) {
38143815
},
38153816
// TODO(rb): add in missing tests for ingress-gateway (snake + camel)
38163817
// TODO(rb): add in missing tests for terminating-gateway (snake + camel)
3818+
{
3819+
desc: "ConfigEntry bootstrap service-intentions (snake-case)",
3820+
args: []string{`-data-dir=` + dataDir},
3821+
json: []string{`{
3822+
"config_entries": {
3823+
"bootstrap": [
3824+
{
3825+
"kind": "service-intentions",
3826+
"name": "web",
3827+
"meta" : {
3828+
"foo": "bar",
3829+
"gir": "zim"
3830+
},
3831+
"sources": [
3832+
{
3833+
"name": "foo",
3834+
"action": "deny",
3835+
"type": "consul",
3836+
"description": "foo desc"
3837+
},
3838+
{
3839+
"name": "bar",
3840+
"action": "allow",
3841+
"description": "bar desc"
3842+
},
3843+
{
3844+
"name": "*",
3845+
"action": "deny",
3846+
"description": "wild desc"
3847+
}
3848+
]
3849+
}
3850+
]
3851+
}
3852+
}`,
3853+
},
3854+
hcl: []string{`
3855+
config_entries {
3856+
bootstrap {
3857+
kind = "service-intentions"
3858+
name = "web"
3859+
meta {
3860+
"foo" = "bar"
3861+
"gir" = "zim"
3862+
}
3863+
sources = [
3864+
{
3865+
name = "foo"
3866+
action = "deny"
3867+
type = "consul"
3868+
description = "foo desc"
3869+
},
3870+
{
3871+
name = "bar"
3872+
action = "allow"
3873+
description = "bar desc"
3874+
}
3875+
]
3876+
sources {
3877+
name = "*"
3878+
action = "deny"
3879+
description = "wild desc"
3880+
}
3881+
}
3882+
}
3883+
`,
3884+
},
3885+
patchActual: func(rt *RuntimeConfig) {
3886+
// Wipe the time tracking fields to make comparison easier.
3887+
for _, raw := range rt.ConfigEntryBootstrap {
3888+
if entry, ok := raw.(*structs.ServiceIntentionsConfigEntry); ok {
3889+
for _, src := range entry.Sources {
3890+
src.LegacyCreateTime = nil
3891+
src.LegacyUpdateTime = nil
3892+
}
3893+
}
3894+
}
3895+
},
3896+
patch: func(rt *RuntimeConfig) {
3897+
rt.DataDir = dataDir
3898+
rt.ConfigEntryBootstrap = []structs.ConfigEntry{
3899+
&structs.ServiceIntentionsConfigEntry{
3900+
Kind: "service-intentions",
3901+
Name: "web",
3902+
Meta: map[string]string{
3903+
"foo": "bar",
3904+
"gir": "zim",
3905+
},
3906+
EnterpriseMeta: *defaultEntMeta,
3907+
Sources: []*structs.SourceIntention{
3908+
{
3909+
Name: "foo",
3910+
Action: "deny",
3911+
Type: "consul",
3912+
Description: "foo desc",
3913+
Precedence: 9,
3914+
EnterpriseMeta: *defaultEntMeta,
3915+
},
3916+
{
3917+
Name: "bar",
3918+
Action: "allow",
3919+
Type: "consul",
3920+
Description: "bar desc",
3921+
Precedence: 9,
3922+
EnterpriseMeta: *defaultEntMeta,
3923+
},
3924+
{
3925+
Name: "*",
3926+
Action: "deny",
3927+
Type: "consul",
3928+
Description: "wild desc",
3929+
Precedence: 8,
3930+
EnterpriseMeta: *defaultEntMeta,
3931+
},
3932+
},
3933+
},
3934+
}
3935+
},
3936+
},
3937+
{
3938+
desc: "ConfigEntry bootstrap service-intentions wildcard destination (snake-case)",
3939+
args: []string{`-data-dir=` + dataDir},
3940+
json: []string{`{
3941+
"config_entries": {
3942+
"bootstrap": [
3943+
{
3944+
"kind": "service-intentions",
3945+
"name": "*",
3946+
"sources": [
3947+
{
3948+
"name": "foo",
3949+
"action": "deny",
3950+
"precedence": 6
3951+
}
3952+
]
3953+
}
3954+
]
3955+
}
3956+
}`,
3957+
},
3958+
hcl: []string{`
3959+
config_entries {
3960+
bootstrap {
3961+
kind = "service-intentions"
3962+
name = "*"
3963+
sources {
3964+
name = "foo"
3965+
action = "deny"
3966+
# should be parsed, but we'll ignore it later
3967+
precedence = 6
3968+
}
3969+
}
3970+
}
3971+
`,
3972+
},
3973+
patchActual: func(rt *RuntimeConfig) {
3974+
// Wipe the time tracking fields to make comparison easier.
3975+
for _, raw := range rt.ConfigEntryBootstrap {
3976+
if entry, ok := raw.(*structs.ServiceIntentionsConfigEntry); ok {
3977+
for _, src := range entry.Sources {
3978+
src.LegacyCreateTime = nil
3979+
src.LegacyUpdateTime = nil
3980+
}
3981+
}
3982+
}
3983+
},
3984+
patch: func(rt *RuntimeConfig) {
3985+
rt.DataDir = dataDir
3986+
rt.ConfigEntryBootstrap = []structs.ConfigEntry{
3987+
&structs.ServiceIntentionsConfigEntry{
3988+
Kind: "service-intentions",
3989+
Name: "*",
3990+
EnterpriseMeta: *defaultEntMeta,
3991+
Sources: []*structs.SourceIntention{
3992+
{
3993+
Name: "foo",
3994+
Action: "deny",
3995+
Type: "consul",
3996+
Precedence: 6,
3997+
EnterpriseMeta: *defaultEntMeta,
3998+
},
3999+
},
4000+
},
4001+
}
4002+
},
4003+
},
38174004

38184005
///////////////////////////////////
38194006
// Defaults sanity checks
@@ -4557,6 +4744,9 @@ func testConfig(t *testing.T, tests []configTest, dataDir string) {
45574744
require.Equal(t, actual.DataDir, actual.ACLTokens.DataDir)
45584745
expected.ACLTokens.DataDir = actual.ACLTokens.DataDir
45594746

4747+
if tt.patchActual != nil {
4748+
tt.patchActual(&actual)
4749+
}
45604750
require.Equal(t, expected, actual)
45614751
})
45624752
}

agent/config_endpoint.go

+14-5
Original file line numberDiff line numberDiff line change
@@ -36,13 +36,14 @@ func (s *HTTPHandlers) configGet(resp http.ResponseWriter, req *http.Request) (i
3636

3737
switch len(pathArgs) {
3838
case 2:
39-
if err := s.parseEntMetaNoWildcard(req, &args.EnterpriseMeta); err != nil {
40-
return nil, err
41-
}
4239
// Both kind/name provided.
4340
args.Kind = pathArgs[0]
4441
args.Name = pathArgs[1]
4542

43+
if err := s.parseEntMetaForConfigEntryKind(args.Kind, req, &args.EnterpriseMeta); err != nil {
44+
return nil, err
45+
}
46+
4647
var reply structs.ConfigEntryResponse
4748
if err := s.agent.RPC("ConfigEntry.Get", &args, &reply); err != nil {
4849
return nil, err
@@ -95,7 +96,8 @@ func (s *HTTPHandlers) configDelete(resp http.ResponseWriter, req *http.Request)
9596
args.Entry = entry
9697
// Parse enterprise meta.
9798
meta := args.Entry.GetEnterpriseMeta()
98-
if err := s.parseEntMetaNoWildcard(req, meta); err != nil {
99+
100+
if err := s.parseEntMetaForConfigEntryKind(entry.GetKind(), req, meta); err != nil {
99101
return nil, err
100102
}
101103

@@ -128,7 +130,7 @@ func (s *HTTPHandlers) ConfigApply(resp http.ResponseWriter, req *http.Request)
128130

129131
// Parse enterprise meta.
130132
var meta structs.EnterpriseMeta
131-
if err := s.parseEntMetaNoWildcard(req, &meta); err != nil {
133+
if err := s.parseEntMetaForConfigEntryKind(args.Entry.GetKind(), req, &meta); err != nil {
132134
return nil, err
133135
}
134136
args.Entry.GetEnterpriseMeta().Merge(&meta)
@@ -150,3 +152,10 @@ func (s *HTTPHandlers) ConfigApply(resp http.ResponseWriter, req *http.Request)
150152

151153
return reply, nil
152154
}
155+
156+
func (s *HTTPHandlers) parseEntMetaForConfigEntryKind(kind string, req *http.Request, entMeta *structs.EnterpriseMeta) error {
157+
if kind == structs.ServiceIntentions {
158+
return s.parseEntMeta(req, entMeta)
159+
}
160+
return s.parseEntMetaNoWildcard(req, entMeta)
161+
}

agent/consul/config.go

+5
Original file line numberDiff line numberDiff line change
@@ -458,6 +458,11 @@ type Config struct {
458458
// disable a background routine.
459459
DisableFederationStateAntiEntropy bool
460460

461+
// OverrideInitialSerfTags solely exists for use in unit tests to ensure
462+
// that a serf tag is initially set to a known value, rather than the
463+
// default to test some consul upgrade scenarios with fewer races.
464+
OverrideInitialSerfTags func(tags map[string]string)
465+
461466
// CAConfig is used to apply the initial Connect CA configuration when
462467
// bootstrapping.
463468
CAConfig *structs.CAConfiguration

0 commit comments

Comments
 (0)