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

Merge branch 'master' into 671_cleanUp_generate

This commit is contained in:
shivkumar dudhani 2020-02-18 13:56:01 -08:00
commit 80c618bf98
22 changed files with 811 additions and 459 deletions

View file

@ -0,0 +1,9 @@
package main
import (
"github.com/nirmata/kyverno/pkg/kyverno"
)
func main() {
kyverno.CLI()
}

View file

@ -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()
}

View file

@ -6,6 +6,8 @@ package main
import (
"flag"
"os"
"regexp"
"strconv"
"sync"
"time"
@ -44,6 +46,11 @@ func main() {
glog.Fatalf("Error creating client: %v\n", err)
}
// Exit for unsupported version of kubernetes cluster
// https://github.com/nirmata/kyverno/issues/700
// - supported from v1.12.7+
isVersionSupported(client)
requests := []request{
// Resource
{validatingWebhookConfigKind, config.ValidatingWebhookConfigurationName},
@ -206,3 +213,32 @@ func merge(done <-chan struct{}, stopCh <-chan struct{}, processes ...<-chan err
}()
return out
}
func isVersionSupported(client *client.Client) {
serverVersion, err := client.DiscoveryClient.GetServerVersion()
if err != nil {
glog.Fatalf("Failed to get kubernetes server version: %v\n", err)
}
exp := regexp.MustCompile(`v(\d*).(\d*).(\d*)`)
groups := exp.FindAllStringSubmatch(serverVersion.String(), -1)
if len(groups) != 1 || len(groups[0]) != 4 {
glog.Fatalf("Failed to extract kubernetes server version: %v.err %v\n", serverVersion, err)
}
// convert string to int
// assuming the version are always intergers
major, err := strconv.Atoi(groups[0][1])
if err != nil {
glog.Fatalf("Failed to extract kubernetes major server version: %v.err %v\n", serverVersion, err)
}
minor, err := strconv.Atoi(groups[0][2])
if err != nil {
glog.Fatalf("Failed to extract kubernetes minor server version: %v.err %v\n", serverVersion, err)
}
sub, err := strconv.Atoi(groups[0][3])
if err != nil {
glog.Fatalf("Failed to extract kubernetes sub minor server version:%v. err %v\n", serverVersion, err)
}
if major <= 1 && minor <= 12 && sub < 7 {
glog.Fatalf("Unsupported kubernetes server version %s. Kyverno is supported from version v1.12.7+", serverVersion)
}
}

6
go.mod
View file

@ -9,14 +9,16 @@ require (
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b
github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7 // indirect
github.com/googleapis/gnostic v0.3.1
github.com/gregjones/httpcache v0.0.0-20190611155906-901d90724c79 // indirect
github.com/hashicorp/golang-lru v0.5.3 // indirect
github.com/imdario/mergo v0.3.8 // indirect
github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af
github.com/json-iterator/go v1.1.9 // indirect
github.com/minio/minio v0.0.0-20200114012931-30922148fbb5
github.com/ory/go-acc v0.1.0 // indirect
github.com/peterbourgon/diskv v2.0.1+incompatible // indirect
github.com/spf13/cobra v0.0.5
github.com/spf13/pflag v1.0.5
github.com/spf13/pflag v1.0.5 // indirect
github.com/tevino/abool v0.0.0-20170917061928-9b9efcf221b5
golang.org/x/crypto v0.0.0-20200109152110-61a87790db17 // indirect
golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553 // indirect
@ -29,10 +31,12 @@ require (
gotest.tools v2.2.0+incompatible
k8s.io/api v0.0.0-20190409021203-6e4e0e4f393b
k8s.io/apimachinery v0.0.0-20190404173353-6a84e37a896d
k8s.io/cli-runtime v0.0.0-20191004110135-b9eb767d2e1a
k8s.io/client-go v11.0.1-0.20190516230509-ae8359b20417+incompatible
k8s.io/klog v1.0.0 // indirect
k8s.io/kube-openapi v0.0.0-20191107075043-30be4d16710a
k8s.io/utils v0.0.0-20200109141947-94aeca20bf09 // indirect
sigs.k8s.io/kustomize v2.0.3+incompatible // indirect
)
// Added for go1.13 migration https://github.com/golang/go/issues/32805

19
go.sum
View file

@ -24,7 +24,9 @@ github.com/Microsoft/go-winio v0.4.11/go.mod h1:VhR8bwka0BXejwEJY73c50VrPtXAaKcy
github.com/NYTimes/gziphandler v0.0.0-20170623195520-56545f4a5d46/go.mod h1:3wb06e3pkSAbeQ52E9H9iFoQsEEwGN64994WTCIhntQ=
github.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5/go.mod h1:lmUJ/7eu/Q8D7ML55dXQrVaamCz2vxCfdQBasLZfHKk=
github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
github.com/PuerkitoBio/purell v1.0.0 h1:0GoNN3taZV6QI81IXgCbxMyEaJDXMSIjArYBCYzVVvs=
github.com/PuerkitoBio/purell v1.0.0/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0=
github.com/PuerkitoBio/urlesc v0.0.0-20160726150825-5bd2802263f2 h1:JCHLVE3B+kJde7bIEo5N4J+ZbLhp0J1Fs+ulyRws4gE=
github.com/PuerkitoBio/urlesc v0.0.0-20160726150825-5bd2802263f2/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE=
github.com/Shopify/sarama v1.24.1/go.mod h1:fGP8eQ6PugKEI0iUETYYtnP6d1pH/bdDMTel1X5ajsU=
github.com/Shopify/toxiproxy v2.1.4+incompatible/go.mod h1:OXgGpZ6Cli1/URJOF1DMxUHB2q5Ap20/P/eIdh4G0pI=
@ -95,6 +97,7 @@ github.com/eapache/queue v1.1.0/go.mod h1:6eCeP0CKFpHLu8blIFXhExK/dRa7WDZfr6jVFP
github.com/eclipse/paho.mqtt.golang v1.2.0/go.mod h1:H9keYFcgq3Qr5OUJm/JZI/i6U7joQ8SYLhZwfeOo6Ts=
github.com/elazarl/go-bindata-assetfs v1.0.0/go.mod h1:v+YaWX3bdea5J/mo8dSETolEo7R71Vk1u8bnjau5yw4=
github.com/elazarl/goproxy v0.0.0-20181003060214-f58a169a71a5/go.mod h1:/Zj4wYkgs4iZTTu3o/KG3Itv/qCCa8VVMlb3i9OVuzc=
github.com/emicklei/go-restful v0.0.0-20170410110728-ff4f55a20633 h1:H2pdYOb3KQ1/YsqVWoWNLQO+fusocsw354rqGTZtAgw=
github.com/emicklei/go-restful v0.0.0-20170410110728-ff4f55a20633/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs=
github.com/evanphx/json-patch v4.5.0+incompatible h1:ouOWdg56aJriqS0huScTkVXPC5IcNrDCXZ6OoTAWu7M=
github.com/evanphx/json-patch v4.5.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk=
@ -108,6 +111,7 @@ github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
github.com/ghodss/yaml v0.0.0-20150909031657-73d445a93680/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
github.com/ghodss/yaml v1.0.1-0.20190212211648-25d852aebe32 h1:Mn26/9ZMNWSw9C9ERFA1PUxfmGpolnw2v0bKOREu5ew=
github.com/ghodss/yaml v1.0.1-0.20190212211648-25d852aebe32/go.mod h1:GIjDIg/heH5DOkXY3YJ/wNhfHsQHoXGjl8G8amsYQ1I=
github.com/go-bindata/go-bindata v3.1.1+incompatible/go.mod h1:xK8Dsgwmeed+BBsSy2XTopBn/8uK2HWuGSnA11C3Joo=
github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
@ -116,9 +120,13 @@ github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9
github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
github.com/go-logr/logr v0.1.0/go.mod h1:ixOQHD9gLJUVQQ2ZOR7zLEifBX6tGkNJF4QyIY7sIas=
github.com/go-ole/go-ole v1.2.4/go.mod h1:XCwSNxSkXRo4vlyPy93sltvi/qJq0jqQhjqQNIwKuxM=
github.com/go-openapi/jsonpointer v0.0.0-20160704185906-46af16f9f7b1 h1:wSt/4CYxs70xbATrGXhokKF1i0tZjENLOo1ioIO13zk=
github.com/go-openapi/jsonpointer v0.0.0-20160704185906-46af16f9f7b1/go.mod h1:+35s3my2LFTysnkMfxsJBAMHj/DoqoB9knIWoYG/Vk0=
github.com/go-openapi/jsonreference v0.0.0-20160704190145-13c6e3589ad9 h1:tF+augKRWlWx0J0B7ZyyKSiTyV6E1zZe+7b3qQlcEf8=
github.com/go-openapi/jsonreference v0.0.0-20160704190145-13c6e3589ad9/go.mod h1:W3Z9FmVs9qj+KR4zFKmDPGiLdk1D9Rlm7cyMvf57TTg=
github.com/go-openapi/spec v0.0.0-20160808142527-6aced65f8501 h1:C1JKChikHGpXwT5UQDFaryIpDtyyGL/CR6C2kB7F1oc=
github.com/go-openapi/spec v0.0.0-20160808142527-6aced65f8501/go.mod h1:J8+jY1nAiCcj+friV/PDoE1/3eeccG9LYBs0tYvLOWc=
github.com/go-openapi/swag v0.0.0-20160704191624-1d0bd113de87 h1:zP3nY8Tk2E6RTkqGYrarZXuzh+ffyLDljLxCy1iJw80=
github.com/go-openapi/swag v0.0.0-20160704191624-1d0bd113de87/go.mod h1:DXUve3Dpr1UfpPtxFw+EFuQ41HhCWZfha5jSVRG7C7I=
github.com/go-sql-driver/mysql v1.4.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w=
github.com/go-sql-driver/mysql v1.4.1/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w=
@ -316,7 +324,9 @@ github.com/golang/protobuf v1.3.2 h1:6nsPYzhq5kReh6QImI3k5qWzO4PEbvbIW2cwSfR/6xs
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
github.com/gomodule/redigo v2.0.0+incompatible/go.mod h1:B4C85qUVwatsJoIUNIfCRsp7qO0iAmpGFZ4EELWSbC4=
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c h1:964Od4U6p2jUkFxvCydnIczKteheJEzHRToSGK3Bnlw=
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
github.com/google/btree v1.0.0 h1:0udJVsspx3VBr5FwtLhQQtuAsVc79tTq0ocGIPAU6qo=
github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
@ -351,6 +361,8 @@ github.com/gorilla/sessions v1.1.3/go.mod h1:8KCfur6+4Mqcc6S0FEfKuN15Vl5MgXW92AE
github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
github.com/gorilla/websocket v1.4.1/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
github.com/gotestyourself/gotestyourself v2.2.0+incompatible/go.mod h1:zZKM6oeNM8k+FRljX1mnzVYeS8wiGgQyvST1/GafPbY=
github.com/gregjones/httpcache v0.0.0-20190611155906-901d90724c79 h1:+ngKgrYPPJrOjhax5N+uePQ0Fh1Z7PheYoUI/0nzkPA=
github.com/gregjones/httpcache v0.0.0-20190611155906-901d90724c79/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA=
github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs=
github.com/grpc-ecosystem/go-grpc-middleware v1.0.1-0.20190118093823-f849b5445de4/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs=
github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk=
@ -443,6 +455,7 @@ github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czP
github.com/magiconair/properties v1.8.1 h1:ZC2Vc7/ZFkGmsVC9KvOjumD+G5lXy2RtTKyzRKO2BQ4=
github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
github.com/mailru/easyjson v0.0.0-20160728113105-d5b7844b561a/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
github.com/mailru/easyjson v0.0.0-20180730094502-03f2033d19d5 h1:0x4qcEHDpruK6ML/m/YSlFUUu0UpRD3I2PHsNCuGnyA=
github.com/mailru/easyjson v0.0.0-20180730094502-03f2033d19d5/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
github.com/markbates/deplist v1.0.4/go.mod h1:gRRbPbbuA8TmMiRvaOzUlRfzfjeCCBqX2A6arxN01MM=
github.com/markbates/deplist v1.0.5/go.mod h1:gRRbPbbuA8TmMiRvaOzUlRfzfjeCCBqX2A6arxN01MM=
@ -566,6 +579,8 @@ github.com/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtP
github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
github.com/pelletier/go-toml v1.4.0 h1:u3Z1r+oOXJIkxqw34zVhyPgjBsm6X2wn21NWs/HfSeg=
github.com/pelletier/go-toml v1.4.0/go.mod h1:PN7xzY2wHTK0K9p34ErDQMlFxa51Fk0OUruD3k1mMwo=
github.com/peterbourgon/diskv v2.0.1+incompatible h1:UBdAOUP5p4RWqPBg048CAvpKN+vxiaj6gdUUzhl4XmI=
github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU=
github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY=
github.com/pierrec/lz4 v2.2.6+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY=
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
@ -950,6 +965,8 @@ k8s.io/api v0.0.0-20190409021203-6e4e0e4f393b h1:aBGgKJUM9Hk/3AE8WaZIApnTxG35kbu
k8s.io/api v0.0.0-20190409021203-6e4e0e4f393b/go.mod h1:iuAfoD4hCxJ8Onx9kaTIt30j7jUFS00AXQi6QMi99vA=
k8s.io/apimachinery v0.0.0-20190404173353-6a84e37a896d h1:Jmdtdt1ZnoGfWWIIik61Z7nKYgO3J+swQJtPYsP9wHA=
k8s.io/apimachinery v0.0.0-20190404173353-6a84e37a896d/go.mod h1:ccL7Eh7zubPUSh9A3USN90/OzHNSVN6zxzde07TDCL0=
k8s.io/cli-runtime v0.0.0-20191004110135-b9eb767d2e1a h1:REMzGxu+NpG9dPRsE9my/fw9iYIecz1S8UFFl6hbe18=
k8s.io/cli-runtime v0.0.0-20191004110135-b9eb767d2e1a/go.mod h1:qWnH3/b8sp/l7EvlDh7ulDU3UWA4P4N1NFbEEP791tM=
k8s.io/client-go v11.0.1-0.20190516230509-ae8359b20417+incompatible h1:bK03DJulJi9j05gwnXUufcs2j7h4M85YFvJ0dIlQ9k4=
k8s.io/client-go v11.0.1-0.20190516230509-ae8359b20417+incompatible/go.mod h1:7vJpHMYJwNQCWgzmNV+VYUl1zCObLyodBc8nIyt8L5s=
k8s.io/gengo v0.0.0-20190128074634-0689ccc1d7d6/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0=
@ -962,6 +979,8 @@ k8s.io/kube-openapi v0.0.0-20191107075043-30be4d16710a/go.mod h1:1TqjTSzOxsLGIKf
k8s.io/utils v0.0.0-20200109141947-94aeca20bf09 h1:sz6xjn8QP74104YNmJpzLbJ+a3ZtHt0tkD0g8vpdWNw=
k8s.io/utils v0.0.0-20200109141947-94aeca20bf09/go.mod h1:sZAwmy6armz5eXlNoLmJcl4F1QuKu7sr+mFQ0byX7Ew=
rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
sigs.k8s.io/kustomize v2.0.3+incompatible h1:JUufWFNlI44MdtnjUqVnvh29rR37PQFzPbLXqhyOyX0=
sigs.k8s.io/kustomize v2.0.3+incompatible/go.mod h1:MkjgH3RdOWrievjo6c9T245dYlB5QeXV4WCbnt/PEpU=
sigs.k8s.io/structured-merge-diff v0.0.0-20190525122527-15d366b2352e/go.mod h1:wWxsB5ozmmv/SG7nM11ayaAW51xMvak/t1r0CSlcokI=
sigs.k8s.io/yaml v1.1.0 h1:4A07+ZFc2wgJwo8YNlQpr1rVlgUDlxXHhPJciaPY5gs=
sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o=

View file

@ -97,6 +97,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)
}

