1
0
Fork 0
mirror of https://github.com/kyverno/kyverno.git synced 2025-03-31 03:45:17 +00:00

support policy apply to multiple resources

This commit is contained in:
shuting 2019-05-20 17:59:13 -07:00
parent 91b7a1b9ac
commit 771dcd358e
4 changed files with 226 additions and 60 deletions

View file

@ -0,0 +1,30 @@
apiVersion : kubepolicy.nirmata.io/v1alpha1
kind : Policy
metadata :
name : policy-deployment
spec :
rules:
- name: deployment-policy
resource:
kind : Deployment
selector :
matchLabels :
cli: test
mutate:
patches:
- path: /metadata/labels/isMutated
op: add
value: "true"
- path: /metadata/labels/app
op: replace
value: "nginx_is_mutated"
validate:
message: "The imagePullPolicy shoud set to Always"
pattern:
spec:
template:
spec:
containers:
- (name): "*"
imagePullPolicy: Always

View file

@ -0,0 +1,23 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deployment
labels:
app: nginx
cli: test
spec:
replicas: 1
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx:1.7.9
imagePullPolicy: Always
ports:
- containerPort: 80

View file

@ -0,0 +1,35 @@
kind: "Deployment"
apiVersion: "extensions/v1beta1"
metadata:
name: "ghost"
labels:
nirmata.io/deployment.name: "ghost"
nirmata.io/application.name: "ghost"
nirmata.io/component: "ghost"
cli: test
spec:
replicas: 1
revisionHistoryLimit: 5
selector:
matchLabels:
nirmata.io/application.name: "ghost"
nirmata.io/component: "ghost"
strategy:
type: "RollingUpdate"
rollingUpdate:
maxSurge: 1
maxUnavailable: 0
template:
metadata:
labels:
nirmata.io/deployment.name: "ghost"
nirmata.io/application.name: "ghost"
nirmata.io/component: "ghost"
spec:
containers:
- name: "ghost"
image: "ghost:2.9.1-alpine"
imagePullPolicy: Always
ports:
- containerPort: 8080
protocol: "TCP"

View file

