redis_storage.go

Copyright 2023 Red Hat, Inc

Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at

 http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.

package
storage
import
(
"context"
"database/sql"
"fmt"
"strings"
"time"
"github.com/Shopify/sarama"
"github.com/rs/zerolog/log"
"github.com/RedHatInsights/insights-operator-utils/redis"
ctypes
"github.com/RedHatInsights/insights-results-types"
"github.com/RedHatInsights/insights-results-aggregator/metrics"
"github.com/RedHatInsights/insights-results-aggregator/migration"
"github.com/RedHatInsights/insights-results-aggregator/types"
)

ExpirationDuration set to keys stored into Redis

const
ExpirationDuration
=
"24h"

DefaultValue represents value that is stored under some key. We don't care about the value right now so it can be empty.

const
DefaultValue
=
""

RedisStorage represents a storage which does nothing (for benchmarking without a storage)

type
RedisStorage
struct
{
Client
redis
.
Client
Expiration
time
.
Duration
}

Init method initializes Redis storage

func
(
storage
*
RedisStorage
)
Init
(
)
error
{
log
.
Info
(
)
.
Msg
(
"Redis database Init()"
)

try to parse expiration duration in runtime

	
expiration
,
err
:=
time
.
ParseDuration
(
ExpirationDuration
)
if
err
!=
nil
{
log
.
Error
(
)
.
Err
(
err
)
.
Str
(
"time"
,
ExpirationDuration
)
.
Msg
(
"Error parsing time"
)
return
err
}
storage
.
Expiration
=
expiration
return
nil
}

Close noop

func
(
storage
*
RedisStorage
)
Close
(
)
error
{
log
.
Info
(
)
.
Msg
(
"Redis database Close()"
)
if
storage
.
Client
.
Connection
!=
nil
{
return
storage
.
Client
.
Connection
.
Close
(
)
}
return
nil
}

GetMigrations noop

func
(
storage
*
RedisStorage
)
GetMigrations
(
)
[
]
migration
.
Migration
{
return
nil
}

GetMaxVersion noop

func
(
storage
*
RedisStorage
)
GetMaxVersion
(
)
migration
.
Version
{
return
migration
.
Version
(
0
)
}

ListOfOrgs noop

func
(
*
RedisStorage
)
ListOfOrgs
(
)
(
[
]
types
.
OrgID
,
error
)
{
return
nil
,
nil
}

ListOfClustersForOrg noop

func
(
*
RedisStorage
)
ListOfClustersForOrg
(
types
.
OrgID
,
time
.
Time
)
(
[
]
types
.
ClusterName
,
error
)
{
return
nil
,
nil
}

ReadReportForCluster noop

func
(
*
RedisStorage
)
ReadReportForCluster
(
types
.
OrgID
,
types
.
ClusterName
)
(
[
]
types
.
RuleOnReport
,
types
.
Timestamp
,
types
.
Timestamp
,
types
.
Timestamp
,
error
)
{
return
[
]
types
.
RuleOnReport
{
}
,
""
,
""
,
""
,
nil
}

ReadReportInfoForCluster noop

func
(
*
RedisStorage
)
ReadReportInfoForCluster
(
types
.
OrgID
,
types
.
ClusterName
)
(
types
.
Version
,
error
)
{
return
""
,
nil
}

ReadClusterVersionsForClusterList noop

func
(
*
RedisStorage
)
ReadClusterVersionsForClusterList
(
types
.
OrgID
,
[
]
string
,
)
(
map
[
types
.
ClusterName
]
types
.
Version
,
error
)
{
return
nil
,
nil
}

ReadSingleRuleTemplateData noop

func
(
*
RedisStorage
)
ReadSingleRuleTemplateData
(
types
.
OrgID
,
types
.
ClusterName
,
types
.
RuleID
,
types
.
ErrorKey
)
(
interface
{
}
,
error
)
{
return
""
,
nil
}

ReadReportForClusterByClusterName noop

