mirror of https://github.com/hashicorp/boundary
parent
fcb29ed919
commit
49a1563f6a
@ -0,0 +1,119 @@
|
||||
// Copyright (c) HashiCorp, Inc.
|
||||
// SPDX-License-Identifier: BUSL-1.1
|
||||
|
||||
package event
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"reflect"
|
||||
|
||||
"google.golang.org/protobuf/proto"
|
||||
)
|
||||
|
||||
// protoFilter is a signature for a struct field validation test
|
||||
type protoFilter func(field reflect.StructField) bool
|
||||
|
||||
// telemetryFilter checks a struct field should be included in observation telemetry data
|
||||
func telemetryFilter(field reflect.StructField) bool {
|
||||
if field.Tag.Get("eventstream") == "observation" {
|
||||
// log.Printf("%s is allowed by Tags (%s:\"%s\")", name, tag, tagValue)
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// filterValue will preserve or zero a value based on if it is classed as observable
|
||||
func filterValue(fv reflect.Value, isObservable bool) {
|
||||
if isObservable {
|
||||
return // let data persist to telemetry
|
||||
}
|
||||
|
||||
// check for nil value (prevent panics)
|
||||
if fv == reflect.ValueOf(nil) {
|
||||
return
|
||||
}
|
||||
|
||||
if fv.Kind() == reflect.Ptr {
|
||||
fv = fv.Elem()
|
||||
}
|
||||
|
||||
// check to see if it's an exported struct field
|
||||
if !fv.CanSet() {
|
||||
return
|
||||
}
|
||||
|
||||
fv.SetZero()
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func recurseStructureWithProtoFilter(value reflect.Value, filterFunc protoFilter, isObservable bool) error {
|
||||
kind := value.Kind()
|
||||
|
||||
switch kind {
|
||||
case reflect.Interface, reflect.Ptr:
|
||||
value = value.Elem()
|
||||
return recurseStructureWithProtoFilter(value, filterFunc, isObservable)
|
||||
case reflect.Map:
|
||||
m := reflect.ValueOf(value)
|
||||
for _, k := range m.MapKeys() {
|
||||
mVal := m.MapIndex(k)
|
||||
if err := recurseStructureWithProtoFilter(mVal, filterFunc, isObservable); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
case reflect.Array, reflect.Slice:
|
||||
if isObservable {
|
||||
for i := 0; i < value.Len(); i++ {
|
||||
sVal := value.Index(i)
|
||||
if err := recurseStructureWithProtoFilter(sVal, filterFunc, isObservable); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if kind == reflect.Slice {
|
||||
value.SetLen(0) // truncate
|
||||
} else {
|
||||
// fixed size, so we zero
|
||||
for i := 0; i < value.Len(); i++ {
|
||||
value.Index(i).SetZero()
|
||||
}
|
||||
}
|
||||
}
|
||||
case reflect.Struct:
|
||||
fields := value.Type()
|
||||
num := fields.NumField()
|
||||
for i := 0; i < num; i++ {
|
||||
field := fields.Field(i)
|
||||
v := value.Field(i)
|
||||
if !field.IsExported() {
|
||||
continue
|
||||
}
|
||||
isObservable := true
|
||||
if filterFunc != nil {
|
||||
isObservable = filterFunc(field)
|
||||
}
|
||||
if err := recurseStructureWithProtoFilter(v, filterFunc, isObservable); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
default:
|
||||
// any other non structured type, we will output or not via filterValue
|
||||
filterValue(value, isObservable)
|
||||
return nil
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func filterProtoMessage(msg proto.Message, filterFunc protoFilter) (proto.Message, error) {
|
||||
if msg == nil {
|
||||
return nil, errors.New("nil message")
|
||||
}
|
||||
|
||||
cloneMsg := proto.Clone(msg)
|
||||
err := recurseStructureWithProtoFilter(reflect.ValueOf(cloneMsg), filterFunc, false)
|
||||
return cloneMsg, err
|
||||
}
|
||||
@ -0,0 +1,105 @@
|
||||
// Copyright (c) HashiCorp, Inc.
|
||||
// SPDX-License-Identifier: BUSL-1.1
|
||||
|
||||
package event
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/hashicorp/boundary/internal/gen/controller/servers"
|
||||
"github.com/hashicorp/boundary/internal/gen/controller/servers/services"
|
||||
tassert "github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func Test_OnlyObservationTaggedFieldsPopulated(t *testing.T) {
|
||||
assert := tassert.New(t)
|
||||
|
||||
input := &services.StatusRequest{
|
||||
Jobs: []*services.JobStatus{
|
||||
{Job: &services.Job{
|
||||
Type: 1,
|
||||
JobInfo: nil,
|
||||
}},
|
||||
},
|
||||
UpdateTags: false,
|
||||
WorkerStatus: &servers.ServerWorkerStatus{
|
||||
PublicId: "testID",
|
||||
Name: "w_1234567890",
|
||||
Description: "A default worker created in",
|
||||
Address: "127.0.0.1:9202",
|
||||
Tags: []*servers.TagPair{
|
||||
{
|
||||
Key: "type",
|
||||
Value: "dev",
|
||||
},
|
||||
},
|
||||
KeyId: "ovary-valid-curler-scrambled-glutinous-alias-rework-debit",
|
||||
ReleaseVersion: "Boundary v0.13.1",
|
||||
OperationalState: "active",
|
||||
},
|
||||
}
|
||||
|
||||
filtered, err := filterProtoMessage(input, telemetryFilter)
|
||||
assert.NoError(err)
|
||||
|
||||
output, ok := filtered.(*services.StatusRequest)
|
||||
assert.True(ok)
|
||||
|
||||
// expected content
|
||||
assert.NotNil(output.WorkerStatus)
|
||||
assert.Equal(input.WorkerStatus.PublicId, output.WorkerStatus.PublicId)
|
||||
assert.Equal(input.WorkerStatus.ReleaseVersion, output.WorkerStatus.ReleaseVersion)
|
||||
assert.Equal(input.WorkerStatus.OperationalState, output.WorkerStatus.OperationalState)
|
||||
|
||||
// non expected content
|
||||
assert.Zero(output.WorkerStatus.Name)
|
||||
assert.Zero(output.WorkerStatus.Address)
|
||||
assert.Zero(output.WorkerStatus.Description)
|
||||
assert.Zero(output.WorkerStatus.KeyId)
|
||||
assert.Len(output.WorkerStatus.Tags, 0)
|
||||
assert.Len(output.Jobs, 0)
|
||||
}
|
||||
|
||||
func Test_AllFieldsPopulatedWithoutFilter(t *testing.T) {
|
||||
assert := tassert.New(t)
|
||||
input := &services.StatusRequest{
|
||||
Jobs: []*services.JobStatus{
|
||||
{Job: &services.Job{
|
||||
Type: 1,
|
||||
JobInfo: nil,
|
||||
}},
|
||||
},
|
||||
UpdateTags: false,
|
||||
WorkerStatus: &servers.ServerWorkerStatus{
|
||||
PublicId: "testID",
|
||||
Name: "w_1234567890",
|
||||
Description: "A default worker created in",
|
||||
Address: "127.0.0.1:9202",
|
||||
Tags: []*servers.TagPair{
|
||||
{
|
||||
Key: "type",
|
||||
Value: "dev",
|
||||
},
|
||||
},
|
||||
KeyId: "ovary-valid-curler-scrambled-glutinous-alias-rework-debit",
|
||||
ReleaseVersion: "Boundary v0.13.1",
|
||||
OperationalState: "active",
|
||||
},
|
||||
}
|
||||
|
||||
filtered, err := filterProtoMessage(input, nil)
|
||||
assert.NoError(err)
|
||||
|
||||
output, ok := filtered.(*services.StatusRequest)
|
||||
assert.True(ok)
|
||||
|
||||
// expected content
|
||||
assert.Equal(input, output)
|
||||
}
|
||||
|
||||
func Test_NilMessageWillError(t *testing.T) {
|
||||
assert := tassert.New(t)
|
||||
|
||||
_, err := filterProtoMessage(nil, nil)
|
||||
assert.Error(err)
|
||||
}
|
||||
Loading…
Reference in new issue