@ -7,6 +7,7 @@ import (
"io/ioutil"
"log"
"os"
"path/filepath"
"strings"
kubepolicy "github.com/nirmata/kube-policy/pkg/apis/policy/v1alpha1"
@ -18,65 +19,73 @@ import (
)
const applyExample = ` # Apply a policy to the resource.
kyverno apply @policy.yaml @resource.yaml`
kyverno apply @policy.yaml @resource.yaml
kyverno apply @policy.yaml @resourceDir/`
// NewCmdApply returns the apply command for kyverno
func NewCmdApply(in io.Reader, out, errout io.Writer) *cobra.Command {
cmd := &cobra.Command{
Use: "apply",
Short: "Apply policy on the resource",
Short: "Apply policy on the resource(s)",
Example: applyExample,
Run: func(cmd *cobra.Command, args []string) {
policy, rawResource, gvk := complete(args)
var output string
policy, resources := complete(args)
_, patchedDocument := engine.Mutate(*policy, rawResource, *gvk)
out, err := prettyPrint(patchedDocument)
if err != nil {
fmt.Printf("JSON parse error: %v\n", err)
fmt.Printf("%v\n", string(patchedDocument))
return
for _, resource := range resources {
patchedDocument, err := applyPolicy(policy, resource.rawResource, resource.gvk)
if err != nil {
fmt.Println(err)
os.Exit(1)
}
out, err := prettyPrint(patchedDocument)
if err != nil {
fmt.Printf("JSON parse error: %v\n", err)
fmt.Printf("%v\n", string(patchedDocument))
return
}
output = output + fmt.Sprintf("---\n%s", string(out))
}
if err := engine.Validate(*policy, rawResource, *gvk); err != nil {
fmt.Println(err)
return
}
fmt.Printf("%v\n", string(out))
fmt.Printf("%v\n", output)
},
}
return cmd
}
func complete(args []string) (*kubepolicy.Policy, []byte, *metav1.GroupVersionKind) {
// TODO: add pre-checks for policy and resource manifest
// order for policy and resource in args could be disordered
func complete(args []string) (*kubepolicy.Policy, []*resourceInfo) {
if len(args) != 2 {
log.Printf("Missing policy and/or resource manifest.")
return nil, nil, nil
policyDir, resourceDir, err := validateDir(args)
if err != nil {
fmt.Printf("Failed to parse file path, err: %v\n", err)
os.Exit(1)
}
// extract policy
policyDir := validateDir(args[0])
policy, err := extractPolicy(policyDir)
if err != nil {
log.Printf("failed to extract policy: %v", err)
os.Exit(1)
}
// fmt.Printf("policy name=%s, rule name=%s, %s/%s\n", policy.ObjectMeta.Name, policy.Spec.Rules[0].Name,
// policy.Spec.Rules[0].ResourceDescription.Kind, *policy.Spec.Rules[0].ResourceDescription.Name)
// extract rawResource
resourceDir := validateDir(args[1])
rawResource, gvk, err := extractResource(resourceDir)
resources, err := extractResource(resourceDir)
if err != nil {
log.Printf("failed to load resource: %v", err)
log.Printf("failed to parse resource: %v", err)
os.Exit(1)
}
return policy, rawResource, gvk
return policy, resources
}
func applyPolicy(policy *kubepolicy.Policy, rawResource []byte, gvk *metav1.GroupVersionKind) ([]byte, error) {
_, patchedDocument := engine.Mutate(*policy, rawResource, *gvk)
if err := engine.Validate(*policy, rawResource, *gvk); err != nil {
return nil, err
}
return patchedDocument, nil
}
func extractPolicy(fileDir string) (*kubepolicy.Policy, error) {
@ -96,42 +105,72 @@ func extractPolicy(fileDir string) (*kubepolicy.Policy, error) {
return nil, fmt.Errorf("failed to decode policy %s, err: %v", policy.Name, err)
}
if policy.TypeMeta.Kind != "Policy" {
return nil, fmt.Errorf("failed to parse policy")
}
return policy, nil
}
func extractResource(fileDir string) ([]byte, *metav1.GroupVersionKind, error) {
file, err := loadFile(fileDir)
type resourceInfo struct {
rawResource []byte
gvk *metav1.GroupVersionKind
}
func extractResource(fileDir string) ([]*resourceInfo, error) {
var files []string
var resources []*resourceInfo
// check if applied on multiple resources
isDir, err := isDir(fileDir)
if err != nil {
return nil, nil, fmt.Errorf("failed to load file: %v", err)
return nil, err
}
data := make(map[interface{}]interface{})
if err = yamlv2.Unmarshal([]byte(file), &data); err != nil {
return nil, nil, fmt.Errorf("failed to parse resource: %v", err)
}
apiVersion, ok := data["apiVersion"].(string)
if !ok {
return nil, nil, fmt.Errorf("failed to parse apiversion: %v", err)
}
kind, ok := data["kind"].(string)
if !ok {
return nil, nil, fmt.Errorf("failed to parse kind of resource: %v", err)
}
var gvk *metav1.GroupVersionKind
gv := strings.Split(apiVersion, "/")
if len(gv) == 2 {
gvk = &metav1.GroupVersionKind{Group: gv[0], Version: gv[1], Kind: kind}
if isDir {
files, err = ScanDir(fileDir)
if err != nil {
return nil, err
}
} else {
gvk = &metav1.GroupVersionKind{Version: gv[0], Kind: kind}
files = []string{fileDir}
}
json, err := yaml.ToJSON(file)
for _, dir := range files {
file, err := loadFile(dir)
if err != nil {
return nil, fmt.Errorf("failed to load file: %v", err)
}
return json, gvk, err
data := make(map[interface{}]interface{})
if err = yamlv2.Unmarshal([]byte(file), &data); err != nil {
return nil, fmt.Errorf("failed to parse resource: %v", err)
}
apiVersion, ok := data["apiVersion"].(string)
if !ok {
return nil, fmt.Errorf("failed to parse apiversion: %v", err)
}
kind, ok := data["kind"].(string)
if !ok {
return nil, fmt.Errorf("failed to parse kind of resource: %v", err)
}
var gvkInfo *metav1.GroupVersionKind
gv := strings.Split(apiVersion, "/")
if len(gv) == 2 {
gvkInfo = &metav1.GroupVersionKind{Group: gv[0], Version: gv[1], Kind: kind}
} else {
gvkInfo = &metav1.GroupVersionKind{Version: gv[0], Kind: kind}
}
json, err := yaml.ToJSON(file)
resources = append(resources, &resourceInfo{rawResource: json, gvk: gvkInfo})
}
return resources, err
}
func loadFile(fileDir string) ([]byte, error) {
@ -142,11 +181,19 @@ func loadFile(fileDir string) ([]byte, error) {
return ioutil.ReadFile(fileDir)
}
func validateDir(dir string) string {
if strings.HasPrefix(dir, "@") {
return dir[1:]
func validateDir(args []string) (policyDir, resourceDir string, err error) {
if len(args) != 2 {
return "", "", fmt.Errorf("missing policy and/or resource manifest")
}
return dir
if strings.HasPrefix(args[0], "@") {
policyDir = args[0][1:]
}
if strings.HasPrefix(args[1], "@") {
resourceDir = args[1][1:]
}
return
}
func prettyPrint(data []byte) ([]byte, error) {
@ -157,3 +204,34 @@ func prettyPrint(data []byte) ([]byte, error) {
return yamlv2.Marshal(&out)
}
func isDir(dir string) (bool, error) {
fi, err := os.Stat(dir)
if err != nil {
return false, err
}
return fi.IsDir(), nil
}
func ScanDir(dir string) ([]string, error) {
var res []string
err := filepath.Walk(dir, func(path string, info os.FileInfo, err error) error {
if err != nil {
fmt.Printf("prevent panic by handling failure accessing a path %q: %v\n", dir, err)
return err
}
/* if len(strings.Split(path, "/")) == 4 {
fmt.Println(path)
} */
res = append(res, path)
return nil
})
if err != nil {
return nil, fmt.Errorf("error walking the path %q: %v", dir, err)
}
return res[1:], nil
}