Backport of catalog: improve the bound workload identity encoding on services into release/1.18.x (#20467)

catalog: improve the bound workload identity encoding on services (#20458)

The endpoints controller currently encodes the list of unique workload identities 
referenced by all workload matched by a Service into a special data-bearing 
status condition on that Service. This allows a downstream controller to avoid an 
expensive watch on the ServiceEndpoints type just to get this data.

The current encoding does not lend itself well to machine parsing, which is what 
the field is meant for, so this PR simplifies the encoding from:

    "blah blah: " + strings.Join(ids, ",") + "."

to

    strings.Join(ids, ",")

It also provides an exported utility function to easily extract this data.

Co-authored-by: R.B. Boyer <4903+rboyer@users.noreply.github.com>
pull/20469/head
hc-github-team-consul-core 10 months ago committed by GitHub
parent f3b80ddcba
commit a49f2d0d59
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

@ -63,6 +63,16 @@ func SimplifyFailoverPolicy(svc *pbcatalog.Service, failover *pbcatalog.Failover
return types.SimplifyFailoverPolicy(svc, failover)
}
// GetBoundIdentities returns the unique list of workload identity references
// encoded into a data-bearing status condition on a Service resource by the
// endpoints controller.
//
// This allows a controller to skip watching ServiceEndpoints (which is
// expensive) to discover this data.
func GetBoundIdentities(res *pbresource.Resource) []string {
return endpoints.GetBoundIdentities(res)
}
// ValidateLocalServiceRefNoSection ensures the following:
//
// - ref is non-nil

@ -0,0 +1,46 @@
// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: BUSL-1.1
package endpoints
import (
"sort"
"strings"
"github.com/hashicorp/consul/proto-public/pbresource"
)
// GetBoundIdentities returns the unique list of workload identity references
// encoded into a data-bearing status condition on a Service resource by the
// endpoints controller.
//
// This allows a controller to skip watching ServiceEndpoints (which is
// expensive) to discover this data.
func GetBoundIdentities(res *pbresource.Resource) []string {
if res.GetStatus() == nil {
return nil
}
status, ok := res.GetStatus()[ControllerID]
if !ok {
return nil
}
var encoded string
for _, cond := range status.GetConditions() {
if cond.GetType() == StatusConditionBoundIdentities && cond.GetState() == pbresource.Condition_STATE_TRUE {
encoded = cond.GetMessage()
break
}
}
if encoded == "" {
return nil
}
identities := strings.Split(encoded, ",")
// Ensure determinstic sort so we don't get into infinite-reconcile
sort.Strings(identities)
return identities
}

@ -0,0 +1,63 @@
// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: BUSL-1.1
package endpoints_test
import (
"testing"
"github.com/stretchr/testify/require"
"github.com/hashicorp/consul/internal/catalog/internal/controllers/endpoints"
"github.com/hashicorp/consul/internal/resource"
"github.com/hashicorp/consul/internal/resource/demo"
rtest "github.com/hashicorp/consul/internal/resource/resourcetest"
"github.com/hashicorp/consul/proto-public/pbresource"
pbdemo "github.com/hashicorp/consul/proto/private/pbdemo/v2"
)
func TestGetBoundIdentities(t *testing.T) {
tenancy := resource.DefaultNamespacedTenancy()
build := func(conds ...*pbresource.Condition) *pbresource.Resource {
b := rtest.Resource(demo.TypeV2Artist, "artist").
WithTenancy(tenancy).
WithData(t, &pbdemo.Artist{Name: "very arty"})
if len(conds) > 0 {
b.WithStatus(endpoints.ControllerID, &pbresource.Status{
Conditions: conds,
})
}
return b.Build()
}
run := endpoints.GetBoundIdentities
require.Empty(t, run(build(nil)))
require.Empty(t, run(build(&pbresource.Condition{
Type: endpoints.StatusConditionBoundIdentities,
State: pbresource.Condition_STATE_TRUE,
Message: "",
})))
require.Equal(t, []string{"foo"}, run(build(&pbresource.Condition{
Type: endpoints.StatusConditionBoundIdentities,
State: pbresource.Condition_STATE_TRUE,
Message: "foo",
})))
require.Empty(t, run(build(&pbresource.Condition{
Type: endpoints.StatusConditionBoundIdentities,
State: pbresource.Condition_STATE_FALSE,
Message: "foo",
})))
require.Equal(t, []string{"bar", "foo"}, run(build(&pbresource.Condition{
Type: endpoints.StatusConditionBoundIdentities,
State: pbresource.Condition_STATE_TRUE,
Message: "bar,foo", // proper order
})))
require.Equal(t, []string{"bar", "foo"}, run(build(&pbresource.Condition{
Type: endpoints.StatusConditionBoundIdentities,
State: pbresource.Condition_STATE_TRUE,
Message: "foo,bar", // incorrect order gets fixed
})))
}

@ -4,7 +4,7 @@
package endpoints
import (
"fmt"
"sort"
"strings"
"github.com/hashicorp/consul/proto-public/pbresource"
@ -24,9 +24,6 @@ const (
StatusReasonWorkloadIdentitiesFound = "WorkloadIdentitiesFound"
StatusReasonNoWorkloadIdentitiesFound = "NoWorkloadIdentitiesFound"
identitiesFoundMessageFormat = "Found workload identities associated with this service: %q."
identitiesNotFoundChangedMessage = "No associated workload identities found."
)
var (
@ -48,15 +45,17 @@ var (
Type: StatusConditionBoundIdentities,
State: pbresource.Condition_STATE_FALSE,
Reason: StatusReasonNoWorkloadIdentitiesFound,
Message: identitiesNotFoundChangedMessage,
Message: "",
}
)
func ConditionIdentitiesFound(identities []string) *pbresource.Condition {
sort.Strings(identities)
return &pbresource.Condition{
Type: StatusConditionBoundIdentities,
State: pbresource.Condition_STATE_TRUE,
Reason: StatusReasonWorkloadIdentitiesFound,
Message: fmt.Sprintf(identitiesFoundMessageFormat, strings.Join(identities, ",")),
Message: strings.Join(identities, ","),
}
}

Loading…
Cancel
Save