func
(
*
RedisStorage
)
ReadReportForClusterByClusterName
(
types
.
ClusterName
,
)
(
[
]
types
.
RuleOnReport
,
types
.
Timestamp
,
error
)
{
return
[
]
types
.
RuleOnReport
{
}
,
""
,
nil
}

WriteReportForCluster method writes rule hits and other information about new report into Redis storage

func
(
storage
*
RedisStorage
)
WriteReportForCluster
(
orgID
types
.
OrgID
,
clusterName
types
.
ClusterName
,
_
types
.
ClusterReport
,
reportItems
[
]
types
.
ReportItem
,
_
time
.
Time
,
gatheredAtTime
time
.
Time
,
storedAtTime
time
.
Time
,
requestID
types
.
RequestID
,
)
error
{

retrieve context

	
ctx
:=
context
.
Background
(
)

construct key to be stored in Redis

	
key
:=
fmt
.
Sprintf
(
"organization:%d:cluster:%s:request:%s"
,
int
(
orgID
)
,
string
(
clusterName
)
,
string
(
requestID
)
)
log
.
Info
(
)
.
Str
(
"key"
,
key
)
.
Msg
(
"Storing key into Redis"
)

try to store key

	
err
:=
storage
.
Client
.
Connection
.
Set
(
ctx
,
key
,
DefaultValue
,
storage
.
Expiration
)
.
Err
(
)
if
err
!=
nil
{
log
.
Error
(
)
.
Err
(
err
)
.
Str
(
"key"
,
key
)
.
Msg
(
"Error storing key into Redis"
)
return
err
}
data
:=
ctypes
.
SimplifiedReport
{
OrgID
:
int
(
orgID
)
,
RequestID
:
string
(
requestID
)
,
ClusterID
:
string
(
clusterName
)
,
ReceivedTimestamp
:
gatheredAtTime
,
ProcessedTimestamp
:
storedAtTime
,
RuleHitsCSV
:
getRuleHitsCSV
(
reportItems
)
,
}

update hash with reports

	
reportsKey
:=
key
+
":reports"
log
.
Info
(
)
.
Str
(
"reportsKey"
,
reportsKey
)
.
Msg
(
"Storing hash into Redis"
)
err
=
storage
.
Client
.
Connection
.
HSet
(
ctx
,
reportsKey
,
data
)
.
Err
(
)
if
err
!=
nil
{
log
.
Error
(
)
.
Err
(
err
)
.
Str
(
"reportsKey"
,
reportsKey
)
.
Msg
(
"Error storing hash into Redis"
)
return
err
}

set EXPIRE on new key

	
err
=
storage
.
Client
.
Connection
.
Expire
(
ctx
,
reportsKey
,
storage
.
Expiration
)
.
Err
(
)
if
err
!=
nil
{
log
.
Error
(
)
.
Err
(
err
)
.
Msg
(
"Error updating expiration (TLL)"
)
return
err
}

everything seems to be ok

	
metrics
.
WrittenReports
.
Inc
(
)
log
.
Info
(
)
.
Msgf
(
"Added data for request %v"
,
requestID
)
return
nil
}

WriteReportInfoForCluster noop

func
(
*
RedisStorage
)
WriteReportInfoForCluster
(
_
types
.
OrgID
,
_
types
.
ClusterName
,
_
[
]
types
.
InfoItem
,
_
time
.
Time
,
)
error
{
return
nil
}

WriteRecommendationsForCluster noop

func
(
*
RedisStorage
)
WriteRecommendationsForCluster
(
types
.
OrgID
,
types
.
ClusterName
,
types
.
ClusterReport
,
types
.
Timestamp
,
)
error
{
return
nil
}

ReportsCount noop

func
(
*
RedisStorage
)
ReportsCount
(
)
(
int
,
error
)
{
return
0
,
nil
}

VoteOnRule noop

func
(
*
RedisStorage
)
VoteOnRule
(
types
.
ClusterName
,
types
.
RuleID
,
types
.
ErrorKey
,
types
.
OrgID
,
types
.
UserID
,
types
.
UserVote
,
string
)
error
{
return
nil
}

