Skip to content

stats/opentelemetry/csm: Get mesh_id local label from "CSM_MESH_ID" environment variable, rather than parsing from bootstrap file #7740

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Oct 15, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 12 additions & 10 deletions stats/opentelemetry/csm/observability_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,6 @@ import (
"google.golang.org/grpc/encoding/gzip"
istats "google.golang.org/grpc/internal/stats"
"google.golang.org/grpc/internal/stubserver"
"google.golang.org/grpc/internal/testutils"
"google.golang.org/grpc/internal/testutils/xds/e2e"
testgrpc "google.golang.org/grpc/interop/grpc_testing"
testpb "google.golang.org/grpc/interop/grpc_testing"
"google.golang.org/grpc/metadata"
Expand All @@ -46,12 +44,11 @@ import (
// Env Vars as well, and mocks the resource detector's returned attribute set to
// simulate the environment. It registers a cleanup function on the provided t
// to restore the environment to its original state.
func setupEnv(t *testing.T, resourceDetectorEmissions map[string]string, nodeID, csmCanonicalServiceName, csmWorkloadName string) {
bootstrapContents := e2e.DefaultBootstrapContents(t, nodeID, "xds_server_uri")
testutils.CreateBootstrapFileForTesting(t, bootstrapContents)

func setupEnv(t *testing.T, resourceDetectorEmissions map[string]string, meshID, csmCanonicalServiceName, csmWorkloadName string) {
oldCSMMeshID, csmMeshIDPresent := os.LookupEnv("CSM_MESH_ID")
oldCSMCanonicalServiceName, csmCanonicalServiceNamePresent := os.LookupEnv("CSM_CANONICAL_SERVICE_NAME")
oldCSMWorkloadName, csmWorkloadNamePresent := os.LookupEnv("CSM_WORKLOAD_NAME")
os.Setenv("CSM_MESH_ID", meshID)
os.Setenv("CSM_CANONICAL_SERVICE_NAME", csmCanonicalServiceName)
os.Setenv("CSM_WORKLOAD_NAME", csmWorkloadName)

Expand All @@ -67,6 +64,11 @@ func setupEnv(t *testing.T, resourceDetectorEmissions map[string]string, nodeID,
return &attrSet
}
t.Cleanup(func() {
if csmMeshIDPresent {
os.Setenv("CSM_MESH_ID", oldCSMMeshID)
} else {
os.Unsetenv("CSM_MESH_ID")
}
if csmCanonicalServiceNamePresent {
os.Setenv("CSM_CANONICAL_SERVICE_NAME", oldCSMCanonicalServiceName)
} else {
Expand Down Expand Up @@ -99,10 +101,10 @@ func (s) TestCSMPluginOptionUnary(t *testing.T) {
"k8s.namespace.name": "k8s_namespace_name_val",
"k8s.cluster.name": "k8s_cluster_name_val",
}
const nodeID = "projects/12345/networks/mesh:mesh_id/nodes/aaaa-aaaa-aaaa-aaaa"
const meshID = "mesh_id"
const csmCanonicalServiceName = "csm_canonical_service_name"
const csmWorkloadName = "csm_workload_name"
setupEnv(t, resourceDetectorEmissions, nodeID, csmCanonicalServiceName, csmWorkloadName)
setupEnv(t, resourceDetectorEmissions, meshID, csmCanonicalServiceName, csmWorkloadName)

attributesWant := map[string]string{
"csm.workload_canonical_service": csmCanonicalServiceName, // from env
Expand Down Expand Up @@ -266,10 +268,10 @@ func (s) TestCSMPluginOptionStreaming(t *testing.T) {
"k8s.namespace.name": "k8s_namespace_name_val",
"k8s.cluster.name": "k8s_cluster_name_val",
}
const nodeID = "projects/12345/networks/mesh:mesh_id/nodes/aaaa-aaaa-aaaa-aaaa"
const meshID = "mesh_id"
const csmCanonicalServiceName = "csm_canonical_service_name"
const csmWorkloadName = "csm_workload_name"
setupEnv(t, resourceDetectorEmissions, nodeID, csmCanonicalServiceName, csmWorkloadName)
setupEnv(t, resourceDetectorEmissions, meshID, csmCanonicalServiceName, csmWorkloadName)

attributesWant := map[string]string{
"csm.workload_canonical_service": csmCanonicalServiceName, // from env
Expand Down
37 changes: 1 addition & 36 deletions stats/opentelemetry/csm/pluginoption.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,13 +24,10 @@ import (
"encoding/base64"
"net/url"
"os"
"strings"

"google.golang.org/grpc/grpclog"
"google.golang.org/grpc/internal/xds/bootstrap"
"google.golang.org/grpc/metadata"
"google.golang.org/grpc/stats/opentelemetry/internal"

"google.golang.org/protobuf/proto"
"google.golang.org/protobuf/types/known/structpb"

Expand Down Expand Up @@ -233,24 +230,6 @@ func constructMetadataFromEnv(ctx context.Context) (map[string]string, string) {
return initializeLocalAndMetadataLabels(labels)
}

// parseMeshIDString parses the mesh id from the node id according to the format
// "projects/[GCP Project number]/networks/mesh:[Mesh ID]/nodes/[UUID]". Returns
// "unknown" if there is a syntax error in the node ID.
func parseMeshIDFromNodeID(nodeID string) string {
meshSplit := strings.Split(nodeID, "/")
if len(meshSplit) != 6 {
return "unknown"
}
if meshSplit[0] != "projects" || meshSplit[2] != "networks" || meshSplit[4] != "nodes" {
return "unknown"
}
meshID, ok := strings.CutPrefix(meshSplit[3], "mesh:")
if !ok { // errors become "unknown"
return "unknown"
}
return meshID
}

// initializeLocalAndMetadataLabels csm local labels for a CSM Plugin Option to
// record. It also builds out a base 64 encoded protobuf.Struct containing the
// metadata exchange labels to be sent as part of metadata exchange from a CSM
Expand All @@ -261,9 +240,7 @@ func initializeLocalAndMetadataLabels(labels map[string]string) (map[string]stri
val := labels["canonical_service"]
localLabels := make(map[string]string)
localLabels["csm.workload_canonical_service"] = val
// Get the CSM Mesh ID from the bootstrap file.
nodeID := getNodeID()
localLabels["csm.mesh_id"] = parseMeshIDFromNodeID(nodeID)
localLabels["csm.mesh_id"] = getEnv("CSM_MESH_ID")

// Metadata exchange labels - can go ahead and encode into proto, and then
// base64.
Expand All @@ -288,18 +265,6 @@ func initializeLocalAndMetadataLabels(labels map[string]string) (map[string]stri
return localLabels, metadataExchangeLabelsEncoded
}

// getNodeID gets the Node ID from the bootstrap data.
func getNodeID() string {
cfg, err := bootstrap.GetConfiguration()
if err != nil {
return "" // will become "unknown"
}
if cfg.Node() == nil {
return ""
}
return cfg.Node().GetId()
}

// metadataExchangeKey is the key for HTTP metadata exchange.
const metadataExchangeKey = "x-envoy-peer-metadata"

Expand Down
57 changes: 5 additions & 52 deletions stats/opentelemetry/csm/pluginoption_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,6 @@ import (

"google.golang.org/grpc/internal/envconfig"
"google.golang.org/grpc/internal/grpctest"
"google.golang.org/grpc/internal/testutils"
"google.golang.org/grpc/internal/testutils/xds/e2e"
"google.golang.org/grpc/metadata"

"github.com/google/go-cmp/cmp"
Expand Down Expand Up @@ -304,51 +302,6 @@ func (s) TestDetermineTargetCSM(t *testing.T) {
}
}

func (s) TestBootstrap(t *testing.T) {
tests := []struct {
name string
nodeID string
meshIDWant string
}{
{
name: "malformed-node-id-unknown",
nodeID: "malformed",
meshIDWant: "unknown",
},
{
name: "node-id-parsed",
nodeID: "projects/12345/networks/mesh:mesh_id/nodes/aaaa-aaaa-aaaa-aaaa",
meshIDWant: "mesh_id",
},
{
name: "wrong-syntax-unknown",
nodeID: "wrong-syntax/12345/networks/mesh:mesh_id/nodes/aaaa-aaaa-aaaa-aaaa",
meshIDWant: "unknown",
},
{
name: "node-id-parsed",
nodeID: "projects/12345/networks/mesh:/nodes/aaaa-aaaa-aaaa-aaaa",
meshIDWant: "",
},
}

for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
bootstrapContents := e2e.DefaultBootstrapContents(t, test.nodeID, "xds_server_uri")
testutils.CreateBootstrapFileForTesting(t, bootstrapContents)
nodeIDGot := getNodeID() // this should return the node ID plumbed into bootstrap above
if nodeIDGot != test.nodeID {
t.Fatalf("getNodeID: got %v, want %v", nodeIDGot, test.nodeID)
}

meshIDGot := parseMeshIDFromNodeID(nodeIDGot)
if meshIDGot != test.meshIDWant {
t.Fatalf("parseMeshIDFromNodeID(%v): got %v, want %v", nodeIDGot, meshIDGot, test.meshIDWant)
}
})
}
}

// TestSetLabels tests the setting of labels, which snapshots the resource and
// environment. It mocks the resource and environment, and then calls into
// labels creation. It verifies to local labels created and metadata exchange
Expand All @@ -360,14 +313,14 @@ func (s) TestSetLabels(t *testing.T) {
resourceKeyValues map[string]string
csmCanonicalServiceNamePopulated bool
csmWorkloadNamePopulated bool
bootstrapGeneratorPopulated bool
meshIDPopulated bool
localLabelsWant map[string]string
metadataExchangeLabelsWant map[string]string
}{
{
name: "no-type",
csmCanonicalServiceNamePopulated: true,
bootstrapGeneratorPopulated: true,
meshIDPopulated: true,
resourceKeyValues: map[string]string{},
localLabelsWant: map[string]string{
"csm.workload_canonical_service": "canonical_service_name_val", // env var populated so should be set.
Expand Down Expand Up @@ -480,9 +433,9 @@ func (s) TestSetLabels(t *testing.T) {
os.Setenv("CSM_WORKLOAD_NAME", "workload_name_val")
defer os.Unsetenv("CSM_WORKLOAD_NAME")
}
if test.bootstrapGeneratorPopulated {
bootstrapContents := e2e.DefaultBootstrapContents(t, "projects/12345/networks/mesh:mesh_id/nodes/aaaa-aaaa-aaaa-aaaa", "xds_server_uri")
testutils.CreateBootstrapFileForTesting(t, bootstrapContents)
if test.meshIDPopulated {
os.Setenv("CSM_MESH_ID", "mesh_id")
defer os.Unsetenv("CSM_MESH_ID")
}
var attributes []attribute.KeyValue
for k, v := range test.resourceKeyValues {
Expand Down