View file

@ -16,6 +16,7 @@ import (
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
patchTypes "k8s.io/apimachinery/pkg/types"
"k8s.io/apimachinery/pkg/version"
"k8s.io/client-go/discovery"
"k8s.io/client-go/discovery/cached/memory"
"k8s.io/client-go/dynamic"
@ -213,6 +214,7 @@ func convertToCSR(obj *unstructured.Unstructured) (*certificates.CertificateSign
//IDiscovery provides interface to mange Kind and GVR mapping
type IDiscovery interface {
GetGVRFromKind(kind string) schema.GroupVersionResource
GetServerVersion() (*version.Info, error)
}
// SetDiscovery sets the discovery client implementation
@ -265,6 +267,11 @@ func (c ServerPreferredResources) GetGVRFromKind(kind string) schema.GroupVersio
return gvr
}
//GetServerVersion returns the server version of the cluster
func (c ServerPreferredResources) GetServerVersion() (*version.Info, error) {
return c.cachedClient.ServerVersion()
}
func loadServerResources(k string, cdi discovery.CachedDiscoveryInterface) (schema.GroupVersionResource, error) {
serverresources, err := cdi.ServerPreferredResources()
emptyGVR := schema.GroupVersionResource{}

View file

@ -6,6 +6,7 @@ import (
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/apimachinery/pkg/version"
"k8s.io/client-go/dynamic/fake"
kubernetesfake "k8s.io/client-go/kubernetes/fake"
)
@ -64,6 +65,10 @@ func (c *fakeDiscoveryClient) getGVR(resource string) schema.GroupVersionResourc
return schema.GroupVersionResource{}
}
func (c *fakeDiscoveryClient) GetServerVersion() (*version.Info, error) {
return nil, nil
}
func (c *fakeDiscoveryClient) GetGVRFromKind(kind string) schema.GroupVersionResource {
resource := strings.ToLower(kind) + "s"
return c.getGVR(resource)

View file

@ -88,7 +88,8 @@ func Mutate(policyContext PolicyContext) (resp response.EngineResponse) {
glog.V(4).Infof(ruleResponse.Message)
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)

View file

@ -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 successfully 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 successfully 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
}

View file

@ -0,0 +1,375 @@
package apply
import (
"encoding/json"
"fmt"
"io/ioutil"
"os"
"path/filepath"
"github.com/nirmata/kyverno/pkg/kyverno/sanitizedError"
policy2 "github.com/nirmata/kyverno/pkg/policy"
"github.com/golang/glog"
"k8s.io/apimachinery/pkg/runtime/schema"
"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/cli-runtime/pkg/genericclioptions"
"k8s.io/client-go/kubernetes/scheme"
)
func Command() *cobra.Command {
var cmd *cobra.Command
var resourcePaths []string
var cluster bool
kubernetesConfig := genericclioptions.NewConfigFlags(true)
cmd = &cobra.Command{
Use: "apply",
Short: "Applies policies on resources",
Example: fmt.Sprintf("To apply on a resource:\nkyverno apply /path/to/policy.yaml /path/to/folderOfPolicies --resource=/path/to/resource1 --resource=/path/to/resource2\n\nTo apply on a cluster\nkyverno apply /path/to/policy.yaml /path/to/folderOfPolicies --cluster"),
RunE: func(cmd *cobra.Command, policyPaths []string) (err error) {
defer func() {
if err != nil {
if !sanitizedError.IsErrorSanitized(err) {
glog.V(4).Info(err)
err = fmt.Errorf("Internal error")
}
}
}()
if len(resourcePaths) == 0 && !cluster {
return sanitizedError.New(fmt.Sprintf("Specify path to resource file or cluster name"))
}
policies, err := getPolicies(policyPaths)
if err != nil {
if !sanitizedError.IsErrorSanitized(err) {
return sanitizedError.New("Could not parse policy paths")
} else {
return err
}
}
for _, policy := range policies {
err := policy2.Validate(*policy)
if err != nil {
return sanitizedError.New(fmt.Sprintf("Policy %v is not valid", policy.Name))
}
}
var dClient discovery.CachedDiscoveryInterface
if cluster {
dClient, err = kubernetesConfig.ToDiscoveryClient()
if err != nil {
return sanitizedError.New(fmt.Errorf("Issues with kubernetes Config").Error())
}
}
resources, err := getResources(policies, resourcePaths, dClient)
if err != nil {
return sanitizedError.New(fmt.Errorf("Issues fetching resources").Error())
}
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 sanitizedError.New(fmt.Errorf("Issues applying policy %v on resource %v", policy.Name, resource.GetName()).Error())
}
}
}
return nil
},
}
cmd.Flags().StringArrayVarP(&resourcePaths, "resource", "r", []string{}, "Path to resource files")
cmd.Flags().BoolVarP(&cluster, "cluster", "c", false, "Checks if policies should be applied to cluster in the current context")
return cmd
}
func getResources(policies []*v1.ClusterPolicy, resourcePaths []string, dClient discovery.CachedDiscoveryInterface) ([]*unstructured.Unstructured, error) {
var resources []*unstructured.Unstructured
var err error
if dClient != nil {
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, dClient)
if err != nil {
return nil, err
}
}
for _, resourcePath := range resourcePaths {
resource, err := getResource(resourcePath)
if err != nil {
return nil, err
}
resources = append(resources, resource)
}
return resources, nil
}
func getResourcesOfTypeFromCluster(resourceTypes []string, dClient discovery.CachedDiscoveryInterface) ([]*unstructured.Unstructured, error) {
var resources []*unstructured.Unstructured
for _, kind := range resourceTypes {
endpoint, err := getListEndpointForKind(kind)
if err != nil {
return nil, err
}
listObjectRaw, err := dClient.RESTClient().Get().RequestURI(endpoint).Do().Raw()
if err != nil {
return nil, 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 getPoliciesInDir(path string) ([]*v1.ClusterPolicy, error) {
var policies []*v1.ClusterPolicy
files, err := ioutil.ReadDir(path)
if err != nil {
return nil, err
}
for _, file := range files {
if file.IsDir() {
policiesFromDir, err := getPoliciesInDir(filepath.Join(path, file.Name()))
if err != nil {
return nil, err
}
policies = append(policies, policiesFromDir...)
} else {
policy, err := getPolicy(filepath.Join(path, file.Name()))
if err != nil {
return nil, err
}
policies = append(policies, policy)
}
}
return policies, nil
}
func getPolicies(paths []string) ([]*v1.ClusterPolicy, error) {
var policies = make([]*v1.ClusterPolicy, 0, len(paths))
for _, path := range paths {
path = filepath.Clean(path)
fileDesc, err := os.Stat(path)
if err != nil {
return nil, err
}
if fileDesc.IsDir() {
policiesFromDir, err := getPoliciesInDir(path)
if err != nil {
return nil, err
}
policies = append(policies, policiesFromDir...)
} else {
policy, err := getPolicy(path)
if err != nil {
return nil, err
}
policies = append(policies, policy)
}
}
return policies, 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, sanitizedError.New(fmt.Sprintf("failed to decode policy in %s", path))
}
if policy.TypeMeta.Kind != "ClusterPolicy" {
return nil, sanitizedError.New(fmt.Sprintf("resource %v is not a cluster policy", policy.Name))
}
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", policy.Name, resource.GetNamespace(), resource.GetKind(), resource.GetName())
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" + string(yamlEncodedResource))
fmt.Printf("\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")
fmt.Printf("\n\n")
}
}
var policyHasGenerate bool
for _, rule := range policy.Spec.Rules {
if rule.HasGenerate() {
policyHasGenerate = true
}
}
if policyHasGenerate {
generateResponse := engine.Generate(engine.PolicyContext{Policy: *policy, NewResource: *resource})
if len(generateResponse.PolicyResponse.Rules) > 0 {
fmt.Printf("\n\nGenerate:")
fmt.Printf("\nResource is valid")
fmt.Printf("\n\n")
} else {
fmt.Printf("\n\nGenerate:")
fmt.Printf("\nResource is invalid")
for i, r := range generateResponse.PolicyResponse.Rules {
fmt.Printf("\n%d. %s", i+1, r.Message)
}
fmt.Printf("\n\n")
}
}
return nil
}

