Skip to content

Commit e5621e9

Browse files
Merge pull request #591 from Vincent056/last_timestamp
CMP-2614: Implement update timestamps on ComplianceCheckResults
2 parents cd98a64 + df14482 commit e5621e9

File tree

5 files changed

+121
-6
lines changed

5 files changed

+121
-6
lines changed

cmd/manager/aggregator.go

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -308,19 +308,24 @@ func createOrUpdateOneResult(crClient aggregatorCrClient, owner metav1.Object, l
308308
}
309309

310310
res.SetLabels(labels)
311-
if annotations != nil {
312-
res.SetAnnotations(annotations)
313-
}
314311

315312
name := res.GetName()
316313

317314
err := backoff.Retry(func() error {
318315
var err error
319316
if !exists {
320317
cmdLog.Info("Creating object", "kind", kind, "name", name)
318+
annotations = setTimestampAnnotations(owner, annotations)
319+
if annotations != nil {
320+
res.SetAnnotations(annotations)
321+
}
321322
err = crClient.getClient().Create(context.TODO(), res)
322323
} else {
323324
cmdLog.Info("Updating object", "kind", kind, "name", name)
325+
annotations = setTimestampAnnotations(owner, annotations)
326+
if annotations != nil {
327+
res.SetAnnotations(annotations)
328+
}
324329
err = crClient.getClient().Update(context.TODO(), res)
325330
}
326331
if err != nil && !errors.IsAlreadyExists(err) {
@@ -336,6 +341,17 @@ func createOrUpdateOneResult(crClient aggregatorCrClient, owner metav1.Object, l
336341
return nil
337342
}
338343

344+
func setTimestampAnnotations(owner metav1.Object, annotations map[string]string) map[string]string {
345+
// If the owner is a scan, we should set the last scanned timestamp
346+
if scan, ok := owner.(*compv1alpha1.ComplianceScan); ok {
347+
if annotations == nil {
348+
annotations = make(map[string]string)
349+
}
350+
annotations[compv1alpha1.LastScannedTimestampAnnotation] = scan.Status.StartTimestamp.Format(time.RFC3339)
351+
}
352+
return annotations
353+
}
354+
339355
func shouldSkipRemediation(
340356
scan *compv1alpha1.ComplianceScan,
341357
rem *compv1alpha1.ComplianceRemediation,

cmd/manager/common.go

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,12 @@ package manager
22

33
import (
44
"fmt"
5-
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
6-
"k8s.io/client-go/discovery"
75
"os"
86
"path/filepath"
97

8+
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
9+
"k8s.io/client-go/discovery"
10+
1011
ocpcfgv1 "github.com/openshift/api/config/v1"
1112
mcfgv1 "github.com/openshift/machine-config-operator/pkg/apis/machineconfiguration.openshift.io/v1"
1213
"github.com/spf13/cobra"
@@ -25,7 +26,8 @@ import (
2526
)
2627

2728
const (
28-
maxRetries = 15
29+
maxRetries = 15
30+
maxRetriesForTimestamp = 3
2931
)
3032

3133
var cmdLog = logf.Log.WithName("cmd")

pkg/apis/compliance/v1alpha1/compliancecheckresult_types.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,9 @@ const ComplianceCheckResultHasRemediation = "compliance.openshift.io/automated-r
2222
// across the target nodes
2323
const ComplianceCheckInconsistentLabel = "compliance.openshift.io/inconsistent-check"
2424

25+
// LastScannedTimestampAnnotation
26+
const LastScannedTimestampAnnotation = "compliance.openshift.io/last-scanned-timestamp"
27+
2528
// ComplianceCheckResultRuleAnnotation exposes the DNS-friendly name of a rule as a label.
2629
// This provides a way to link a result to a Rule object.
2730
const ComplianceCheckResultRuleAnnotation = "compliance.openshift.io/rule"

tests/e2e/framework/common.go

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1151,6 +1151,43 @@ func (f *Framework) AssertScanIsCompliant(name, namespace string) error {
11511151
return nil
11521152
}
11531153

1154+
// AssertComplianceCheckResultTimestamps checks if the timestamps in the compliance check results are within the expected range
1155+
func (f *Framework) AssertComplianceCheckResultTimestamps(scanName, namespace string) error {
1156+
cs := &compv1alpha1.ComplianceScan{}
1157+
defer f.logContainerOutput(namespace, scanName)
1158+
err := f.Client.Get(context.TODO(), types.NamespacedName{Name: scanName, Namespace: namespace}, cs)
1159+
if err != nil {
1160+
return err
1161+
}
1162+
ccr := &compv1alpha1.ComplianceCheckResultList{}
1163+
lo := &client.ListOptions{
1164+
LabelSelector: labels.SelectorFromSet(map[string]string{
1165+
"compliance.openshift.io/scan-name": scanName,
1166+
}),
1167+
}
1168+
err = f.Client.List(context.TODO(), ccr, lo)
1169+
if err != nil {
1170+
return err
1171+
}
1172+
for _, check := range ccr.Items {
1173+
annotations := check.GetAnnotations()
1174+
if annotations == nil {
1175+
return fmt.Errorf("Check %s has no annotations", check.Name)
1176+
}
1177+
if annotations[compv1alpha1.LastScannedTimestampAnnotation] == "" {
1178+
return fmt.Errorf("Check %s has no timestamp annotation", check.Name)
1179+
}
1180+
timestamp, err := time.Parse(time.RFC3339, annotations[compv1alpha1.LastScannedTimestampAnnotation])
1181+
if err != nil {
1182+
return fmt.Errorf("Error parsing timestamp for check %s: %v", check.Name, err)
1183+
}
1184+
if timestamp.Before(cs.Status.StartTimestamp.Time) || timestamp.After(cs.Status.EndTimestamp.Time) {
1185+
return fmt.Errorf("Timestamp for check %s is not within the expected range: %v", check.Name, timestamp)
1186+
}
1187+
}
1188+
return nil
1189+
}
1190+
11541191
// AssertScanGUIDMatches checks if the scan has the expected GUID
11551192
func (f *Framework) AssertScanGUIDMatches(name, namespace, expectedGUID string) error {
11561193
cs := &compv1alpha1.ComplianceScan{}

tests/e2e/parallel/main_test.go

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -651,6 +651,63 @@ func TestSingleScanSucceeds(t *testing.T) {
651651
}
652652
}
653653

654+
func TestSingleScanTimestamps(t *testing.T) {
655+
t.Parallel()
656+
f := framework.Global
657+
658+
scanName := framework.GetObjNameFromTest(t)
659+
testScan := &compv1alpha1.ComplianceScan{
660+
ObjectMeta: metav1.ObjectMeta{
661+
Name: scanName,
662+
Namespace: f.OperatorNamespace,
663+
},
664+
Spec: compv1alpha1.ComplianceScanSpec{
665+
Profile: "xccdf_org.ssgproject.content_profile_moderate",
666+
Content: framework.RhcosContentFile,
667+
Rule: "xccdf_org.ssgproject.content_rule_no_netrc_files",
668+
ComplianceScanSettings: compv1alpha1.ComplianceScanSettings{
669+
Debug: true,
670+
},
671+
},
672+
}
673+
// use Context's create helper to create the object and add a cleanup function for the new object
674+
err := f.Client.Create(context.TODO(), testScan, nil)
675+
if err != nil {
676+
t.Fatalf("failed to create scan %s: %s", scanName, err)
677+
}
678+
defer f.Client.Delete(context.TODO(), testScan)
679+
680+
err = f.WaitForScanStatus(f.OperatorNamespace, scanName, compv1alpha1.PhaseDone)
681+
if err != nil {
682+
t.Fatal(err)
683+
}
684+
685+
// assertComplianceCheckResultTimestamps checks that the timestamps are set
686+
// and that they are set to the same value of startTimestamp of the scan
687+
err = f.AssertComplianceCheckResultTimestamps(scanName, f.OperatorNamespace)
688+
if err != nil {
689+
t.Fatal(err)
690+
}
691+
692+
// rerun the scan
693+
err = f.ReRunScan(scanName, f.OperatorNamespace)
694+
if err != nil {
695+
t.Fatal(err)
696+
}
697+
err = f.WaitForScanStatus(f.OperatorNamespace, scanName, compv1alpha1.PhaseDone)
698+
if err != nil {
699+
t.Fatal(err)
700+
}
701+
702+
// assertComplianceCheckResultTimestamps checks that the timestamps are set
703+
// and that they are set to the same value of startTimestamp of the scan
704+
err = f.AssertComplianceCheckResultTimestamps(scanName, f.OperatorNamespace)
705+
if err != nil {
706+
t.Fatal(err)
707+
}
708+
709+
}
710+
654711
func TestScanProducesRemediations(t *testing.T) {
655712
t.Parallel()
656713
f := framework.Global

0 commit comments

Comments
 (0)