2020-03-06 03:00:18 +05:30
package apply
import (
2022-09-30 10:12:21 +02:00
"context"
2020-03-06 03:00:18 +05:30
"fmt"
2022-12-02 20:03:04 +05:30
"net/url"
2020-06-08 17:01:56 +05:30
"os"
2020-07-21 00:41:30 +05:30
"path/filepath"
"strings"
2020-04-03 10:30:52 +05:30
"time"
2021-02-07 20:26:56 -08:00
"github.com/go-git/go-billy/v5/memfs"
2022-12-02 20:03:04 +05:30
kyvernov1 "github.com/kyverno/kyverno/api/kyverno/v1"
2022-04-25 20:20:40 +08:00
"github.com/kyverno/kyverno/api/kyverno/v1beta1"
2023-07-06 13:48:19 +02:00
"github.com/kyverno/kyverno/cmd/cli/kubectl-kyverno/utils/color"
2022-04-14 17:50:18 +05:30
"github.com/kyverno/kyverno/cmd/cli/kubectl-kyverno/utils/common"
sanitizederror "github.com/kyverno/kyverno/cmd/cli/kubectl-kyverno/utils/sanitizedError"
"github.com/kyverno/kyverno/cmd/cli/kubectl-kyverno/utils/store"
2023-08-18 15:58:47 +05:30
"github.com/kyverno/kyverno/cmd/cli/kubectl-kyverno/utils/values"
2023-04-12 14:51:03 +02:00
"github.com/kyverno/kyverno/pkg/autogen"
2022-08-31 14:03:47 +08:00
"github.com/kyverno/kyverno/pkg/clients/dclient"
2022-09-20 19:05:18 +05:30
"github.com/kyverno/kyverno/pkg/config"
2023-04-12 14:51:03 +02:00
engineapi "github.com/kyverno/kyverno/pkg/engine/api"
2020-10-15 17:29:07 -07:00
"github.com/kyverno/kyverno/pkg/openapi"
2022-12-03 19:56:09 +01:00
gitutils "github.com/kyverno/kyverno/pkg/utils/git"
2023-04-28 21:54:17 +08:00
policyvalidation "github.com/kyverno/kyverno/pkg/validation/policy"
2020-03-06 03:00:18 +05:30
"github.com/spf13/cobra"
2023-05-10 11:12:53 +03:00
"k8s.io/api/admissionregistration/v1alpha1"
2020-10-15 17:29:07 -07:00
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
2022-11-22 14:37:27 +01:00
"k8s.io/client-go/dynamic"
2022-07-25 07:49:51 +02:00
"k8s.io/client-go/kubernetes"
2022-12-09 22:15:23 +05:30
"sigs.k8s.io/controller-runtime/pkg/log"
2023-07-04 18:28:22 +02:00
"sigs.k8s.io/yaml"
2020-03-06 03:00:18 +05:30
)
2023-08-18 15:58:47 +05:30
// load res
// load pol
// apply
// show res
2023-07-04 18:28:22 +02:00
const divider = "----------------------------------------------------------------------"
2021-10-14 22:44:11 +05:30
type SkippedInvalidPolicies struct {
skipped [ ] string
invalid [ ] string
}
2022-09-20 19:05:18 +05:30
type ApplyCommandConfig struct {
2023-07-03 12:04:07 +02:00
KubeConfig string
Context string
Namespace string
MutateLogPath string
Variables [ ] string
ValuesFile string
UserInfoPath string
Cluster bool
PolicyReport bool
Stdin bool
RegistryAccess bool
AuditWarn bool
ResourcePaths [ ] string
PolicyPaths [ ] string
GitBranch string
warnExitCode int
warnNoPassed bool
2022-09-20 19:05:18 +05:30
}
2022-12-16 14:50:15 +01:00
var (
applyHelp = `
2020-11-20 12:27:02 +05:30
To apply on a resource :
2022-08-18 12:22:30 +05:30
kyverno apply / path / to / policy . yaml / path / to / folderOfPolicies -- resource = / path / to / resource1 -- resource = / path / to / resource2
To apply on a folder of resources :
kyverno apply / path / to / policy . yaml / path / to / folderOfPolicies -- resource = / path / to / resources /
2020-11-20 12:27:02 +05:30
To apply on a cluster :
2022-08-18 12:22:30 +05:30
kyverno apply / path / to / policy . yaml / path / to / folderOfPolicies -- cluster
2020-11-20 12:27:02 +05:30
2022-12-02 20:03:04 +05:30
To apply policies from a gitSourceURL on a cluster :
2023-07-03 15:22:05 +02:00
Example : Taking github . com as a gitSourceURL here . Some other standards gitSourceURL are : gitlab . com , bitbucket . org , etc .
kyverno apply https : //github.com/kyverno/policies/openshift/ --git-branch main --cluster
2020-11-20 12:27:02 +05:30
To apply policy with variables :
2023-07-03 15:22:05 +02:00
1. To apply single policy with variable on single resource use flag "set" .
Example :
kyverno apply / path / to / policy . yaml -- resource / path / to / resource . yaml -- set < variable1 >= < value1 > , < variable2 >= < value2 >
2. To apply multiple policy with variable on multiple resource use flag "values_file" .
Example :
kyverno apply / path / to / policy1 . yaml / path / to / policy2 . yaml -- resource / path / to / resource1 . yaml -- resource / path / to / resource2 . yaml - f / path / to / value . yaml
Format of value . yaml :
policies :
2023-07-04 18:28:22 +02:00
- name : < policy1 name >
rules :
2023-07-03 15:22:05 +02:00
- name : < rule1 name >
values :
< context variable1 in policy1 rule1 > : < value >
< context variable2 in policy1 rule1 > : < value >
2023-07-04 18:28:22 +02:00
- name : < rule2 name >
values :
< context variable1 in policy1 rule2 > : < value >
< context variable2 in policy1 rule2 > : < value >
resources :
- name : < resource1 name >
values :
< variable1 in policy1 > : < value >
< variable2 in policy1 > : < value >
- name : < resource2 name >
values :
< variable1 in policy1 > : < value >
< variable2 in policy1 > : < value >
- name : < policy2 name >
resources :
- name : < resource1 name >
2023-07-03 15:22:05 +02:00
values :
2023-07-04 18:28:22 +02:00
< variable1 in policy2 > : < value >
< variable2 in policy2 > : < value >
- name : < resource2 name >
values :
< variable1 in policy2 > : < value >
< variable2 in policy2 > : < value >
2023-07-03 15:22:05 +02:00
namespaceSelector :
2023-07-04 18:28:22 +02:00
- name : < namespace1 name >
2023-07-03 15:22:05 +02:00
labels :
2023-07-04 18:28:22 +02:00
< label key > : < label value >
- name : < namespace2 name >
2023-07-03 15:22:05 +02:00
labels :
2023-07-04 18:28:22 +02:00
< label key > : < label value >
2022-12-09 22:15:23 +05:30
# If policy is matching on Kind / Subresource , then this is required
subresources :
- subresource :
name : < name of subresource >
kind : < kind of subresource >
2023-01-05 14:13:54 +05:30
group : < group of subresource >
2022-12-09 22:15:23 +05:30
version : < version of subresource >
parentResource :
name : < name of parent resource >
kind : < kind of parent resource >
2023-01-05 14:13:54 +05:30
group : < group of parent resource >
2022-12-09 22:15:23 +05:30
version : < version of parent resource >
2020-11-20 12:27:02 +05:30
2020-12-07 11:26:04 -08:00
More info : https : //kyverno.io/docs/kyverno-cli/
`
2022-12-16 14:50:15 +01:00
// allow os.exit to be overwritten during unit tests
osExit = os . Exit
)
2020-12-07 11:26:04 -08:00
func Command ( ) * cobra . Command {
var cmd * cobra . Command
2023-07-31 17:15:47 +03:00
var removeColor , detailedResults , table bool
2022-09-20 19:05:18 +05:30
applyCommandConfig := & ApplyCommandConfig { }
2020-12-07 11:26:04 -08:00
cmd = & cobra . Command {
Use : "apply" ,
2022-12-27 22:46:01 +08:00
Short : "Applies policies on resources." ,
2020-12-07 11:26:04 -08:00
Example : applyHelp ,
2020-03-06 03:00:18 +05:30
RunE : func ( cmd * cobra . Command , policyPaths [ ] string ) ( err error ) {
2023-07-06 13:48:19 +02:00
color . InitColors ( removeColor )
2020-03-06 03:00:18 +05:30
defer func ( ) {
if err != nil {
2020-12-07 11:26:04 -08:00
if ! sanitizederror . IsErrorSanitized ( err ) {
2020-03-20 11:43:21 -07:00
log . Log . Error ( err , "failed to sanitize" )
2020-08-05 23:53:27 +05:30
err = fmt . Errorf ( "internal error" )
2020-03-06 03:00:18 +05:30
}
}
} ( )
2022-09-20 19:05:18 +05:30
applyCommandConfig . PolicyPaths = policyPaths
2023-07-04 18:28:22 +02:00
rc , _ , skipInvalidPolicies , responses , err := applyCommandConfig . applyCommandHelper ( )
2020-10-30 16:38:19 +05:30
if err != nil {
return err
2020-08-11 22:59:50 +05:30
}
2023-07-04 18:28:22 +02:00
printSkippedAndInvalidPolicies ( skipInvalidPolicies )
if applyCommandConfig . PolicyReport {
printReport ( responses , applyCommandConfig . AuditWarn )
2023-07-06 13:48:19 +02:00
} else if table {
2023-07-31 17:15:47 +03:00
printTable ( detailedResults , applyCommandConfig . AuditWarn , responses ... )
2023-07-04 18:28:22 +02:00
} else {
printViolations ( rc )
}
exit ( rc , applyCommandConfig . warnExitCode , applyCommandConfig . warnNoPassed )
2020-12-20 01:21:31 +05:30
return nil
} ,
}
2023-07-03 12:04:07 +02:00
cmd . Flags ( ) . StringSliceVarP ( & applyCommandConfig . ResourcePaths , "resource" , "r" , [ ] string { } , "Path to resource files" )
2022-09-20 19:05:18 +05:30
cmd . Flags ( ) . BoolVarP ( & applyCommandConfig . Cluster , "cluster" , "c" , false , "Checks if policies should be applied to cluster in the current context" )
cmd . Flags ( ) . StringVarP ( & applyCommandConfig . MutateLogPath , "output" , "o" , "" , "Prints the mutated resources in provided file/directory" )
2021-08-02 16:38:43 +05:30
// currently `set` flag supports variable for single policy applied on single resource
2022-09-20 19:05:18 +05:30
cmd . Flags ( ) . StringVarP ( & applyCommandConfig . UserInfoPath , "userinfo" , "u" , "" , "Admission Info including Roles, Cluster Roles and Subjects" )
2023-07-03 12:04:07 +02:00
cmd . Flags ( ) . StringSliceVarP ( & applyCommandConfig . Variables , "set" , "s" , nil , "Variables that are required" )
2022-09-20 19:05:18 +05:30
cmd . Flags ( ) . StringVarP ( & applyCommandConfig . ValuesFile , "values-file" , "f" , "" , "File containing values for policy variables" )
cmd . Flags ( ) . BoolVarP ( & applyCommandConfig . PolicyReport , "policy-report" , "p" , false , "Generates policy report when passed (default policyviolation)" )
cmd . Flags ( ) . StringVarP ( & applyCommandConfig . Namespace , "namespace" , "n" , "" , "Optional Policy parameter passed with cluster flag" )
cmd . Flags ( ) . BoolVarP ( & applyCommandConfig . Stdin , "stdin" , "i" , false , "Optional mutate policy parameter to pipe directly through to kubectl" )
2023-07-03 12:04:07 +02:00
cmd . Flags ( ) . BoolVar ( & applyCommandConfig . RegistryAccess , "registry" , false , "If set to true, access the image registry using local docker credentials to populate external data" )
cmd . Flags ( ) . StringVar ( & applyCommandConfig . KubeConfig , "kubeconfig" , "" , "path to kubeconfig file with authorization and master location information" )
cmd . Flags ( ) . StringVar ( & applyCommandConfig . Context , "context" , "" , "The name of the kubeconfig context to use" )
2022-12-02 20:03:04 +05:30
cmd . Flags ( ) . StringVarP ( & applyCommandConfig . GitBranch , "git-branch" , "b" , "" , "test git repository branch" )
2023-07-03 12:04:07 +02:00
cmd . Flags ( ) . BoolVar ( & applyCommandConfig . AuditWarn , "audit-warn" , false , "If set to true, will flag audit policies as warnings instead of failures" )
2022-12-05 13:15:22 -05:00
cmd . Flags ( ) . IntVar ( & applyCommandConfig . warnExitCode , "warn-exit-code" , 0 , "Set the exit code for warnings; if failures or errors are found, will exit 1" )
2023-07-03 12:04:07 +02:00
cmd . Flags ( ) . BoolVar ( & applyCommandConfig . warnNoPassed , "warn-no-pass" , false , "Specify if warning exit code should be raised if no objects satisfied a policy; can be used together with --warn-exit-code flag" )
2023-07-06 13:48:19 +02:00
cmd . Flags ( ) . BoolVar ( & removeColor , "remove-color" , false , "Remove any color from output" )
2023-07-31 17:15:47 +03:00
cmd . Flags ( ) . BoolVar ( & detailedResults , "detailed-results" , false , "If set to true, display detailed results" )
2023-07-06 13:48:19 +02:00
cmd . Flags ( ) . BoolVarP ( & table , "table" , "t" , false , "Show results in table format" )
2020-12-20 01:21:31 +05:30
return cmd
}
2020-08-05 23:53:27 +05:30
2023-07-04 12:54:22 +02:00
func ( c * ApplyCommandConfig ) applyCommandHelper ( ) ( * common . ResultCounts , [ ] * unstructured . Unstructured , SkippedInvalidPolicies , [ ] engineapi . EngineResponse , error ) {
2023-08-18 15:58:47 +05:30
rc , uu , skipInvalidPolicies , er , err := c . checkArguments ( )
if err != nil {
return rc , uu , skipInvalidPolicies , er , err
2023-07-04 12:54:22 +02:00
}
2023-08-18 15:58:47 +05:30
rc , uu , skipInvalidPolicies , er , err , mutateLogPathIsDir := c . getMutateLogPathIsDir ( skipInvalidPolicies )
2023-07-04 18:28:22 +02:00
if err != nil {
2023-08-18 15:58:47 +05:30
return rc , uu , skipInvalidPolicies , er , err
2023-07-04 18:28:22 +02:00
}
2023-08-18 15:58:47 +05:30
rc , uu , skipInvalidPolicies , er , err = c . cleanPreviousContent ( mutateLogPathIsDir , skipInvalidPolicies )
if err != nil {
return rc , uu , skipInvalidPolicies , er , err
2023-07-04 18:28:22 +02:00
}
var userInfo v1beta1 . RequestInfo
if c . UserInfoPath != "" {
userInfo , err = common . GetUserInfoFromPath ( nil , c . UserInfoPath , false , "" )
if err != nil {
fmt . Printf ( "Error: failed to load request info\nCause: %s\n" , err )
osExit ( 1 )
}
2022-10-19 22:09:15 +05:30
}
2023-07-04 12:54:22 +02:00
variables , globalValMap , valuesMap , namespaceSelectorMap , subresources , err := common . GetVariable ( c . Variables , c . ValuesFile , nil , false , "" )
2020-12-20 01:21:31 +05:30
if err != nil {
if ! sanitizederror . IsErrorSanitized ( err ) {
2023-07-04 12:54:22 +02:00
return nil , nil , skipInvalidPolicies , nil , sanitizederror . NewWithError ( "failed to decode yaml" , err )
2020-12-20 01:21:31 +05:30
}
2023-07-04 12:54:22 +02:00
return nil , nil , skipInvalidPolicies , nil , err
2020-12-20 01:21:31 +05:30
}
2023-02-09 16:15:51 +01:00
openApiManager , err := openapi . NewManager ( log . Log )
2020-12-20 01:21:31 +05:30
if err != nil {
2023-07-04 12:54:22 +02:00
return nil , nil , skipInvalidPolicies , nil , sanitizederror . NewWithError ( "failed to initialize openAPIController" , err )
2020-12-20 01:21:31 +05:30
}
2023-08-18 15:58:47 +05:30
rc , uu , skipInvalidPolicies , er , err , dClient := c . initStoreAndClusterClient ( skipInvalidPolicies )
if err != nil {
return rc , uu , skipInvalidPolicies , er , err
2023-07-04 18:28:22 +02:00
}
2023-08-18 15:58:47 +05:30
rc , uu , skipInvalidPolicies , er , err , policies , validatingAdmissionPolicies := c . loadPolicies ( skipInvalidPolicies )
if err != nil {
return rc , uu , skipInvalidPolicies , er , err
}
resources := c . loadResources ( policies , validatingAdmissionPolicies , dClient )
rc , uu , skipInvalidPolicies , er , err = c . applyPolicytoResource ( variables , policies , validatingAdmissionPolicies , resources , openApiManager , skipInvalidPolicies , valuesMap , dClient , subresources , globalValMap , userInfo , mutateLogPathIsDir , namespaceSelectorMap )
if err != nil {
return rc , uu , skipInvalidPolicies , er , err
}
rc , uu , skipInvalidPolicies , er , err = c . applyValidatingAdmissionPolicytoResource ( validatingAdmissionPolicies , resources , rc , dClient , subresources , skipInvalidPolicies , er )
if err != nil {
return rc , uu , skipInvalidPolicies , er , err
}
return rc , resources , skipInvalidPolicies , er , nil
}
func ( c * ApplyCommandConfig ) getMutateLogPathIsDir ( skipInvalidPolicies SkippedInvalidPolicies ) ( * common . ResultCounts , [ ] * unstructured . Unstructured , SkippedInvalidPolicies , [ ] engineapi . EngineResponse , error , bool ) {
mutateLogPathIsDir , err := checkMutateLogPath ( c . MutateLogPath )
if err != nil {
if ! sanitizederror . IsErrorSanitized ( err ) {
return nil , nil , skipInvalidPolicies , nil , sanitizederror . NewWithError ( "failed to create file/folder" , err ) , false
2020-12-20 01:21:31 +05:30
}
2023-08-18 15:58:47 +05:30
return nil , nil , skipInvalidPolicies , nil , err , false
}
return nil , nil , skipInvalidPolicies , nil , err , mutateLogPathIsDir
}
func ( c * ApplyCommandConfig ) applyValidatingAdmissionPolicytoResource ( validatingAdmissionPolicies [ ] v1alpha1 . ValidatingAdmissionPolicy , resources [ ] * unstructured . Unstructured , rc * common . ResultCounts , dClient dclient . Interface , subresources [ ] values . Subresource , skipInvalidPolicies SkippedInvalidPolicies , responses [ ] engineapi . EngineResponse ) ( * common . ResultCounts , [ ] * unstructured . Unstructured , SkippedInvalidPolicies , [ ] engineapi . EngineResponse , error ) {
validatingAdmissionPolicy := common . ValidatingAdmissionPolicies { }
for _ , resource := range resources {
for _ , policy := range validatingAdmissionPolicies {
applyPolicyConfig := common . ApplyPolicyConfig {
ValidatingAdmissionPolicy : policy ,
Resource : resource ,
PolicyReport : c . PolicyReport ,
Rc : rc ,
Client : dClient ,
AuditWarn : c . AuditWarn ,
Subresources : subresources ,
}
ers , err := validatingAdmissionPolicy . ApplyPolicyOnResource ( applyPolicyConfig )
if err != nil {
return rc , resources , skipInvalidPolicies , responses , sanitizederror . NewWithError ( fmt . Errorf ( "failed to apply policy %v on resource %v" , policy . GetName ( ) , resource . GetName ( ) ) . Error ( ) , err )
}
responses = append ( responses , ers ... )
2022-07-25 07:49:51 +02:00
}
2023-08-18 15:58:47 +05:30
}
return rc , resources , skipInvalidPolicies , responses , nil
}
func ( c * ApplyCommandConfig ) applyPolicytoResource ( variables map [ string ] string , policies [ ] kyvernov1 . PolicyInterface , validatingAdmissionPolicies [ ] v1alpha1 . ValidatingAdmissionPolicy , resources [ ] * unstructured . Unstructured , openApiManager openapi . Manager , skipInvalidPolicies SkippedInvalidPolicies , valuesMap map [ string ] map [ string ] values . Resource , dClient dclient . Interface , subresources [ ] values . Subresource , globalValMap map [ string ] string , userInfo v1beta1 . RequestInfo , mutateLogPathIsDir bool , namespaceSelectorMap map [ string ] map [ string ] string ) ( * common . ResultCounts , [ ] * unstructured . Unstructured , SkippedInvalidPolicies , [ ] engineapi . EngineResponse , error ) {
if len ( variables ) != 0 {
variables = common . SetInStoreContext ( policies , variables )
}
if ! c . Stdin {
var policyRulesCount int
for _ , policy := range policies {
policyRulesCount += len ( autogen . ComputeRules ( policy ) )
2022-11-22 14:37:27 +01:00
}
2023-08-18 15:58:47 +05:30
policyRulesCount += len ( validatingAdmissionPolicies )
fmt . Printf ( "\nApplying %d policy rule(s) to %d resource(s)...\n" , policyRulesCount , len ( resources ) )
}
var rc common . ResultCounts
var responses [ ] engineapi . EngineResponse
for _ , resource := range resources {
for _ , policy := range policies {
_ , err := policyvalidation . Validate ( policy , nil , nil , true , openApiManager , config . KyvernoUserName ( config . KyvernoServiceAccountName ( ) ) )
if err != nil {
log . Log . Error ( err , "policy validation error" )
if strings . HasPrefix ( err . Error ( ) , "variable 'element.name'" ) {
skipInvalidPolicies . invalid = append ( skipInvalidPolicies . invalid , policy . GetName ( ) )
} else {
skipInvalidPolicies . skipped = append ( skipInvalidPolicies . skipped , policy . GetName ( ) )
}
continue
}
matches := common . HasVariables ( policy )
variable := common . RemoveDuplicateAndObjectVariables ( matches )
if len ( variable ) > 0 {
if len ( variables ) == 0 {
// check policy in variable file
if c . ValuesFile == "" || valuesMap [ policy . GetName ( ) ] == nil {
skipInvalidPolicies . skipped = append ( skipInvalidPolicies . skipped , policy . GetName ( ) )
continue
}
}
}
kindOnwhichPolicyIsApplied := common . GetKindsFromPolicy ( policy , subresources , dClient )
thisPolicyResourceValues , err := common . CheckVariableForPolicy ( valuesMap , globalValMap , policy . GetName ( ) , resource . GetName ( ) , resource . GetKind ( ) , variables , kindOnwhichPolicyIsApplied , variable )
if err != nil {
return & rc , resources , skipInvalidPolicies , responses , sanitizederror . NewWithError ( fmt . Sprintf ( "policy `%s` have variables. pass the values for the variables for resource `%s` using set/values_file flag" , policy . GetName ( ) , resource . GetName ( ) ) , err )
}
applyPolicyConfig := common . ApplyPolicyConfig {
Policy : policy ,
Resource : resource ,
MutateLogPath : c . MutateLogPath ,
MutateLogPathIsDir : mutateLogPathIsDir ,
Variables : thisPolicyResourceValues ,
UserInfo : userInfo ,
PolicyReport : c . PolicyReport ,
NamespaceSelectorMap : namespaceSelectorMap ,
Stdin : c . Stdin ,
Rc : & rc ,
PrintPatchResource : true ,
Client : dClient ,
AuditWarn : c . AuditWarn ,
Subresources : subresources ,
}
ers , err := common . ApplyPolicyOnResource ( applyPolicyConfig )
if err != nil {
return & rc , resources , skipInvalidPolicies , responses , sanitizederror . NewWithError ( fmt . Errorf ( "failed to apply policy %v on resource %v" , policy . GetName ( ) , resource . GetName ( ) ) . Error ( ) , err )
}
responses = append ( responses , processSkipEngineResponses ( ers , applyPolicyConfig ) ... )
2020-12-20 01:21:31 +05:30
}
}
2023-08-18 15:58:47 +05:30
return & rc , resources , skipInvalidPolicies , responses , nil
}
func ( c * ApplyCommandConfig ) loadResources ( policies [ ] kyvernov1 . PolicyInterface , validatingAdmissionPolicies [ ] v1alpha1 . ValidatingAdmissionPolicy , dClient dclient . Interface ) [ ] * unstructured . Unstructured {
resources , err := common . GetResourceAccordingToResourcePath ( nil , c . ResourcePaths , c . Cluster , policies , validatingAdmissionPolicies , dClient , c . Namespace , c . PolicyReport , false , "" )
if err != nil {
fmt . Printf ( "Error: failed to load resources\nCause: %s\n" , err )
osExit ( 1 )
}
return resources
}
func ( c * ApplyCommandConfig ) loadPolicies ( skipInvalidPolicies SkippedInvalidPolicies ) ( * common . ResultCounts , [ ] * unstructured . Unstructured , SkippedInvalidPolicies , [ ] engineapi . EngineResponse , error , [ ] kyvernov1 . PolicyInterface , [ ] v1alpha1 . ValidatingAdmissionPolicy ) {
2023-07-04 18:28:22 +02:00
// load policies
fs := memfs . New ( )
2022-12-02 20:03:04 +05:30
var policies [ ] kyvernov1 . PolicyInterface
2023-05-10 11:12:53 +03:00
var validatingAdmissionPolicies [ ] v1alpha1 . ValidatingAdmissionPolicy
2022-12-02 20:03:04 +05:30
2023-08-16 21:33:42 +05:30
for _ , policy := range c . PolicyPaths {
policyPaths := [ ] string { policy }
isGit := common . IsGitSourcePath ( policyPaths )
2022-12-02 20:03:04 +05:30
2023-08-16 21:33:42 +05:30
if isGit {
gitSourceURL , err := url . Parse ( policyPaths [ 0 ] )
if err != nil {
fmt . Printf ( "Error: failed to load policies\nCause: %s\n" , err )
osExit ( 1 )
}
2022-12-16 14:50:15 +01:00
2023-08-16 21:33:42 +05:30
pathElems := strings . Split ( gitSourceURL . Path [ 1 : ] , "/" )
if len ( pathElems ) <= 1 {
err := fmt . Errorf ( "invalid URL path %s - expected https://<any_git_source_domain>/:owner/:repository/:branch (without --git-branch flag) OR https://<any_git_source_domain>/:owner/:repository/:directory (with --git-branch flag)" , gitSourceURL . Path )
fmt . Printf ( "Error: failed to parse URL \nCause: %s\n" , err )
osExit ( 1 )
}
gitSourceURL . Path = strings . Join ( [ ] string { pathElems [ 0 ] , pathElems [ 1 ] } , "/" )
repoURL := gitSourceURL . String ( )
var gitPathToYamls string
c . GitBranch , gitPathToYamls = common . GetGitBranchOrPolicyPaths ( c . GitBranch , repoURL , policyPaths )
_ , cloneErr := gitutils . Clone ( repoURL , fs , c . GitBranch )
if cloneErr != nil {
fmt . Printf ( "Error: failed to clone repository \nCause: %s\n" , cloneErr )
log . Log . V ( 3 ) . Info ( fmt . Sprintf ( "failed to clone repository %v as it is not valid" , repoURL ) , "error" , cloneErr )
osExit ( 1 )
}
policyYamls , err := gitutils . ListYamls ( fs , gitPathToYamls )
if err != nil {
2023-08-18 15:58:47 +05:30
return nil , nil , skipInvalidPolicies , nil , sanitizederror . NewWithError ( "failed to list YAMLs in repository" , err ) , nil , nil
2023-08-16 21:33:42 +05:30
}
policyPaths = policyYamls
2022-12-02 20:03:04 +05:30
}
2023-08-16 21:33:42 +05:30
policiesFromFile , admissionPoliciesFromFile , err := common . GetPoliciesFromPaths ( fs , policyPaths , isGit , "" )
2022-12-02 20:03:04 +05:30
if err != nil {
2023-08-16 21:33:42 +05:30
fmt . Printf ( "Error: failed to load policies\nCause: %s\n" , err )
osExit ( 1 )
2022-12-02 20:03:04 +05:30
}
2023-08-16 21:33:42 +05:30
policies = append ( policies , policiesFromFile ... )
validatingAdmissionPolicies = append ( validatingAdmissionPolicies , admissionPoliciesFromFile ... )
2020-12-20 01:21:31 +05:30
}
2023-08-16 21:33:42 +05:30
2023-08-18 15:58:47 +05:30
return nil , nil , skipInvalidPolicies , nil , nil , policies , validatingAdmissionPolicies
}
2021-08-02 16:38:43 +05:30
2023-08-18 15:58:47 +05:30
func ( c * ApplyCommandConfig ) initStoreAndClusterClient ( skipInvalidPolicies SkippedInvalidPolicies ) ( * common . ResultCounts , [ ] * unstructured . Unstructured , SkippedInvalidPolicies , [ ] engineapi . EngineResponse , error , dclient . Interface ) {
store . SetLocal ( true )
store . SetRegistryAccess ( c . RegistryAccess )
if c . Cluster {
store . AllowApiCall ( true )
2020-12-20 01:21:31 +05:30
}
2023-08-18 15:58:47 +05:30
var err error
var dClient dclient . Interface
if c . Cluster {
restConfig , err := config . CreateClientConfigWithContext ( c . KubeConfig , c . Context )
2020-12-20 01:21:31 +05:30
if err != nil {
2023-08-18 15:58:47 +05:30
return nil , nil , skipInvalidPolicies , nil , err , nil
2020-12-20 01:21:31 +05:30
}
2023-08-18 15:58:47 +05:30
kubeClient , err := kubernetes . NewForConfig ( restConfig )
if err != nil {
return nil , nil , skipInvalidPolicies , nil , err , nil
2020-12-20 01:21:31 +05:30
}
2023-08-18 15:58:47 +05:30
dynamicClient , err := dynamic . NewForConfig ( restConfig )
if err != nil {
return nil , nil , skipInvalidPolicies , nil , err , nil
}
dClient , err = dclient . NewClient ( context . Background ( ) , dynamicClient , kubeClient , 15 * time . Minute )
if err != nil {
return nil , nil , skipInvalidPolicies , nil , err , nil
2020-12-20 01:21:31 +05:30
}
2020-03-06 03:00:18 +05:30
}
2023-08-18 15:58:47 +05:30
return nil , nil , skipInvalidPolicies , nil , err , dClient
}
2020-03-06 03:00:18 +05:30
2023-08-18 15:58:47 +05:30
func ( c * ApplyCommandConfig ) cleanPreviousContent ( mutateLogPathIsDir bool , skipInvalidPolicies SkippedInvalidPolicies ) ( * common . ResultCounts , [ ] * unstructured . Unstructured , SkippedInvalidPolicies , [ ] engineapi . EngineResponse , error ) {
// empty the previous contents of the file just in case if the file already existed before with some content(so as to perform overwrites)
// the truncation of files for the case when mutateLogPath is dir, is handled under pkg/kyverno/apply/common.go
if ! mutateLogPathIsDir && c . MutateLogPath != "" {
c . MutateLogPath = filepath . Clean ( c . MutateLogPath )
// Necessary for us to include the file via variable as it is part of the CLI.
_ , err := os . OpenFile ( c . MutateLogPath , os . O_TRUNC | os . O_WRONLY , 0 o600 ) // #nosec G304
if err != nil {
if ! sanitizederror . IsErrorSanitized ( err ) {
return nil , nil , skipInvalidPolicies , nil , sanitizederror . NewWithError ( "failed to truncate the existing file at " + c . MutateLogPath , err )
2023-05-10 11:12:53 +03:00
}
2023-08-18 15:58:47 +05:30
return nil , nil , skipInvalidPolicies , nil , err
2023-05-10 11:12:53 +03:00
}
}
2023-08-18 15:58:47 +05:30
return nil , nil , skipInvalidPolicies , nil , nil
}
2023-05-10 11:12:53 +03:00
2023-08-18 15:58:47 +05:30
func ( c * ApplyCommandConfig ) checkArguments ( ) ( * common . ResultCounts , [ ] * unstructured . Unstructured , SkippedInvalidPolicies , [ ] engineapi . EngineResponse , error ) {
var skipInvalidPolicies SkippedInvalidPolicies
if c . ValuesFile != "" && c . Variables != nil {
return nil , nil , skipInvalidPolicies , nil , sanitizederror . New ( "pass the values either using set flag or values_file flag" )
}
if len ( c . PolicyPaths ) == 0 {
return nil , nil , skipInvalidPolicies , nil , sanitizederror . New ( "require policy" )
}
if ( len ( c . PolicyPaths ) > 0 && c . PolicyPaths [ 0 ] == "-" ) && len ( c . ResourcePaths ) > 0 && c . ResourcePaths [ 0 ] == "-" {
return nil , nil , skipInvalidPolicies , nil , sanitizederror . New ( "a stdin pipe can be used for either policies or resources, not both" )
}
if len ( c . ResourcePaths ) == 0 && ! c . Cluster {
return nil , nil , skipInvalidPolicies , nil , sanitizederror . New ( "resource file(s) or cluster required" )
}
return nil , nil , skipInvalidPolicies , nil , nil
2020-03-06 03:00:18 +05:30
}
2023-07-04 18:28:22 +02:00
func printSkippedAndInvalidPolicies ( skipInvalidPolicies SkippedInvalidPolicies ) {
2021-10-14 22:44:11 +05:30
if len ( skipInvalidPolicies . skipped ) > 0 {
2021-10-29 11:16:13 +01:00
fmt . Println ( divider )
fmt . Println ( "Policies Skipped (as required variables are not provided by the user):" )
2021-10-14 22:44:11 +05:30
for i , policyName := range skipInvalidPolicies . skipped {
2021-10-29 11:16:13 +01:00
fmt . Printf ( "%d. %s\n" , i + 1 , policyName )
2021-10-14 22:44:11 +05:30
}
2021-10-29 11:16:13 +01:00
fmt . Println ( divider )
2021-10-14 22:44:11 +05:30
}
if len ( skipInvalidPolicies . invalid ) > 0 {
2021-10-29 11:16:13 +01:00
fmt . Println ( divider )
fmt . Println ( "Invalid Policies:" )
2021-10-14 22:44:11 +05:30
for i , policyName := range skipInvalidPolicies . invalid {
2021-10-29 11:16:13 +01:00
fmt . Printf ( "%d. %s\n" , i + 1 , policyName )
2021-08-31 16:24:31 +05:30
}
2021-10-29 11:16:13 +01:00
fmt . Println ( divider )
2021-08-31 16:24:31 +05:30
}
2023-07-04 18:28:22 +02:00
}
2021-08-31 16:24:31 +05:30
2023-07-04 18:28:22 +02:00
func printReport ( engineResponses [ ] engineapi . EngineResponse , auditWarn bool ) {
clustered , namespaced := buildPolicyReports ( auditWarn , engineResponses ... )
if len ( clustered ) > 0 || len ( namespaced ) > 0 {
fmt . Println ( divider )
fmt . Println ( "POLICY REPORT:" )
fmt . Println ( divider )
report := mergeClusterReport ( clustered , namespaced )
yamlReport , _ := yaml . Marshal ( report )
fmt . Println ( string ( yamlReport ) )
2020-10-30 16:38:19 +05:30
} else {
2023-07-04 18:28:22 +02:00
fmt . Println ( divider )
fmt . Println ( "POLICY REPORT: skip generating policy report (no validate policy found/resource skipped)" )
2021-09-03 17:17:22 +05:30
}
2023-07-04 18:28:22 +02:00
}
2020-10-30 16:38:19 +05:30
2023-07-04 18:28:22 +02:00
func printViolations ( rc * common . ResultCounts ) {
fmt . Printf ( "\npass: %d, fail: %d, warn: %d, error: %d, skip: %d \n" , rc . Pass , rc . Fail , rc . Warn , rc . Error , rc . Skip )
}
func exit ( rc * common . ResultCounts , warnExitCode int , warnNoPassed bool ) {
2021-09-03 17:17:22 +05:30
if rc . Fail > 0 || rc . Error > 0 {
2022-12-16 14:50:15 +01:00
osExit ( 1 )
2022-12-05 13:15:22 -05:00
} else if rc . Warn > 0 && warnExitCode != 0 {
2022-12-16 14:50:15 +01:00
osExit ( warnExitCode )
2023-03-27 11:18:44 +02:00
} else if rc . Pass == 0 && warnNoPassed {
osExit ( warnExitCode )
2020-10-30 16:38:19 +05:30
}
}
2023-08-08 13:05:01 +05:30
func processSkipEngineResponses ( responses [ ] engineapi . EngineResponse , c common . ApplyPolicyConfig ) [ ] engineapi . EngineResponse {
var processedEngineResponses [ ] engineapi . EngineResponse
for _ , response := range responses {
if ! response . IsEmpty ( ) {
2023-08-15 22:41:43 +03:00
pol := response . Policy ( )
if polType := pol . GetType ( ) ; polType == engineapi . ValidatingAdmissionPolicyType {
return processedEngineResponses
}
for _ , rule := range autogen . ComputeRules ( pol . GetPolicy ( ) . ( kyvernov1 . PolicyInterface ) ) {
2023-08-08 13:05:01 +05:30
if rule . HasValidate ( ) || rule . HasVerifyImageChecks ( ) || rule . HasVerifyImages ( ) {
ruleFoundInEngineResponse := false
for _ , valResponseRule := range response . PolicyResponse . Rules {
if rule . Name == valResponseRule . Name ( ) {
ruleFoundInEngineResponse = true
}
}
if ! ruleFoundInEngineResponse {
response . PolicyResponse . Rules = append ( response . PolicyResponse . Rules ,
* engineapi . RuleSkip (
rule . Name ,
engineapi . Validation ,
rule . Validation . Message ,
) ,
)
}
}
}
}
processedEngineResponses = append ( processedEngineResponses , response )
}
return processedEngineResponses
}