2019-05-13 18:17:28 -07:00
package engine
2019-05-09 22:26:22 -07:00
import (
2020-03-06 01:09:38 +05:30
"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
2020-03-06 01:09:38 +05:30
"github.com/nirmata/kyverno/pkg/engine/utils"
"github.com/nirmata/kyverno/pkg/engine/context"
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"
2019-12-12 15:02:59 -08:00
"github.com/nirmata/kyverno/pkg/engine/response"
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 (
2020-01-24 12:05:53 -08:00
//PodControllers stores the list of Pod-controllers in csv string
PodControllers = "DaemonSet,Deployment,Job,StatefulSet"
//PodControllersAnnotation defines the annotation key for Pod-Controllers
2019-12-26 15:34:19 -08:00
PodControllersAnnotation = "pod-policies.kyverno.io/autogen-controllers"
2020-01-24 12:05:53 -08:00
//PodTemplateAnnotation defines the annotation key for Pod-Template
PodTemplateAnnotation = "pod-policies.kyverno.io/autogen-applied"
2019-12-26 15:34:19 -08:00
)
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-11-13 17:56:56 -08:00
patchedResource := policyContext . NewResource
2019-08-23 18:34:23 -07:00
for _ , rule := range policy . Spec . Rules {
2020-02-14 11:59:28 -08:00
var ruleResponse response . RuleResponse
2019-08-23 18:34:23 -07:00
//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-11 21:23:26 -08:00
startTime := time . Now ( )
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
2020-02-07 14:45:43 +05:30
if err := MatchesResourceDescription ( resource , rule , policyContext . AdmissionInfo ) ; err != nil {
glog . V ( 4 ) . Infof ( "resource %s/%s does not satisfy the resource description for the rule:\n%s" , resource . GetNamespace ( ) , resource . GetName ( ) , err . Error ( ) )
2019-08-23 18:34:23 -07:00
continue
}
2020-01-07 15:13:57 -08:00
2020-02-04 12:13:41 -08:00
// operate on the copy of the conditions, as we perform variable substitution
copyConditions := copyConditions ( rule . Conditions )
2020-01-07 15:13:57 -08:00
// evaluate pre-conditions
2020-02-14 11:59:28 -08:00
// - handle variable subsitutions
2020-02-04 12:13:41 -08:00
if ! variables . EvaluateConditions ( ctx , copyConditions ) {
2020-01-07 15:13:57 -08:00
glog . V ( 4 ) . Infof ( "resource %s/%s does not satisfy the conditions for the rule " , resource . GetNamespace ( ) , resource . GetName ( ) )
continue
}
2020-02-14 11:59:28 -08:00
mutation := rule . Mutation . DeepCopy ( )
2019-08-23 18:34:23 -07:00
// Process Overlay
2020-02-14 11:59:28 -08:00
if mutation . Overlay != nil {
overlay := mutation . Overlay
// subsiitue the variables
var err error
if overlay , err = variables . SubstituteVars ( ctx , overlay ) ; err != nil {
// variable subsitution failed
ruleResponse . Success = false
ruleResponse . Message = err . Error ( )
resp . PolicyResponse . Rules = append ( resp . PolicyResponse . Rules , ruleResponse )
continue
}
2020-01-09 12:24:37 -08:00
2020-02-14 11:59:28 -08:00
ruleResponse , patchedResource = mutate . ProcessOverlay ( rule . Name , overlay , patchedResource )
if ruleResponse . Success {
2020-01-09 12:24:37 -08:00
// - overlay pattern does not match the resource conditions
if ruleResponse . Patches == nil {
glog . V ( 4 ) . Infof ( ruleResponse . Message )
continue
}
2020-01-17 00:05:15 +05:30
glog . V ( 4 ) . 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 )
2020-02-14 11:59:28 -08:00
incrementAppliedRuleCount ( & resp )
2019-08-23 18:34:23 -07:00
}
// 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 )
2020-02-14 11:59:28 -08:00
incrementAppliedRuleCount ( & resp )
2019-08-23 18:34:23 -07:00
}
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-02-14 11:59:28 -08:00
ruleResponse , patchedResource = mutate . ProcessOverlay ( rule . Name , 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
}
2020-03-06 01:09:38 +05:30
func mutateResourceWithOverlay ( resource unstructured . Unstructured , overlay interface { } ) ( unstructured . Unstructured , error ) {
patches , err := mutate . MutateResourceWithOverlay ( resource . UnstructuredContent ( ) , overlay )
if err != nil {
return unstructured . Unstructured { } , err
}
if len ( patches ) == 0 {
return resource , nil
}
// convert to RAW
resourceRaw , err := resource . MarshalJSON ( )
if err != nil {
return unstructured . Unstructured { } , err
}
var patchResource [ ] byte
patchResource , err = utils . ApplyPatches ( resourceRaw , patches )
if err != nil {
return unstructured . Unstructured { } , err
}
resource = unstructured . Unstructured { }
err = resource . UnmarshalJSON ( patchResource )
if err != nil {
return unstructured . Unstructured { } , err
}
return resource , nil
}
// ForceMutate does not check any conditions, it simply mutates the given resource
func ForceMutate ( ctx context . EvalInterface , policy kyverno . ClusterPolicy , resource unstructured . Unstructured ) ( unstructured . Unstructured , error ) {
var err error
for _ , rule := range policy . Spec . Rules {
if ! rule . HasMutate ( ) {
continue
}
mutation := rule . Mutation . DeepCopy ( )
if mutation . Overlay != nil {
overlay := mutation . Overlay
if overlay , err = variables . SubstituteVars ( ctx , overlay ) ; err != nil {
return unstructured . Unstructured { } , err
}
resource , err = mutateResourceWithOverlay ( resource , overlay )
if err != nil {
return unstructured . Unstructured { } , fmt . Errorf ( "could not mutate resource with overlay on rule %v:%v" , rule . Name , err )
}
}
if rule . Mutation . Patches != nil {
var resp response . RuleResponse
resp , resource = mutate . ProcessPatches ( rule , resource )
if ! resp . Success {
return unstructured . Unstructured { } , fmt . Errorf ( resp . Message )
}
}
}
return resource , nil
}
2020-02-14 11:59:28 -08:00
func incrementAppliedRuleCount ( resp * response . EngineResponse ) {
resp . PolicyResponse . RulesAppliedCount ++
}
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
} ,
}