View file

@ -0,0 +1,37 @@
package apply
import (
"fmt"
"strings"
"github.com/nirmata/kyverno/pkg/openapi"
)
func getListEndpointForKind(kind string) (string, error) {
definitionName := openapi.GetDefinitionNameFromKind(kind)
definitionNameWithoutPrefix := strings.Replace(definitionName, "io.k8s.", "", -1)
parts := strings.Split(definitionNameWithoutPrefix, ".")
definitionPrefix := strings.Join(parts[:len(parts)-1], ".")
defPrefixToApiPrefix := map[string]string{
"api.core.v1": "/api/v1",
"api.apps.v1": "/apis/apps/v1",
"api.batch.v1": "/apis/batch/v1",
"api.admissionregistration.v1": "/apis/admissionregistration.k8s.io/v1",
"kube-aggregator.pkg.apis.apiregistration.v1": "/apis/apiregistration.k8s.io/v1",
"apiextensions-apiserver.pkg.apis.apiextensions.v1": "/apis/apiextensions.k8s.io/v1",
"api.autoscaling.v1": "/apis/autoscaling/v1/",
"api.storage.v1": "/apis/storage.k8s.io/v1",
"api.coordination.v1": "/apis/coordination.k8s.io/v1",
"api.scheduling.v1": "/apis/scheduling.k8s.io/v1",
"api.rbac.v1": "/apis/rbac.authorization.k8s.io/v1",
}
if defPrefixToApiPrefix[definitionPrefix] == "" {
return "", fmt.Errorf("Unsupported resource type %v", kind)
}
return defPrefixToApiPrefix[definitionPrefix] + "/" + strings.ToLower(kind) + "s", nil
}

