Copyright 2020 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 server
import (
"encoding/json"
"net/http"
"time"
"github.com/rs/zerolog/log"
"github.com/RedHatInsights/insights-operator-utils/generators"
"github.com/RedHatInsights/insights-operator-utils/responses"
"github.com/RedHatInsights/insights-results-aggregator/storage"
"github.com/RedHatInsights/insights-results-aggregator/types"
)
const accountStr = "account"
|
disableRuleForCluster disables a rule for specified cluster, excluding it from reports
|
func ( server * HTTPServer ) disableRuleForCluster ( writer http . ResponseWriter , request * http . Request ) {
server . toggleRuleForCluster ( writer , request , storage . RuleToggleDisable )
}
|
enableRuleForCluster enables a previously disabled rule, showing it on reports again
|
func ( server * HTTPServer ) enableRuleForCluster ( writer http . ResponseWriter , request * http . Request ) {
server . toggleRuleForCluster ( writer , request , storage . RuleToggleEnable )
}
|
toggleRuleForCluster contains shared functionality for enable/disable
|
func ( server * HTTPServer ) toggleRuleForCluster ( writer http . ResponseWriter , request * http . Request , toggleRule storage . RuleToggle ) {
clusterID , ruleID , errorKey , successful := server . readClusterRuleParams ( writer , request )
if ! successful {
|
everything has been handled already
|
return
}
successful = server . checkUserClusterPermissions ( writer , request , clusterID )
if ! successful {
|
everything has been handled already
|
return
}
orgID , successful := readOrgID ( writer , request )
if ! successful {
|
everything has been handled already
|
return
}
err := server . Storage . ToggleRuleForCluster ( clusterID , ruleID , errorKey , orgID , toggleRule )
if err != nil {
log . Error ( ) . Err ( err ) . Msg ( "Unable to toggle rule for selected cluster" )
handleServerError ( writer , err )
return
}
err = responses . SendOK ( writer , responses . BuildOkResponse ( ) )
if err != nil {
log . Error ( ) . Err ( err ) . Msg ( responseDataError )
}
}
|
listOfDisabledRules returns list of rules disabled for given organization
|
func ( server HTTPServer ) listOfDisabledRules ( writer http . ResponseWriter , request * http . Request ) {
log . Info ( ) . Msg ( "Lisf of disabled rules" )
|
retrieve org ID
|
orgID , successful := readOrgID ( writer , request )
if ! successful {
|
everything has been handled already
|
return
}
log . Info ( ) . Int ( orgIDStr , int ( orgID ) ) . Msg ( "disabled rules for org_id" )
|
try to read list of disabled rules by an organization from database
|
disabledRules , err := server . Storage . ListOfDisabledRules ( orgID )
if err != nil {
log . Error ( ) . Err ( err ) . Msg ( "Unable to read list of disabled rules" )
handleServerError ( writer , err )
return
}
log . Info ( ) . Int ( "disabled rules" , len ( disabledRules ) ) . Msg ( "list of disabled rules" )
|
try to send JSON payload to the client in a HTTP response
|
err = responses . SendOK ( writer ,
responses . BuildOkResponseWithData ( "rules" , disabledRules ) )
if err != nil {
log . Error ( ) . Err ( err ) . Msg ( responseDataError )
}
}
|
listOfReasons returns list of reasons why rule(s) have been disabled from an
account
|
func ( server HTTPServer ) listOfReasons ( writer http . ResponseWriter , request * http . Request ) {
log . Info ( ) . Msg ( "Lisf of reasons" )
|
retrieve account (user) ID
|
userID , successful := readUserID ( writer , request )
if ! successful {
|
everything has been handled already
|
return
}
log . Info ( ) . Str ( accountStr , string ( userID ) ) . Msg ( "reasons for disabling rules" )
|
try to read list of reasons by an account/user from database
|
reasons , err := server . Storage . ListOfReasons ( userID )
if err != nil {
log . Error ( ) . Err ( err ) . Msg ( "Unable to read list of reasons" )
handleServerError ( writer , err )
return
}
log . Info ( ) . Int ( "reasons" , len ( reasons ) ) . Msg ( "list of reasons" )
|
try to send JSON payload to the client in a HTTP response
|
err = responses . SendOK ( writer ,
responses . BuildOkResponseWithData ( "reasons" , reasons ) )
if err != nil {
log . Error ( ) . Err ( err ) . Msg ( responseDataError )
}
}
|
listOfDisabledRulesForClusters returns list of rules disabled from an organization for given clusters
|
func ( server HTTPServer ) listOfDisabledRulesForClusters ( writer http . ResponseWriter , request * http . Request ) {
|
Extract org_id from URL
|
orgID , ok := readOrgID ( writer , request )
if ! ok {
|
everything has been handled
|
return
}
log . Info ( ) . Int ( orgIDStr , int ( orgID ) ) . Msg ( "listOfDisabledRulesForClusters" )
var listOfClusters [ ] string
err := json . NewDecoder ( request . Body ) . Decode ( & listOfClusters )
if err != nil {
handleServerError ( writer , err )
return
}
log . Info ( ) . Int ( orgIDStr , int ( orgID ) ) . Msgf ( "listOfDisabledRulesForClusters number of clusters: %d" , len ( listOfClusters ) )
|
try to read list of disabled rules by an organization from database for given list of clusters
|
disabledRules , err := server . Storage . ListOfDisabledRulesForClusters ( listOfClusters , orgID )
if err != nil {
log . Error ( ) . Err ( err ) . Msg ( "Unable to read list of disabled rules" )
handleServerError ( writer , err )
return
}
log . Info ( ) . Int ( orgIDStr , int ( orgID ) ) . Int ( "#disabled rules" , len ( disabledRules ) ) . Msg ( "listOfDisabledRulesForClusters" )
|
try to send JSON payload to the client in a HTTP response
|
err = responses . SendOK ( writer ,
responses . BuildOkResponseWithData ( "rules" , disabledRules ) )
if err != nil {
log . Error ( ) . Err ( err ) . Msg ( responseDataError )
}
}
|
listOfDisabledClusters returns list of clusters disabled for a rule and user
|
func ( server HTTPServer ) listOfDisabledClusters ( writer http . ResponseWriter , request * http . Request ) {
log . Info ( ) . Msg ( "Lisf of disabled clusters" )
orgID , successful := readOrgID ( writer , request )
if ! successful {
|
everything has been handled already
|
return
}
log . Info ( ) . Int ( orgIDStr , int ( orgID ) ) . Msg ( "disabled clusters for organization" )
ruleID , successful := readRuleID ( writer , request )
if ! successful {
|
everything has been handled already
|
return
}
errorKey , successful := readErrorKey ( writer , request )
if ! successful {
|
everything has been handled already
|
return
}
log . Info ( ) . Int ( orgIDStr , int ( orgID ) ) . Msgf ( "disabled clusters for rule ID %v|%v" , ruleID , errorKey )
|
get disabled rules from DB
|
disabledClusters , err := server . Storage . ListOfDisabledClusters ( orgID , ruleID , errorKey )
if err != nil {
log . Error ( ) . Err ( err ) . Msg ( "Unable to read list of disabled clusters" )
handleServerError ( writer , err )
return
}
log . Info ( ) . Int ( orgIDStr , int ( orgID ) ) . Int ( "number of disabled clusters" , len ( disabledClusters ) ) . Msg ( "list of disabled clusters" )
err = responses . SendOK ( writer , responses . BuildOkResponseWithData ( "clusters" , disabledClusters ) )
if err != nil {
log . Error ( ) . Err ( err ) . Msg ( responseDataError )
}
}
|
getRuleToggleMapForCluster retrieves list of disabled rules and returns a map of rule_ids
with disabled status.
|
func ( server HTTPServer ) getRuleToggleMapForCluster (
clusterName types . ClusterName ,
orgID types . OrgID ,
) ( map [ types . RuleID ] bool , error ) {
toggleMap := make ( map [ types . RuleID ] bool )
disabledRules , err := server . Storage . ListOfDisabledRules ( orgID )
if err != nil {
return toggleMap , err
}
for _ , rule := range disabledRules {
if rule . ClusterID != clusterName {
continue
}
compositeRuleID , err := generators . GenerateCompositeRuleID ( types . RuleFQDN ( rule . RuleID ) , rule . ErrorKey )
if err != nil {
log . Error ( ) . Err ( err ) . Msgf ( "error generating composite rule ID for rule [%+v]" , rule )
continue
}
toggleMap [ compositeRuleID ] = true
}
return toggleMap , nil
}
|
getFeedbackAndTogglesOnRules fills in rule toggles and user feedbacks on the rule reports
|
func ( server HTTPServer ) getFeedbackAndTogglesOnRules (
clusterName types . ClusterName ,
userID types . UserID ,
orgID types . OrgID ,
rules [ ] types . RuleOnReport ,
) ( [ ] types . RuleOnReport , error ) {
togglesRules , err := server . getRuleToggleMapForCluster ( clusterName , orgID )
if err != nil {
log . Error ( ) . Err ( err ) . Msg ( "Unable to retrieve disabled status from database" )
return nil , err
}
feedbacks , err := server . Storage . GetUserFeedbackOnRules ( clusterName , rules , userID )
if err != nil {
log . Error ( ) . Err ( err ) . Msg ( "Unable to retrieve feedback results from database" )
return nil , err
}
disableFeedbacks , err := server . Storage . GetUserDisableFeedbackOnRules ( clusterName , rules , userID )
if err != nil {
log . Error ( ) . Err ( err ) . Msg ( "Unable to retrieve disable feedback results from database" )
return nil , err
}
for i := range rules {
ruleID := rules [ i ] . Module
compositeRuleID , err := generators . GenerateCompositeRuleID ( types . RuleFQDN ( ruleID ) , rules [ i ] . ErrorKey )
if err != nil {
log . Error ( ) . Err ( err ) . Msgf ( "error generating composite rule ID for rule [%+v]" , rules [ i ] )
return nil , err
}
if vote , found := feedbacks [ ruleID ] ; found {
rules [ i ] . UserVote = vote
} else {
rules [ i ] . UserVote = types . UserVoteNone
}
if disabled , found := togglesRules [ compositeRuleID ] ; found {
rules [ i ] . Disabled = disabled
} else {
rules [ i ] . Disabled = false
}
if disableFeedback , found := disableFeedbacks [ ruleID ] ; found {
rules [ i ] . DisableFeedback = disableFeedback . Message
rules [ i ] . DisabledAt = types . Timestamp ( disableFeedback . UpdatedAt . Format ( time . RFC3339 ) )
}
}
return rules , nil
}
func ( server HTTPServer ) saveDisableFeedback ( writer http . ResponseWriter , request * http . Request ) {
clusterID , ruleID , errorKey , successful := server . readClusterRuleParams ( writer , request )
if ! successful {
|
everything has been handled already
|
return
}
userID , successful := readUserID ( writer , request )
if ! successful {
|
everything has been handled already
|
return
}
orgID , successful := readOrgID ( writer , request )
if ! successful {
|
everything has been handled already
|
return
}
successful = server . checkUserClusterPermissions ( writer , request , clusterID )
if ! successful {
|
everything has been handled already
|
return
}
feedback , err := server . getFeedbackMessageFromBody ( request )
if err != nil {
handleServerError ( writer , err )
return
}
err = server . Storage . AddFeedbackOnRuleDisable ( clusterID , ruleID , errorKey , orgID , userID , feedback )
if err != nil {
handleServerError ( writer , err )
return
}
err = responses . SendOK ( writer , responses . BuildOkResponseWithData (
"message" , feedback ,
) )
if err != nil {
log . Error ( ) . Err ( err ) . Msg ( responseDataError )
}
}
|
getFeedbackAndTogglesOnRule
|
func ( server HTTPServer ) getFeedbackAndTogglesOnRule (
clusterName types . ClusterName ,
userID types . UserID ,
rule types . RuleOnReport ,
) types . RuleOnReport {
ruleToggle , err := server . Storage . GetFromClusterRuleToggle ( clusterName , rule . Module )
if err != nil {
log . Warn ( ) . Err ( err ) . Msg ( "Rule toggle was not found" )
rule . Disabled = false
} else {
rule . Disabled = ruleToggle . Disabled == storage . RuleToggleDisable
rule . DisabledAt = types . Timestamp ( ruleToggle . DisabledAt . Time . UTC ( ) . Format ( time . RFC3339 ) )
}
disableFeedback , err := server . Storage . GetUserFeedbackOnRuleDisable ( clusterName , rule . Module , rule . ErrorKey , userID )
if err != nil {
log . Warn ( ) . Err ( err ) . Msg ( "Feedback for rule was not found" )
rule . DisableFeedback = ""
} else {
log . Info ( ) . Msgf ( "feedback Message: '%v'" , disableFeedback . Message )
rule . DisableFeedback = disableFeedback . Message
}
userVote , err := server . Storage . GetUserFeedbackOnRule ( clusterName , rule . Module , rule . ErrorKey , userID )
if err != nil {
log . Warn ( ) . Err ( err ) . Msg ( "User vote for rule was not found" )
rule . UserVote = types . UserVoteNone
} else {
rule . UserVote = userVote . UserVote
}
return rule
}
|
enableRuleSystemWide method re-enables a rule for all clusters
|
func ( server HTTPServer ) enableRuleSystemWide ( writer http . ResponseWriter , request * http . Request ) {
log . Info ( ) . Msg ( "enableRuleSystemWide" )
|
read unique rule+user selector
|
selector , successful := readSystemWideRuleSelectors ( writer , request )
if ! successful {
|
everything has been handled
|
return
}
|
try to enable rule
|
err := server . Storage . EnableRuleSystemWide (
selector . OrgID , selector . RuleID , selector . ErrorKey ,
)
|
handle any storage error
|
if err != nil {
handleServerError ( writer , err )
return
}
|
try to send JSON payload to the client in a HTTP response
|
err = responses . SendOK ( writer , responses . BuildOkResponseWithData (
"status" , "rule enabled" ,
) )
if err != nil {
log . Error ( ) . Err ( err ) . Msg ( responseDataError )
}
}
|
disableRuleSystemWide method disables a rule for all clusters
|
func ( server HTTPServer ) disableRuleSystemWide ( writer http . ResponseWriter , request * http . Request ) {
log . Info ( ) . Msg ( "disableRuleSystemWide" )
|
read unique rule+user selector
|
selector , successful := readSystemWideRuleSelectors ( writer , request )
if ! successful {
|
everything has been handled
|
return
}
|
read justification from request body
|
justification , err := server . getJustificationFromBody ( request )
if err != nil {
handleServerError ( writer , err )
return
}
|
try to disable rule
|
err = server . Storage . DisableRuleSystemWide (
selector . OrgID , selector . RuleID , selector . ErrorKey , justification ,
)
|
handle any storage error
|
if err != nil {
handleServerError ( writer , err )
return
}
|
try to send JSON payload to the client in a HTTP response
|
err = responses . SendOK ( writer , responses . BuildOkResponseWithData (
"justification" , justification ,
) )
if err != nil {
log . Error ( ) . Err ( err ) . Msg ( responseDataError )
}
}
|
updateRuleSystemWide method updates disable justification of a rule for all clusters
|
func ( server HTTPServer ) updateRuleSystemWide ( writer http . ResponseWriter , request * http . Request ) {
log . Info ( ) . Msg ( "updateRuleSystemWide" )
|
read unique rule+user selector
|
selector , successful := readSystemWideRuleSelectors ( writer , request )
if ! successful {
|
everything has been handled
|
return
}
|
read justification from request body
|
justification , err := server . getJustificationFromBody ( request )
if err != nil {
handleServerError ( writer , err )
return
}
|
try to update rule disable justification
|
err = server . Storage . UpdateDisabledRuleJustification (
selector . OrgID , selector . RuleID , selector . ErrorKey , justification ,
)
|
handle any storage error
|
if err != nil {
handleServerError ( writer , err )
return
}
|
try to send JSON payload to the client in a HTTP response
|
err = responses . SendOK ( writer , responses . BuildOkResponseWithData (
"justification" , justification ,
) )
if err != nil {
log . Error ( ) . Err ( err ) . Msg ( responseDataError )
}
}
|
readRuleSystemWide method returns information about rule that has been
disabled for all systems. In case such rule does not exists or was not
disabled, HTTP code 404/Not Found is returned instead
|
func ( server HTTPServer ) readRuleSystemWide ( writer http . ResponseWriter , request * http . Request ) {
log . Info ( ) . Msg ( "readRuleSystemWide" )
|
read unique rule+user selector
|
selector , successful := readSystemWideRuleSelectors ( writer , request )
if ! successful {
|
everything has been handled
|
return
}
|
try to retrieve disabled rule from storage
|
disabledRule , found , err := server . Storage . ReadDisabledRule (
selector . OrgID , selector . RuleID , selector . ErrorKey ,
)
|
handle any storage error
|
if err != nil {
log . Error ( ) . Err ( err ) . Msg ( "System-wide rule disable not found" )
handleServerError ( writer , err )
return
}
|
handle situation when rule was not disabled ie. found in the storage
|
if ! found {
const message = "Rule was not disabled"
log . Info ( ) . Msg ( message )
err := responses . SendNotFound ( writer , message )
if err != nil {
log . Error ( ) . Err ( err ) . Msg ( "Unable to send response data" )
}
return
}
|
try to send JSON payload to the client in a HTTP response
|
err = responses . SendOK ( writer ,
responses . BuildOkResponseWithData ( "disabledRule" , disabledRule ) )
if err != nil {
log . Error ( ) . Err ( err ) . Msg ( responseDataError )
}
}
|
listOfDisabledRulesSystemWide returns a list of rules disabled from current account
|
func ( server HTTPServer ) listOfDisabledRulesSystemWide ( writer http . ResponseWriter , request * http . Request ) {
log . Info ( ) . Msg ( "listOfDisabledRulesSystemWide" )
orgID , successful := readOrgID ( writer , request )
if ! successful {
return
}
|
try to retrieve list of disabled rules from storage
|
disabledRules , err := server . Storage . ListOfSystemWideDisabledRules ( orgID )
|
handle any storage error
|
if err != nil {
log . Error ( ) . Err ( err ) . Msg ( "System-wide rules disable not found" )
handleServerError ( writer , err )
return
}
|
try to send JSON payload to the client in a HTTP response
|
err = responses . SendOK ( writer ,
responses . BuildOkResponseWithData ( "disabledRules" , disabledRules ) )
if err != nil {
log . Error ( ) . Err ( err ) . Msg ( responseDataError )
}
}
|
SystemWideRuleSelector contains all four fields that are used to select
system-wide rule disable
|
type SystemWideRuleSelector struct {
OrgID types . OrgID
RuleID types . RuleID
ErrorKey types . ErrorKey
}
|
readSystemWideRuleSelectors helper function read all four parameters that
are used to select system-wide rule disable
|
func readSystemWideRuleSelectors ( writer http . ResponseWriter , request * http . Request ) ( SystemWideRuleSelector , bool ) {
var selector = SystemWideRuleSelector { }
var successful bool
selector . OrgID , successful = readOrgID ( writer , request )
if ! successful {
return selector , false
}
selector . RuleID , successful = readRuleID ( writer , request )
if ! successful {
return selector , false
}
selector . ErrorKey , successful = readErrorKey ( writer , request )
if ! successful {
return selector , false
}
log . Info ( ) . Msgf (
"System-wide disabled rule selector: org: %v rule ID: %v error key: %v" ,
selector . OrgID , selector . RuleID , selector . ErrorKey ,
)
return selector , true
}
|