2019-05-13 18:17:28 -07:00
package engine
2019-05-09 22:26:22 -07:00
import (
2020-01-10 11:59:05 -08:00
"fmt"
2019-12-27 15:57:43 -08:00
"reflect"
2019-12-26 15:34:19 -08:00
"strings"
2019-08-19 16:40:10 -07:00
"time"
2019-08-09 16:55:43 -07:00
2019-06-25 18:16:02 -07:00
"github.com/golang/glog"
2019-12-26 15:34:19 -08:00
kyverno "github.com/nirmata/kyverno/pkg/api/kyverno/v1"
2020-01-07 17:06:17 -08:00
"github.com/nirmata/kyverno/pkg/engine/mutate"
"github.com/nirmata/kyverno/pkg/engine/rbac"
2019-12-12 15:02:59 -08:00
"github.com/nirmata/kyverno/pkg/engine/response"
2020-01-10 11:59:05 -08:00
"github.com/nirmata/kyverno/pkg/engine/utils"
2020-01-07 15:13:57 -08:00
"github.com/nirmata/kyverno/pkg/engine/variables"
2020-01-16 11:57:28 -08:00
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
2019-05-09 22:26:22 -07:00
)
2019-12-26 15:34:19 -08:00
const (
PodControllers = "DaemonSet,Deployment,Job,StatefulSet"
PodControllersAnnotation = "pod-policies.kyverno.io/autogen-controllers"
PodTemplateAnnotation = "pod-policies.kyverno.io/autogen-applied"
)
2019-05-13 21:27:47 +03:00
// Mutate performs mutation. Overlay first and then mutation patches
2019-12-12 15:02:59 -08:00
func Mutate ( policyContext PolicyContext ) ( resp response . EngineResponse ) {
2019-08-23 18:34:23 -07:00
startTime := time . Now ( )
2019-11-08 18:57:27 -08:00
policy := policyContext . Policy
2019-11-13 13:13:07 -08:00
resource := policyContext . NewResource
2019-12-12 18:25:54 -08:00
ctx := policyContext . Context
2019-11-08 18:57:27 -08:00
2020-01-16 11:57:28 -08:00
startMutateResultResponse ( & resp , policy , resource )
2019-08-23 18:34:23 -07:00
glog . V ( 4 ) . Infof ( "started applying mutation rules of policy %q (%v)" , policy . Name , startTime )
2020-01-16 11:57:28 -08:00
defer endMutateResultResponse ( & resp , startTime )
2019-08-23 18:34:23 -07:00
incrementAppliedRuleCount := func ( ) {
// rules applied succesfully count
2019-12-12 15:02:59 -08:00
resp . PolicyResponse . RulesAppliedCount ++
2019-08-23 18:34:23 -07:00
}
2019-11-13 17:56:56 -08:00
patchedResource := policyContext . NewResource
2019-08-23 18:34:23 -07:00
for _ , rule := range policy . Spec . Rules {
//TODO: to be checked before calling the resources as well
2019-12-26 15:34:19 -08:00
if ! rule . HasMutate ( ) && ! strings . Contains ( PodControllers , resource . GetKind ( ) ) {
2019-08-23 18:34:23 -07:00
continue
}
2019-11-08 18:57:27 -08:00
2020-01-10 11:59:05 -08:00
if paths := validateGeneralRuleInfoVariables ( ctx , rule ) ; len ( paths ) != 0 {
glog . Infof ( "referenced path not present in rule %s, resource %s/%s/%s, path: %s" , rule . Name , resource . GetKind ( ) , resource . GetNamespace ( ) , resource . GetName ( ) , paths )
resp . PolicyResponse . Rules = append ( resp . PolicyResponse . Rules ,
2020-01-10 17:15:44 -08:00
newPathNotPresentRuleResponse ( rule . Name , utils . Mutation . String ( ) , fmt . Sprintf ( "path not present in rule info: %s" , paths ) ) )
2020-01-10 11:59:05 -08:00
continue
}
2019-11-11 21:23:26 -08:00
startTime := time . Now ( )
2020-01-07 10:33:28 -08:00
if ! rbac . MatchAdmissionInfo ( rule , policyContext . AdmissionInfo ) {
2019-11-11 18:52:26 -08:00
glog . V ( 3 ) . Infof ( "rule '%s' cannot be applied on %s/%s/%s, admission permission: %v" ,
2019-11-08 18:57:27 -08:00
rule . Name , resource . GetKind ( ) , resource . GetNamespace ( ) , resource . GetName ( ) , policyContext . AdmissionInfo )
continue
}
2019-11-11 21:23:26 -08:00
glog . V ( 4 ) . Infof ( "Time: Mutate matchAdmissionInfo %v" , time . Since ( startTime ) )
2019-11-08 18:57:27 -08:00
2019-08-23 18:34:23 -07:00
// check if the resource satisfies the filter conditions defined in the rule
//TODO: this needs to be extracted, to filter the resource so that we can avoid passing resources that
// dont statisfy a policy rule resource description
ok := MatchesResourceDescription ( resource , rule )
if ! ok {
glog . V ( 4 ) . Infof ( "resource %s/%s does not satisfy the resource description for the rule " , resource . GetNamespace ( ) , resource . GetName ( ) )
continue
}
2020-01-07 15:13:57 -08:00
// evaluate pre-conditions
if ! variables . EvaluateConditions ( ctx , rule . Conditions ) {
glog . V ( 4 ) . Infof ( "resource %s/%s does not satisfy the conditions for the rule " , resource . GetNamespace ( ) , resource . GetName ( ) )
continue
}
2019-08-23 18:34:23 -07:00
// Process Overlay
if rule . Mutation . Overlay != nil {
2019-12-12 15:02:59 -08:00
var ruleResponse response . RuleResponse
2020-01-07 17:06:17 -08:00
ruleResponse , patchedResource = mutate . ProcessOverlay ( ctx , rule , patchedResource )
2020-01-09 12:24:37 -08:00
if ruleResponse . Success == true {
// - variable substitution path is not present
if ruleResponse . PathNotPresent {
glog . V ( 4 ) . Infof ( ruleResponse . Message )
resp . PolicyResponse . Rules = append ( resp . PolicyResponse . Rules , ruleResponse )
continue
}
// - overlay pattern does not match the resource conditions
if ruleResponse . Patches == nil {
glog . V ( 4 ) . Infof ( ruleResponse . Message )
continue
}
2019-11-13 17:56:56 -08:00
glog . Infof ( "Mutate overlay in rule '%s' successfully applied on %s/%s/%s" , rule . Name , resource . GetKind ( ) , resource . GetNamespace ( ) , resource . GetName ( ) )
2019-08-23 18:34:23 -07:00
}
2019-11-13 17:56:56 -08:00
2019-12-12 15:02:59 -08:00
resp . PolicyResponse . Rules = append ( resp . PolicyResponse . Rules , ruleResponse )
2019-08-23 18:34:23 -07:00
incrementAppliedRuleCount ( )
}
// Process Patches
if rule . Mutation . Patches != nil {
2019-12-12 15:02:59 -08:00
var ruleResponse response . RuleResponse
2020-01-07 17:06:17 -08:00
ruleResponse , patchedResource = mutate . ProcessPatches ( rule , patchedResource )
2019-11-11 21:03:34 -08:00
glog . Infof ( "Mutate patches in rule '%s' successfully applied on %s/%s/%s" , rule . Name , resource . GetKind ( ) , resource . GetNamespace ( ) , resource . GetName ( ) )
2019-12-12 15:02:59 -08:00
resp . PolicyResponse . Rules = append ( resp . PolicyResponse . Rules , ruleResponse )
2019-08-23 18:34:23 -07:00
incrementAppliedRuleCount ( )
}
2019-12-26 15:34:19 -08:00
// insert annotation to podtemplate if resource is pod controller
2019-12-27 15:57:43 -08:00
// skip inserting on existing resource
2020-01-08 10:44:41 -08:00
if reflect . DeepEqual ( policyContext . AdmissionInfo , kyverno . RequestInfo { } ) {
2019-12-27 15:57:43 -08:00
continue
}
2019-12-26 15:34:19 -08:00
if strings . Contains ( PodControllers , resource . GetKind ( ) ) {
var ruleResponse response . RuleResponse
2020-01-07 17:06:17 -08:00
ruleResponse , patchedResource = mutate . ProcessOverlay ( ctx , podTemplateRule , patchedResource )
2019-12-26 15:34:19 -08:00
if ! ruleResponse . Success {
2019-12-26 18:41:14 -08:00
glog . Errorf ( "Failed to insert annotation to podTemplate of %s/%s/%s: %s" , resource . GetKind ( ) , resource . GetNamespace ( ) , resource . GetName ( ) , ruleResponse . Message )
2019-12-26 15:34:19 -08:00
continue
}
2019-12-27 15:57:43 -08:00
if ruleResponse . Success && ruleResponse . Patches != nil {
2019-12-27 14:59:12 -08:00
glog . V ( 2 ) . Infof ( "Inserted annotation to podTemplate of %s/%s/%s: %s" , resource . GetKind ( ) , resource . GetNamespace ( ) , resource . GetName ( ) , ruleResponse . Message )
2019-12-26 18:41:14 -08:00
resp . PolicyResponse . Rules = append ( resp . PolicyResponse . Rules , ruleResponse )
}
2019-12-26 15:34:19 -08:00
}
2019-08-23 18:34:23 -07:00
}
// send the patched resource
2019-12-12 15:02:59 -08:00
resp . PatchedResource = patchedResource
return resp
2019-08-23 18:34:23 -07:00
}
2019-12-26 15:34:19 -08:00
2020-01-16 11:57:28 -08:00
func startMutateResultResponse ( resp * response . EngineResponse , policy kyverno . ClusterPolicy , resource unstructured . Unstructured ) {
// set policy information
resp . PolicyResponse . Policy = policy . Name
// resource details
resp . PolicyResponse . Resource . Name = resource . GetName ( )
resp . PolicyResponse . Resource . Namespace = resource . GetNamespace ( )
resp . PolicyResponse . Resource . Kind = resource . GetKind ( )
resp . PolicyResponse . Resource . APIVersion = resource . GetAPIVersion ( )
2020-01-16 14:29:44 -08:00
// TODO(shuting): set response with mutationFailureAction
2020-01-16 11:57:28 -08:00
}
func endMutateResultResponse ( resp * response . EngineResponse , startTime time . Time ) {
resp . PolicyResponse . ProcessingTime = time . Since ( startTime )
glog . V ( 4 ) . Infof ( "finished applying mutation rules policy %v (%v)" , resp . PolicyResponse . Policy , resp . PolicyResponse . ProcessingTime )
glog . V ( 4 ) . Infof ( "Mutation Rules appplied count %v for policy %q" , resp . PolicyResponse . RulesAppliedCount , resp . PolicyResponse . Policy )
}
2019-12-26 15:34:19 -08:00
// podTemplateRule mutate pod template with annotation
// pod-policies.kyverno.io/autogen-applied=true
var podTemplateRule = kyverno . Rule {
2019-12-26 18:41:14 -08:00
Name : "autogen-annotate-podtemplate" ,
2019-12-26 15:34:19 -08:00
Mutation : kyverno . Mutation {
2019-12-26 18:41:14 -08:00
Overlay : map [ string ] interface { } {
"spec" : map [ string ] interface { } {
"template" : map [ string ] interface { } {
"metadata" : map [ string ] interface { } {
"annotations" : map [ string ] interface { } {
2020-01-15 18:15:48 -08:00
"+(pod-policies.kyverno.io/autogen-applied)" : "true" ,
2019-12-26 18:41:14 -08:00
} ,
} ,
} ,
} ,
} ,
2019-12-26 15:34:19 -08:00
} ,
}