Skip to content

Commit

Permalink
Fix status history toggling (#111)
Browse files Browse the repository at this point in the history
Description of problem:
When Pod security policy is created and the status is changed from inform to enforce, the status is toggling. Not able to enforce pod security policy.

How to fix
Set default value when Kube API value omitted

ref: https://issues.redhat.com/browse/ACM-3109
Signed-off-by: Yi Rae Kim <yikim@redhat.com>
  • Loading branch information
yiraeChristineKim committed Mar 31, 2023
1 parent e868a2b commit b70748b
Show file tree
Hide file tree
Showing 15 changed files with 484 additions and 38 deletions.
97 changes: 97 additions & 0 deletions controllers/configurationpolicy_controller_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import (

"github.com/stretchr/testify/assert"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/types"
"k8s.io/client-go/kubernetes/scheme"
Expand Down Expand Up @@ -754,3 +755,99 @@ func TestShouldEvaluatePolicy(t *testing.T) {
)
}
}

func TestShouldHandleSingleKeyFalse(t *testing.T) {
t.Parallel()

var unstruct unstructured.Unstructured
var unstructObj unstructured.Unstructured

var update, skip bool

type ExpectResult struct {
key string
expect bool
}

type TestSingleKey struct {
input map[string]interface{}
fromAPI map[string]interface{}
expectResult ExpectResult
}

tests := []TestSingleKey{
{
input: map[string]interface{}{
"hostIPC": false,
"container": "test",
},
fromAPI: map[string]interface{}{
"container": "test",
},
expectResult: ExpectResult{
"hostIPC",
false,
},
},
{
input: map[string]interface{}{
"container": map[string]interface{}{
"image": "nginx1.7.9",
"name": "nginx",
"hostIPC": false,
},
},
fromAPI: map[string]interface{}{
"container": map[string]interface{}{
"image": "nginx1.7.9",
"name": "nginx",
},
},
expectResult: ExpectResult{
"container",
false,
},
},
{
input: map[string]interface{}{
"hostIPC": true,
"container": "test",
},
fromAPI: map[string]interface{}{
"container": "test",
},
expectResult: ExpectResult{
"hostIPC",
true,
},
},
{
input: map[string]interface{}{
"container": map[string]interface{}{
"image": "nginx1.7.9",
"name": "nginx",
"hostIPC": true,
},
},
fromAPI: map[string]interface{}{
"container": map[string]interface{}{
"image": "nginx1.7.9",
"name": "nginx",
},
},
expectResult: ExpectResult{
"container",
true,
},
},
}

for _, test := range tests {
unstruct.Object = test.input
unstructObj.Object = test.fromAPI
key := test.expectResult.key
_, update, _, skip = handleSingleKey(key, unstruct, &unstructObj, "musthave")
assert.Equal(t, update, test.expectResult.expect)
assert.False(t, skip)
}
}
22 changes: 21 additions & 1 deletion controllers/configurationpolicy_utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,16 @@ func equalObjWithSort(mergedObj interface{}, oldObj interface{}) (areEqual bool)
return false
}
default:
// NOTE: when type is string, int, bool
var oVal interface{}

if oldObj == nil && mergedObj != nil {
ref := reflect.ValueOf(mergedObj)
oVal = reflect.Zero(ref.Type()).Interface()

return fmt.Sprint(oVal) == fmt.Sprint(mergedObj)
}