View file

@ -1,107 +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
}
//ConvertToUnstructured converts the resource to unstructured format
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
}

View file

@ -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
}

50
pkg/kyverno/main.go Normal file
View file

@ -0,0 +1,50 @@
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...)
cli.SilenceUsage = true
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")
}

View file

@ -0,0 +1,20 @@
package sanitizedError
type customError struct {
message string
}
func (c customError) Error() string {
return c.message
}
func New(message string) error {
return customError{message: message}
}
func IsErrorSanitized(err error) bool {
if _, ok := err.(customError); !ok {
return false
}
return true
}

View file

@ -0,0 +1,142 @@
package validate
import (
"encoding/json"
"fmt"
"io/ioutil"
"os"
"path/filepath"
"github.com/nirmata/kyverno/pkg/kyverno/sanitizedError"
"github.com/golang/glog"
policyvalidate "github.com/nirmata/kyverno/pkg/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/policy.yaml /path/to/folderOfPolicies",
RunE: func(cmd *cobra.Command, policyPaths []string) (err error) {
defer func() {
if err != nil {
if !sanitizedError.IsErrorSanitized(err) {
glog.V(4).Info(err)
err = fmt.Errorf("Internal error")
}
}
}()
policies, err := getPolicies(policyPaths)
if err != nil {
if !sanitizedError.IsErrorSanitized(err) {
return sanitizedError.New("Could not parse policy paths")
} else {
return err
}
}
for _, policy := range policies {
err = policyvalidate.Validate(*policy)
if err != nil {
fmt.Println("Policy " + policy.Name + " is invalid")
} else {
fmt.Println("Policy " + policy.Name + " is valid")
}
}
return nil
},
}
return cmd
}
func getPoliciesInDir(path string) ([]*v1.ClusterPolicy, error) {
var policies []*v1.ClusterPolicy
files, err := ioutil.ReadDir(path)
if err != nil {
return nil, err
}
for _, file := range files {
if file.IsDir() {
policiesFromDir, err := getPoliciesInDir(filepath.Join(path, file.Name()))
if err != nil {
return nil, err
}
policies = append(policies, policiesFromDir...)
} else {
policy, err := getPolicy(filepath.Join(path, file.Name()))
if err != nil {
return nil, err
}
policies = append(policies, policy)
}
}
return policies, nil
}
func getPolicies(paths []string) ([]*v1.ClusterPolicy, error) {
var policies = make([]*v1.ClusterPolicy, 0, len(paths))
for _, path := range paths {
path = filepath.Clean(path)
fileDesc, err := os.Stat(path)
if err != nil {
return nil, err
}
if fileDesc.IsDir() {
policiesFromDir, err := getPoliciesInDir(path)
if err != nil {
return nil, err
}
policies = append(policies, policiesFromDir...)
} else {
policy, err := getPolicy(path)
if err != nil {
return nil, err
}
policies = append(policies, policy)
}
}
return policies, 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, sanitizedError.New(fmt.Sprintf("failed to decode policy in %s", path))
}
if policy.TypeMeta.Kind != "ClusterPolicy" {
return nil, sanitizedError.New(fmt.Sprintf("resource %v is not a cluster policy", policy.Name))
}
return policy, nil
}

