Skip to content

Commit

Permalink
Inject input.policy.revision without overwriting. (#1767)
Browse files Browse the repository at this point in the history
Inject input.policy.revision without overwriting.

Ensure each input configuration gets a policy.revision key that contains
the revision number of the Fleet policy, but correctly handle the case
where a policy object already exists.

The policy.revision key was added as a requirement to support endpoint
security, and is probably generally useful for detecting when the
overall agent policy has changed.
  • Loading branch information
cmacknz authored Nov 22, 2022
1 parent 67021cf commit 8e9c758
Show file tree
Hide file tree
Showing 2 changed files with 96 additions and 6 deletions.
40 changes: 34 additions & 6 deletions pkg/component/component.go
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,6 @@ func (r *RuntimeSpecs) ToComponents(policy map[string]interface{}, monitoringInj
// PolicyToComponents takes the policy and generated a component model along with providing a mapping between component
// and the running binary.
func (r *RuntimeSpecs) PolicyToComponents(policy map[string]interface{}) ([]Component, map[string]string, error) {
const revision = "revision"
outputsMap, err := toIntermediate(policy)
if err != nil {
return nil, nil, err
Expand Down Expand Up @@ -222,11 +221,11 @@ func (r *RuntimeSpecs) PolicyToComponents(policy map[string]interface{}) ([]Comp
// skip; not enabled
continue
}
if v, ok := policy[revision]; ok {
input.input["policy"] = map[string]interface{}{
revision: v,
}
}

// Inject the top level fleet policy revision into each into configuration. This
// allows individual inputs (like endpoint) to detect policy changes more easily.
injectInputPolicyID(policy, input.input)

cfg, cfgErr := ExpectedConfig(input.input)
if cfg != nil {
cfg.Type = inputType // ensure alias is replaced in the ExpectedConfig to be non-alias type
Expand Down Expand Up @@ -326,6 +325,35 @@ func (r *RuntimeSpecs) PolicyToComponents(policy map[string]interface{}) ([]Comp
return components, componentIdsInputMap, nil
}

// Injects or creates a policy.revision sub-object in the input map.
func injectInputPolicyID(fleetPolicy map[string]interface{}, input map[string]interface{}) {
if input == nil {
return
}

// If there is no top level fleet policy revision, there's nothing to inject.
revision, exists := fleetPolicy["revision"]
if !exists {
return
}

// Check if a policy key exists with a non-nil policy object.
policyObj, exists := input["policy"]
if exists && policyObj != nil {
// If the policy object converts to map[string]interface{}, inject the revision key.
// Note that if the interface conversion here fails, we do nothing because we don't
// know what type of object exists with the policy key.
if policyMap, ok := policyObj.(map[string]interface{}); ok {
policyMap["revision"] = revision
}
} else {
// If there was no policy key or the value was nil, then inject a policy object with a revision key.
input["policy"] = map[string]interface{}{
"revision": revision,
}
}
}

func componentToShipperConfig(comp Component) (*proto.UnitExpectedConfig, error) {
cfgUnits := make([]interface{}, 0, len(comp.Units))
for _, unit := range comp.Units {
Expand Down
62 changes: 62 additions & 0 deletions pkg/component/component_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1518,6 +1518,68 @@ func TestToComponents(t *testing.T) {
}
}

func TestInjectingInputPolicyID(t *testing.T) {
const testRevision = 10
fleetPolicy := map[string]interface{}{
"revision": testRevision,
}

tests := []struct {
name string
policy map[string]interface{}
in map[string]interface{}
out map[string]interface{}
}{
{"NilEverything", nil, nil, nil},
{"NilInput", fleetPolicy, nil, nil},
{"NilPolicy", nil,
map[string]interface{}{},
map[string]interface{}{},
},
{"EmptyPolicy", map[string]interface{}{},
map[string]interface{}{},
map[string]interface{}{},
},
{"CreatePolicyRevision", fleetPolicy,
map[string]interface{}{},
map[string]interface{}{
"policy": map[string]interface{}{"revision": testRevision},
},
},
{"NilPolicyObjectType", fleetPolicy,
map[string]interface{}{
"policy": nil,
},
map[string]interface{}{
"policy": map[string]interface{}{"revision": testRevision},
},
},
{"InjectPolicyRevision", fleetPolicy,
map[string]interface{}{
"policy": map[string]interface{}{"key": "value"},
},
map[string]interface{}{
"policy": map[string]interface{}{"key": "value", "revision": testRevision},
},
},
{"UnknownPolicyObjectType", fleetPolicy,
map[string]interface{}{
"policy": map[string]int{"key": 10},
},
map[string]interface{}{
"policy": map[string]int{"key": 10},
},
},
}

for _, tc := range tests {
t.Run(tc.name, func(t *testing.T) {
injectInputPolicyID(tc.policy, tc.in)
assert.Equal(t, tc.out, tc.in)
})
}
}

func assertEqualUnitExpectedConfigs(t *testing.T, expected *Unit, actual *Unit) {
t.Helper()
assert.Equal(t, expected.ID, actual.ID)
Expand Down

0 comments on commit 8e9c758

Please sign in to comment.