AddOrUpdateFeedbackOnRule noop

func
(
*
RedisStorage
)
AddOrUpdateFeedbackOnRule
(
types
.
ClusterName
,
types
.
RuleID
,
types
.
ErrorKey
,
types
.
OrgID
,
types
.
UserID
,
string
,
)
error
{
return
nil
}

AddFeedbackOnRuleDisable noop

func
(
*
RedisStorage
)
AddFeedbackOnRuleDisable
(
types
.
ClusterName
,
types
.
RuleID
,
types
.
ErrorKey
,
types
.
OrgID
,
types
.
UserID
,
string
,
)
error
{
return
nil
}

GetUserFeedbackOnRuleDisable noop

func
(
*
RedisStorage
)
GetUserFeedbackOnRuleDisable
(
types
.
ClusterName
,
types
.
RuleID
,
types
.
ErrorKey
,
types
.
UserID
,
)
(
*
UserFeedbackOnRule
,
error
)
{
return
nil
,
nil
}

GetUserFeedbackOnRule noop

func
(
*
RedisStorage
)
GetUserFeedbackOnRule
(
types
.
ClusterName
,
types
.
RuleID
,
types
.
ErrorKey
,
types
.
UserID
,
)
(
*
UserFeedbackOnRule
,
error
)
{
return
nil
,
nil
}

DeleteReportsForOrg noop

func
(
*
RedisStorage
)
DeleteReportsForOrg
(
types
.
OrgID
)
error
{
return
nil
}

DeleteReportsForCluster noop

func
(
*
RedisStorage
)
DeleteReportsForCluster
(
types
.
ClusterName
)
error
{
return
nil
}

GetRuleByID noop

func
(
*
RedisStorage
)
GetRuleByID
(
types
.
RuleID
)
(
*
types
.
Rule
,
error
)
{
return
nil
,
nil
}

GetOrgIDByClusterID noop

func
(
*
RedisStorage
)
GetOrgIDByClusterID
(
types
.
ClusterName
)
(
types
.
OrgID
,
error
)
{
return
0
,
nil
}

CreateRule noop

func
(
*
RedisStorage
)
CreateRule
(
types
.
Rule
)
error
{
return
nil
}

DeleteRule noop

func
(
*
RedisStorage
)
DeleteRule
(
types
.
RuleID
)
error
{
return
nil
}

CreateRuleErrorKey noop

func
(
*
RedisStorage
)
CreateRuleErrorKey
(
types
.
RuleErrorKey
)
error
{
return
nil
}

DeleteRuleErrorKey noop

func
(
*
RedisStorage
)
DeleteRuleErrorKey
(
types
.
RuleID
,
types
.
ErrorKey
)
error
{
return
nil
}

WriteConsumerError noop

func
(
*
RedisStorage
)
WriteConsumerError
(
*
sarama
.
ConsumerMessage
,
error
)
error
{
return
nil
}

ToggleRuleForCluster noop

func
(
*
RedisStorage
)
ToggleRuleForCluster
(
types
.
ClusterName
,
types
.
RuleID
,
types
.
ErrorKey
,
types
.
OrgID
,
RuleToggle
,
)
error
{
return
nil
}

DeleteFromRuleClusterToggle noop

func
(
*
RedisStorage
)
DeleteFromRuleClusterToggle
(
types
.
ClusterName
,
types
.
RuleID
)
error
{
return
nil
}

GetFromClusterRuleToggle noop

func
(
*
RedisStorage
)
GetFromClusterRuleToggle
(
types
.
ClusterName
,
types
.
RuleID
,
)
(
*
ClusterRuleToggle
,
error
)
{
return
nil
,
nil
}

GetTogglesForRules noop

func
(
*
RedisStorage
)
GetTogglesForRules
(
types
.
ClusterName
,
[
]
types
.
RuleOnReport
,
types
.
OrgID
,
)
(
map
[
types
.
RuleID
]
bool
,
error
)
{
return
nil
,
nil
}