View 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
},
}
}

View file

@ -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)
}

5
pkg/openapi/helper.go Normal file
View file

@ -0,0 +1,5 @@
package openapi
func GetDefinitionNameFromKind(kind string) string {
return openApiGlobalState.kindToDefinitionName[kind]
}

View file

@ -77,8 +77,14 @@ func ValidatePolicyMutation(policy v1.ClusterPolicy) error {
var kindToRules = make(map[string][]v1.Rule)
for _, rule := range policy.Spec.Rules {
rule.MatchResources.Selector = nil
if rule.HasMutate() {
rule.MatchResources = v1.MatchResources{
UserInfo: v1.UserInfo{},
ResourceDescription: v1.ResourceDescription{
Kinds: rule.MatchResources.Kinds,
},
}
rule.ExcludeResources = v1.ExcludeResources{}
for _, kind := range rule.MatchResources.Kinds {
kindToRules[kind] = append(kindToRules[kind], rule)
}
@ -157,6 +163,10 @@ func setValidationGlobalState() error {
openApiGlobalState.kindToDefinitionName[path[len(path)-1]] = definition.GetName()
}
for _, path := range openApiGlobalState.document.GetPaths().GetPath() {
path.GetName()
}
openApiGlobalState.models, err = proto.NewOpenAPIData(openApiGlobalState.document)
if err != nil {
return err

View file

@ -31,7 +31,7 @@ func (ws *WebhookServer) handlePolicyMutation(request *v1beta1.AdmissionRequest)
}
}
// Generate JSON Patches for defaults
patches, updateMsgs := generateJSONPatchesForDefaults(policy, request.Operation)
patches, updateMsgs := generateJSONPatchesForDefaults(policy)
if patches != nil {
patchType := v1beta1.PatchTypeJSONPatch
glog.V(4).Infof("defaulted values %v policy %s", updateMsgs, policy.Name)
@ -50,7 +50,7 @@ func (ws *WebhookServer) handlePolicyMutation(request *v1beta1.AdmissionRequest)
}
}
func generateJSONPatchesForDefaults(policy *kyverno.ClusterPolicy, operation v1beta1.Operation) ([]byte, []string) {
func generateJSONPatchesForDefaults(policy *kyverno.ClusterPolicy) ([]byte, []string) {
var patches [][]byte
var updateMsgs []string
@ -66,20 +66,18 @@ func generateJSONPatchesForDefaults(policy *kyverno.ClusterPolicy, operation v1b
updateMsgs = append(updateMsgs, updateMsg)
}
// TODO(shuting): enable this feature on policy UPDATE
if operation == v1beta1.Create {
patch, errs := generatePodControllerRule(*policy)
if len(errs) > 0 {
var errMsgs []string
for _, err := range errs {
errMsgs = append(errMsgs, err.Error())
}
glog.Errorf("failed auto generatig rule for pod controllers: %s", errMsgs)
updateMsgs = append(updateMsgs, strings.Join(errMsgs, ";"))
patch, errs := generatePodControllerRule(*policy)
if len(errs) > 0 {
var errMsgs []string
for _, err := range errs {
errMsgs = append(errMsgs, err.Error())
}
patches = append(patches, patch...)
glog.Errorf("failed auto generating rule for pod controllers: %s", errMsgs)
updateMsgs = append(updateMsgs, strings.Join(errMsgs, ";"))
}
patches = append(patches, patch...)
return utils.JoinPatches(patches), updateMsgs
}
@ -170,25 +168,73 @@ func generatePodControllerRule(policy kyverno.ClusterPolicy) (patches [][]byte,
return
}
func createRuleMap(rules []kyverno.Rule) map[string]kyvernoRule {
var ruleMap = make(map[string]kyvernoRule)
for _, rule := range rules {
var jsonFriendlyStruct kyvernoRule
jsonFriendlyStruct.Name = rule.Name
if !reflect.DeepEqual(rule.MatchResources, kyverno.MatchResources{}) {
jsonFriendlyStruct.MatchResources = rule.MatchResources.DeepCopy()
}
if !reflect.DeepEqual(rule.ExcludeResources, kyverno.ExcludeResources{}) {
jsonFriendlyStruct.ExcludeResources = rule.ExcludeResources.DeepCopy()
}
if !reflect.DeepEqual(rule.Mutation, kyverno.Mutation{}) {
jsonFriendlyStruct.Mutation = rule.Mutation.DeepCopy()
}
if !reflect.DeepEqual(rule.Validation, kyverno.Validation{}) {
jsonFriendlyStruct.Validation = rule.Validation.DeepCopy()
}
ruleMap[rule.Name] = jsonFriendlyStruct
}
return ruleMap
}
// generateRulePatches generates rule for podControllers based on scenario A and C
func generateRulePatches(policy kyverno.ClusterPolicy, controllers string) (rulePatches [][]byte, errs []error) {
var genRule kyvernoRule
insertIdx := len(policy.Spec.Rules)
ruleMap := createRuleMap(policy.Spec.Rules)
var ruleIndex = make(map[string]int)
for index, rule := range policy.Spec.Rules {
ruleIndex[rule.Name] = index
}
for _, rule := range policy.Spec.Rules {
patchPostion := insertIdx
genRule = generateRuleForControllers(rule, controllers)
if reflect.DeepEqual(genRule, kyvernoRule{}) {
continue
}
operation := "add"
if existingAutoGenRule, alreadyExists := ruleMap[genRule.Name]; alreadyExists {
existingAutoGenRuleRaw, _ := json.Marshal(existingAutoGenRule)
genRuleRaw, _ := json.Marshal(genRule)
if string(existingAutoGenRuleRaw) == string(genRuleRaw) {
continue
}
operation = "replace"
patchPostion = ruleIndex[genRule.Name]
}
// generate patch bytes
jsonPatch := struct {
Path string `json:"path"`
Op string `json:"op"`
Value interface{} `json:"value"`
}{
fmt.Sprintf("/spec/rules/%s", strconv.Itoa(insertIdx)),
"add",
fmt.Sprintf("/spec/rules/%s", strconv.Itoa(patchPostion)),
operation,
genRule,
}
pbytes, err := json.Marshal(jsonPatch)
@ -227,6 +273,10 @@ type kyvernoRule struct {
}
func generateRuleForControllers(rule kyverno.Rule, controllers string) kyvernoRule {
if strings.HasPrefix(rule.Name, "autogen-") {
return kyvernoRule{}
}
match := rule.MatchResources
exclude := rule.ExcludeResources
if !utils.ContainsString(match.ResourceDescription.Kinds, "Pod") ||