Skip to content

Commit 32d2b0e

Browse files
authored
Adding tasks for log summary generation and history modifier for error audit logs (#377)
* Added new fieldset related tasks and history modifiers for error audit logs * fix issues pointed by gemini-code-assist
1 parent 4767e6a commit 32d2b0e

16 files changed

Lines changed: 820 additions & 7 deletions

File tree

pkg/model/k8s_operation.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ type KubernetesObjectOperation struct {
4343
Verb enum.RevisionVerb
4444
}
4545

46-
func (o *KubernetesObjectOperation) CovertToResourcePath() string {
46+
func (o *KubernetesObjectOperation) ResourcePath() string {
4747
if o.SubResourceName != "" {
4848
return strings.ToLower(strings.Join([]string{
4949
o.APIVersion,

pkg/task/inspection/commonlogk8saudit/impl/timelinegrouping_task.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ func (d *defaultResourceGroupDecider) Name() string {
5757
// GetResourceGroup implements resourceGroupDecider. It returns the resource path
5858
// by converting the log's Operation to a resource path.
5959
func (d *defaultResourceGroupDecider) GetResourceGroup(log *commonlogk8saudit_contract.AuditLogParserInput) (string, error) {
60-
return log.Operation.CovertToResourcePath(), nil
60+
return log.Operation.ResourcePath(), nil
6161
}
6262

6363
// Ensure defaultResourceGroupDecider implements resourceGroupDecider.
@@ -136,7 +136,7 @@ func (s *subresourceResourceGroupDecider) GetResourceGroup(log *commonlogk8saudi
136136
if s.defaultBehaviorOverrides[log.Operation.SubResourceName] == Parent {
137137
return resourcepath.NameLayerGeneralItem(log.Operation.APIVersion, log.Operation.GetSingularKindName(), log.Operation.Namespace, log.Operation.Name).Path, nil
138138
} else {
139-
return log.Operation.CovertToResourcePath(), nil
139+
return log.Operation.ResourcePath(), nil
140140
}
141141
}
142142

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
// Copyright 2025 Google LLC
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
package commonlogk8sauditv2_contract
16+
17+
import (
18+
"github.com/GoogleCloudPlatform/khi/pkg/common/structured"
19+
"github.com/GoogleCloudPlatform/khi/pkg/model"
20+
"github.com/GoogleCloudPlatform/khi/pkg/model/enum"
21+
"github.com/GoogleCloudPlatform/khi/pkg/model/log"
22+
)
23+
24+
type K8sAuditLogFieldSet struct {
25+
OperationID string
26+
IsFirst bool
27+
IsLast bool
28+
K8sOperation *model.KubernetesObjectOperation
29+
RequestURI string
30+
Principal string
31+
StatusCode int
32+
StatusMessage string
33+
IsError bool
34+
Request *structured.NodeReader
35+
Response *structured.NodeReader
36+
}
37+
38+
// Kind implements log.FieldSet.
39+
func (k *K8sAuditLogFieldSet) Kind() string {
40+
return "k8s_audit_log"
41+
}
42+
43+
// LongRunning returns true if the log is a long-running operation.
44+
func (k *K8sAuditLogFieldSet) LongRunning() bool {
45+
return (k.IsFirst && !k.IsLast) || (!k.IsFirst && k.IsLast)
46+
}
47+
48+
// VerbString returns the string representation of the verb.
49+
func (k *K8sAuditLogFieldSet) VerbString() string {
50+
if k.K8sOperation == nil {
51+
return ""
52+
}
53+
return enum.RevisionVerbs[k.K8sOperation.Verb].Label
54+
}
55+
56+
var _ log.FieldSet = (*K8sAuditLogFieldSet)(nil)
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
// Copyright 2025 Google LLC
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
package commonlogk8sauditv2_contract
16+
17+
import (
18+
inspectiontaskbase "github.com/GoogleCloudPlatform/khi/pkg/core/inspection/taskbase"
19+
"github.com/GoogleCloudPlatform/khi/pkg/core/task/taskid"
20+
"github.com/GoogleCloudPlatform/khi/pkg/model/log"
21+
)
22+
23+
var TaskIDPrefix = "khi.google.com/k8s-common-auditlog-v2/"
24+
25+
// K8sAuditLogProviderRef is the task reference for the task to fetch k8s audit log.
26+
// The actual implementation for this reference must provide log array with the K8sAuditLogFieldSet.
27+
var K8sAuditLogProviderRef = taskid.NewTaskReference[[]*log.Log](TaskIDPrefix + "k8s-auditlog-provider")
28+
29+
// K8sAuditLogSerializerTaskID is the task ID for the task to serialize the k8s audit log.
30+
var K8sAuditLogSerializerTaskID = taskid.NewDefaultImplementationID[[]*log.Log](TaskIDPrefix + "k8s-auditlog-serializer")
31+
32+
// LogSummaryGrouperTaskID is the task ID for the task to group logs for summary generation.
33+
var LogSummaryGrouperTaskID = taskid.NewDefaultImplementationID[inspectiontaskbase.LogGroupMap](TaskIDPrefix + "log-summary-grouper")
34+
35+
// ChangeTargetGrouperTaskID is the task ID for the task to group logs by the target resource.
36+
var ChangeTargetGrouperTaskID = taskid.NewDefaultImplementationID[inspectiontaskbase.LogGroupMap](TaskIDPrefix + "change-target-grouper")
37+
38+
// LogSummaryHistoryModifierTaskID is the task ID for the task to generate log summary from given k8s audit log.
39+
var LogSummaryHistoryModifierTaskID = taskid.NewDefaultImplementationID[struct{}](TaskIDPrefix + "log-summary-history-modifier")
40+
41+
// ConditionHistoryModifierTaskID is the task ID for the task to generate condition history.
42+
var ConditionHistoryModifierTaskID = taskid.NewDefaultImplementationID[struct{}](TaskIDPrefix + "condition-history-modifier")
43+
44+
// SuccessLogFilterTaskID is the task ID for the task to filter success logs.
45+
var SuccessLogFilterTaskID = taskid.NewDefaultImplementationID[[]*log.Log](TaskIDPrefix + "success-log-filter")
46+
47+
// NonSuccessLogFilterTaskID is the task ID for the task to filter non-success logs.
48+
var NonSuccessLogFilterTaskID = taskid.NewDefaultImplementationID[[]*log.Log](TaskIDPrefix + "non-success-log-filter")
49+
50+
// NonSuccessLogGrouperTaskID is the task ID for the task to group non-success logs.
51+
var NonSuccessLogGrouperTaskID = taskid.NewDefaultImplementationID[inspectiontaskbase.LogGroupMap](TaskIDPrefix + "non-success-log-grouper")
52+
53+
// NonSuccessLogHistoryModifierTaskID is the task ID for the task to generate history from non-success logs.
54+
var NonSuccessLogHistoryModifierTaskID = taskid.NewDefaultImplementationID[struct{}](TaskIDPrefix + "non-success-history-modifier")
55+
56+
// LogSorterTaskID is the task ID for the task to sort logs by time.
57+
var LogSorterTaskID = taskid.NewDefaultImplementationID[[]*log.Log](TaskIDPrefix + "log-sorter")
Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
// Copyright 2025 Google LLC
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
package commonlogk8sauditv2_impl
16+
17+
import (
18+
"testing"
19+
20+
"github.com/GoogleCloudPlatform/khi/pkg/model"
21+
"github.com/GoogleCloudPlatform/khi/pkg/model/history"
22+
"github.com/GoogleCloudPlatform/khi/pkg/model/log"
23+
commonlogk8sauditv2_contract "github.com/GoogleCloudPlatform/khi/pkg/task/inspection/commonlogk8sauditv2/contract"
24+
"github.com/GoogleCloudPlatform/khi/pkg/testutil/testchangeset"
25+
)
26+
27+
func TestNonSuccessLogHistoryModifierTaskSetting_AddEventForLog(t *testing.T) {
28+
testCases := []struct {
29+
desc string
30+
input *commonlogk8sauditv2_contract.K8sAuditLogFieldSet
31+
wantPath []string
32+
}{
33+
{
34+
desc: "error on name layer resource",
35+
input: &commonlogk8sauditv2_contract.K8sAuditLogFieldSet{
36+
K8sOperation: &model.KubernetesObjectOperation{
37+
APIVersion: "core/v1",
38+
PluralKind: "pods",
39+
Namespace: "kube-system",
40+
Name: "kube-dns",
41+
SubResourceName: "",
42+
},
43+
},
44+
wantPath: []string{
45+
"core/v1#pod#kube-system#kube-dns",
46+
},
47+
},
48+
{
49+
desc: "error on subresource layer resource but not included in the exception map",
50+
input: &commonlogk8sauditv2_contract.K8sAuditLogFieldSet{
51+
K8sOperation: &model.KubernetesObjectOperation{
52+
APIVersion: "core/v1",
53+
PluralKind: "pods",
54+
Namespace: "kube-system",
55+
Name: "kube-dns",
56+
SubResourceName: "binding",
57+
},
58+
},
59+
wantPath: []string{
60+
"core/v1#pod#kube-system#kube-dns#binding",
61+
},
62+
},
63+
{
64+
desc: "error on subresource layer resource and included in the exception map",
65+
input: &commonlogk8sauditv2_contract.K8sAuditLogFieldSet{
66+
K8sOperation: &model.KubernetesObjectOperation{
67+
APIVersion: "core/v1",
68+
PluralKind: "pods",
69+
Namespace: "kube-system",
70+
Name: "kube-dns",
71+
SubResourceName: "status",
72+
},
73+
},
74+
wantPath: []string{
75+
"core/v1#pod#kube-system#kube-dns",
76+
},
77+
},
78+
}
79+
80+
for _, tc := range testCases {
81+
t.Run(tc.desc, func(t *testing.T) {
82+
l := log.NewLogWithFieldSetsForTest(tc.input)
83+
cs := history.NewChangeSet(l)
84+
85+
setting := &nonSuccessLogHistoryModifierTaskSetting{
86+
subresourceMapToWriteToParent: map[string]struct{}{
87+
"status": {},
88+
},
89+
}
90+
err := setting.addEventForLog(l, cs)
91+
if err != nil {
92+
t.Fatalf("failed to add event for log: %v", err)
93+
}
94+
asserter := testchangeset.MatchResourcePathSet{
95+
WantResourcePaths: tc.wantPath,
96+
}
97+
98+
asserter.Assert(t, cs)
99+
})
100+
}
101+
}
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
// Copyright 2025 Google LLC
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
package commonlogk8sauditv2_impl
16+
17+
import (
18+
"context"
19+
20+
inspectiontaskbase "github.com/GoogleCloudPlatform/khi/pkg/core/inspection/taskbase"
21+
"github.com/GoogleCloudPlatform/khi/pkg/core/task/taskid"
22+
"github.com/GoogleCloudPlatform/khi/pkg/model/enum"
23+
"github.com/GoogleCloudPlatform/khi/pkg/model/history"
24+
"github.com/GoogleCloudPlatform/khi/pkg/model/history/resourcepath"
25+
"github.com/GoogleCloudPlatform/khi/pkg/model/log"
26+
commonlogk8sauditv2_contract "github.com/GoogleCloudPlatform/khi/pkg/task/inspection/commonlogk8sauditv2/contract"
27+
)
28+
29+
var NonSuccessLogHistoryModifierTask = inspectiontaskbase.NewHistoryModifierTask[struct{}](commonlogk8sauditv2_contract.NonSuccessLogHistoryModifierTaskID, &nonSuccessLogHistoryModifierTaskSetting{
30+
subresourceMapToWriteToParent: map[string]struct{}{
31+
"status": {},
32+
"finalize": {},
33+
"approve": {},
34+
},
35+
})
36+
37+
type nonSuccessLogHistoryModifierTaskSetting struct {
38+
subresourceMapToWriteToParent map[string]struct{}
39+
}
40+
41+
// Dependencies implements inspectiontaskbase.HistoryModifer.
42+
func (e *nonSuccessLogHistoryModifierTaskSetting) Dependencies() []taskid.UntypedTaskReference {
43+
return []taskid.UntypedTaskReference{}
44+
}
45+
46+
// GroupedLogTask implements inspectiontaskbase.HistoryModifer.
47+
func (e *nonSuccessLogHistoryModifierTaskSetting) GroupedLogTask() taskid.TaskReference[inspectiontaskbase.LogGroupMap] {
48+
return commonlogk8sauditv2_contract.NonSuccessLogGrouperTaskID.Ref()
49+
}
50+
51+
// LogSerializerTask implements inspectiontaskbase.HistoryModifer.
52+
func (e *nonSuccessLogHistoryModifierTaskSetting) LogSerializerTask() taskid.TaskReference[[]*log.Log] {
53+
return commonlogk8sauditv2_contract.K8sAuditLogSerializerTaskID.Ref()
54+
}
55+
56+
// ModifyChangeSetFromLog implements inspectiontaskbase.HistoryModifer.
57+
func (e *nonSuccessLogHistoryModifierTaskSetting) ModifyChangeSetFromLog(ctx context.Context, l *log.Log, cs *history.ChangeSet, builder *history.Builder, prevGroupData struct{}) (struct{}, error) {
58+
return struct{}{}, e.addEventForLog(l, cs)
59+
}
60+
61+
var _ inspectiontaskbase.HistoryModifer[struct{}] = (*nonSuccessLogHistoryModifierTaskSetting)(nil)
62+
63+
func (e *nonSuccessLogHistoryModifierTaskSetting) addEventForLog(l *log.Log, cs *history.ChangeSet) error {
64+
fieldSet := log.MustGetFieldSet(l, &commonlogk8sauditv2_contract.K8sAuditLogFieldSet{})
65+
op := *fieldSet.K8sOperation
66+
if _, ok := e.subresourceMapToWriteToParent[op.SubResourceName]; op.SubResourceName != "" && ok {
67+
op.SubResourceName = ""
68+
}
69+
cs.AddEvent(resourcepath.ResourcePath{
70+
Path: op.ResourcePath(),
71+
ParentRelationship: enum.RelationshipChild,
72+
})
73+
return nil
74+
}
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
// Copyright 2025 Google LLC
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
package commonlogk8sauditv2_impl
16+
17+
import (
18+
"context"
19+
20+
inspectiontaskbase "github.com/GoogleCloudPlatform/khi/pkg/core/inspection/taskbase"
21+
"github.com/GoogleCloudPlatform/khi/pkg/model/log"
22+
commonlogk8sauditv2_contract "github.com/GoogleCloudPlatform/khi/pkg/task/inspection/commonlogk8sauditv2/contract"
23+
)
24+
25+
// SuccessLogFilterTask filters out non-success logs.
26+
var SuccessLogFilterTask = inspectiontaskbase.NewLogFilterTask(
27+
commonlogk8sauditv2_contract.SuccessLogFilterTaskID,
28+
commonlogk8sauditv2_contract.K8sAuditLogProviderRef,
29+
func(ctx context.Context, l *log.Log) bool {
30+
return !log.MustGetFieldSet(l, &commonlogk8sauditv2_contract.K8sAuditLogFieldSet{}).IsError
31+
},
32+
)
33+
34+
// NonSuccessLogFilterTask filters out success logs.
35+
var NonSuccessLogFilterTask = inspectiontaskbase.NewLogFilterTask(
36+
commonlogk8sauditv2_contract.NonSuccessLogFilterTaskID,
37+
commonlogk8sauditv2_contract.K8sAuditLogProviderRef,
38+
func(ctx context.Context, l *log.Log) bool {
39+
return log.MustGetFieldSet(l, &commonlogk8sauditv2_contract.K8sAuditLogFieldSet{}).IsError
40+
},
41+
)

0 commit comments

Comments
 (0)