func recordChangeSetForLog()

in pkg/source/common/k8s_audit/recorder/statusrecorder/recorder.go [45:136]


func recordChangeSetForLog(ctx context.Context, resourcePath string, log *types.AuditLogParserInput, prevStatus *model.K8sResourceContainingStatus, cs *history.ChangeSet, builder *history.Builder) (*model.K8sResourceContainingStatus, error) {
	var resourceContainingStatus model.K8sResourceContainingStatus
	err := log.ResourceBodyReader.ReadReflect("", &resourceContainingStatus)
	if err != nil {
		return prevStatus, err
	}
	if resourceContainingStatus.Status == nil || len(resourceContainingStatus.Status.Conditions) == 0 {
		// This resource has no status field or no conditions in status field
		return &resourceContainingStatus, nil
	}

	deletionStatus := manifestutil.ParseDeletionStatus(ctx, log.ResourceBodyReader, log.Operation)
	isDeletionRequest := deletionStatus == manifestutil.DeletionStatusDeleted
	for _, condition := range resourceContainingStatus.Status.Conditions {
		lastTransitionTime, err := time.Parse(time.RFC3339, condition.LastTransitionTime)
		if err != nil {
			continue
		}
		conditionTime := lastTransitionTime
		lastHeartbeatTime, err := time.Parse(time.RFC3339, condition.LastHeartbeatTime)
		if err == nil && lastHeartbeatTime.Sub(conditionTime) > 0 {
			conditionTime = lastHeartbeatTime
		}
		lastProbeTime, err := time.Parse(time.RFC3339, condition.LastProbeTime)
		if err == nil && lastProbeTime.Sub(lastHeartbeatTime) > 0 {
			conditionTime = lastProbeTime
		}
		// Ignore if the transition time was older than the last revision
		statusPath := resourcepath.Status(resourcepath.FromK8sOperation(*log.Operation), condition.Type)
		if log.Operation.SubResourceName != "" {
			parentOp := model.KubernetesObjectOperation{
				APIVersion: log.Operation.APIVersion,
				PluralKind: log.Operation.PluralKind,
				Namespace:  log.Operation.Namespace,
				Name:       log.Operation.Name,
				Verb:       log.Operation.Verb,
			}
			statusPath = resourcepath.Status(resourcepath.FromK8sOperation(parentOp), condition.Type)
		}
		tb := builder.GetTimelineBuilder(statusPath.Path)
		latest := tb.GetLatestRevision()
		latestTime := time.Time{}
		if latest != nil {
			latestTime = latest.ChangeTime
		} else {
			creationTime := manifestutil.ParseCreationTime(log.ResourceBodyReader, time.Time{})

			if err == nil && conditionTime.Sub(creationTime) != 0 {
				cs.RecordRevision(statusPath, &history.StagingResourceRevision{
					Verb:       enum.RevisionVerbStatusUnknown,
					Body:       "# status is unknown but existence is inferred from the later log.",
					Partial:    false,
					Inferred:   true,
					Requestor:  "",
					ChangeTime: creationTime,
					State:      enum.RevisionStateConditionUnknown,
				})
			}
		}
		prevCondition := lookUpConditionFromStatus(prevStatus, condition.Type)
		latestUpdateTimeIsLaterThanPrevRevision := conditionTime.Sub(latestTime) > 0 // The previous revision is older than the timestamps written on the condition. This should be recorded as the change even if the reason and state is same to show the heart beat timing.
		conditionUpdated := prevCondition != nil && (prevCondition.Status != condition.Status || prevCondition.Message != condition.Message || prevCondition.Reason != condition.Reason)
		if latestUpdateTimeIsLaterThanPrevRevision || conditionUpdated {
			conditionYaml, err := goyaml.Marshal(condition)
			if err != nil {
				continue
			}
			cs.RecordRevision(statusPath, &history.StagingResourceRevision{
				Verb:       conditionStateToRevisionVerb(condition.Status),
				Body:       string(conditionYaml),
				Partial:    false,
				Requestor:  "",
				ChangeTime: conditionTime,
				State:      conditionStateToRevisionState(condition.Status),
			})
		}
		if isDeletionRequest {
			cs.RecordRevision(statusPath, &history.StagingResourceRevision{
				Verb:       enum.RevisionVerbDelete,
				Body:       "",
				Partial:    false,
				Requestor:  log.Requestor,
				ChangeTime: log.Log.Timestamp(),
				State:      enum.RevisionStateDeleted,
			})
		}
	}
	if isDeletionRequest {
		return nil, nil
	}
	return &resourceContainingStatus, nil
}