if !reflect.DeepEqual(fmt.Sprint(mergedObj), fmt.Sprint(oldObj)) {
return false
}
Expand Down Expand Up @@ -175,8 +185,12 @@ func checkFieldsWithSort(mergedObj map[string]interface{}, oldObj map[string]int
// extra check to see if value is a byte value
mQty, err := apiRes.ParseQuantity(mVal)
if err != nil {
oVal := oldObj[i]
if oVal == nil {
oVal = ""
}
// An error indicates the value is a regular string, so check equality normally
if fmt.Sprint(oldObj[i]) != fmt.Sprint(mVal) {
if fmt.Sprint(oVal) != fmt.Sprint(mVal) {
return false
}
} else {
Expand All @@ -194,6 +208,12 @@ func checkFieldsWithSort(mergedObj map[string]interface{}, oldObj map[string]int
default:
// if field is not an object, just do a basic compare to check for a match
oVal := oldObj[i]
// When oVal value omitted because of omitempty
if oVal == nil && mVal != nil {
ref := reflect.ValueOf(mVal)
oVal = reflect.Zero(ref.Type()).Interface()
}

if fmt.Sprint(oVal) != fmt.Sprint(mVal) {
return false
}
Expand Down
31 changes: 31 additions & 0 deletions controllers/configurationpolicy_utils_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -118,3 +118,34 @@ func TestCheckFieldsWithSort(t *testing.T) {

assert.True(t, checkFieldsWithSort(mergedObj, oldObj))
}

func TestEqualObjWithSort(t *testing.T) {
t.Parallel()

oldObj := map[string]interface{}{
"nonResourceURLs": []string{"/version", "/healthz"},
"verbs": []string{"get"},
}
mergedObj := map[string]interface{}{
"nonResourceURLs": []string{"/version", "/healthz"},
"verbs": []string{"get"},
"apiGroups": []interface{}{},
"resources": []interface{}{},
}

assert.True(t, equalObjWithSort(mergedObj, oldObj))
assert.False(t, equalObjWithSort(mergedObj, nil))

oldObj = map[string]interface{}{
"nonResourceURLs": []string{"/version", "/healthz"},
"verbs": []string{"get"},
}
mergedObj = map[string]interface{}{
"nonResourceURLs": []string{"/version", "/healthz"},
"verbs": []string{"post"},
"apiGroups": []interface{}{},
"resources": []interface{}{},
}

assert.False(t, equalObjWithSort(mergedObj, oldObj))
}
Empty file added policy-security.yml
Empty file.
22 changes: 12 additions & 10 deletions test/e2e/case12_list_compare_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -356,7 +356,17 @@ var _ = Describe("Test list handling for musthave", func() {
deleteConfigPolicies(policies)
})
})
Describe("Create a statefulset object with a byte quantity field on managed cluster in ns:"+testNamespace, func() {
Describe("Create a statefulset object with a byte quantity field "+
"on managed cluster in ns:"+testNamespace, Ordered, func() {
cleanup := func() {
// Delete the policies and ignore any errors (in case it was deleted previously)
policies := []string{
case12ByteCreate,
case12ByteInform,
}

deleteConfigPolicies(policies)
}
It("should only add the list item with the rounded byte value once", func() {
By("Creating " + case12ByteCreate + " and " + case12ByteInform + " on managed")
utils.Kubectl("apply", "-f", case12ByteCreateYaml, "-n", testNamespace)
Expand Down Expand Up @@ -389,14 +399,6 @@ var _ = Describe("Test list handling for musthave", func() {
return utils.GetComplianceState(managedPlc)
}, defaultTimeoutSeconds, 1).Should(Equal("Compliant"))
})

It("Cleans up", func() {
policies := []string{
case12ByteCreate,
case12ByteInform,
}

deleteConfigPolicies(policies)
})
AfterAll(cleanup)
})
})
3 changes: 2 additions & 1 deletion test/e2e/case20_delete_objects_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -527,7 +527,8 @@ var _ = Describe("Test objects that should be deleted are actually being deleted
return nil
}, defaultTimeoutSeconds, 1).Should(BeNil())
})
It("deletes the pod after the policy is deleted", func() {
AfterAll(func() {
By("deletes the pod after the policy is deleted")
deleteConfigPolicies([]string{case20ConfigPolicyNameCreate})
Eventually(func() interface{} {
pod := utils.GetWithTimeout(clientManagedDynamic, gvrPod,
Expand Down
5 changes: 1 addition & 4 deletions test/e2e/case25_related_object_metric_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ var _ = Describe("Test related object metrics", Ordered, func() {
// Delete the policies and ignore any errors (in case it was deleted previously)
cmd := exec.Command("kubectl", "delete",
"-f", policyYaml,
"-n", testNamespace)
"-n", testNamespace, "--ignore-not-found")
_, _ = cmd.CombinedOutput()
opt := metav1.ListOptions{}
utils.ListWithTimeout(
Expand All @@ -66,13 +66,10 @@ var _ = Describe("Test related object metrics", Ordered, func() {
}

It("should clean up", cleanup)

It("should have no common related object metrics after clean up", func() {
By("Checking metric endpoint for related object gauges")
Eventually(func() interface{} {
return utils.GetMetrics("common_related_objects")
}, defaultTimeoutSeconds, 1).Should(Equal([]string{}))
})

AfterAll(cleanup)
})
35 changes: 15 additions & 20 deletions test/e2e/case26_user_error_metric_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,21 @@ var _ = Describe("Test related object metrics", Ordered, func() {
policy1Name = "case26-test-policy-1"
policyYaml = "../resources/case26_user_error_metric/case26-missing-crd.yaml"
)
cleanup := func() {
// Delete the policies and ignore any errors (in case it was deleted previously)
cmd := exec.Command("kubectl", "delete",
"-f", policyYaml,
"-n", testNamespace, "--ignore-not-found")
_, _ = cmd.CombinedOutput()
opt := metav1.ListOptions{}
utils.ListWithTimeout(
clientManagedDynamic, gvrConfigPolicy, opt, 0, false, defaultTimeoutSeconds)
By("Checking metric endpoint for related object gauges")
Eventually(func() interface{} {
return utils.GetMetrics("policy_user_errors")
}, defaultTimeoutSeconds, 1).Should(Equal([]string{}))
}

It("should create policy", func() {
By("Creating " + policyYaml)
utils.Kubectl("apply",
Expand All @@ -36,25 +51,5 @@ var _ = Describe("Test related object metrics", Ordered, func() {
}, defaultTimeoutSeconds, 1).Should(Equal([]string{"policies", "counter", "1"}))
})

cleanup := func() {
// Delete the policies and ignore any errors (in case it was deleted previously)
cmd := exec.Command("kubectl", "delete",
"-f", policyYaml,
"-n", testNamespace)
_, _ = cmd.CombinedOutput()
opt := metav1.ListOptions{}
utils.ListWithTimeout(
clientManagedDynamic, gvrConfigPolicy, opt, 0, false, defaultTimeoutSeconds)
}

It("should clean up", cleanup)

It("should have no common related object metrics after clean up", func() {
By("Checking metric endpoint for related object gauges")
Eventually(func() interface{} {
return utils.GetMetrics("policy_user_errors")
}, defaultTimeoutSeconds, 1).Should(Equal([]string{}))
})

AfterAll(cleanup)
})
1 change: 0 additions & 1 deletion test/e2e/case28_evauluation_metric_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,6 @@ var _ = Describe("Test config policy evaluation metrics", Ordered, func() {
opt := metav1.ListOptions{}
utils.ListWithTimeout(
clientManagedDynamic, gvrConfigPolicy, opt, 0, false, defaultTimeoutSeconds)

Eventually(func() interface{} {
return utils.GetMetrics(
"config_policy_evaluation_total", fmt.Sprintf(`name=\"%s\"`, policyName))
Expand Down
Loading

0 comments on commit b70748b

Please sign in to comment.