2020-03-04 18:56:59 +05:30
package openapi
import (
2020-03-05 22:50:32 +05:30
"encoding/json"
2020-03-04 18:56:59 +05:30
"time"
2020-03-06 01:09:38 +05:30
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
2020-03-04 18:56:59 +05:30
"github.com/golang/glog"
2020-03-05 22:50:32 +05:30
"gopkg.in/yaml.v2"
"github.com/googleapis/gnostic/compiler"
openapi_v2 "github.com/googleapis/gnostic/OpenAPIv2"
2020-03-04 18:56:59 +05:30
client "github.com/nirmata/kyverno/pkg/dclient"
"k8s.io/apimachinery/pkg/util/wait"
)
2020-03-05 22:50:32 +05:30
type crdDefinition struct {
Spec struct {
Names struct {
Kind string ` json:"kind" `
} ` json:"names" `
Versions [ ] struct {
Schema struct {
OpenAPIV3Schema interface { } ` json:"openAPIV3Schema" `
} ` json:"schema" `
} ` json:"versions" `
} ` json:"spec" `
}
2020-03-04 18:56:59 +05:30
type crdSync struct {
client * client . Client
}
func NewCRDSync ( client * client . Client ) * crdSync {
return & crdSync {
client : client ,
}
}
func ( c * crdSync ) Run ( workers int , stopCh <- chan struct { } ) {
2020-03-05 22:50:32 +05:30
newDoc , err := c . client . DiscoveryClient . OpenAPISchema ( )
if err != nil {
glog . V ( 4 ) . Infof ( "cannot get openapi schema: %v" , err )
}
err = useOpenApiDocument ( newDoc )
if err != nil {
glog . V ( 4 ) . Infof ( "Could not set custom OpenApi document: %v\n" , err )
}
2020-03-04 18:56:59 +05:30
for i := 0 ; i < workers ; i ++ {
2020-03-05 22:50:32 +05:30
go wait . Until ( c . sync , time . Second * 10 , stopCh )
2020-03-04 18:56:59 +05:30
}
2020-03-04 19:16:26 +05:30
<- stopCh
2020-03-04 18:56:59 +05:30
}
2020-03-05 22:50:32 +05:30
func ( c * crdSync ) sync ( ) {
openApiGlobalState . mutex . Lock ( )
defer openApiGlobalState . mutex . Unlock ( )
crds , err := c . client . ListResource ( "CustomResourceDefinition" , "" , nil )
2020-03-04 18:56:59 +05:30
if err != nil {
2020-03-05 22:50:32 +05:30
glog . V ( 4 ) . Infof ( "could not fetch crd's from server: %v" , err )
return
2020-03-04 18:56:59 +05:30
}
2020-03-06 01:09:38 +05:30
deleteCRDFromPreviousSync ( )
2020-03-05 22:50:32 +05:30
for _ , crd := range crds . Items {
2020-03-06 01:09:38 +05:30
parseCRD ( crd )
}
}
func deleteCRDFromPreviousSync ( ) {
for _ , crd := range openApiGlobalState . crdList {
delete ( openApiGlobalState . kindToDefinitionName , crd )
delete ( openApiGlobalState . definitions , crd )
}
openApiGlobalState . crdList = [ ] string { }
}
func parseCRD ( crd unstructured . Unstructured ) {
var crdDefinition crdDefinition
crdRaw , _ := json . Marshal ( crd . Object )
_ = json . Unmarshal ( crdRaw , & crdDefinition )
crdName := crdDefinition . Spec . Names . Kind
if len ( crdDefinition . Spec . Versions ) < 1 {
glog . V ( 4 ) . Infof ( "could not parse crd schema, no versions present" )
return
2020-03-04 18:56:59 +05:30
}
2020-03-06 01:09:38 +05:30
var schema yaml . MapSlice
schemaRaw , _ := json . Marshal ( crdDefinition . Spec . Versions [ 0 ] . Schema . OpenAPIV3Schema )
2020-03-24 19:08:46 +05:30
schemaRaw = addingDefaultFieldsToSchema ( schemaRaw )
2020-03-06 01:09:38 +05:30
_ = yaml . Unmarshal ( schemaRaw , & schema )
parsedSchema , err := openapi_v2 . NewSchema ( schema , compiler . NewContext ( "schema" , nil ) )
if err != nil {
glog . V ( 4 ) . Infof ( "could not parse crd schema:%v" , err )
return
}
openApiGlobalState . crdList = append ( openApiGlobalState . crdList , crdName )
openApiGlobalState . kindToDefinitionName [ crdName ] = crdName
openApiGlobalState . definitions [ crdName ] = parsedSchema
2020-03-04 18:56:59 +05:30
}
2020-03-24 19:08:46 +05:30
// addingDefaultFieldsToSchema will add any default missing fields like apiVersion, metadata
func addingDefaultFieldsToSchema ( schemaRaw [ ] byte ) [ ] byte {
var schema struct {
Properties map [ string ] interface { } ` json:"properties" `
}
_ = json . Unmarshal ( schemaRaw , & schema )
if schema . Properties [ "apiVersion" ] == nil {
apiVersionDefRaw := ` { "description":"APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources","type":"string"} `
apiVersionDef := make ( map [ string ] interface { } )
_ = json . Unmarshal ( [ ] byte ( apiVersionDefRaw ) , & apiVersionDef )
schema . Properties [ "apiVersion" ] = apiVersionDef
}
if schema . Properties [ "metadata" ] == nil {
metadataDefRaw := ` { "$ref":"#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta","description":"Standard object's metadata. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata"} `
metadataDef := make ( map [ string ] interface { } )
_ = json . Unmarshal ( [ ] byte ( metadataDefRaw ) , & metadataDef )
schema . Properties [ "metadata" ] = metadataDef
}
schemaWithDefaultFields , _ := json . Marshal ( schema )
return schemaWithDefaultFields
}