GetUserFeedbackOnRules noop

func
(
*
RedisStorage
)
GetUserFeedbackOnRules
(
types
.
ClusterName
,
[
]
types
.
RuleOnReport
,
types
.
UserID
,
)
(
map
[
types
.
RuleID
]
types
.
UserVote
,
error
)
{
return
nil
,
nil
}

GetRuleWithContent noop

func
(
*
RedisStorage
)
GetRuleWithContent
(
types
.
RuleID
,
types
.
ErrorKey
,
)
(
*
types
.
RuleWithContent
,
error
)
{
return
nil
,
nil
}

GetUserDisableFeedbackOnRules noop

func
(
*
RedisStorage
)
GetUserDisableFeedbackOnRules
(
types
.
ClusterName
,
[
]
types
.
RuleOnReport
,
types
.
UserID
,
)
(
map
[
types
.
RuleID
]
UserFeedbackOnRule
,
error
)
{
return
nil
,
nil
}

DoesClusterExist noop

func
(
*
RedisStorage
)
DoesClusterExist
(
types
.
ClusterName
)
(
bool
,
error
)
{
return
false
,
nil
}

ReadOrgIDsForClusters read organization IDs for given list of cluster names.

func
(
*
RedisStorage
)
ReadOrgIDsForClusters
(
_
[
]
types
.
ClusterName
)
(
[
]
types
.
OrgID
,
error
)
{
return
nil
,
nil
}

ReadReportsForClusters function reads reports for given list of cluster names.

func
(
*
RedisStorage
)
ReadReportsForClusters
(
_
[
]
types
.
ClusterName
)
(
map
[
types
.
ClusterName
]
types
.
ClusterReport
,
error
)
{
return
nil
,
nil
}

ListOfDisabledRules function returns list of all rules disabled from a specified account (noop).

func
(
*
RedisStorage
)
ListOfDisabledRules
(
_
types
.
OrgID
)
(
[
]
ctypes
.
DisabledRule
,
error
)
{
return
nil
,
nil
}

ListOfReasons function returns list of reasons for all rules disabled from a specified account (noop).

func
(
*
RedisStorage
)
ListOfReasons
(
_
types
.
UserID
)
(
[
]
DisabledRuleReason
,
error
)
{
return
nil
,
nil
}

ListOfDisabledClusters function returns list of all clusters disabled for a rule from a specified account (noop).

func
(
*
RedisStorage
)
ListOfDisabledClusters
(
_
types
.
OrgID
,
_
types
.
RuleID
,
_
types
.
ErrorKey
,
)
(
[
]
ctypes
.
DisabledClusterInfo
,
error
)
{
return
nil
,
nil
}

ListOfDisabledRulesForClusters function returns list of disabled rules for given clusters from a specified account (noop).

func
(
*
RedisStorage
)
ListOfDisabledRulesForClusters
(
_
[
]
string
,
_
types
.
OrgID
,
)
(
[
]
ctypes
.
DisabledRule
,
error
)
{
return
nil
,
nil
}

RateOnRule function stores the vote (rating) given by an user to a rule+error key

func
(
*
RedisStorage
)
RateOnRule
(
types
.
OrgID
,
types
.
RuleID
,
types
.
ErrorKey
,
types
.
UserVote
,
)
error
{
return
nil
}

GetRuleRating retrieves rating for given rule and user

func
(
*
RedisStorage
)
GetRuleRating
(
_
types
.
OrgID
,
_
types
.
RuleSelector
,
)
(
ruleRating
types
.
RuleRating
,
err
error
,
)
{
return
}

DisableRuleSystemWide disables the selected rule for all clusters visible to given user

func
(
*
RedisStorage
)
DisableRuleSystemWide
(
_
types
.
OrgID
,
_
types
.
RuleID
,
_
types
.
ErrorKey
,
_
string
,
)
error
{
return
nil
}

EnableRuleSystemWide enables the selected rule for all clusters visible to given user

