mirror of
https://github.com/kyverno/kyverno.git
synced 2025-03-31 03:45:17 +00:00
extended cli
This commit is contained in:
parent
8bba25bc7c
commit
79999c4948
19 changed files with 417 additions and 454 deletions
1
Makefile
1
Makefile
|
@ -12,7 +12,6 @@ REGISTRY=index.docker.io
|
|||
REPO=$(REGISTRY)/nirmata/kyverno
|
||||
IMAGE_TAG=$(GIT_VERSION)
|
||||
GOOS ?= $(shell go env GOOS)
|
||||
PACKAGE ?=github.com/nirmata/kyverno
|
||||
LD_FLAGS="-s -w -X $(PACKAGE)/pkg/version.BuildVersion=$(GIT_VERSION) -X $(PACKAGE)/pkg/version.BuildHash=$(GIT_HASH) -X $(PACKAGE)/pkg/version.BuildTime=$(TIMESTAMP)"
|
||||
|
||||
##################################
|
||||
|
|
9
cmd/cli/kyverno/main.go
Normal file
9
cmd/cli/kyverno/main.go
Normal file
|
@ -0,0 +1,9 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"github.com/nirmata/kyverno/pkg/kyverno"
|
||||
)
|
||||
|
||||
func main() {
|
||||
kyverno.CLI()
|
||||
}
|
|
@ -1,25 +0,0 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
goflag "flag"
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"github.com/nirmata/kyverno/pkg/config"
|
||||
kyverno "github.com/nirmata/kyverno/pkg/kyverno"
|
||||
flag "github.com/spf13/pflag"
|
||||
)
|
||||
|
||||
func main() {
|
||||
cmd := kyverno.NewDefaultKyvernoCommand()
|
||||
if err := cmd.Execute(); err != nil {
|
||||
fmt.Fprintf(os.Stderr, "%v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
func init() {
|
||||
flag.CommandLine.AddGoFlagSet(goflag.CommandLine)
|
||||
config.LogDefaultFlags()
|
||||
flag.Parse()
|
||||
}
|
|
@ -520,10 +520,10 @@ spec:
|
|||
serviceAccountName: kyverno-service-account
|
||||
initContainers:
|
||||
- name: kyverno-pre
|
||||
image: nirmata/kyvernopre:v1.1.1
|
||||
image: nirmata/kyvernopre:v1.1.0
|
||||
containers:
|
||||
- name: kyverno
|
||||
image: nirmata/kyverno:v1.1.1
|
||||
image: nirmata/kyverno:v1.1.0
|
||||
args:
|
||||
- "--filterK8Resources=[Event,*,*][*,kube-system,*][*,kube-public,*][*,kube-node-lease,*][Node,*,*][APIService,*,*][TokenReview,*,*][SubjectAccessReview,*,*][*,kyverno,*]"
|
||||
# customize webhook timout
|
||||
|
|
|
@ -83,6 +83,6 @@ func CreateClientConfig(kubeconfig string) (*rest.Config, error) {
|
|||
glog.Info("Using in-cluster configuration")
|
||||
return rest.InClusterConfig()
|
||||
}
|
||||
glog.Infof("Using configuration from '%s'", kubeconfig)
|
||||
glog.V(4).Infof("Using configuration from '%s'", kubeconfig)
|
||||
return clientcmd.BuildConfigFromFlags("", kubeconfig)
|
||||
}
|
||||
|
|
|
@ -387,11 +387,11 @@ func preparePath(path string) string {
|
|||
}
|
||||
|
||||
annPath := "/metadata/annotations/"
|
||||
idx := strings.Index(path, annPath)
|
||||
// escape slash in annotation patch
|
||||
if strings.Contains(path, annPath) {
|
||||
idx := strings.Index(path, annPath)
|
||||
p := path[idx+len(annPath):]
|
||||
path = path[:idx+len(annPath)] + strings.ReplaceAll(p, "/", "~1")
|
||||
path = annPath + strings.ReplaceAll(p, "/", "~1")
|
||||
}
|
||||
return path
|
||||
}
|
||||
|
|
|
@ -105,7 +105,7 @@ func Mutate(policyContext PolicyContext) (resp response.EngineResponse) {
|
|||
continue
|
||||
}
|
||||
|
||||
glog.Infof("Mutate overlay in rule '%s' successfully applied on %s/%s/%s", rule.Name, resource.GetKind(), resource.GetNamespace(), resource.GetName())
|
||||
glog.V(4).Infof("Mutate overlay in rule '%s' successfully applied on %s/%s/%s", rule.Name, resource.GetKind(), resource.GetNamespace(), resource.GetName())
|
||||
}
|
||||
|
||||
resp.PolicyResponse.Rules = append(resp.PolicyResponse.Rules, ruleResponse)
|
||||
|
@ -156,7 +156,7 @@ var podTemplateRule = kyverno.Rule{
|
|||
"template": map[string]interface{}{
|
||||
"metadata": map[string]interface{}{
|
||||
"annotations": map[string]interface{}{
|
||||
"+(pod-policies.kyverno.io/autogen-applied)": "true",
|
||||
"pod-policies.kyverno.io/autogen-applied": "true",
|
||||
},
|
||||
},
|
||||
},
|
||||
|
|
|
@ -25,12 +25,11 @@ func (ri RuleType) String() string {
|
|||
}
|
||||
|
||||
// ApplyPatches patches given resource with given patches and returns patched document
|
||||
// return origin resource if any error occurs
|
||||
func ApplyPatches(resource []byte, patches [][]byte) ([]byte, error) {
|
||||
joinedPatches := JoinPatches(patches)
|
||||
patch, err := jsonpatch.DecodePatch(joinedPatches)
|
||||
if err != nil {
|
||||
return resource, err
|
||||
return nil, err
|
||||
}
|
||||
|
||||
patchedDocument, err := patch.Apply(resource)
|
||||
|
|
|
@ -1,251 +0,0 @@
|
|||
package apply
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
|
||||
"github.com/golang/glog"
|
||||
kyverno "github.com/nirmata/kyverno/pkg/api/kyverno/v1"
|
||||
"github.com/nirmata/kyverno/pkg/engine"
|
||||
"github.com/spf13/cobra"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
unstructured "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
schema "k8s.io/apimachinery/pkg/runtime/schema"
|
||||
yaml "k8s.io/apimachinery/pkg/util/yaml"
|
||||
memory "k8s.io/client-go/discovery/cached/memory"
|
||||
dynamic "k8s.io/client-go/dynamic"
|
||||
kubernetes "k8s.io/client-go/kubernetes"
|
||||
"k8s.io/client-go/kubernetes/scheme"
|
||||
"k8s.io/client-go/restmapper"
|
||||
)
|
||||
|
||||
const (
|
||||
applyExample = ` # Apply a policy to the resource.
|
||||
kyverno apply @policy.yaml @resource.yaml
|
||||
kyverno apply @policy.yaml @resourceDir/
|
||||
kyverno apply @policy.yaml @resource.yaml --kubeconfig=$PATH_TO_KUBECONFIG_FILE`
|
||||
|
||||
defaultYamlSeparator = "---"
|
||||
)
|
||||
|
||||
// NewCmdApply returns the apply command for kyverno
|
||||
func NewCmdApply(in io.Reader, out, errout io.Writer) *cobra.Command {
|
||||
var kubeconfig string
|
||||
cmd := &cobra.Command{
|
||||
Use: "apply",
|
||||
Short: "Apply policy on the resource(s)",
|
||||
Example: applyExample,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
policy, resources := complete(kubeconfig, args)
|
||||
output := applyPolicy(policy, resources)
|
||||
fmt.Printf("%v\n", output)
|
||||
},
|
||||
}
|
||||
|
||||
cmd.Flags().StringVar(&kubeconfig, "kubeconfig", "", "path to kubeconfig file")
|
||||
return cmd
|
||||
}
|
||||
|
||||
func complete(kubeconfig string, args []string) (*kyverno.ClusterPolicy, []*resourceInfo) {
|
||||
policyDir, resourceDir, err := validateDir(args)
|
||||
if err != nil {
|
||||
glog.Errorf("Failed to parse file path, err: %v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
// extract policy
|
||||
policy, err := extractPolicy(policyDir)
|
||||
if err != nil {
|
||||
glog.Errorf("Failed to extract policy: %v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
// extract rawResource
|
||||
resources, err := extractResource(resourceDir, kubeconfig)
|
||||
if err != nil {
|
||||
glog.Errorf("Failed to parse resource: %v", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
return policy, resources
|
||||
}
|
||||
|
||||
func applyPolicy(policy *kyverno.ClusterPolicy, resources []*resourceInfo) (output string) {
|
||||
for _, resource := range resources {
|
||||
patchedDocument, err := applyPolicyOnRaw(policy, resource.rawResource, resource.gvk)
|
||||
if err != nil {
|
||||
glog.Errorf("Error applying policy on resource %s, err: %v\n", resource.gvk.Kind, err)
|
||||
continue
|
||||
}
|
||||
|
||||
out, err := prettyPrint(patchedDocument)
|
||||
if err != nil {
|
||||
glog.Errorf("JSON parse error: %v\n", err)
|
||||
continue
|
||||
}
|
||||
|
||||
output = output + fmt.Sprintf("---\n%s", string(out))
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func applyPolicyOnRaw(policy *kyverno.ClusterPolicy, rawResource []byte, gvk *metav1.GroupVersionKind) ([]byte, error) {
|
||||
patchedResource := rawResource
|
||||
var err error
|
||||
|
||||
rname := engine.ParseNameFromObject(rawResource)
|
||||
rns := engine.ParseNamespaceFromObject(rawResource)
|
||||
resource, err := ConvertToUnstructured(rawResource)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
//TODO check if the kind information is present resource
|
||||
// Process Mutation
|
||||
engineResponse := engine.Mutate(engine.PolicyContext{Policy: *policy, NewResource: *resource})
|
||||
if !engineResponse.IsSuccesful() {
|
||||
glog.Infof("Failed to apply policy %s on resource %s/%s", policy.Name, rname, rns)
|
||||
for _, r := range engineResponse.PolicyResponse.Rules {
|
||||
glog.Warning(r.Message)
|
||||
}
|
||||
} else if len(engineResponse.PolicyResponse.Rules) > 0 {
|
||||
glog.Infof("Mutation from policy %s has applied succesfully to %s %s/%s", policy.Name, gvk.Kind, rname, rns)
|
||||
|
||||
// Process Validation
|
||||
engineResponse := engine.Validate(engine.PolicyContext{Policy: *policy, NewResource: *resource})
|
||||
|
||||
if !engineResponse.IsSuccesful() {
|
||||
glog.Infof("Failed to apply policy %s on resource %s/%s", policy.Name, rname, rns)
|
||||
for _, r := range engineResponse.PolicyResponse.Rules {
|
||||
glog.Warning(r.Message)
|
||||
}
|
||||
return patchedResource, fmt.Errorf("policy %s on resource %s/%s not satisfied", policy.Name, rname, rns)
|
||||
} else if len(engineResponse.PolicyResponse.Rules) > 0 {
|
||||
glog.Infof("Validation from policy %s has applied succesfully to %s %s/%s", policy.Name, gvk.Kind, rname, rns)
|
||||
}
|
||||
}
|
||||
return patchedResource, nil
|
||||
}
|
||||
|
||||
func extractPolicy(fileDir string) (*kyverno.ClusterPolicy, error) {
|
||||
policy := &kyverno.ClusterPolicy{}
|
||||
|
||||
file, err := loadFile(fileDir)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to load file: %v", err)
|
||||
}
|
||||
|
||||
policyBytes, err := yaml.ToJSON(file)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := json.Unmarshal(policyBytes, policy); err != nil {
|
||||
return nil, fmt.Errorf("failed to decode policy %s, err: %v", policy.Name, err)
|
||||
}
|
||||
|
||||
if policy.TypeMeta.Kind != "ClusterPolicy" {
|
||||
return nil, fmt.Errorf("failed to parse policy")
|
||||
}
|
||||
|
||||
return policy, nil
|
||||
}
|
||||
|
||||
type resourceInfo struct {
|
||||
rawResource []byte
|
||||
gvk *metav1.GroupVersionKind
|
||||
}
|
||||
|
||||
func extractResource(fileDir, kubeconfig string) ([]*resourceInfo, error) {
|
||||
var files []string
|
||||
var resources []*resourceInfo
|
||||
|
||||
// check if applied on multiple resources
|
||||
isDir, err := isDir(fileDir)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if isDir {
|
||||
files, err = scanDir(fileDir)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
} else {
|
||||
files = []string{fileDir}
|
||||
}
|
||||
|
||||
for _, dir := range files {
|
||||
data, err := loadFile(dir)
|
||||
if err != nil {
|
||||
glog.Warningf("Error while loading file: %v\n", err)
|
||||
continue
|
||||
}
|
||||
|
||||
dd := bytes.Split(data, []byte(defaultYamlSeparator))
|
||||
|
||||
for _, d := range dd {
|
||||
decode := scheme.Codecs.UniversalDeserializer().Decode
|
||||
obj, gvk, err := decode([]byte(d), nil, nil)
|
||||
if err != nil {
|
||||
glog.Warningf("Error while decoding YAML object, err: %s\n", err)
|
||||
continue
|
||||
}
|
||||
|
||||
actualObj, err := convertToActualObject(kubeconfig, gvk, obj)
|
||||
if err != nil {
|
||||
glog.V(3).Infof("Failed to convert resource %s to actual k8s object: %v\n", gvk.Kind, err)
|
||||
glog.V(3).Infof("Apply policy on raw resource.\n")
|
||||
}
|
||||
|
||||
raw, err := json.Marshal(actualObj)
|
||||
if err != nil {
|
||||
glog.Warningf("Error while marshalling manifest, err: %v\n", err)
|
||||
continue
|
||||
}
|
||||
|
||||
gvkInfo := &metav1.GroupVersionKind{Group: gvk.Group, Version: gvk.Version, Kind: gvk.Kind}
|
||||
resources = append(resources, &resourceInfo{rawResource: raw, gvk: gvkInfo})
|
||||
}
|
||||
}
|
||||
|
||||
return resources, err
|
||||
}
|
||||
|
||||
func convertToActualObject(kubeconfig string, gvk *schema.GroupVersionKind, obj runtime.Object) (interface{}, error) {
|
||||
clientConfig, err := createClientConfig(kubeconfig)
|
||||
if err != nil {
|
||||
return obj, err
|
||||
}
|
||||
|
||||
dynamicClient, err := dynamic.NewForConfig(clientConfig)
|
||||
if err != nil {
|
||||
return obj, err
|
||||
}
|
||||
|
||||
kclient, err := kubernetes.NewForConfig(clientConfig)
|
||||
if err != nil {
|
||||
return obj, err
|
||||
}
|
||||
|
||||
asUnstructured := &unstructured.Unstructured{}
|
||||
if err := scheme.Scheme.Convert(obj, asUnstructured, nil); err != nil {
|
||||
return obj, err
|
||||
}
|
||||
|
||||
mapper := restmapper.NewDeferredDiscoveryRESTMapper(memory.NewMemCacheClient(kclient.Discovery()))
|
||||
mapping, err := mapper.RESTMapping(schema.GroupKind{Group: gvk.Group, Kind: gvk.Kind}, gvk.Version)
|
||||
if err != nil {
|
||||
return obj, err
|
||||
}
|
||||
|
||||
actualObj, err := dynamicClient.Resource(mapping.Resource).Namespace("default").Create(asUnstructured, metav1.CreateOptions{DryRun: []string{metav1.DryRunAll}})
|
||||
if err != nil {
|
||||
return obj, err
|
||||
}
|
||||
|
||||
return actualObj, nil
|
||||
}
|
267
pkg/kyverno/apply/command.go
Normal file
267
pkg/kyverno/apply/command.go
Normal file
|
@ -0,0 +1,267 @@
|
|||
package apply
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"strings"
|
||||
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
|
||||
"github.com/nirmata/kyverno/pkg/config"
|
||||
"k8s.io/client-go/discovery"
|
||||
|
||||
"k8s.io/apimachinery/pkg/util/yaml"
|
||||
|
||||
"github.com/nirmata/kyverno/pkg/engine"
|
||||
|
||||
engineutils "github.com/nirmata/kyverno/pkg/engine/utils"
|
||||
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
|
||||
v1 "github.com/nirmata/kyverno/pkg/api/kyverno/v1"
|
||||
"github.com/spf13/cobra"
|
||||
yamlv2 "gopkg.in/yaml.v2"
|
||||
"k8s.io/client-go/kubernetes/scheme"
|
||||
)
|
||||
|
||||
func Command() *cobra.Command {
|
||||
var resourcePath, kubeConfig string
|
||||
|
||||
cmd := &cobra.Command{
|
||||
Use: "apply",
|
||||
Short: "Applies policies on resources",
|
||||
Example: fmt.Sprintf("To apply on a resource:\nkyverno apply /path/to/policy1 /path/to/policy2 --resource=/path/to/resource\n\nTo apply on a cluster\nkyverno apply /path/to/policy1 /path/to/policy2 --kubeConfig=/path/to/kubeConfig"),
|
||||
RunE: func(cmd *cobra.Command, policyPaths []string) error {
|
||||
if resourcePath == "" && kubeConfig == "" {
|
||||
fmt.Println("Specify path to resource file or kube config")
|
||||
}
|
||||
|
||||
var policies []*v1.ClusterPolicy
|
||||
for _, policyPath := range policyPaths {
|
||||
policy, err := getPolicy(policyPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
policies = append(policies, policy)
|
||||
}
|
||||
|
||||
resources, err := getResources(policies, kubeConfig, resourcePath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for i, policy := range policies {
|
||||
for j, resource := range resources {
|
||||
if !(j == 0 && i == 0) {
|
||||
fmt.Printf("\n\n=======================================================================\n")
|
||||
}
|
||||
|
||||
err = applyPolicyOnResource(policy, resource)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
cmd.Flags().StringVar(&resourcePath, "resource", "", "path to resource file")
|
||||
cmd.Flags().StringVar(&kubeConfig, "kubeConfig", "", "path to .kube/config file")
|
||||
return cmd
|
||||
}
|
||||
|
||||
func getResources(policies []*v1.ClusterPolicy, kubeConfig, resourcePath string) ([]*unstructured.Unstructured, error) {
|
||||
var resources []*unstructured.Unstructured
|
||||
var err error
|
||||
|
||||
if kubeConfig != "" {
|
||||
var resourceTypesMap = make(map[string]bool)
|
||||
var resourceTypes []string
|
||||
for _, policy := range policies {
|
||||
for _, rule := range policy.Spec.Rules {
|
||||
for _, kind := range rule.MatchResources.Kinds {
|
||||
resourceTypesMap[kind] = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for kind := range resourceTypesMap {
|
||||
resourceTypes = append(resourceTypes, kind)
|
||||
}
|
||||
|
||||
resources, err = getResourcesOfTypeFromCluster(resourceTypes, kubeConfig)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
if resourcePath != "" {
|
||||
resource, err := getResource(resourcePath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
resources = append(resources, resource)
|
||||
}
|
||||
|
||||
return resources, nil
|
||||
}
|
||||
|
||||
func getResourcesOfTypeFromCluster(resourceTypes []string, kubeConfig string) ([]*unstructured.Unstructured, error) {
|
||||
var resources []*unstructured.Unstructured
|
||||
|
||||
clientConfig, err := config.CreateClientConfig(kubeConfig)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
dClient, err := discovery.NewDiscoveryClientForConfig(clientConfig)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
//var kindToListApi = map[string]string{
|
||||
// "pod": "/api/v1/pods",
|
||||
// "services": "/api/v1/services",
|
||||
//}
|
||||
|
||||
for _, kind := range resourceTypes {
|
||||
listObjectRaw, err := dClient.RESTClient().Get().RequestURI("/api/v1/" + strings.ToLower(kind) + "s").Do().Raw()
|
||||
if err != nil {
|
||||
log.Println(2, err)
|
||||
}
|
||||
|
||||
listObject, err := engineutils.ConvertToUnstructured(listObjectRaw)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
resourceList, err := listObject.ToList()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
version := resourceList.GetAPIVersion()
|
||||
for _, resource := range resourceList.Items {
|
||||
resource.SetGroupVersionKind(schema.GroupVersionKind{
|
||||
Group: "",
|
||||
Version: version,
|
||||
Kind: kind,
|
||||
})
|
||||
resources = append(resources, resource.DeepCopy())
|
||||
}
|
||||
}
|
||||
|
||||
return resources, nil
|
||||
}
|
||||
|
||||
func getPolicy(path string) (*v1.ClusterPolicy, error) {
|
||||
policy := &v1.ClusterPolicy{}
|
||||
|
||||
file, err := ioutil.ReadFile(path)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to load file: %v", err)
|
||||
}
|
||||
|
||||
policyBytes, err := yaml.ToJSON(file)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := json.Unmarshal(policyBytes, policy); err != nil {
|
||||
return nil, fmt.Errorf("failed to decode policy %s, err: %v", policy.Name, err)
|
||||
}
|
||||
|
||||
if policy.TypeMeta.Kind != "ClusterPolicy" {
|
||||
return nil, fmt.Errorf("failed to parse policy")
|
||||
}
|
||||
|
||||
return policy, nil
|
||||
}
|
||||
|
||||
func getResource(path string) (*unstructured.Unstructured, error) {
|
||||
|
||||
resourceYaml, err := ioutil.ReadFile(path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
decode := scheme.Codecs.UniversalDeserializer().Decode
|
||||
resourceObject, metaData, err := decode(resourceYaml, nil, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
resourceUnstructured, err := runtime.DefaultUnstructuredConverter.ToUnstructured(&resourceObject)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
resourceJSON, err := json.Marshal(resourceUnstructured)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
resource, err := engineutils.ConvertToUnstructured(resourceJSON)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
resource.SetGroupVersionKind(*metaData)
|
||||
|
||||
if resource.GetNamespace() == "" {
|
||||
resource.SetNamespace("default")
|
||||
}
|
||||
|
||||
return resource, nil
|
||||
}
|
||||
|
||||
func applyPolicyOnResource(policy *v1.ClusterPolicy, resource *unstructured.Unstructured) error {
|
||||
|
||||
fmt.Printf("\n\nApplying Policy %s on Resource %s/%s/%s/%s", policy.Name, resource.GetNamespace(), resource.GetKind(), resource.GetName(), resource.GetUID())
|
||||
|
||||
mutateResponse := engine.Mutate(engine.PolicyContext{Policy: *policy, NewResource: *resource})
|
||||
if !mutateResponse.IsSuccesful() {
|
||||
fmt.Printf("\n\nMutation:")
|
||||
fmt.Printf("\nFailed to apply mutation")
|
||||
for i, r := range mutateResponse.PolicyResponse.Rules {
|
||||
fmt.Printf("\n%d. %s", i+1, r.Message)
|
||||
}
|
||||
fmt.Printf("\n\n")
|
||||
} else {
|
||||
if len(mutateResponse.PolicyResponse.Rules) > 0 {
|
||||
fmt.Printf("\n\nMutation:")
|
||||
fmt.Printf("\nMutation has been applied succesfully")
|
||||
yamlEncodedResource, err := yamlv2.Marshal(mutateResponse.PatchedResource.Object)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
fmt.Printf("\n\n" + strings.ReplaceAll(string(yamlEncodedResource), "\n", "\n"))
|
||||
}
|
||||
}
|
||||
|
||||
validateResponse := engine.Validate(engine.PolicyContext{Policy: *policy, NewResource: mutateResponse.PatchedResource})
|
||||
if !validateResponse.IsSuccesful() {
|
||||
fmt.Printf("\n\nValidation:")
|
||||
fmt.Printf("\nResource is invalid")
|
||||
for i, r := range validateResponse.PolicyResponse.Rules {
|
||||
fmt.Printf("\n%d. %s", i+1, r.Message)
|
||||
}
|
||||
fmt.Printf("\n\n")
|
||||
} else {
|
||||
if len(validateResponse.PolicyResponse.Rules) > 0 {
|
||||
fmt.Printf("\n\nValidation:")
|
||||
fmt.Printf("\nResource is valid")
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
|
@ -1,105 +0,0 @@
|
|||
package apply
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/golang/glog"
|
||||
yamlv2 "gopkg.in/yaml.v2"
|
||||
unstructured "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
rest "k8s.io/client-go/rest"
|
||||
clientcmd "k8s.io/client-go/tools/clientcmd"
|
||||
)
|
||||
|
||||
func createClientConfig(kubeconfig string) (*rest.Config, error) {
|
||||
if kubeconfig == "" {
|
||||
defaultKC := defaultKubeconfigPath()
|
||||
if _, err := os.Stat(defaultKC); err == nil {
|
||||
kubeconfig = defaultKC
|
||||
}
|
||||
}
|
||||
|
||||
return clientcmd.BuildConfigFromFlags("", kubeconfig)
|
||||
}
|
||||
|
||||
func defaultKubeconfigPath() string {
|
||||
home, err := os.UserHomeDir()
|
||||
if err != nil {
|
||||
glog.Warningf("Warning: failed to get home dir: %v\n", err)
|
||||
return ""
|
||||
}
|
||||
|
||||
return filepath.Join(home, ".kube", "config")
|
||||
}
|
||||
|
||||
func loadFile(fileDir string) ([]byte, error) {
|
||||
if _, err := os.Stat(fileDir); os.IsNotExist(err) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return ioutil.ReadFile(fileDir)
|
||||
}
|
||||
|
||||
func validateDir(args []string) (policyDir, resourceDir string, err error) {
|
||||
if len(args) != 2 {
|
||||
return "", "", fmt.Errorf("missing policy and/or resource manifest")
|
||||
}
|
||||
|
||||
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) {
|
||||
out := make(map[interface{}]interface{})
|
||||
if err := yamlv2.Unmarshal(data, &out); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
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 {
|
||||
return fmt.Errorf("prevent panic by handling failure accessing a path %q: %v", dir, err)
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
func ConvertToUnstructured(data []byte) (*unstructured.Unstructured, error) {
|
||||
resource := &unstructured.Unstructured{}
|
||||
err := resource.UnmarshalJSON(data)
|
||||
if err != nil {
|
||||
glog.V(4).Infof("failed to unmarshall resource: %v", err)
|
||||
return nil, err
|
||||
}
|
||||
return resource, nil
|
||||
}
|
|
@ -1,27 +0,0 @@
|
|||
package cmd
|
||||
|
||||
import (
|
||||
"io"
|
||||
"os"
|
||||
|
||||
"github.com/nirmata/kyverno/pkg/kyverno/apply"
|
||||
"github.com/nirmata/kyverno/pkg/kyverno/version"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
// NewDefaultKyvernoCommand ...
|
||||
func NewDefaultKyvernoCommand() *cobra.Command {
|
||||
return NewKyvernoCommand(os.Stdin, os.Stdout, os.Stderr)
|
||||
}
|
||||
|
||||
// NewKyvernoCommand returns the new kynerno command
|
||||
func NewKyvernoCommand(in io.Reader, out, errout io.Writer) *cobra.Command {
|
||||
cmds := &cobra.Command{
|
||||
Use: "kyverno",
|
||||
Short: "kyverno manages native policies of Kubernetes",
|
||||
}
|
||||
|
||||
cmds.AddCommand(apply.NewCmdApply(in, out, errout))
|
||||
cmds.AddCommand(version.NewCmdVersion(out))
|
||||
return cmds
|
||||
}
|
48
pkg/kyverno/main.go
Normal file
48
pkg/kyverno/main.go
Normal file
|
@ -0,0 +1,48 @@
|
|||
package kyverno
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"os"
|
||||
|
||||
"github.com/nirmata/kyverno/pkg/kyverno/validate"
|
||||
|
||||
"github.com/nirmata/kyverno/pkg/kyverno/apply"
|
||||
|
||||
"github.com/nirmata/kyverno/pkg/kyverno/version"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
func CLI() {
|
||||
cli := &cobra.Command{
|
||||
Use: "kyverno",
|
||||
Short: "kyverno manages native policies of Kubernetes",
|
||||
}
|
||||
|
||||
configureGlog(cli)
|
||||
|
||||
commands := []*cobra.Command{
|
||||
version.Command(),
|
||||
apply.Command(),
|
||||
validate.Command(),
|
||||
}
|
||||
|
||||
cli.AddCommand(commands...)
|
||||
|
||||
if err := cli.Execute(); err != nil {
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
func configureGlog(cli *cobra.Command) {
|
||||
flag.Parse()
|
||||
flag.Set("logtostderr", "true")
|
||||
|
||||
cli.PersistentFlags().AddGoFlagSet(flag.CommandLine)
|
||||
cli.PersistentFlags().MarkHidden("alsologtostderr")
|
||||
cli.PersistentFlags().MarkHidden("logtostderr")
|
||||
cli.PersistentFlags().MarkHidden("log_dir")
|
||||
cli.PersistentFlags().MarkHidden("log_backtrace_at")
|
||||
cli.PersistentFlags().MarkHidden("stderrthreshold")
|
||||
cli.PersistentFlags().MarkHidden("vmodule")
|
||||
}
|
64
pkg/kyverno/validate/command.go
Normal file
64
pkg/kyverno/validate/command.go
Normal file
|
@ -0,0 +1,64 @@
|
|||
package validate
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
|
||||
policyvalidate "github.com/nirmata/kyverno/pkg/engine/policy"
|
||||
|
||||
v1 "github.com/nirmata/kyverno/pkg/api/kyverno/v1"
|
||||
"github.com/spf13/cobra"
|
||||
"k8s.io/apimachinery/pkg/util/yaml"
|
||||
)
|
||||
|
||||
func Command() *cobra.Command {
|
||||
cmd := &cobra.Command{
|
||||
Use: "validate",
|
||||
Short: "Validates kyverno policies",
|
||||
Example: "kyverno validate /path/to/policy1 /path/to/policy2",
|
||||
RunE: func(cmd *cobra.Command, policyPaths []string) error {
|
||||
for _, policyPath := range policyPaths {
|
||||
policy, err := getPolicy(policyPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = policyvalidate.Validate(*policy)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
fmt.Println("Policy " + policy.Name + " is valid")
|
||||
}
|
||||
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
||||
func getPolicy(path string) (*v1.ClusterPolicy, error) {
|
||||
policy := &v1.ClusterPolicy{}
|
||||
|
||||
file, err := ioutil.ReadFile(path)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to load file: %v", err)
|
||||
}
|
||||
|
||||
policyBytes, err := yaml.ToJSON(file)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := json.Unmarshal(policyBytes, policy); err != nil {
|
||||
return nil, fmt.Errorf("failed to decode policy %s, err: %v", policy.Name, err)
|
||||
}
|
||||
|
||||
if policy.TypeMeta.Kind != "ClusterPolicy" {
|
||||
return nil, fmt.Errorf("failed to parse policy")
|
||||
}
|
||||
|
||||
return policy, nil
|
||||
}
|
21
pkg/kyverno/version/command.go
Normal file
21
pkg/kyverno/version/command.go
Normal file
|
@ -0,0 +1,21 @@
|
|||
package version
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/nirmata/kyverno/pkg/version"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
func Command() *cobra.Command {
|
||||
return &cobra.Command{
|
||||
Use: "version",
|
||||
Short: "Shows current version of kyverno",
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
fmt.Printf("Version: %s\n", version.BuildVersion)
|
||||
fmt.Printf("Time: %s\n", version.BuildTime)
|
||||
fmt.Printf("Git commit ID: %s\n", version.BuildHash)
|
||||
return nil
|
||||
},
|
||||
}
|
||||
}
|
|
@ -1,29 +0,0 @@
|
|||
package version
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
|
||||
"github.com/nirmata/kyverno/pkg/version"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
// NewCmdVersion is a command to display the build version
|
||||
func NewCmdVersion(cmdOut io.Writer) *cobra.Command {
|
||||
|
||||
versionCmd := &cobra.Command{
|
||||
Use: "version",
|
||||
Short: "",
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
showVersion()
|
||||
},
|
||||
}
|
||||
|
||||
return versionCmd
|
||||
}
|
||||
|
||||
func showVersion() {
|
||||
fmt.Printf("Version: %s\n", version.BuildVersion)
|
||||
fmt.Printf("Time: %s\n", version.BuildTime)
|
||||
fmt.Printf("Git commit ID: %s\n", version.BuildHash)
|
||||
}
|
|
@ -63,7 +63,6 @@ func (wrc *WebhookRegistrationClient) constructOwner() v1.OwnerReference {
|
|||
|
||||
func generateDebugWebhook(name, url string, caData []byte, validate bool, timeoutSeconds int32, resource, apiGroups, apiVersions string, operationTypes []admregapi.OperationType) admregapi.Webhook {
|
||||
sideEffect := admregapi.SideEffectClassNoneOnDryRun
|
||||
failurePolicy := admregapi.Ignore
|
||||
return admregapi.Webhook{
|
||||
Name: name,
|
||||
ClientConfig: admregapi.WebhookClientConfig{
|
||||
|
@ -89,13 +88,11 @@ func generateDebugWebhook(name, url string, caData []byte, validate bool, timeou
|
|||
},
|
||||
AdmissionReviewVersions: []string{"v1beta1"},
|
||||
TimeoutSeconds: &timeoutSeconds,
|
||||
FailurePolicy: &failurePolicy,
|
||||
}
|
||||
}
|
||||
|
||||
func generateWebhook(name, servicePath string, caData []byte, validation bool, timeoutSeconds int32, resource, apiGroups, apiVersions string, operationTypes []admregapi.OperationType) admregapi.Webhook {
|
||||
sideEffect := admregapi.SideEffectClassNoneOnDryRun
|
||||
failurePolicy := admregapi.Ignore
|
||||
return admregapi.Webhook{
|
||||
Name: name,
|
||||
ClientConfig: admregapi.WebhookClientConfig{
|
||||
|
@ -125,6 +122,5 @@ func generateWebhook(name, servicePath string, caData []byte, validation bool, t
|
|||
},
|
||||
AdmissionReviewVersions: []string{"v1beta1"},
|
||||
TimeoutSeconds: &timeoutSeconds,
|
||||
FailurePolicy: &failurePolicy,
|
||||
}
|
||||
}
|
||||
|
|
|
@ -91,8 +91,6 @@ func (ws *WebhookServer) HandleMutation(request *v1beta1.AdmissionRequest, resou
|
|||
// gather patches
|
||||
patches = append(patches, engineResponse.GetPatches()...)
|
||||
glog.V(4).Infof("Mutation from policy %s has applied succesfully to %s %s/%s", policy.Name, request.Kind.Kind, resource.GetNamespace(), resource.GetName())
|
||||
|
||||
policyContext.NewResource = engineResponse.PatchedResource
|
||||
}
|
||||
|
||||
// generate annotations
|
||||
|
|
|
@ -3,7 +3,6 @@ kind: ClusterPolicy
|
|||
metadata:
|
||||
name: disallow-default-namespace
|
||||
annotations:
|
||||
pod-policies.kyverno.io/autogen-controllers: none
|
||||
policies.kyverno.io/category: Workload Isolation
|
||||
policies.kyverno.io/description: Kubernetes namespaces are an optional feature
|
||||
that provide a way to segment and isolate cluster resources across multiple
|
||||
|
|
Loading…
Add table
Reference in a new issue