func
(
*
RedisStorage
)
EnableRuleSystemWide
(
_
types
.
OrgID
,
_
types
.
RuleID
,
_
types
.
ErrorKey
,
)
error
{
return
nil
}

UpdateDisabledRuleJustification change justification for already disabled rule

func
(
*
RedisStorage
)
UpdateDisabledRuleJustification
(
_
types
.
OrgID
,
_
types
.
RuleID
,
_
types
.
ErrorKey
,
_
string
,
)
error
{
return
nil
}

ReadDisabledRule function returns disabled rule (if disabled) from database

func
(
*
RedisStorage
)
ReadDisabledRule
(
_
types
.
OrgID
,
_
types
.
RuleID
,
_
types
.
ErrorKey
,
)
(
ctypes
.
SystemWideRuleDisable
,
bool
,
error
)
{
return
ctypes
.
SystemWideRuleDisable
{
}
,
true
,
nil
}

ListOfSystemWideDisabledRules function returns list of all rules that have been disabled for all clusters by given user

func
(
*
RedisStorage
)
ListOfSystemWideDisabledRules
(
_
types
.
OrgID
,
)
(
[
]
ctypes
.
SystemWideRuleDisable
,
error
)
{
return
nil
,
nil
}

ReadRecommendationsForClusters reads all recommendations from recommendation table for given organization

func
(
*
RedisStorage
)
ReadRecommendationsForClusters
(
_
[
]
string
,
_
types
.
OrgID
,
)
(
ctypes
.
RecommendationImpactedClusters
,
error
)
{
return
nil
,
nil
}

ListOfClustersForOrgSpecificRule returns list of all clusters for given organization that are affected by given rule

func
(
*
RedisStorage
)
ListOfClustersForOrgSpecificRule
(
_
types
.
OrgID
,
_
types
.
RuleSelector
,
_
[
]
string
,
)
(
[
]
ctypes
.
HittingClustersData
,
error
)
{
return
nil
,
nil
}

ReadClusterListRecommendations retrieves cluster IDs and a list of hitting rules for each one

func
(
*
RedisStorage
)
ReadClusterListRecommendations
(
_
[
]
string
,
_
types
.
OrgID
,
)
(
ctypes
.
ClusterRecommendationMap
,
error
)
{
return
nil
,
nil
}

MigrateToLatest migrates the database to the latest available migration version. This must be done before an Init() call.

func
(
*
RedisStorage
)
MigrateToLatest
(
)
error
{
return
nil
}

GetConnection returns db connection(useful for testing)

func
(
*
RedisStorage
)
GetConnection
(
)
*
sql
.
DB
{
return
nil
}

GetDBSchema returns db schema (unused for Redis)

func
(
*
RedisStorage
)
GetDBSchema
(
)
migration
.
Schema
{
return
migration
.
Schema
(
""
)
}

PrintRuleDisableDebugInfo is a temporary helper function used to print form cluster rule toggle related tables

func
(
*
RedisStorage
)
PrintRuleDisableDebugInfo
(
)
{
log
.
Info
(
)
.
Msg
(
"PrintRuleDisableDebugInfo() skipped for Redis storage"
)
}

GetDBDriverType returns db driver type

func
(
*
RedisStorage
)
GetDBDriverType
(
)
types
.
DBDriver
{
return
types
.
DBDriverGeneral
}
func
getRuleHitsCSV
(
reportItems
[
]
types
.
ReportItem
)
string
{

usage of strings.Builder is more efficient than consecutive string concatenation

	
var
output
strings
.
Builder
for
i
,
reportItem
:=
range
reportItems
{

rule separator

		
if
i
>
0
{
output
.
WriteRune
(
','
)
}

strip .report suffix from rule module added automatically by running insights evaluation

		
output
.
WriteString
(
strings
.
TrimSuffix
(
string
(
reportItem
.
Module
)
,
ReportSuffix
)
)
output
.
WriteRune
(
'|'
)
output
.
WriteString
(
string
(
reportItem
.
ErrorKey
)
)
}

convert back to string

	
return
output
.
String
(
)
}