mirror of
https://github.com/kyverno/kyverno.git
synced 2025-03-06 16:06:56 +00:00
Merge branch '254_dynamic_webhook_configurations' into feature_redesign
This commit is contained in:
commit
3abd422de4
1054 changed files with 73556 additions and 23600 deletions
2
.github/ISSUE_TEMPLATE/bug_report.md
vendored
2
.github/ISSUE_TEMPLATE/bug_report.md
vendored
|
@ -1,7 +1,7 @@
|
|||
---
|
||||
name: Bug report
|
||||
about: Create a report to help us improve
|
||||
title: "[BUG}"
|
||||
title: "[BUG]"
|
||||
labels: bug
|
||||
assignees: ''
|
||||
|
||||
|
|
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -4,3 +4,4 @@ Gopkg.lock
|
|||
kyverno
|
||||
gh-pages/public
|
||||
_output
|
||||
coverage.txt
|
||||
|
|
27
README.md
27
README.md
|
@ -34,9 +34,10 @@ metadata:
|
|||
spec:
|
||||
rules:
|
||||
- name: check-pod-resources
|
||||
resource:
|
||||
kinds:
|
||||
- Pod
|
||||
match:
|
||||
resources:
|
||||
kinds:
|
||||
- Pod
|
||||
validate:
|
||||
message: "CPU and memory resource requests and limits are required"
|
||||
pattern:
|
||||
|
@ -67,9 +68,10 @@ metadata:
|
|||
spec:
|
||||
rules:
|
||||
- name: set-image-pull-policy
|
||||
resource:
|
||||
kinds:
|
||||
- Deployment
|
||||
match:
|
||||
resources:
|
||||
kinds:
|
||||
- Deployment
|
||||
mutate:
|
||||
overlay:
|
||||
spec:
|
||||
|
@ -94,12 +96,13 @@ metadata:
|
|||
spec:
|
||||
rules:
|
||||
- name: "zk-kafka-address"
|
||||
resource:
|
||||
kinds:
|
||||
- Namespace
|
||||
selector:
|
||||
matchExpressions:
|
||||
- {key: kafka, operator: Exists}
|
||||
match:
|
||||
resources:
|
||||
kinds:
|
||||
- Namespace
|
||||
selector:
|
||||
matchExpressions:
|
||||
- {key: kafka, operator: Exists}
|
||||
generate:
|
||||
kind: ConfigMap
|
||||
name: zk-kafka-address
|
||||
|
|
|
@ -231,7 +231,7 @@ spec:
|
|||
containers:
|
||||
- name: kyverno
|
||||
image: nirmata/kyverno:latest
|
||||
args: ["--filterK8Resources","[Event,*,*][*,kube-system,*][*,kube-public,*][*,kube-node-lease,*]Node,*,*][APIService,*,*][TokenReview,*,*][SubjectAccessReview,*,*][*,kyverno,*]"]
|
||||
args: ["--filterK8Resources","[Event,*,*][*,kube-system,*][*,kube-public,*][*,kube-node-lease,*][Node,*,*][APIService,*,*][TokenReview,*,*][SubjectAccessReview,*,*][*,kyverno,*]"]
|
||||
ports:
|
||||
- containerPort: 443
|
||||
securityContext:
|
||||
|
|
246
definitions/install_custom_args.yaml
Normal file
246
definitions/install_custom_args.yaml
Normal file
|
@ -0,0 +1,246 @@
|
|||
apiVersion: apiextensions.k8s.io/v1beta1
|
||||
kind: CustomResourceDefinition
|
||||
metadata:
|
||||
name: policies.kyverno.io
|
||||
spec:
|
||||
group: kyverno.io
|
||||
versions:
|
||||
- name: v1alpha1
|
||||
served: true
|
||||
storage: true
|
||||
scope: Cluster
|
||||
names:
|
||||
kind: Policy
|
||||
plural: policies
|
||||
singular: policy
|
||||
subresources:
|
||||
status: {}
|
||||
validation:
|
||||
openAPIV3Schema:
|
||||
properties:
|
||||
spec:
|
||||
required:
|
||||
- rules
|
||||
properties:
|
||||
# default values to be handled by user
|
||||
validationFailureAction:
|
||||
type: string
|
||||
enum:
|
||||
- enforce # blocks the resorce api-reques if a rule fails. Default behavior
|
||||
- audit # allows resource creationg and reports the failed validation rules as violations
|
||||
rules:
|
||||
type: array
|
||||
items:
|
||||
type: object
|
||||
required:
|
||||
- name
|
||||
- match
|
||||
properties:
|
||||
name:
|
||||
type: string
|
||||
match:
|
||||
type: object
|
||||
required:
|
||||
- resources
|
||||
properties:
|
||||
resources:
|
||||
type: object
|
||||
required:
|
||||
- kinds
|
||||
properties:
|
||||
kinds:
|
||||
type: array
|
||||
items:
|
||||
type: string
|
||||
name:
|
||||
type: string
|
||||
namespace:
|
||||
type: string
|
||||
selector:
|
||||
properties:
|
||||
matchLabels:
|
||||
type: object
|
||||
additionalProperties:
|
||||
type: string
|
||||
matchExpressions:
|
||||
type: array
|
||||
items:
|
||||
type: object
|
||||
required:
|
||||
- key
|
||||
- operator
|
||||
properties:
|
||||
key:
|
||||
type: string
|
||||
operator:
|
||||
type: string
|
||||
values:
|
||||
type: array
|
||||
items:
|
||||
type: string
|
||||
exclude:
|
||||
type: object
|
||||
required:
|
||||
- resources
|
||||
properties:
|
||||
resources:
|
||||
type: object
|
||||
properties:
|
||||
kinds:
|
||||
type: array
|
||||
items:
|
||||
type: string
|
||||
name:
|
||||
type: string
|
||||
namespace:
|
||||
type: string
|
||||
selector:
|
||||
properties:
|
||||
matchLabels:
|
||||
type: object
|
||||
additionalProperties:
|
||||
type: string
|
||||
matchExpressions:
|
||||
type: array
|
||||
items:
|
||||
type: object
|
||||
required:
|
||||
- key
|
||||
- operator
|
||||
properties:
|
||||
key:
|
||||
type: string
|
||||
operator:
|
||||
type: string
|
||||
values:
|
||||
type: array
|
||||
items:
|
||||
type: string
|
||||
mutate:
|
||||
type: object
|
||||
properties:
|
||||
overlay:
|
||||
AnyValue: {}
|
||||
patches:
|
||||
type: array
|
||||
items:
|
||||
type: object
|
||||
required:
|
||||
- path
|
||||
- op
|
||||
properties:
|
||||
path:
|
||||
type: string
|
||||
op:
|
||||
type: string
|
||||
enum:
|
||||
- add
|
||||
- replace
|
||||
- remove
|
||||
value:
|
||||
AnyValue: {}
|
||||
validate:
|
||||
type: object
|
||||
required:
|
||||
- pattern
|
||||
properties:
|
||||
message:
|
||||
type: string
|
||||
pattern:
|
||||
AnyValue: {}
|
||||
generate:
|
||||
type: object
|
||||
required:
|
||||
- kind
|
||||
- name
|
||||
properties:
|
||||
kind:
|
||||
type: string
|
||||
name:
|
||||
type: string
|
||||
clone:
|
||||
type: object
|
||||
required:
|
||||
- namespace
|
||||
- name
|
||||
properties:
|
||||
namespace:
|
||||
type: string
|
||||
name:
|
||||
type: string
|
||||
data:
|
||||
AnyValue: {}
|
||||
---
|
||||
kind: Namespace
|
||||
apiVersion: v1
|
||||
metadata:
|
||||
name: "kyverno"
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
namespace: kyverno
|
||||
name: kyverno-svc
|
||||
labels:
|
||||
app: kyverno
|
||||
spec:
|
||||
ports:
|
||||
- port: 443
|
||||
targetPort: 443
|
||||
selector:
|
||||
app: kyverno
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: ServiceAccount
|
||||
metadata:
|
||||
name: kyverno-service-account
|
||||
namespace: kyverno
|
||||
---
|
||||
kind: ClusterRoleBinding
|
||||
apiVersion: rbac.authorization.k8s.io/v1beta1
|
||||
metadata:
|
||||
name: kyverno-admin
|
||||
roleRef:
|
||||
apiGroup: rbac.authorization.k8s.io
|
||||
kind: ClusterRole
|
||||
name: cluster-admin
|
||||
subjects:
|
||||
- kind: ServiceAccount
|
||||
name: kyverno-service-account
|
||||
namespace: kyverno
|
||||
---
|
||||
apiVersion: extensions/v1beta1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
namespace: kyverno
|
||||
name: kyverno
|
||||
labels:
|
||||
app: kyverno
|
||||
spec:
|
||||
replicas: 1
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: kyverno
|
||||
spec:
|
||||
serviceAccountName: kyverno-service-account
|
||||
containers:
|
||||
- name: kyverno
|
||||
image: nirmata/kyverno:latest
|
||||
args:
|
||||
- "--webhooktimeout=4"
|
||||
# open one of the profiling flag here
|
||||
- "--cpu=true"
|
||||
- "--filterK8Resources=[Event,*,*][*,kube-system,*][*,kube-public,*][*,kube-node-lease,*]Node,*,*][APIService,*,*][TokenReview,*,*][SubjectAccessReview,*,*][*,kyverno,*]"
|
||||
ports:
|
||||
- containerPort: 443
|
||||
securityContext:
|
||||
privileged: true
|
||||
volumeMounts:
|
||||
- mountPath: /opt/nirmata
|
||||
name: profiling-volume
|
||||
volumes:
|
||||
- name: profiling-volume
|
||||
hostPath:
|
||||
path: /opt/nirmata
|
||||
type: Directory
|
|
@ -1,10 +1,10 @@
|
|||
apiVersion: kyverno.io/v1alpha1
|
||||
kind: Policy
|
||||
metadata:
|
||||
name: "default-networkPolicy"
|
||||
name: defaultgeneratenetworkpolicy
|
||||
spec:
|
||||
rules:
|
||||
- name: "default-networkPolicy"
|
||||
- name: "default-networkpolicy"
|
||||
match:
|
||||
resources:
|
||||
kinds:
|
||||
|
@ -12,7 +12,7 @@ spec:
|
|||
name: "devtest"
|
||||
generate:
|
||||
kind: NetworkPolicy
|
||||
name: default-networkPolicy
|
||||
name: defaultnetworkpolicy
|
||||
data:
|
||||
spec:
|
||||
# select all pods in the namespace
|
||||
|
|
49
init.go
49
init.go
|
@ -2,6 +2,10 @@ package main
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
"math/rand"
|
||||
"time"
|
||||
|
||||
"github.com/pkg/profile"
|
||||
|
||||
"github.com/golang/glog"
|
||||
client "github.com/nirmata/kyverno/pkg/dclient"
|
||||
|
@ -51,3 +55,48 @@ func initTLSPemPair(configuration *rest.Config, client *client.Client) (*tls.Tls
|
|||
glog.Infoln("Using existing TLS key/certificate pair")
|
||||
return tlsPair, nil
|
||||
}
|
||||
|
||||
var prof interface {
|
||||
Stop()
|
||||
}
|
||||
|
||||
func enableProfiling(cpu, memory bool) interface {
|
||||
Stop()
|
||||
} {
|
||||
|
||||
file := "/opt/nirmata/kyverno/" + randomString(6)
|
||||
if cpu {
|
||||
glog.Infof("Enable cpu profiling ...")
|
||||
prof = profile.Start(profile.CPUProfile, profile.ProfilePath(file))
|
||||
} else if memory {
|
||||
glog.Infof("Enable memory profiling ...")
|
||||
prof = profile.Start(profile.MemProfile, profile.ProfilePath(file))
|
||||
}
|
||||
|
||||
return prof
|
||||
}
|
||||
|
||||
func disableProfiling(p interface{ Stop() }) {
|
||||
if p != nil {
|
||||
p.Stop()
|
||||
}
|
||||
}
|
||||
|
||||
// generate random string
|
||||
const charset = "abcdefghijklmnopqrstuvwxyz" +
|
||||
"ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
|
||||
|
||||
var seededRand *rand.Rand = rand.New(
|
||||
rand.NewSource(time.Now().UnixNano()))
|
||||
|
||||
func stringWithCharset(length int, charset string) string {
|
||||
b := make([]byte, length)
|
||||
for i := range b {
|
||||
b[i] = charset[seededRand.Intn(len(charset))]
|
||||
}
|
||||
return string(b)
|
||||
}
|
||||
|
||||
func randomString(length int) string {
|
||||
return stringWithCharset(length, charset)
|
||||
}
|
||||
|
|
31
main.go
31
main.go
|
@ -23,11 +23,15 @@ var (
|
|||
kubeconfig string
|
||||
serverIP string
|
||||
filterK8Resources string
|
||||
cpu bool
|
||||
memory bool
|
||||
webhookTimeout int
|
||||
)
|
||||
|
||||
func main() {
|
||||
defer glog.Flush()
|
||||
printVersionInfo()
|
||||
prof = enableProfiling(cpu, memory)
|
||||
|
||||
// CLIENT CONFIG
|
||||
clientConfig, err := createClientConfig(kubeconfig)
|
||||
|
@ -94,16 +98,23 @@ func main() {
|
|||
if err != nil {
|
||||
glog.Fatalf("Failed to initialize TLS key/certificate pair: %v\n", err)
|
||||
}
|
||||
server, err := webhooks.NewWebhookServer(pclient, client, tlsPair, pInformer.Kyverno().V1alpha1().Policies(), pInformer.Kyverno().V1alpha1().PolicyViolations(), egen, filterK8Resources)
|
||||
if err != nil {
|
||||
glog.Fatalf("Unable to create webhook server: %v\n", err)
|
||||
}
|
||||
|
||||
webhookRegistrationClient, err := webhooks.NewWebhookRegistrationClient(clientConfig, client, serverIP)
|
||||
// WEBHOOK REGISTRATION
|
||||
// -- validationwebhookconfiguration (Policy)
|
||||
// -- mutatingwebhookconfiguration (All resources)
|
||||
webhookRegistrationClient, err := webhooks.NewWebhookRegistrationClient(clientConfig, client, serverIP, int32(webhookTimeout))
|
||||
if err != nil {
|
||||
glog.Fatalf("Unable to register admission webhooks on cluster: %v\n", err)
|
||||
}
|
||||
|
||||
if err = webhookRegistrationClient.Register(); err != nil {
|
||||
glog.Fatalf("Failed registering Admission Webhooks: %v\n", err)
|
||||
}
|
||||
server, err := webhooks.NewWebhookServer(pclient, client, tlsPair, pInformer.Kyverno().V1alpha1().Policies(), pInformer.Kyverno().V1alpha1().PolicyViolations(), egen, webhookRegistrationClient, filterK8Resources)
|
||||
if err != nil {
|
||||
glog.Fatalf("Unable to create webhook server: %v\n", err)
|
||||
}
|
||||
|
||||
stopCh := signals.SetupSignalHandler()
|
||||
|
||||
if err = webhookRegistrationClient.Register(); err != nil {
|
||||
|
@ -119,11 +130,21 @@ func main() {
|
|||
|
||||
//TODO add WG for the go routines?
|
||||
server.RunAsync()
|
||||
|
||||
<-stopCh
|
||||
disableProfiling(prof)
|
||||
server.Stop()
|
||||
}
|
||||
|
||||
func init() {
|
||||
// profiling feature gate
|
||||
// cpu and memory profiling cannot be enabled at same time
|
||||
// if both cpu and memory are enabled
|
||||
// by default is to profile cpu
|
||||
flag.BoolVar(&cpu, "cpu", false, "cpu profilling feature gate, default to false || cpu and memory profiling cannot be enabled at the same time")
|
||||
flag.BoolVar(&memory, "memory", false, "memory profilling feature gate, default to false || cpu and memory profiling cannot be enabled at the same time")
|
||||
|
||||
flag.IntVar(&webhookTimeout, "webhooktimeout", 2, "timeout for webhook configurations")
|
||||
flag.StringVar(&kubeconfig, "kubeconfig", "", "Path to a kubeconfig. Only required if out-of-cluster.")
|
||||
flag.StringVar(&serverIP, "serverIP", "", "IP address where Kyverno controller runs. Only required if out-of-cluster.")
|
||||
flag.StringVar(&filterK8Resources, "filterK8Resources", "", "k8 resource in format [kind,namespace,name] where policy is not evaluated by the admission webhook. example --filterKind \"[Deployment, kyverno, kyverno]\" --filterKind \"[Deployment, kyverno, kyverno],[Events, *, *]\"")
|
||||
|
|
281
pkg/controller/controller.go
Normal file
281
pkg/controller/controller.go
Normal file
|
@ -0,0 +1,281 @@
|
|||
package controller
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
"time"
|
||||
|
||||
"github.com/nirmata/kyverno/pkg/annotations"
|
||||
"github.com/nirmata/kyverno/pkg/info"
|
||||
"github.com/nirmata/kyverno/pkg/utils"
|
||||
|
||||
"github.com/nirmata/kyverno/pkg/engine"
|
||||
|
||||
"github.com/golang/glog"
|
||||
v1alpha1 "github.com/nirmata/kyverno/pkg/apis/policy/v1alpha1"
|
||||
lister "github.com/nirmata/kyverno/pkg/client/listers/policy/v1alpha1"
|
||||
client "github.com/nirmata/kyverno/pkg/dclient"
|
||||
"github.com/nirmata/kyverno/pkg/event"
|
||||
"github.com/nirmata/kyverno/pkg/sharedinformer"
|
||||
violation "github.com/nirmata/kyverno/pkg/violation"
|
||||
"k8s.io/apimachinery/pkg/api/errors"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
utilruntime "k8s.io/apimachinery/pkg/util/runtime"
|
||||
"k8s.io/apimachinery/pkg/util/wait"
|
||||
"k8s.io/client-go/tools/cache"
|
||||
"k8s.io/client-go/util/workqueue"
|
||||
)
|
||||
|
||||
//PolicyController to manage Policy CRD
|
||||
type PolicyController struct {
|
||||
client *client.Client
|
||||
policyLister lister.PolicyLister
|
||||
policySynced cache.InformerSynced
|
||||
violationBuilder violation.Generator
|
||||
eventController event.Generator
|
||||
queue workqueue.RateLimitingInterface
|
||||
filterK8Resources []utils.K8Resource
|
||||
}
|
||||
|
||||
// NewPolicyController from cmd args
|
||||
func NewPolicyController(client *client.Client,
|
||||
policyInformer sharedinformer.PolicyInformer,
|
||||
violationBuilder violation.Generator,
|
||||
eventController event.Generator,
|
||||
filterK8Resources string) *PolicyController {
|
||||
|
||||
controller := &PolicyController{
|
||||
client: client,
|
||||
policyLister: policyInformer.GetLister(),
|
||||
policySynced: policyInformer.GetInfomer().HasSynced,
|
||||
violationBuilder: violationBuilder,
|
||||
eventController: eventController,
|
||||
filterK8Resources: utils.ParseKinds(filterK8Resources),
|
||||
queue: workqueue.NewNamedRateLimitingQueue(workqueue.DefaultControllerRateLimiter(), policyWorkQueueName),
|
||||
}
|
||||
|
||||
policyInformer.GetInfomer().AddEventHandler(cache.ResourceEventHandlerFuncs{
|
||||
AddFunc: controller.createPolicyHandler,
|
||||
UpdateFunc: controller.updatePolicyHandler,
|
||||
DeleteFunc: controller.deletePolicyHandler,
|
||||
})
|
||||
return controller
|
||||
}
|
||||
|
||||
func (pc *PolicyController) createPolicyHandler(resource interface{}) {
|
||||
pc.enqueuePolicy(resource)
|
||||
}
|
||||
|
||||
func (pc *PolicyController) updatePolicyHandler(oldResource, newResource interface{}) {
|
||||
newPolicy := newResource.(*v1alpha1.Policy)
|
||||
oldPolicy := oldResource.(*v1alpha1.Policy)
|
||||
newPolicy.Status = v1alpha1.Status{}
|
||||
oldPolicy.Status = v1alpha1.Status{}
|
||||
newPolicy.ResourceVersion = ""
|
||||
oldPolicy.ResourceVersion = ""
|
||||
if reflect.DeepEqual(newPolicy, oldPolicy) {
|
||||
return
|
||||
}
|
||||
pc.enqueuePolicy(newResource)
|
||||
}
|
||||
|
||||
func (pc *PolicyController) deletePolicyHandler(resource interface{}) {
|
||||
var object metav1.Object
|
||||
var ok bool
|
||||
if object, ok = resource.(metav1.Object); !ok {
|
||||
glog.Error("error decoding object, invalid type")
|
||||
return
|
||||
}
|
||||
cleanAnnotations(pc.client, resource, pc.filterK8Resources)
|
||||
glog.Infof("policy deleted: %s", object.GetName())
|
||||
}
|
||||
|
||||
func (pc *PolicyController) enqueuePolicy(obj interface{}) {
|
||||
var key string
|
||||
var err error
|
||||
if key, err = cache.MetaNamespaceKeyFunc(obj); err != nil {
|
||||
glog.Error(err)
|
||||
return
|
||||
}
|
||||
pc.queue.Add(key)
|
||||
}
|
||||
|
||||
// Run is main controller thread
|
||||
func (pc *PolicyController) Run(stopCh <-chan struct{}) error {
|
||||
defer utilruntime.HandleCrash()
|
||||
|
||||
if ok := cache.WaitForCacheSync(stopCh, pc.policySynced); !ok {
|
||||
return fmt.Errorf("failed to wait for caches to sync")
|
||||
}
|
||||
|
||||
for i := 0; i < policyControllerWorkerCount; i++ {
|
||||
go wait.Until(pc.runWorker, time.Second, stopCh)
|
||||
}
|
||||
glog.Info("started policy controller workers")
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
//Stop to perform actions when controller is stopped
|
||||
func (pc *PolicyController) Stop() {
|
||||
pc.queue.ShutDown()
|
||||
glog.Info("shutting down policy controller workers")
|
||||
}
|
||||
|
||||
func (pc *PolicyController) runWorker() {
|
||||
for pc.processNextWorkItem() {
|
||||
}
|
||||
}
|
||||
|
||||
func (pc *PolicyController) processNextWorkItem() bool {
|
||||
obj, shutdown := pc.queue.Get()
|
||||
if shutdown {
|
||||
return false
|
||||
}
|
||||
|
||||
err := func(obj interface{}) error {
|
||||
defer pc.queue.Done(obj)
|
||||
err := pc.syncHandler(obj)
|
||||
pc.handleErr(err, obj)
|
||||
return nil
|
||||
}(obj)
|
||||
if err != nil {
|
||||
glog.Error(err)
|
||||
return true
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func (pc *PolicyController) handleErr(err error, key interface{}) {
|
||||
if err == nil {
|
||||
pc.queue.Forget(key)
|
||||
return
|
||||
}
|
||||
// This controller retries if something goes wrong. After that, it stops trying.
|
||||
if pc.queue.NumRequeues(key) < policyWorkQueueRetryLimit {
|
||||
glog.Warningf("Error syncing events %v: %v", key, err)
|
||||
// Re-enqueue the key rate limited. Based on the rate limiter on the
|
||||
// queue and the re-enqueue history, the key will be processed later again.
|
||||
pc.queue.AddRateLimited(key)
|
||||
return
|
||||
}
|
||||
pc.queue.Forget(key)
|
||||
glog.Error(err)
|
||||
glog.Warningf("Dropping the key out of the queue: %v", err)
|
||||
}
|
||||
|
||||
func (pc *PolicyController) syncHandler(obj interface{}) error {
|
||||
var key string
|
||||
var ok bool
|
||||
if key, ok = obj.(string); !ok {
|
||||
return fmt.Errorf("expected string in workqueue but got %#v", obj)
|
||||
}
|
||||
_, name, err := cache.SplitMetaNamespaceKey(key)
|
||||
if err != nil {
|
||||
glog.Errorf("invalid policy key: %s", key)
|
||||
return nil
|
||||
}
|
||||
// Get Policy
|
||||
policy, err := pc.policyLister.Get(name)
|
||||
if err != nil {
|
||||
if errors.IsNotFound(err) {
|
||||
glog.Errorf("policy '%s' in work queue no longer exists", key)
|
||||
return nil
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
glog.Infof("process policy %s on existing resources", policy.GetName())
|
||||
// Process policy on existing resources
|
||||
policyInfos := engine.ProcessExisting(pc.client, policy, pc.filterK8Resources)
|
||||
|
||||
events, violations := pc.createEventsAndViolations(policyInfos)
|
||||
// Events, Violations
|
||||
pc.eventController.Add(events...)
|
||||
err = pc.violationBuilder.Add(violations...)
|
||||
if err != nil {
|
||||
glog.Error(err)
|
||||
}
|
||||
|
||||
// Annotations
|
||||
pc.createAnnotations(policyInfos)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (pc *PolicyController) createAnnotations(policyInfos []*info.PolicyInfo) {
|
||||
for _, pi := range policyInfos {
|
||||
//get resource
|
||||
obj, err := pc.client.GetResource(pi.RKind, pi.RNamespace, pi.RName)
|
||||
if err != nil {
|
||||
glog.Error(err)
|
||||
continue
|
||||
}
|
||||
// add annotation for policy application
|
||||
ann := obj.GetAnnotations()
|
||||
// if annotations are nil then create a map and patch
|
||||
// else
|
||||
// add the exact patch
|
||||
patch, err := annotations.PatchAnnotations(ann, pi, info.All)
|
||||
if patch == nil {
|
||||
/// nothing to patch
|
||||
return
|
||||
}
|
||||
_, err = pc.client.PatchResource(pi.RKind, pi.RNamespace, pi.RName, patch)
|
||||
if err != nil {
|
||||
glog.Error(err)
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (pc *PolicyController) createEventsAndViolations(policyInfos []*info.PolicyInfo) ([]*event.Info, []*violation.Info) {
|
||||
events := []*event.Info{}
|
||||
violations := []*violation.Info{}
|
||||
// Create events from the policyInfo
|
||||
for _, policyInfo := range policyInfos {
|
||||
frules := []v1alpha1.FailedRule{}
|
||||
sruleNames := []string{}
|
||||
|
||||
for _, rule := range policyInfo.Rules {
|
||||
if !rule.IsSuccessful() {
|
||||
e := &event.Info{}
|
||||
frule := v1alpha1.FailedRule{Name: rule.Name}
|
||||
switch rule.RuleType {
|
||||
case info.Mutation, info.Validation, info.Generation:
|
||||
// Events
|
||||
e = event.NewEvent(policyInfo.RKind, policyInfo.RNamespace, policyInfo.RName, event.PolicyViolation, event.FProcessRule, rule.Name, policyInfo.Name)
|
||||
switch rule.RuleType {
|
||||
case info.Mutation:
|
||||
frule.Type = info.Mutation.String()
|
||||
case info.Validation:
|
||||
frule.Type = info.Validation.String()
|
||||
case info.Generation:
|
||||
frule.Type = info.Generation.String()
|
||||
}
|
||||
frule.Error = rule.GetErrorString()
|
||||
default:
|
||||
glog.Info("Unsupported Rule type")
|
||||
}
|
||||
frule.Error = rule.GetErrorString()
|
||||
frules = append(frules, frule)
|
||||
events = append(events, e)
|
||||
} else {
|
||||
sruleNames = append(sruleNames, rule.Name)
|
||||
}
|
||||
}
|
||||
|
||||
if !policyInfo.IsSuccessful() {
|
||||
e := event.NewEvent("Policy", "", policyInfo.Name, event.PolicyViolation, event.FResourcePolcy, policyInfo.RNamespace+"/"+policyInfo.RName, concatFailedRules(frules))
|
||||
events = append(events, e)
|
||||
// Violation
|
||||
v := violation.BuldNewViolation(policyInfo.Name, policyInfo.RKind, policyInfo.RNamespace, policyInfo.RName, event.PolicyViolation.String(), policyInfo.GetFailedRules())
|
||||
violations = append(violations, v)
|
||||
} else {
|
||||
// clean up violations
|
||||
pc.violationBuilder.RemoveInactiveViolation(policyInfo.Name, policyInfo.RKind, policyInfo.RNamespace, policyInfo.RName, info.Mutation)
|
||||
pc.violationBuilder.RemoveInactiveViolation(policyInfo.Name, policyInfo.RKind, policyInfo.RNamespace, policyInfo.RName, info.Validation)
|
||||
}
|
||||
}
|
||||
return events, violations
|
||||
}
|
147
pkg/controller/controller_test.go
Normal file
147
pkg/controller/controller_test.go
Normal file
|
@ -0,0 +1,147 @@
|
|||
package controller
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/golang/glog"
|
||||
types "github.com/nirmata/kyverno/pkg/apis/policy/v1alpha1"
|
||||
client "github.com/nirmata/kyverno/pkg/dclient"
|
||||
event "github.com/nirmata/kyverno/pkg/event"
|
||||
"github.com/nirmata/kyverno/pkg/sharedinformer"
|
||||
violation "github.com/nirmata/kyverno/pkg/violation"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
"k8s.io/sample-controller/pkg/signals"
|
||||
)
|
||||
|
||||
func TestCreatePolicy(t *testing.T) {
|
||||
f := newFixture(t)
|
||||
// new policy is added to policy lister and explictly passed to sync-handler
|
||||
// to process the existing
|
||||
policy := newPolicy("test-policy")
|
||||
f.policyLister = append(f.policyLister, policy)
|
||||
f.objects = append(f.objects, policy)
|
||||
// run controller
|
||||
f.runControler("test-policy")
|
||||
}
|
||||
|
||||
func (f *fixture) runControler(policyName string) {
|
||||
policyInformerFactory, err := sharedinformer.NewFakeSharedInformerFactory()
|
||||
if err != nil {
|
||||
f.t.Fatal(err)
|
||||
}
|
||||
|
||||
eventController := event.NewEventController(f.Client, policyInformerFactory)
|
||||
violationBuilder := violation.NewPolicyViolationBuilder(f.Client, policyInformerFactory, eventController)
|
||||
|
||||
// new controller
|
||||
policyController := NewPolicyController(
|
||||
f.Client,
|
||||
policyInformerFactory,
|
||||
violationBuilder,
|
||||
eventController,
|
||||
"")
|
||||
|
||||
stopCh := signals.SetupSignalHandler()
|
||||
// start informer & controller
|
||||
policyInformerFactory.Run(stopCh)
|
||||
if err = policyController.Run(stopCh); err != nil {
|
||||
glog.Fatalf("Error running PolicyController: %v\n", err)
|
||||
}
|
||||
// add policy to the informer
|
||||
for _, p := range f.policyLister {
|
||||
policyInformerFactory.GetInfomer().GetIndexer().Add(p)
|
||||
}
|
||||
|
||||
// sync handler
|
||||
// reads the policy from the policy lister and processes them
|
||||
err = policyController.syncHandler(policyName)
|
||||
if err != nil {
|
||||
f.t.Fatal(err)
|
||||
}
|
||||
policyController.Stop()
|
||||
|
||||
}
|
||||
|
||||
type fixture struct {
|
||||
t *testing.T
|
||||
Client *client.Client
|
||||
policyLister []*types.Policy
|
||||
objects []runtime.Object
|
||||
}
|
||||
|
||||
func newFixture(t *testing.T) *fixture {
|
||||
|
||||
// init groupversion
|
||||
regResource := []schema.GroupVersionResource{
|
||||
schema.GroupVersionResource{Group: "group", Version: "version", Resource: "thekinds"},
|
||||
schema.GroupVersionResource{Group: "group2", Version: "version", Resource: "thekinds"},
|
||||
schema.GroupVersionResource{Group: "", Version: "v1", Resource: "namespaces"},
|
||||
schema.GroupVersionResource{Group: "apps", Version: "v1", Resource: "deployments"},
|
||||
}
|
||||
|
||||
objects := []runtime.Object{newUnstructured("group/version", "TheKind", "ns-foo", "name-foo"),
|
||||
newUnstructured("group2/version", "TheKind", "ns-foo", "name2-foo"),
|
||||
newUnstructured("group/version", "TheKind", "ns-foo", "name-bar"),
|
||||
newUnstructured("group/version", "TheKind", "ns-foo", "name-baz"),
|
||||
newUnstructured("group2/version", "TheKind", "ns-foo", "name2-baz"),
|
||||
newUnstructured("apps/v1", "Deployment", "kyverno", "kyverno"),
|
||||
}
|
||||
|
||||
scheme := runtime.NewScheme()
|
||||
// Create mock client
|
||||
fclient, err := client.NewMockClient(scheme, objects...)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// set discovery Client
|
||||
fclient.SetDiscovery(client.NewFakeDiscoveryClient(regResource))
|
||||
|
||||
f := &fixture{
|
||||
t: t,
|
||||
Client: fclient,
|
||||
}
|
||||
|
||||
return f
|
||||
}
|
||||
|
||||
// create mock client with initial resouces
|
||||
// set registered resources for gvr
|
||||
func (f *fixture) setupFixture() {
|
||||
scheme := runtime.NewScheme()
|
||||
fclient, err := client.NewMockClient(scheme, f.objects...)
|
||||
if err != nil {
|
||||
f.t.Fatal(err)
|
||||
}
|
||||
|
||||
regresource := []schema.GroupVersionResource{
|
||||
schema.GroupVersionResource{Group: "kyverno.io",
|
||||
Version: "v1alpha1",
|
||||
Resource: "policys"}}
|
||||
fclient.SetDiscovery(client.NewFakeDiscoveryClient(regresource))
|
||||
}
|
||||
|
||||
func newPolicy(name string) *types.Policy {
|
||||
return &types.Policy{
|
||||
TypeMeta: metav1.TypeMeta{APIVersion: types.SchemeGroupVersion.String()},
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: name,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func newUnstructured(apiVersion, kind, namespace, name string) *unstructured.Unstructured {
|
||||
return &unstructured.Unstructured{
|
||||
Object: map[string]interface{}{
|
||||
"apiVersion": apiVersion,
|
||||
"kind": kind,
|
||||
"metadata": map[string]interface{}{
|
||||
"namespace": namespace,
|
||||
"name": name,
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
|
@ -3,12 +3,14 @@ package engine
|
|||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"github.com/golang/glog"
|
||||
kyverno "github.com/nirmata/kyverno/pkg/api/kyverno/v1alpha1"
|
||||
client "github.com/nirmata/kyverno/pkg/dclient"
|
||||
"github.com/nirmata/kyverno/pkg/info"
|
||||
"github.com/nirmata/kyverno/pkg/utils"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
)
|
||||
|
@ -22,7 +24,7 @@ func Generate(client *client.Client, policy kyverno.Policy, ns unstructured.Unst
|
|||
}
|
||||
glog.V(4).Infof("applying policy %s generate rule %s on resource %s/%s/%s", policy.Name, rule.Name, ns.GetKind(), ns.GetNamespace(), ns.GetName())
|
||||
ri := info.NewRuleInfo(rule.Name, info.Generation)
|
||||
err := applyRuleGenerator(client, ns, rule.Generation)
|
||||
err := applyRuleGenerator(client, ns, rule.Generation, policy.GetCreationTimestamp())
|
||||
if err != nil {
|
||||
ri.Fail()
|
||||
ri.Addf("Failed to apply rule generator, err %v.", rule.Name, err)
|
||||
|
@ -36,11 +38,15 @@ func Generate(client *client.Client, policy kyverno.Policy, ns unstructured.Unst
|
|||
return ris
|
||||
}
|
||||
|
||||
func applyRuleGenerator(client *client.Client, ns unstructured.Unstructured, gen kyverno.Generation) error {
|
||||
func applyRuleGenerator(client *client.Client, ns unstructured.Unstructured, gen kyverno.Generation, policyCreationTime metav1.Time) error {
|
||||
var err error
|
||||
resource := &unstructured.Unstructured{}
|
||||
var rdata map[string]interface{}
|
||||
|
||||
// To manage existing resource , we compare the creation time for the default resource to be generate and policy creation time
|
||||
processExisting := func() bool {
|
||||
nsCreationTime := ns.GetCreationTimestamp()
|
||||
return nsCreationTime.Before(&policyCreationTime)
|
||||
}()
|
||||
if gen.Data != nil {
|
||||
glog.V(4).Info("generate rule: creates new resource")
|
||||
// 1> Check if resource exists
|
||||
|
@ -85,12 +91,15 @@ func applyRuleGenerator(client *client.Client, ns unstructured.Unstructured, gen
|
|||
glog.V(4).Infof("generate rule: clone reference resource %s/%s/%s present", gen.Kind, gen.Kind, gen.Clone.Namespace, gen.Clone.Name)
|
||||
rdata = resource.UnstructuredContent()
|
||||
}
|
||||
if processExisting {
|
||||
// for existing resources we generate an error which indirectly generates a policy violation
|
||||
return fmt.Errorf("resource %s not found in existing namespace %s", gen.Name, ns.GetName())
|
||||
}
|
||||
resource.SetUnstructuredContent(rdata)
|
||||
resource.SetName(gen.Name)
|
||||
resource.SetNamespace(ns.GetName())
|
||||
// Reset resource version
|
||||
resource.SetResourceVersion("")
|
||||
|
||||
_, err = client.CreateResource(gen.Kind, ns.GetName(), resource, false)
|
||||
if err != nil {
|
||||
glog.V(4).Infof("generate rule: unable to create resource %s/%s/%s: %v", gen.Kind, resource.GetNamespace(), resource.GetName(), err)
|
||||
|
|
|
@ -10,11 +10,21 @@ import (
|
|||
)
|
||||
|
||||
// Mutate performs mutation. Overlay first and then mutation patches
|
||||
//TODO: check if gvk needs to be passed or can be set in resource
|
||||
func Mutate(policy kyverno.Policy, resource unstructured.Unstructured) ([][]byte, []info.RuleInfo) {
|
||||
//TODO: convert rawResource to unstructured to avoid unmarhalling all the time for get some resource information
|
||||
var patches [][]byte
|
||||
var ruleInfos []info.RuleInfo
|
||||
func Mutate(policy kyverno.Policy, resource unstructured.Unstructured) EngineResponse {
|
||||
var allPatches, rulePatches [][]byte
|
||||
var err error
|
||||
var errs []error
|
||||
ris := []info.RuleInfo{}
|
||||
|
||||
patchedDocument, err := resource.MarshalJSON()
|
||||
if err != nil {
|
||||
glog.Errorf("unable to marshal resource : %v\n", err)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
glog.V(4).Infof("unable to marshal resource : %v", err)
|
||||
return EngineResponse{PatchedResource: resource}
|
||||
}
|
||||
|
||||
for _, rule := range policy.Spec.Rules {
|
||||
if reflect.DeepEqual(rule.Mutation, kyverno.Mutation{}) {
|
||||
|
@ -34,9 +44,9 @@ func Mutate(policy kyverno.Policy, resource unstructured.Unstructured) ([][]byte
|
|||
|
||||
// Process Overlay
|
||||
if rule.Mutation.Overlay != nil {
|
||||
oPatches, err := processOverlay(resource, rule)
|
||||
rulePatches, err = processOverlay(rule, patchedDocument)
|
||||
if err == nil {
|
||||
if len(oPatches) == 0 {
|
||||
if len(rulePatches) == 0 {
|
||||
// if array elements dont match then we skip(nil patch, no error)
|
||||
// or if acnohor is defined and doenst match
|
||||
// policy is not applicable
|
||||
|
@ -44,14 +54,13 @@ func Mutate(policy kyverno.Policy, resource unstructured.Unstructured) ([][]byte
|
|||
continue
|
||||
}
|
||||
|
||||
glog.V(4).Infof("overlay applied succesfully on resource %s/%s", resource.GetNamespace(), resource.GetName())
|
||||
ruleInfo.Add("Overlay succesfully applied")
|
||||
ruleInfo.Addf("Rule %s: Overlay succesfully applied.", rule.Name)
|
||||
|
||||
// update rule information
|
||||
// strip slashes from string
|
||||
patch := JoinPatches(oPatches)
|
||||
ruleInfo.Changes = string(patch)
|
||||
patches = append(patches, oPatches...)
|
||||
ruleInfo.Patches = rulePatches
|
||||
allPatches = append(allPatches, rulePatches...)
|
||||
|
||||
glog.V(4).Infof("overlay applied succesfully on resource %s/%s", resource.GetNamespace(), resource.GetName())
|
||||
} else {
|
||||
glog.V(4).Infof("failed to apply overlay: %v", err)
|
||||
ruleInfo.Fail()
|
||||
|
@ -61,7 +70,7 @@ func Mutate(policy kyverno.Policy, resource unstructured.Unstructured) ([][]byte
|
|||
|
||||
// Process Patches
|
||||
if len(rule.Mutation.Patches) != 0 {
|
||||
jsonPatches, errs := processPatches(resource, rule)
|
||||
rulePatches, errs = processPatches(rule, patchedDocument)
|
||||
if len(errs) > 0 {
|
||||
ruleInfo.Fail()
|
||||
for _, err := range errs {
|
||||
|
@ -71,10 +80,29 @@ func Mutate(policy kyverno.Policy, resource unstructured.Unstructured) ([][]byte
|
|||
} else {
|
||||
glog.V(4).Infof("patches applied succesfully on resource %s/%s", resource.GetNamespace(), resource.GetName())
|
||||
ruleInfo.Addf("Patches succesfully applied.")
|
||||
patches = append(patches, jsonPatches...)
|
||||
|
||||
ruleInfo.Patches = rulePatches
|
||||
allPatches = append(allPatches, rulePatches...)
|
||||
}
|
||||
}
|
||||
ruleInfos = append(ruleInfos, ruleInfo)
|
||||
|
||||
patchedDocument, err = ApplyPatches(patchedDocument, rulePatches)
|
||||
if err != nil {
|
||||
glog.Errorf("Failed to apply patches on ruleName=%s, err%v\n:", rule.Name, err)
|
||||
}
|
||||
|
||||
ris = append(ris, ruleInfo)
|
||||
}
|
||||
|
||||
patchedResource, err := ConvertToUnstructured(patchedDocument)
|
||||
if err != nil {
|
||||
glog.Errorf("Failed to convert patched resource to unstructuredtype, err%v\n:", err)
|
||||
return EngineResponse{PatchedResource: resource}
|
||||
}
|
||||
|
||||
return EngineResponse{
|
||||
Patches: allPatches,
|
||||
PatchedResource: *patchedResource,
|
||||
RuleInfos: ris,
|
||||
}
|
||||
return patches, ruleInfos
|
||||
}
|
||||
|
|
|
@ -12,29 +12,27 @@ import (
|
|||
|
||||
jsonpatch "github.com/evanphx/json-patch"
|
||||
kyverno "github.com/nirmata/kyverno/pkg/api/kyverno/v1alpha1"
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
)
|
||||
|
||||
// ProcessOverlay handles validating admission request
|
||||
// rawResource handles validating admission request
|
||||
// Checks the target resources for rules defined in the policy
|
||||
func processOverlay(resourceUnstr unstructured.Unstructured, rule kyverno.Rule) ([][]byte, error) {
|
||||
|
||||
//TODO check if there is better solution
|
||||
resourceRaw, err := resourceUnstr.MarshalJSON()
|
||||
if err != nil {
|
||||
glog.V(4).Infof("unable to marshal resource : %v", err)
|
||||
return nil, err
|
||||
}
|
||||
// TODO: pass in the unstructured object in stead of raw byte?
|
||||
func processOverlay(rule kyverno.Rule, rawResource []byte) ([][]byte, error) {
|
||||
var resource interface{}
|
||||
if err := json.Unmarshal(resourceRaw, &resource); err != nil {
|
||||
if err := json.Unmarshal(rawResource, &resource); err != nil {
|
||||
glog.V(4).Infof("unable to unmarshal resource : %v", err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
resourceInfo := ParseResourceInfoFromObject(rawResource)
|
||||
patches, err := processOverlayPatches(resource, rule.Mutation.Overlay)
|
||||
if err != nil && strings.Contains(err.Error(), "Conditions are not met") {
|
||||
glog.V(4).Infof("overlay pattern %s does not match resource %s/%s", rule.Mutation.Overlay, resourceUnstr.GetNamespace(), resourceUnstr.GetName())
|
||||
return nil, nil
|
||||
// glog.V(4).Infof("overlay pattern %s does not match resource %s/%s", rule.Mutation.Overlay, resourceUnstr.GetNamespace(), resourceUnstr.GetName())
|
||||
glog.Infof("Resource does not meet conditions in overlay pattern, resource=%s, rule=%s\n", resourceInfo, rule.Name)
|
||||
// patches, err := processOverlayPatches(resource, rule.Mutation.Overlay)
|
||||
// if err != nil && strings.Contains(err.Error(), "Conditions are not met") {
|
||||
// glog.V(4).Infof("overlay pattern %s does not match resource %s/%s", rule.Mutation.Overlay, resourceUnstr.GetNamespace(), resourceUnstr.GetName())
|
||||
// return nil, nil
|
||||
}
|
||||
|
||||
return patches, err
|
||||
|
|
|
@ -3,11 +3,9 @@ package engine
|
|||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"reflect"
|
||||
|
||||
"github.com/golang/glog"
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
|
||||
jsonpatch "github.com/evanphx/json-patch"
|
||||
kyverno "github.com/nirmata/kyverno/pkg/api/kyverno/v1alpha1"
|
||||
|
@ -15,15 +13,8 @@ import (
|
|||
|
||||
// ProcessPatches Returns array from separate patches that can be applied to the document
|
||||
// Returns error ONLY in case when creation of resource should be denied.
|
||||
func processPatches(resourceUnstr unstructured.Unstructured, rule kyverno.Rule) (allPatches [][]byte, errs []error) {
|
||||
//TODO check if there is better solution
|
||||
resource, err := resourceUnstr.MarshalJSON()
|
||||
if err != nil {
|
||||
glog.V(4).Infof("unable to marshal resource : %v", err)
|
||||
errs = append(errs, fmt.Errorf("unable to marshal resource : %v", err))
|
||||
return nil, errs
|
||||
}
|
||||
|
||||
// TODO: pass in the unstructured object in stead of raw byte?
|
||||
func processPatches(rule kyverno.Rule, resource []byte) (allPatches [][]byte, errs []error) {
|
||||
if len(resource) == 0 {
|
||||
errs = append(errs, errors.New("Source document for patching is empty"))
|
||||
return nil, errs
|
||||
|
|
|
@ -5,7 +5,7 @@ import (
|
|||
|
||||
"gotest.tools/assert"
|
||||
|
||||
types "github.com/nirmata/kyverno/pkg/apis/policy/v1alpha1"
|
||||
types "github.com/nirmata/kyverno/pkg/api/kyverno/v1alpha1"
|
||||
)
|
||||
|
||||
const endpointsDocument string = `{
|
||||
|
@ -58,7 +58,7 @@ func makeRuleWithPatches(patches []types.Patch) types.Rule {
|
|||
Patches: patches,
|
||||
}
|
||||
return types.Rule{
|
||||
Mutation: &mutation,
|
||||
Mutation: mutation,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -10,6 +10,7 @@ import (
|
|||
|
||||
"github.com/minio/minio/pkg/wildcard"
|
||||
kyverno "github.com/nirmata/kyverno/pkg/api/kyverno/v1alpha1"
|
||||
"github.com/nirmata/kyverno/pkg/info"
|
||||
"github.com/nirmata/kyverno/pkg/utils"
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
|
||||
|
@ -17,6 +18,142 @@ import (
|
|||
"k8s.io/apimachinery/pkg/labels"
|
||||
)
|
||||
|
||||
type EngineResponse struct {
|
||||
Patches [][]byte
|
||||
PatchedResource unstructured.Unstructured
|
||||
RuleInfos []info.RuleInfo
|
||||
}
|
||||
|
||||
// //ListResourcesThatApplyToPolicy returns list of resources that are filtered by policy rules
|
||||
// func ListResourcesThatApplyToPolicy(client *client.Client, policy *kyverno.Policy, filterK8Resources []utils.K8Resource) map[string]resourceInfo {
|
||||
// // key uid
|
||||
// resourceMap := map[string]resourceInfo{}
|
||||
// for _, rule := range policy.Spec.Rules {
|
||||
// // Match
|
||||
// for _, k := range rule.MatchResources.Kinds {
|
||||
// namespaces := []string{}
|
||||
// if k == "Namespace" {
|
||||
// namespaces = []string{""}
|
||||
// } else {
|
||||
// if rule.MatchResources.Namespace != "" {
|
||||
// // if namespace is specified then we add the namespace
|
||||
// namespaces = append(namespaces, rule.MatchResources.Namespace)
|
||||
// } else {
|
||||
// // no namespace specified, refer to all namespaces
|
||||
// namespaces = getAllNamespaces(client)
|
||||
// }
|
||||
|
||||
// // Check if exclude namespace is not clashing
|
||||
// namespaces = excludeNamespaces(namespaces, rule.ExcludeResources.Namespace)
|
||||
// }
|
||||
|
||||
// // If kind is namespace then namespace is "", override
|
||||
// // Get resources in the namespace
|
||||
// for _, ns := range namespaces {
|
||||
// rMap := getResourcesPerNamespace(k, client, ns, rule, filterK8Resources)
|
||||
// mergeresources(resourceMap, rMap)
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// return resourceMap
|
||||
// }
|
||||
|
||||
// func getResourcesPerNamespace(kind string, client *client.Client, namespace string, rule kyverno.Rule, filterK8Resources []utils.K8Resource) map[string]resourceInfo {
|
||||
// resourceMap := map[string]resourceInfo{}
|
||||
// // List resources
|
||||
// list, err := client.ListResource(kind, namespace, rule.MatchResources.Selector)
|
||||
// if err != nil {
|
||||
// glog.Errorf("unable to list resource for %s with label selector %s", kind, rule.MatchResources.Selector.String())
|
||||
// return nil
|
||||
// }
|
||||
// var selector labels.Selector
|
||||
// // exclude label selector
|
||||
// if rule.ExcludeResources.Selector != nil {
|
||||
// selector, err = v1helper.LabelSelectorAsSelector(rule.ExcludeResources.Selector)
|
||||
// if err != nil {
|
||||
// glog.Error(err)
|
||||
// }
|
||||
// }
|
||||
// for _, res := range list.Items {
|
||||
// // exclude label selectors
|
||||
// if selector != nil {
|
||||
// set := labels.Set(res.GetLabels())
|
||||
// if selector.Matches(set) {
|
||||
// // if matches
|
||||
// continue
|
||||
// }
|
||||
// }
|
||||
// var name string
|
||||
// // match
|
||||
// // name
|
||||
// // wild card matching
|
||||
// name = rule.MatchResources.Name
|
||||
// if name != "" {
|
||||
// // if does not match then we skip
|
||||
// if !wildcard.Match(name, res.GetName()) {
|
||||
// continue
|
||||
// }
|
||||
// }
|
||||
// // exclude
|
||||
// // name
|
||||
// // wild card matching
|
||||
// name = rule.ExcludeResources.Name
|
||||
// if name != "nil" {
|
||||
// // if matches then we skip
|
||||
// if wildcard.Match(name, res.GetName()) {
|
||||
// continue
|
||||
// }
|
||||
// }
|
||||
// gvk := res.GroupVersionKind()
|
||||
|
||||
// ri := resourceInfo{Resource: res, Gvk: &metav1.GroupVersionKind{Group: gvk.Group,
|
||||
// Version: gvk.Version,
|
||||
// Kind: gvk.Kind}}
|
||||
// // Skip the filtered resources
|
||||
// if utils.SkipFilteredResources(gvk.Kind, res.GetNamespace(), res.GetName(), filterK8Resources) {
|
||||
// continue
|
||||
// }
|
||||
|
||||
// resourceMap[string(res.GetUID())] = ri
|
||||
// }
|
||||
// return resourceMap
|
||||
// }
|
||||
|
||||
// // merge b into a map
|
||||
// func mergeresources(a, b map[string]resourceInfo) {
|
||||
// for k, v := range b {
|
||||
// a[k] = v
|
||||
// }
|
||||
// }
|
||||
|
||||
// func getAllNamespaces(client *client.Client) []string {
|
||||
// namespaces := []string{}
|
||||
// // get all namespaces
|
||||
// nsList, err := client.ListResource("Namespace", "", nil)
|
||||
// if err != nil {
|
||||
// glog.Error(err)
|
||||
// return namespaces
|
||||
// }
|
||||
// for _, ns := range nsList.Items {
|
||||
// namespaces = append(namespaces, ns.GetName())
|
||||
// }
|
||||
// return namespaces
|
||||
// }
|
||||
|
||||
// func excludeNamespaces(namespaces []string, excludeNs string) []string {
|
||||
// if excludeNs == "" {
|
||||
// return namespaces
|
||||
// }
|
||||
// filteredNamespaces := []string{}
|
||||
// for _, n := range namespaces {
|
||||
// if n == excludeNs {
|
||||
// continue
|
||||
// }
|
||||
// filteredNamespaces = append(filteredNamespaces, n)
|
||||
// }
|
||||
// return filteredNamespaces
|
||||
// }
|
||||
|
||||
//MatchesResourceDescription checks if the resource matches resource desription of the rule or not
|
||||
func MatchesResourceDescription(resource unstructured.Unstructured, rule kyverno.Rule) bool {
|
||||
matches := rule.MatchResources.ResourceDescription
|
||||
|
@ -355,3 +492,18 @@ func convertToFloat(value interface{}) (float64, error) {
|
|||
return 0, fmt.Errorf("Could not convert %T to float64", value)
|
||||
}
|
||||
}
|
||||
|
||||
type resourceInfo struct {
|
||||
Resource unstructured.Unstructured
|
||||
Gvk *metav1.GroupVersionKind
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
|
|
@ -3,9 +3,340 @@ package engine
|
|||
import (
|
||||
"testing"
|
||||
|
||||
types "github.com/nirmata/kyverno/pkg/api/kyverno/v1alpha1"
|
||||
"gotest.tools/assert"
|
||||
)
|
||||
|
||||
func TestResourceMeetsDescription_Kind(t *testing.T) {
|
||||
resourceName := "test-config-map"
|
||||
resourceDescription := types.ResourceDescription{
|
||||
Kinds: []string{"ConfigMap"},
|
||||
Name: resourceName,
|
||||
Selector: &metav1.LabelSelector{
|
||||
MatchLabels: nil,
|
||||
MatchExpressions: nil,
|
||||
},
|
||||
}
|
||||
excludeResourcesResourceDesc := types.ResourceDescription{}
|
||||
groupVersionKind := metav1.GroupVersionKind{Kind: "ConfigMap"}
|
||||
|
||||
rawResource := []byte(`{
|
||||
"metadata":{
|
||||
"name":"test-config-map",
|
||||
"namespace":"default",
|
||||
"creationTimestamp":null,
|
||||
"labels":{
|
||||
"label1":"test1",
|
||||
"label2":"test2"
|
||||
}
|
||||
}
|
||||
}`)
|
||||
|
||||
assert.Assert(t, ResourceMeetsDescription(rawResource, resourceDescription, excludeResourcesResourceDesc, groupVersionKind))
|
||||
resourceDescription.Kinds[0] = "Deployment"
|
||||
assert.Assert(t, false == ResourceMeetsDescription(rawResource, resourceDescription, excludeResourcesResourceDesc, groupVersionKind))
|
||||
resourceDescription.Kinds[0] = "ConfigMap"
|
||||
groupVersionKind.Kind = "Deployment"
|
||||
assert.Assert(t, false == ResourceMeetsDescription(rawResource, resourceDescription, excludeResourcesResourceDesc, groupVersionKind))
|
||||
}
|
||||
|
||||
func TestResourceMeetsDescription_Name(t *testing.T) {
|
||||
resourceName := "test-config-map"
|
||||
resourceDescription := types.ResourceDescription{
|
||||
Kinds: []string{"ConfigMap"},
|
||||
Name: resourceName,
|
||||
Selector: &metav1.LabelSelector{
|
||||
MatchLabels: nil,
|
||||
MatchExpressions: nil,
|
||||
},
|
||||
}
|
||||
excludeResourcesResourceDesc := types.ResourceDescription{}
|
||||
|
||||
groupVersionKind := metav1.GroupVersionKind{Kind: "ConfigMap"}
|
||||
|
||||
rawResource := []byte(`{
|
||||
"metadata":{
|
||||
"name":"test-config-map",
|
||||
"namespace":"default",
|
||||
"creationTimestamp":null,
|
||||
"labels":{
|
||||
"label1":"test1",
|
||||
"label2":"test2"
|
||||
}
|
||||
}
|
||||
}`)
|
||||
|
||||
assert.Assert(t, ResourceMeetsDescription(rawResource, resourceDescription, excludeResourcesResourceDesc, groupVersionKind))
|
||||
resourceDescription.Name = "test-config-map-new"
|
||||
assert.Assert(t, false == ResourceMeetsDescription(rawResource, resourceDescription, excludeResourcesResourceDesc, groupVersionKind))
|
||||
|
||||
rawResource = []byte(`{
|
||||
"metadata":{
|
||||
"name":"test-config-map-new",
|
||||
"namespace":"default",
|
||||
"creationTimestamp":null,
|
||||
"labels":{
|
||||
"label1":"test1",
|
||||
"label2":"test2"
|
||||
}
|
||||
}
|
||||
}`)
|
||||
assert.Assert(t, ResourceMeetsDescription(rawResource, resourceDescription, excludeResourcesResourceDesc, groupVersionKind))
|
||||
|
||||
rawResource = []byte(`{
|
||||
"metadata":{
|
||||
"name":"",
|
||||
"namespace":"default",
|
||||
"creationTimestamp":null,
|
||||
"labels":{
|
||||
"label1":"test1",
|
||||
"label2":"test2"
|
||||
}
|
||||
}
|
||||
}`)
|
||||
assert.Assert(t, false == ResourceMeetsDescription(rawResource, resourceDescription, excludeResourcesResourceDesc, groupVersionKind))
|
||||
}
|
||||
|
||||
func TestResourceMeetsDescription_MatchExpressions(t *testing.T) {
|
||||
resourceName := "test-config-map"
|
||||
resourceDescription := types.ResourceDescription{
|
||||
Kinds: []string{"ConfigMap"},
|
||||
Name: resourceName,
|
||||
Selector: &metav1.LabelSelector{
|
||||
MatchLabels: nil,
|
||||
MatchExpressions: []metav1.LabelSelectorRequirement{
|
||||
metav1.LabelSelectorRequirement{
|
||||
Key: "label2",
|
||||
Operator: "NotIn",
|
||||
Values: []string{
|
||||
"sometest1",
|
||||
},
|
||||
},
|
||||
metav1.LabelSelectorRequirement{
|
||||
Key: "label1",
|
||||
Operator: "In",
|
||||
Values: []string{
|
||||
"test1",
|
||||
"test8",
|
||||
"test201",
|
||||
},
|
||||
},
|
||||
metav1.LabelSelectorRequirement{
|
||||
Key: "label3",
|
||||
Operator: "DoesNotExist",
|
||||
Values: nil,
|
||||
},
|
||||
metav1.LabelSelectorRequirement{
|
||||
Key: "label2",
|
||||
Operator: "In",
|
||||
Values: []string{
|
||||
"test2",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
excludeResourcesResourceDesc := types.ResourceDescription{}
|
||||
|
||||
groupVersionKind := metav1.GroupVersionKind{Kind: "ConfigMap"}
|
||||
rawResource := []byte(`{
|
||||
"metadata":{
|
||||
"name":"test-config-map",
|
||||
"namespace":"default",
|
||||
"creationTimestamp":null,
|
||||
"labels":{
|
||||
"label1":"test1",
|
||||
"label2":"test2"
|
||||
}
|
||||
}
|
||||
}`)
|
||||
|
||||
assert.Assert(t, ResourceMeetsDescription(rawResource, resourceDescription, excludeResourcesResourceDesc, groupVersionKind))
|
||||
|
||||
rawResource = []byte(`{
|
||||
"metadata":{
|
||||
"name":"test-config-map",
|
||||
"namespace":"default",
|
||||
"creationTimestamp":null,
|
||||
"labels":{
|
||||
"label1":"test1234567890",
|
||||
"label2":"test2"
|
||||
}
|
||||
}
|
||||
}`)
|
||||
|
||||
assert.Assert(t, false == ResourceMeetsDescription(rawResource, resourceDescription, excludeResourcesResourceDesc, groupVersionKind))
|
||||
}
|
||||
|
||||
func TestResourceMeetsDescription_MatchLabels(t *testing.T) {
|
||||
resourceName := "test-config-map"
|
||||
resourceDescription := types.ResourceDescription{
|
||||
Kinds: []string{"ConfigMap"},
|
||||
Name: resourceName,
|
||||
Selector: &metav1.LabelSelector{
|
||||
MatchLabels: map[string]string{
|
||||
"label1": "test1",
|
||||
"label2": "test2",
|
||||
},
|
||||
MatchExpressions: nil,
|
||||
},
|
||||
}
|
||||
groupVersionKind := metav1.GroupVersionKind{Kind: "ConfigMap"}
|
||||
excludeResourcesResourceDesc := types.ResourceDescription{}
|
||||
|
||||
rawResource := []byte(`{
|
||||
"metadata":{
|
||||
"name":"test-config-map",
|
||||
"namespace":"default",
|
||||
"creationTimestamp":null,
|
||||
"labels":{
|
||||
"label1":"test1",
|
||||
"label2":"test2"
|
||||
}
|
||||
}
|
||||
}`)
|
||||
assert.Assert(t, ResourceMeetsDescription(rawResource, resourceDescription, excludeResourcesResourceDesc, groupVersionKind))
|
||||
|
||||
rawResource = []byte(`{
|
||||
"metadata":{
|
||||
"name":"test-config-map",
|
||||
"namespace":"default",
|
||||
"creationTimestamp":null,
|
||||
"labels":{
|
||||
"label3":"test1",
|
||||
"label2":"test2"
|
||||
}
|
||||
}
|
||||
}`)
|
||||
assert.Assert(t, false == ResourceMeetsDescription(rawResource, resourceDescription, excludeResourcesResourceDesc, groupVersionKind))
|
||||
|
||||
resourceDescription = types.ResourceDescription{
|
||||
Kinds: []string{"ConfigMap"},
|
||||
Name: resourceName,
|
||||
Selector: &metav1.LabelSelector{
|
||||
MatchLabels: map[string]string{
|
||||
"label3": "test1",
|
||||
"label2": "test2",
|
||||
},
|
||||
MatchExpressions: nil,
|
||||
},
|
||||
}
|
||||
|
||||
assert.Assert(t, ResourceMeetsDescription(rawResource, resourceDescription, excludeResourcesResourceDesc, groupVersionKind))
|
||||
}
|
||||
|
||||
func TestResourceMeetsDescription_MatchLabelsAndMatchExpressions(t *testing.T) {
|
||||
resourceName := "test-config-map"
|
||||
resourceDescription := types.ResourceDescription{
|
||||
Kinds: []string{"ConfigMap"},
|
||||
Name: resourceName,
|
||||
Selector: &metav1.LabelSelector{
|
||||
MatchLabels: map[string]string{
|
||||
"label1": "test1",
|
||||
},
|
||||
MatchExpressions: []metav1.LabelSelectorRequirement{
|
||||
metav1.LabelSelectorRequirement{
|
||||
Key: "label2",
|
||||
Operator: "In",
|
||||
Values: []string{
|
||||
"test2",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
groupVersionKind := metav1.GroupVersionKind{Kind: "ConfigMap"}
|
||||
excludeResourcesResourceDesc := types.ResourceDescription{}
|
||||
|
||||
rawResource := []byte(`{
|
||||
"metadata":{
|
||||
"name":"test-config-map",
|
||||
"namespace":"default",
|
||||
"creationTimestamp":null,
|
||||
"labels":{
|
||||
"label1":"test1",
|
||||
"label2":"test2"
|
||||
}
|
||||
}
|
||||
}`)
|
||||
|
||||
assert.Assert(t, ResourceMeetsDescription(rawResource, resourceDescription, excludeResourcesResourceDesc, groupVersionKind))
|
||||
|
||||
resourceDescription = types.ResourceDescription{
|
||||
Kinds: []string{"ConfigMap"},
|
||||
Name: resourceName,
|
||||
Selector: &metav1.LabelSelector{
|
||||
MatchLabels: map[string]string{
|
||||
"label1": "test1",
|
||||
},
|
||||
MatchExpressions: []metav1.LabelSelectorRequirement{
|
||||
metav1.LabelSelectorRequirement{
|
||||
Key: "label2",
|
||||
Operator: "NotIn",
|
||||
Values: []string{
|
||||
"sometest1",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
rawResource = []byte(`{
|
||||
"metadata":{
|
||||
"name":"test-config-map",
|
||||
"namespace":"default",
|
||||
"creationTimestamp":null,
|
||||
"labels":{
|
||||
"label1":"test1",
|
||||
"label2":"test2"
|
||||
}
|
||||
}
|
||||
}`)
|
||||
assert.Assert(t, ResourceMeetsDescription(rawResource, resourceDescription, excludeResourcesResourceDesc, groupVersionKind))
|
||||
|
||||
resourceDescription = types.ResourceDescription{
|
||||
Kinds: []string{"ConfigMap"},
|
||||
Name: resourceName,
|
||||
Selector: &metav1.LabelSelector{
|
||||
MatchLabels: map[string]string{
|
||||
"label1": "test1",
|
||||
},
|
||||
MatchExpressions: []metav1.LabelSelectorRequirement{
|
||||
metav1.LabelSelectorRequirement{
|
||||
Key: "label2",
|
||||
Operator: "In",
|
||||
Values: []string{
|
||||
"sometest1",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
assert.Assert(t, false == ResourceMeetsDescription(rawResource, resourceDescription, excludeResourcesResourceDesc, groupVersionKind))
|
||||
|
||||
resourceDescription = types.ResourceDescription{
|
||||
Kinds: []string{"ConfigMap"},
|
||||
Name: resourceName,
|
||||
Selector: &metav1.LabelSelector{
|
||||
MatchLabels: map[string]string{
|
||||
"label1": "test1",
|
||||
"label3": "test3",
|
||||
},
|
||||
MatchExpressions: []metav1.LabelSelectorRequirement{
|
||||
metav1.LabelSelectorRequirement{
|
||||
Key: "label2",
|
||||
Operator: "In",
|
||||
Values: []string{
|
||||
"test2",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
assert.Assert(t, false == ResourceMeetsDescription(rawResource, resourceDescription, excludeResourcesResourceDesc, groupVersionKind))
|
||||
}
|
||||
// func TestResourceMeetsDescription_Kind(t *testing.T) {
|
||||
// resourceName := "test-config-map"
|
||||
// resourceDescription := types.ResourceDescription{
|
||||
|
|
|
@ -17,19 +17,17 @@ import (
|
|||
|
||||
// Validate handles validating admission request
|
||||
// Checks the target resources for rules defined in the policy
|
||||
func Validate(policy kyverno.Policy, resource unstructured.Unstructured) ([]info.RuleInfo, error) {
|
||||
//TODO: convert rawResource to unstructured to avoid unmarhalling all the time for get some resource information
|
||||
//TODO: pass unstructured instead of rawResource ?
|
||||
|
||||
func Validate(policy kyverno.Policy, resource unstructured.Unstructured) EngineResponse {
|
||||
resourceRaw, err := resource.MarshalJSON()
|
||||
if err != nil {
|
||||
glog.V(4).Infof("unable to marshal resource : %v", err)
|
||||
return nil, err
|
||||
glog.V(4).Infof("Skip processing validating rule, unable to marshal resource : %v\n", err)
|
||||
return EngineResponse{PatchedResource: resource}
|
||||
}
|
||||
|
||||
var resourceInt interface{}
|
||||
if err := json.Unmarshal(resourceRaw, &resourceInt); err != nil {
|
||||
glog.V(4).Infof("unable to unmarshal resource : %v", err)
|
||||
return nil, err
|
||||
glog.V(4).Infof("unable to unmarshal resource : %v\n", err)
|
||||
return EngineResponse{PatchedResource: resource}
|
||||
}
|
||||
|
||||
var ruleInfos []info.RuleInfo
|
||||
|
@ -40,7 +38,7 @@ func Validate(policy kyverno.Policy, resource unstructured.Unstructured) ([]info
|
|||
}
|
||||
|
||||
// check if the resource satisfies the filter conditions defined in the rule
|
||||
//TODO: this needs to be extracted, to filter the resource so that we can avoid passing resources that
|
||||
// TODO: this needs to be extracted, to filter the resource so that we can avoid passing resources that
|
||||
// dont statisfy a policy rule resource description
|
||||
ok := MatchesResourceDescription(resource, rule)
|
||||
if !ok {
|
||||
|
@ -61,7 +59,7 @@ func Validate(policy kyverno.Policy, resource unstructured.Unstructured) ([]info
|
|||
ruleInfos = append(ruleInfos, ruleInfo)
|
||||
}
|
||||
|
||||
return ruleInfos, nil
|
||||
return EngineResponse{RuleInfos: ruleInfos}
|
||||
}
|
||||
|
||||
// validateResourceWithPattern is a start of element-by-element validation process
|
||||
|
|
|
@ -4,9 +4,8 @@ import (
|
|||
"encoding/json"
|
||||
"testing"
|
||||
|
||||
kubepolicy "github.com/nirmata/kyverno/pkg/apis/policy/v1alpha1"
|
||||
kubepolicy "github.com/nirmata/kyverno/pkg/api/kyverno/v1alpha1"
|
||||
"gotest.tools/assert"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
)
|
||||
|
||||
func TestValidateString_AsteriskTest(t *testing.T) {
|
||||
|
@ -1570,11 +1569,10 @@ func TestValidate_ServiceTest(t *testing.T) {
|
|||
var policy kubepolicy.Policy
|
||||
json.Unmarshal(rawPolicy, &policy)
|
||||
|
||||
gvk := metav1.GroupVersionKind{
|
||||
Kind: "Service",
|
||||
}
|
||||
_, err := Validate(policy, rawResource, gvk)
|
||||
assert.Assert(t, err == nil)
|
||||
resourceUnstructured, err := ConvertToUnstructured(rawResource)
|
||||
assert.NilError(t, err)
|
||||
res := Validate(policy, *resourceUnstructured)
|
||||
assert.Assert(t, len(res.RuleInfos) == 0)
|
||||
}
|
||||
|
||||
func TestValidate_MapHasFloats(t *testing.T) {
|
||||
|
@ -1668,10 +1666,8 @@ func TestValidate_MapHasFloats(t *testing.T) {
|
|||
var policy kubepolicy.Policy
|
||||
json.Unmarshal(rawPolicy, &policy)
|
||||
|
||||
gvk := metav1.GroupVersionKind{
|
||||
Kind: "Deployment",
|
||||
}
|
||||
|
||||
_, err := Validate(policy, rawResource, gvk)
|
||||
resourceUnstructured, err := ConvertToUnstructured(rawResource)
|
||||
assert.NilError(t, err)
|
||||
res := Validate(policy, *resourceUnstructured)
|
||||
assert.Assert(t, len(res.RuleInfos) == 0)
|
||||
}
|
||||
|
|
|
@ -85,7 +85,6 @@ func (gen *Generator) Run(workers int, stopCh <-chan struct{}) {
|
|||
go wait.Until(gen.runWorker, time.Second, stopCh)
|
||||
}
|
||||
<-stopCh
|
||||
|
||||
}
|
||||
|
||||
func (gen *Generator) runWorker() {
|
||||
|
|
|
@ -21,8 +21,8 @@ const (
|
|||
|
||||
func (k MsgKey) String() string {
|
||||
return [...]string{
|
||||
"Failed to satisfy policy on resource '%s'.The following rule(s) '%s' failed to apply. Created Policy Violation",
|
||||
"Failed to process rule '%s' of policy '%s'. Created Policy Violation",
|
||||
"Policy violation on resource '%s'. The rule(s) '%s' failed to apply",
|
||||
"Failed to process rule '%s' of policy '%s'.",
|
||||
"Policy applied successfully on the resource '%s'",
|
||||
"Rule(s) '%s' of Policy '%s' applied successfully",
|
||||
"Resource %s creation blocked by rule(s) %s",
|
||||
|
|
160
pkg/gencontroller/controller.go
Normal file
160
pkg/gencontroller/controller.go
Normal file
|
@ -0,0 +1,160 @@
|
|||
package gencontroller
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"k8s.io/apimachinery/pkg/util/wait"
|
||||
|
||||
"github.com/golang/glog"
|
||||
client "github.com/nirmata/kyverno/pkg/dclient"
|
||||
"github.com/nirmata/kyverno/pkg/event"
|
||||
"k8s.io/apimachinery/pkg/api/errors"
|
||||
|
||||
v1Informer "k8s.io/client-go/informers/core/v1"
|
||||
v1CoreLister "k8s.io/client-go/listers/core/v1"
|
||||
"k8s.io/client-go/tools/cache"
|
||||
"k8s.io/client-go/util/workqueue"
|
||||
)
|
||||
|
||||
//Controller watches the 'Namespace' resource creation/update and applied the generation rules on them
|
||||
type Controller struct {
|
||||
client *client.Client
|
||||
namespaceLister v1CoreLister.NamespaceLister
|
||||
namespaceSynced cache.InformerSynced
|
||||
policyLister policyLister.PolicyLister
|
||||
eventController event.Generator
|
||||
violationBuilder violation.Generator
|
||||
annotationsController annotations.Controller
|
||||
workqueue workqueue.RateLimitingInterface
|
||||
}
|
||||
|
||||
//NewGenController returns a new Controller to manage generation rules
|
||||
func NewGenController(client *client.Client,
|
||||
eventController event.Generator,
|
||||
policyInformer policySharedInformer.PolicyInformer,
|
||||
violationBuilder violation.Generator,
|
||||
namespaceInformer v1Informer.NamespaceInformer,
|
||||
annotationsController annotations.Controller) *Controller {
|
||||
|
||||
// create the controller
|
||||
controller := &Controller{
|
||||
client: client,
|
||||
namespaceLister: namespaceInformer.Lister(),
|
||||
namespaceSynced: namespaceInformer.Informer().HasSynced,
|
||||
policyLister: policyInformer.GetLister(),
|
||||
eventController: eventController,
|
||||
violationBuilder: violationBuilder,
|
||||
annotationsController: annotationsController,
|
||||
workqueue: workqueue.NewNamedRateLimitingQueue(workqueue.DefaultControllerRateLimiter(), wqNamespace),
|
||||
}
|
||||
namespaceInformer.Informer().AddEventHandler(cache.ResourceEventHandlerFuncs{
|
||||
AddFunc: controller.createNamespaceHandler,
|
||||
UpdateFunc: controller.updateNamespaceHandler,
|
||||
})
|
||||
|
||||
return controller
|
||||
}
|
||||
func (c *Controller) createNamespaceHandler(resource interface{}) {
|
||||
c.enqueueNamespace(resource)
|
||||
}
|
||||
|
||||
func (c *Controller) updateNamespaceHandler(oldResoruce, newResource interface{}) {
|
||||
// DO we need to anything if the namespace is modified ?
|
||||
}
|
||||
|
||||
func (c *Controller) enqueueNamespace(obj interface{}) {
|
||||
var key string
|
||||
var err error
|
||||
if key, err = cache.MetaNamespaceKeyFunc(obj); err != nil {
|
||||
glog.Error(err)
|
||||
return
|
||||
}
|
||||
c.workqueue.Add(key)
|
||||
}
|
||||
|
||||
//Run to run the controller
|
||||
func (c *Controller) Run(stopCh <-chan struct{}) error {
|
||||
|
||||
if ok := cache.WaitForCacheSync(stopCh, c.namespaceSynced); !ok {
|
||||
return fmt.Errorf("failed to wait for caches to sync")
|
||||
}
|
||||
|
||||
for i := 0; i < workerCount; i++ {
|
||||
go wait.Until(c.runWorker, time.Second, stopCh)
|
||||
}
|
||||
glog.Info("started namespace controller workers")
|
||||
return nil
|
||||
}
|
||||
|
||||
//Stop to stop the controller
|
||||
func (c *Controller) Stop() {
|
||||
c.workqueue.ShutDown()
|
||||
glog.Info("shutting down namespace controller workers")
|
||||
}
|
||||
|
||||
func (c *Controller) runWorker() {
|
||||
for c.processNextWorkItem() {
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Controller) processNextWorkItem() bool {
|
||||
obj, shutdown := c.workqueue.Get()
|
||||
if shutdown {
|
||||
return false
|
||||
}
|
||||
err := func(obj interface{}) error {
|
||||
defer c.workqueue.Done(obj)
|
||||
err := c.syncHandler(obj)
|
||||
c.handleErr(err, obj)
|
||||
return nil
|
||||
}(obj)
|
||||
if err != nil {
|
||||
glog.Error(err)
|
||||
return true
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func (c *Controller) handleErr(err error, key interface{}) {
|
||||
if err == nil {
|
||||
c.workqueue.Forget(key)
|
||||
return
|
||||
}
|
||||
if c.workqueue.NumRequeues(key) < wqRetryLimit {
|
||||
glog.Warningf("Error syncing events %v: %v", key, err)
|
||||
c.workqueue.AddRateLimited(key)
|
||||
return
|
||||
}
|
||||
c.workqueue.Forget(key)
|
||||
glog.Error(err)
|
||||
glog.Warningf("Dropping the key %q out of the queue: %v", key, err)
|
||||
}
|
||||
|
||||
func (c *Controller) syncHandler(obj interface{}) error {
|
||||
var key string
|
||||
var ok bool
|
||||
if key, ok = obj.(string); !ok {
|
||||
return fmt.Errorf("expected string in workqueue but got %v", obj)
|
||||
}
|
||||
// Namespace is cluster wide resource
|
||||
_, name, err := cache.SplitMetaNamespaceKey(key)
|
||||
if err != nil {
|
||||
glog.Errorf("invalid namespace key: %s", key)
|
||||
return err
|
||||
}
|
||||
// Get Namespace
|
||||
ns, err := c.namespaceLister.Get(name)
|
||||
if err != nil {
|
||||
if errors.IsNotFound(err) {
|
||||
glog.Errorf("namespace '%s' in work queue no longer exists", key)
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
//TODO: need to find a way to store the policy such that we can directly queury the
|
||||
// policies with generation policies
|
||||
// PolicyListerExpansion
|
||||
c.processNamespace(ns)
|
||||
return nil
|
||||
}
|
155
pkg/gencontroller/generation.go
Normal file
155
pkg/gencontroller/generation.go
Normal file
|
@ -0,0 +1,155 @@
|
|||
package gencontroller
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
|
||||
"github.com/golang/glog"
|
||||
"github.com/nirmata/kyverno/pkg/engine"
|
||||
event "github.com/nirmata/kyverno/pkg/event"
|
||||
"github.com/nirmata/kyverno/pkg/info"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/labels"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
)
|
||||
|
||||
func (c *Controller) processNamespace(ns *corev1.Namespace) error {
|
||||
//Get all policies and then verify if the namespace matches any of the defined selectors
|
||||
policies, err := c.listPolicies(ns)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// process policy on namespace
|
||||
for _, p := range policies {
|
||||
c.processPolicy(ns, p)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Controller) listPolicies(ns *corev1.Namespace) ([]*v1alpha1.Policy, error) {
|
||||
var fpolicies []*v1alpha1.Policy
|
||||
policies, err := c.policyLister.List(labels.NewSelector())
|
||||
if err != nil {
|
||||
glog.Error("Unable to connect to policy controller. Unable to access policies not applying GENERATION rules")
|
||||
return nil, err
|
||||
}
|
||||
for _, p := range policies {
|
||||
// Check if the policy contains a generatoin rule
|
||||
for _, r := range p.Spec.Rules {
|
||||
if r.Generation != nil {
|
||||
// Check if the resource meets the description
|
||||
data, err := json.Marshal(ns)
|
||||
if err != nil {
|
||||
glog.Error(err)
|
||||
continue
|
||||
}
|
||||
// convert types of GVK
|
||||
nsGvk := schema.FromAPIVersionAndKind("v1", "Namespace")
|
||||
// Hardcode as we have a informer on specified gvk
|
||||
gvk := metav1.GroupVersionKind{Group: nsGvk.Group, Kind: nsGvk.Kind, Version: nsGvk.Version}
|
||||
if engine.ResourceMeetsDescription(data, r.MatchResources.ResourceDescription, r.ExcludeResources.ResourceDescription, gvk) {
|
||||
fpolicies = append(fpolicies, p)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return fpolicies, nil
|
||||
}
|
||||
|
||||
func (c *Controller) processPolicy(ns *corev1.Namespace, p *v1alpha1.Policy) {
|
||||
var eventInfo *event.Info
|
||||
var onViolation bool
|
||||
var msg string
|
||||
|
||||
policyInfo := info.NewPolicyInfo(p.Name,
|
||||
"Namespace",
|
||||
ns.Name,
|
||||
"",
|
||||
p.Spec.ValidationFailureAction) // Namespace has no namespace..WOW
|
||||
|
||||
// convert to unstructured
|
||||
unstrMap, err := runtime.DefaultUnstructuredConverter.ToUnstructured(ns)
|
||||
if err != nil {
|
||||
glog.Error(err)
|
||||
return
|
||||
}
|
||||
unstObj := unstructured.Unstructured{Object: unstrMap}
|
||||
ruleInfos := engine.Generate(c.client, p, unstObj)
|
||||
policyInfo.AddRuleInfos(ruleInfos)
|
||||
|
||||
// generate annotations on namespace
|
||||
c.createAnnotations(policyInfo)
|
||||
//TODO generate namespace on created resources
|
||||
|
||||
if !policyInfo.IsSuccessful() {
|
||||
glog.Infof("Failed to apply policy %s on resource %s %s", p.Name, ns.Kind, ns.Name)
|
||||
for _, r := range ruleInfos {
|
||||
glog.Warning(r.Msgs)
|
||||
|
||||
if msg = strings.Join(r.Msgs, " "); strings.Contains(msg, "rule configuration not present in resource") {
|
||||
onViolation = true
|
||||
msg = fmt.Sprintf(`Resource creation violates generate rule '%s' of policy '%s'`, r.Name, policyInfo.Name)
|
||||
}
|
||||
}
|
||||
|
||||
if onViolation {
|
||||
glog.Infof("Adding violation for generation rule of policy %s\n", policyInfo.Name)
|
||||
// Policy Violation
|
||||
v := violation.BuldNewViolation(policyInfo.Name, policyInfo.RKind, policyInfo.RNamespace, policyInfo.RName, event.PolicyViolation.String(), policyInfo.FailedRules())
|
||||
c.violationBuilder.Add(v)
|
||||
} else {
|
||||
// Event
|
||||
eventInfo = event.NewEvent(policyKind, "", policyInfo.Name, event.RequestBlocked,
|
||||
event.FPolicyApplyBlockCreate, policyInfo.RNamespace+"/"+policyInfo.RName, policyInfo.GetRuleNames(false))
|
||||
|
||||
glog.V(2).Infof("Request blocked event info has prepared for %s/%s\n", policyKind, policyInfo.Name)
|
||||
|
||||
c.eventController.Add(eventInfo)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
glog.Infof("Generation from policy %s has succesfully applied to %s/%s", p.Name, policyInfo.RKind, policyInfo.RName)
|
||||
|
||||
eventInfo = event.NewEvent(policyInfo.RKind, policyInfo.RNamespace, policyInfo.RName,
|
||||
event.PolicyApplied, event.SRulesApply, policyInfo.GetRuleNames(true), policyInfo.Name)
|
||||
|
||||
glog.V(2).Infof("Success event info has prepared for %s/%s\n", policyInfo.RKind, policyInfo.RName)
|
||||
|
||||
c.eventController.Add(eventInfo)
|
||||
}
|
||||
|
||||
func (c *Controller) createAnnotations(pi *info.PolicyInfo) {
|
||||
//get resource
|
||||
obj, err := c.client.GetResource(pi.RKind, pi.RNamespace, pi.RName)
|
||||
if err != nil {
|
||||
glog.Error(err)
|
||||
return
|
||||
}
|
||||
// add annotation for policy application
|
||||
ann := obj.GetAnnotations()
|
||||
// Generation rules
|
||||
gpatch, err := annotations.PatchAnnotations(ann, pi, info.Generation)
|
||||
if err != nil {
|
||||
glog.Error(err)
|
||||
return
|
||||
}
|
||||
if gpatch == nil {
|
||||
// nothing to patch
|
||||
return
|
||||
}
|
||||
// add the anotation to the resource
|
||||
_, err = c.client.PatchResource(pi.RKind, pi.RNamespace, pi.RName, gpatch)
|
||||
if err != nil {
|
||||
glog.Error(err)
|
||||
return
|
||||
}
|
||||
}
|
|
@ -104,7 +104,7 @@ type RuleInfo struct {
|
|||
Name string
|
||||
RuleType RuleType
|
||||
Msgs []string
|
||||
Changes string // this will store the mutation patch being applied by the rule
|
||||
Patches [][]byte // this will store the mutation patch being applied by the rule
|
||||
success bool
|
||||
}
|
||||
|
||||
|
|
|
@ -31,10 +31,9 @@ func applyPolicy(policy kyverno.Policy, resource unstructured.Unstructured) (inf
|
|||
}
|
||||
|
||||
//VALIDATION
|
||||
vruleInfos, err := engine.Validate(policy, resource)
|
||||
policyInfo.AddRuleInfos(vruleInfos)
|
||||
if err != nil {
|
||||
return policyInfo, err
|
||||
engineResponse := engine.Validate(policy, resource)
|
||||
if len(engineResponse.RuleInfos) != 0 {
|
||||
policyInfo.AddRuleInfos(engineResponse.RuleInfos)
|
||||
}
|
||||
|
||||
//TODO: GENERATION
|
||||
|
@ -42,7 +41,9 @@ func applyPolicy(policy kyverno.Policy, resource unstructured.Unstructured) (inf
|
|||
}
|
||||
|
||||
func mutation(policy kyverno.Policy, resource unstructured.Unstructured) ([]info.RuleInfo, error) {
|
||||
patches, ruleInfos := engine.Mutate(policy, resource)
|
||||
engineResponse := engine.Mutate(policy, resource)
|
||||
patches := engineResponse.Patches
|
||||
ruleInfos := engineResponse.RuleInfos
|
||||
if len(ruleInfos) == 0 {
|
||||
//no rules processed
|
||||
return nil, nil
|
||||
|
|
113
pkg/webhooks/annotations.go
Normal file
113
pkg/webhooks/annotations.go
Normal file
|
@ -0,0 +1,113 @@
|
|||
package webhooks
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
|
||||
jsonpatch "github.com/evanphx/json-patch"
|
||||
"github.com/golang/glog"
|
||||
"github.com/nirmata/kyverno/pkg/info"
|
||||
)
|
||||
|
||||
const (
|
||||
policyAnnotation = "policies.kyverno.io"
|
||||
// lastAppliedPatches = policyAnnotation + "last-applied-patches"
|
||||
)
|
||||
|
||||
type policyPatch struct {
|
||||
PolicyName string `json:"policyname"`
|
||||
// RulePatches []string `json:"patches"`
|
||||
RulePatches interface{} `json:"patches"`
|
||||
}
|
||||
|
||||
type rulePatch struct {
|
||||
RuleName string `json:"rulename"`
|
||||
Op string `json:"op"`
|
||||
Path string `json:"path"`
|
||||
}
|
||||
|
||||
type response struct {
|
||||
Op string `json:"op"`
|
||||
Path string `json:"path"`
|
||||
Value interface{} `json:"value"`
|
||||
}
|
||||
|
||||
func prepareAnnotationPatches(resource *unstructured.Unstructured, policyInfos []info.PolicyInfo) []byte {
|
||||
annots := resource.GetAnnotations()
|
||||
if annots == nil {
|
||||
annots = map[string]string{}
|
||||
}
|
||||
|
||||
var patchResponse response
|
||||
value := annotationFromPolicies(policyInfos)
|
||||
if _, ok := annots[policyAnnotation]; ok {
|
||||
// create update patch string
|
||||
patchResponse = response{
|
||||
Op: "replace",
|
||||
Path: "/metadata/annotations/" + policyAnnotation,
|
||||
Value: string(value),
|
||||
}
|
||||
} else {
|
||||
patchResponse = response{
|
||||
Op: "add",
|
||||
Path: "/metadata/annotations",
|
||||
Value: map[string]string{policyAnnotation: string(value)},
|
||||
}
|
||||
}
|
||||
|
||||
patchByte, _ := json.Marshal(patchResponse)
|
||||
|
||||
// check the patch
|
||||
_, err := jsonpatch.DecodePatch([]byte("[" + string(patchByte) + "]"))
|
||||
if err != nil {
|
||||
glog.Errorf("Failed to make patch from annotation'%s', err: %v\n ", string(patchByte), err)
|
||||
}
|
||||
|
||||
return patchByte
|
||||
}
|
||||
|
||||
func annotationFromPolicies(policyInfos []info.PolicyInfo) []byte {
|
||||
var policyPatches []policyPatch
|
||||
for _, policyInfo := range policyInfos {
|
||||
var pp policyPatch
|
||||
|
||||
pp.PolicyName = policyInfo.Name
|
||||
pp.RulePatches = annotationFromPolicy(policyInfo)
|
||||
policyPatches = append(policyPatches, pp)
|
||||
}
|
||||
|
||||
result, _ := json.Marshal(policyPatches)
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
func annotationFromPolicy(policyInfo info.PolicyInfo) []rulePatch {
|
||||
if !policyInfo.IsSuccessful() {
|
||||
glog.V(2).Infof("Policy %s failed, skip preparing annotation\n", policyInfo.Name)
|
||||
return nil
|
||||
}
|
||||
|
||||
var rulePatches []rulePatch
|
||||
for _, ruleInfo := range policyInfo.Rules {
|
||||
|
||||
for _, patch := range ruleInfo.Patches {
|
||||
var patchmap map[string]string
|
||||
|
||||
if err := json.Unmarshal(patch, &patchmap); err != nil {
|
||||
glog.Errorf("Failed to parse patch bytes, err: %v\n", err)
|
||||
continue
|
||||
}
|
||||
|
||||
rp := rulePatch{
|
||||
RuleName: ruleInfo.Name,
|
||||
Op: patchmap["op"],
|
||||
Path: patchmap["path"]}
|
||||
|
||||
rulePatches = append(rulePatches, rp)
|
||||
glog.V(4).Infof("Annotation value prepared: %v\n", rulePatches)
|
||||
}
|
||||
}
|
||||
|
||||
return rulePatches
|
||||
}
|
|
@ -6,99 +6,85 @@ import (
|
|||
"github.com/nirmata/kyverno/pkg/info"
|
||||
"github.com/nirmata/kyverno/pkg/utils"
|
||||
v1beta1 "k8s.io/api/admission/v1beta1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/labels"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
)
|
||||
|
||||
// HandleMutation handles mutating webhook admission request
|
||||
func (ws *WebhookServer) HandleMutation(request *v1beta1.AdmissionRequest) *v1beta1.AdmissionResponse {
|
||||
func (ws *WebhookServer) HandleMutation(request *v1beta1.AdmissionRequest) (bool, engine.EngineResponse) {
|
||||
var patches [][]byte
|
||||
var policyInfos []info.PolicyInfo
|
||||
// map to store the mutation changes on the resource
|
||||
// mAnn := map[string]string{}
|
||||
glog.V(4).Infof("Receive request in mutating webhook: Kind=%s, Namespace=%s Name=%s UID=%s patchOperation=%s",
|
||||
|
||||
glog.V(5).Infof("Receive request in mutating webhook: Kind=%s, Namespace=%s Name=%s UID=%s patchOperation=%s",
|
||||
request.Kind.Kind, request.Namespace, request.Name, request.UID, request.Operation)
|
||||
|
||||
resource, err := engine.ConvertToUnstructured(request.Object.Raw)
|
||||
if err != nil {
|
||||
glog.Errorf("unable to convert raw resource to unstructured: %v", err)
|
||||
}
|
||||
|
||||
//TODO: check if resource gvk is available in raw resource,
|
||||
// if not then set it from the api request
|
||||
resource.SetGroupVersionKind(schema.GroupVersionKind{Group: request.Kind.Group, Version: request.Kind.Version, Kind: request.Kind.Kind})
|
||||
//TODO: check if the name and namespace is also passed right in the resource?
|
||||
|
||||
engineResponse := engine.EngineResponse{PatchedResource: *resource}
|
||||
|
||||
policies, err := ws.pLister.List(labels.NewSelector())
|
||||
if err != nil {
|
||||
//TODO check if the CRD is created ?
|
||||
// Unable to connect to policy Lister to access policies
|
||||
glog.Error("Unable to connect to policy controller to access policies. Mutation Rules are NOT being applied")
|
||||
glog.Errorln("Unable to connect to policy controller to access policies. Mutation Rules are NOT being applied")
|
||||
glog.Warning(err)
|
||||
return &v1beta1.AdmissionResponse{
|
||||
Allowed: true,
|
||||
}
|
||||
return true, engineResponse
|
||||
}
|
||||
|
||||
resource, err := convertToUnstructured(request.Object.Raw)
|
||||
if err != nil {
|
||||
glog.Errorf("unable to convert raw resource to unstructured: %v", err)
|
||||
}
|
||||
//TODO: check if resource gvk is available in raw resource,
|
||||
// if not then set it from the api request
|
||||
resource.SetGroupVersionKind(schema.GroupVersionKind{Group: request.Kind.Group, Version: request.Kind.Version, Kind: request.Kind.Kind})
|
||||
//TODO: check if the name and namespace is also passed right in the resource?
|
||||
// all the patches to be applied on the resource
|
||||
|
||||
for _, policy := range policies {
|
||||
|
||||
// check if policy has a rule for the admission request kind
|
||||
if !utils.Contains(getApplicableKindsForPolicy(policy), request.Kind.Kind) {
|
||||
continue
|
||||
}
|
||||
|
||||
policyInfo := info.NewPolicyInfo(policy.Name, resource.GetKind(), resource.GetName(), resource.GetNamespace(), policy.Spec.ValidationFailureAction)
|
||||
|
||||
glog.V(4).Infof("Handling mutation for Kind=%s, Namespace=%s Name=%s UID=%s patchOperation=%s",
|
||||
resource.GetKind(), resource.GetNamespace(), resource.GetName(), request.UID, request.Operation)
|
||||
glog.V(4).Infof("Applying policy %s with %d rules\n", policy.ObjectMeta.Name, len(policy.Spec.Rules))
|
||||
|
||||
// resource, err := utils.ConvertToUnstructured(request.Object.Raw)
|
||||
// if err != nil {
|
||||
// glog.Errorf("unable to process policy %s resource %v: %v", policy.GetName(), request.Resource, err)
|
||||
// continue
|
||||
// }
|
||||
//TODO: check if the GVK information is present in the request of we set it explicity here ?
|
||||
// resource.SetGroupVersionKind(schema.GroupVersionKind{Group: request.Kind.Group, Version: request.Kind.Version, Kind: request.Kind.Kind})
|
||||
//TODO: passing policy value as we dont wont to modify the policy
|
||||
engineResponse = engine.Mutate(*policy, *resource)
|
||||
policyInfo.AddRuleInfos(engineResponse.RuleInfos)
|
||||
|
||||
policyPatches, ruleInfos := engine.Mutate(*policy, *resource)
|
||||
policyInfo.AddRuleInfos(ruleInfos)
|
||||
policyInfos = append(policyInfos, policyInfo)
|
||||
if !policyInfo.IsSuccessful() {
|
||||
glog.V(4).Infof("Failed to apply policy %s on resource %s/%s", policy.Name, resource.GetNamespace(), resource.GetName())
|
||||
glog.V(4).Infof("Failed to apply policy %s on resource %s/%s\n", policy.Name, resource.GetNamespace(), resource.GetName())
|
||||
glog.V(4).Info("Failed rule details")
|
||||
for _, r := range ruleInfos {
|
||||
for _, r := range engineResponse.RuleInfos {
|
||||
glog.V(4).Infof("%s: %s\n", r.Name, r.Msgs)
|
||||
}
|
||||
continue
|
||||
}
|
||||
// build annotations per policy being applied to show the mutation changes
|
||||
patches = append(patches, policyPatches...)
|
||||
|
||||
patches = append(patches, engineResponse.Patches...)
|
||||
policyInfos = append(policyInfos, policyInfo)
|
||||
glog.V(4).Infof("Mutation from policy %s has applied succesfully to %s %s/%s", policy.Name, request.Kind.Kind, resource.GetNamespace(), resource.GetName())
|
||||
}
|
||||
|
||||
// ADD ANNOTATIONS
|
||||
// TODO: merge the annotation patch with the patch response
|
||||
// ADD EVENTS
|
||||
if len(patches) > 0 {
|
||||
eventsInfo := newEventInfoFromPolicyInfo(policyInfos, (request.Operation == v1beta1.Update), info.Mutation)
|
||||
ws.eventGen.Add(eventsInfo...)
|
||||
|
||||
annotation := prepareAnnotationPatches(resource, policyInfos)
|
||||
patches = append(patches, annotation)
|
||||
}
|
||||
// ADD POLICY VIOLATIONS
|
||||
|
||||
ok, msg := isAdmSuccesful(policyInfos)
|
||||
if ok {
|
||||
patchType := v1beta1.PatchTypeJSONPatch
|
||||
return &v1beta1.AdmissionResponse{
|
||||
Allowed: true,
|
||||
Patch: engine.JoinPatches(patches),
|
||||
PatchType: &patchType,
|
||||
}
|
||||
}
|
||||
return &v1beta1.AdmissionResponse{
|
||||
Allowed: false,
|
||||
Result: &metav1.Status{
|
||||
Message: msg,
|
||||
},
|
||||
engineResponse.Patches = patches
|
||||
return true, engineResponse
|
||||
}
|
||||
|
||||
glog.Errorf("Failed to mutate the resource: %s\n", msg)
|
||||
return false, engineResponse
|
||||
}
|
||||
|
|
|
@ -13,16 +13,39 @@ import (
|
|||
|
||||
//HandlePolicyValidation performs the validation check on policy resource
|
||||
func (ws *WebhookServer) HandlePolicyValidation(request *v1beta1.AdmissionRequest) *v1beta1.AdmissionResponse {
|
||||
return ws.validateUniqueRuleName(request.Object.Raw)
|
||||
var policy *kyverno.Policy
|
||||
admissionResp := &v1beta1.AdmissionResponse{
|
||||
Allowed: true,
|
||||
}
|
||||
|
||||
raw := request.Object.Raw
|
||||
if request.Operation == v1beta1.Delete {
|
||||
raw = request.OldObject.Raw
|
||||
}
|
||||
if err := json.Unmarshal(raw, &policy); err != nil {
|
||||
glog.Errorf("Failed to unmarshal policy admission request, err %v\n", err)
|
||||
return &v1beta1.AdmissionResponse{Allowed: false}
|
||||
}
|
||||
|
||||
if request.Operation != v1beta1.Delete {
|
||||
admissionResp = ws.validateUniqueRuleName(policy)
|
||||
}
|
||||
|
||||
if admissionResp.Allowed {
|
||||
ws.manageWebhookConfigurations(*policy, request.Operation)
|
||||
}
|
||||
|
||||
return admissionResp
|
||||
}
|
||||
|
||||
// Verify if the Rule names are unique within a policy
|
||||
func (ws *WebhookServer) validateUniqueRuleName(rawPolicy []byte) *v1beta1.AdmissionResponse {
|
||||
var policy *kyverno.Policy
|
||||
func (ws *WebhookServer) validateUniqueRuleName(policy *kyverno.Policy) *v1beta1.AdmissionResponse {
|
||||
// =======
|
||||
// func (ws *WebhookServer) validateUniqueRuleName(rawPolicy []byte) *v1beta1.AdmissionResponse {
|
||||
// var policy *kyverno.Policy
|
||||
// >>>>>>> policyViolation
|
||||
var ruleNames []string
|
||||
|
||||
json.Unmarshal(rawPolicy, &policy)
|
||||
|
||||
for _, rule := range policy.Spec.Rules {
|
||||
if utils.Contains(ruleNames, rule.Name) {
|
||||
msg := fmt.Sprintf(`The policy "%s" is invalid: duplicate rule name: "%s"`, policy.Name, rule.Name)
|
||||
|
|
|
@ -9,10 +9,10 @@ import (
|
|||
"github.com/golang/glog"
|
||||
"github.com/nirmata/kyverno/pkg/config"
|
||||
client "github.com/nirmata/kyverno/pkg/dclient"
|
||||
|
||||
"github.com/tevino/abool"
|
||||
admregapi "k8s.io/api/admissionregistration/v1beta1"
|
||||
errorsapi "k8s.io/apimachinery/pkg/api/errors"
|
||||
meta "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
admregclient "k8s.io/client-go/kubernetes/typed/admissionregistration/v1beta1"
|
||||
rest "k8s.io/client-go/rest"
|
||||
)
|
||||
|
@ -23,11 +23,14 @@ type WebhookRegistrationClient struct {
|
|||
client *client.Client
|
||||
clientConfig *rest.Config
|
||||
// serverIP should be used if running Kyverno out of clutser
|
||||
serverIP string
|
||||
serverIP string
|
||||
timeoutSeconds int32
|
||||
MutationRegistered *abool.AtomicBool
|
||||
ValidationRegistered *abool.AtomicBool
|
||||
}
|
||||
|
||||
// NewWebhookRegistrationClient creates new WebhookRegistrationClient instance
|
||||
func NewWebhookRegistrationClient(clientConfig *rest.Config, client *client.Client, serverIP string) (*WebhookRegistrationClient, error) {
|
||||
func NewWebhookRegistrationClient(clientConfig *rest.Config, client *client.Client, serverIP string, webhookTimeout int32) (*WebhookRegistrationClient, error) {
|
||||
registrationClient, err := admregclient.NewForConfig(clientConfig)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
@ -36,10 +39,13 @@ func NewWebhookRegistrationClient(clientConfig *rest.Config, client *client.Clie
|
|||
glog.V(3).Infof("Registering webhook client using serverIP %s\n", serverIP)
|
||||
|
||||
return &WebhookRegistrationClient{
|
||||
registrationClient: registrationClient,
|
||||
client: client,
|
||||
clientConfig: clientConfig,
|
||||
serverIP: serverIP,
|
||||
registrationClient: registrationClient,
|
||||
client: client,
|
||||
clientConfig: clientConfig,
|
||||
serverIP: serverIP,
|
||||
timeoutSeconds: webhookTimeout,
|
||||
MutationRegistered: abool.New(),
|
||||
ValidationRegistered: abool.New(),
|
||||
}, nil
|
||||
}
|
||||
|
||||
|
@ -48,68 +54,114 @@ func (wrc *WebhookRegistrationClient) Register() error {
|
|||
if wrc.serverIP != "" {
|
||||
glog.Infof("Registering webhook with url https://%s\n", wrc.serverIP)
|
||||
}
|
||||
// For the case if cluster already has this configs
|
||||
wrc.Deregister()
|
||||
|
||||
// For the case if cluster already has this configs
|
||||
wrc.DeregisterAll()
|
||||
|
||||
// register policy validating webhook during inital start
|
||||
return wrc.RegisterPolicyValidatingWebhook()
|
||||
}
|
||||
|
||||
func (wrc *WebhookRegistrationClient) RegisterMutatingWebhook() error {
|
||||
mutatingWebhookConfig, err := wrc.constructMutatingWebhookConfig(wrc.clientConfig)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
_, err = wrc.registrationClient.MutatingWebhookConfigurations().Create(mutatingWebhookConfig)
|
||||
if err != nil {
|
||||
if _, err = wrc.registrationClient.MutatingWebhookConfigurations().Create(mutatingWebhookConfig); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
wrc.MutationRegistered.Set()
|
||||
return nil
|
||||
}
|
||||
|
||||
func (wrc *WebhookRegistrationClient) RegisterValidatingWebhook() error {
|
||||
validationWebhookConfig, err := wrc.constructValidatingWebhookConfig(wrc.clientConfig)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
_, err = wrc.registrationClient.ValidatingWebhookConfigurations().Create(validationWebhookConfig)
|
||||
if err != nil {
|
||||
if _, err = wrc.registrationClient.ValidatingWebhookConfigurations().Create(validationWebhookConfig); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
wrc.ValidationRegistered.Set()
|
||||
return nil
|
||||
}
|
||||
|
||||
func (wrc *WebhookRegistrationClient) RegisterPolicyValidatingWebhook() error {
|
||||
policyValidationWebhookConfig, err := wrc.contructPolicyValidatingWebhookConfig()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
_, err = wrc.registrationClient.ValidatingWebhookConfigurations().Create(policyValidationWebhookConfig)
|
||||
if err != nil {
|
||||
if _, err = wrc.registrationClient.ValidatingWebhookConfigurations().Create(policyValidationWebhookConfig); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
glog.V(3).Infoln("Policy validating webhook registered")
|
||||
return nil
|
||||
}
|
||||
|
||||
// Deregister deletes webhook configs from cluster
|
||||
// DeregisterAll deletes webhook configs from cluster
|
||||
// This function does not fail on error:
|
||||
// Register will fail if the config exists, so there is no need to fail on error
|
||||
func (wrc *WebhookRegistrationClient) Deregister() {
|
||||
func (wrc *WebhookRegistrationClient) DeregisterAll() {
|
||||
wrc.deregisterMutatingWebhook()
|
||||
wrc.deregisterValidatingWebhook()
|
||||
|
||||
if wrc.serverIP != "" {
|
||||
if err := wrc.registrationClient.MutatingWebhookConfigurations().Delete(config.MutatingWebhookConfigurationDebug, &meta.DeleteOptions{}); err != nil {
|
||||
if !errorsapi.IsNotFound(err) {
|
||||
glog.Errorf("Failed to deregister debug mutatingWebhookConfiguratinos, err: %v\n", err)
|
||||
}
|
||||
err := wrc.registrationClient.ValidatingWebhookConfigurations().Delete(config.PolicyValidatingWebhookConfigurationDebug, &v1.DeleteOptions{})
|
||||
if err != nil && !errorsapi.IsNotFound(err) {
|
||||
glog.Error(err)
|
||||
}
|
||||
if err := wrc.registrationClient.ValidatingWebhookConfigurations().Delete(config.ValidatingWebhookConfigurationDebug, &meta.DeleteOptions{}); err != nil {
|
||||
if !errorsapi.IsNotFound(err) {
|
||||
glog.Errorf("Failed to deregister debug validatingWebhookConfiguratinos, err: %v\n", err)
|
||||
}
|
||||
}
|
||||
if err := wrc.registrationClient.ValidatingWebhookConfigurations().Delete(config.PolicyValidatingWebhookConfigurationDebug, &meta.DeleteOptions{}); err != nil {
|
||||
if !errorsapi.IsNotFound(err) {
|
||||
glog.Errorf("Failed to deregister debug policyValidatingWebhookConfiguratinos, err: %v\n", err)
|
||||
}
|
||||
}
|
||||
err := wrc.registrationClient.ValidatingWebhookConfigurations().Delete(config.PolicyValidatingWebhookConfigurationName, &v1.DeleteOptions{})
|
||||
if err != nil && !errorsapi.IsNotFound(err) {
|
||||
glog.Error(err)
|
||||
}
|
||||
}
|
||||
|
||||
func (wrc *WebhookRegistrationClient) deregister() {
|
||||
wrc.deregisterMutatingWebhook()
|
||||
wrc.deregisterValidatingWebhook()
|
||||
}
|
||||
|
||||
func (wrc *WebhookRegistrationClient) deregisterMutatingWebhook() {
|
||||
if wrc.serverIP != "" {
|
||||
err := wrc.registrationClient.MutatingWebhookConfigurations().Delete(config.MutatingWebhookConfigurationDebug, &v1.DeleteOptions{})
|
||||
if err != nil && !errorsapi.IsNotFound(err) {
|
||||
glog.Error(err)
|
||||
} else {
|
||||
wrc.MutationRegistered.UnSet()
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
wrc.registrationClient.MutatingWebhookConfigurations().Delete(config.MutatingWebhookConfigurationName, &meta.DeleteOptions{})
|
||||
wrc.registrationClient.ValidatingWebhookConfigurations().Delete(config.ValidatingWebhookConfigurationName, &meta.DeleteOptions{})
|
||||
wrc.registrationClient.ValidatingWebhookConfigurations().Delete(config.PolicyValidatingWebhookConfigurationName, &meta.DeleteOptions{})
|
||||
err := wrc.registrationClient.MutatingWebhookConfigurations().Delete(config.MutatingWebhookConfigurationName, &v1.DeleteOptions{})
|
||||
if err != nil && !errorsapi.IsNotFound(err) {
|
||||
glog.Error(err)
|
||||
} else {
|
||||
wrc.MutationRegistered.UnSet()
|
||||
}
|
||||
}
|
||||
|
||||
func (wrc *WebhookRegistrationClient) deregisterValidatingWebhook() {
|
||||
if wrc.serverIP != "" {
|
||||
err := wrc.registrationClient.ValidatingWebhookConfigurations().Delete(config.ValidatingWebhookConfigurationDebug, &v1.DeleteOptions{})
|
||||
if err != nil && !errorsapi.IsNotFound(err) {
|
||||
glog.Error(err)
|
||||
}
|
||||
wrc.ValidationRegistered.UnSet()
|
||||
return
|
||||
}
|
||||
|
||||
err := wrc.registrationClient.ValidatingWebhookConfigurations().Delete(config.ValidatingWebhookConfigurationName, &v1.DeleteOptions{})
|
||||
if err != nil && !errorsapi.IsNotFound(err) {
|
||||
glog.Error(err)
|
||||
}
|
||||
wrc.ValidationRegistered.UnSet()
|
||||
}
|
||||
|
||||
func (wrc *WebhookRegistrationClient) constructMutatingWebhookConfig(configuration *rest.Config) (*admregapi.MutatingWebhookConfiguration, error) {
|
||||
|
@ -130,10 +182,10 @@ func (wrc *WebhookRegistrationClient) constructMutatingWebhookConfig(configurati
|
|||
}
|
||||
|
||||
return &admregapi.MutatingWebhookConfiguration{
|
||||
ObjectMeta: meta.ObjectMeta{
|
||||
ObjectMeta: v1.ObjectMeta{
|
||||
Name: config.MutatingWebhookConfigurationName,
|
||||
Labels: config.KubePolicyAppLabels,
|
||||
OwnerReferences: []meta.OwnerReference{
|
||||
OwnerReferences: []v1.OwnerReference{
|
||||
wrc.constructOwner(),
|
||||
},
|
||||
},
|
||||
|
@ -142,7 +194,9 @@ func (wrc *WebhookRegistrationClient) constructMutatingWebhookConfig(configurati
|
|||
config.MutatingWebhookName,
|
||||
config.MutatingWebhookServicePath,
|
||||
caData,
|
||||
false),
|
||||
false,
|
||||
wrc.timeoutSeconds,
|
||||
),
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
|
@ -152,7 +206,7 @@ func (wrc *WebhookRegistrationClient) contructDebugMutatingWebhookConfig(caData
|
|||
glog.V(3).Infof("Debug MutatingWebhookConfig is registered with url %s\n", url)
|
||||
|
||||
return &admregapi.MutatingWebhookConfiguration{
|
||||
ObjectMeta: meta.ObjectMeta{
|
||||
ObjectMeta: v1.ObjectMeta{
|
||||
Name: config.MutatingWebhookConfigurationDebug,
|
||||
Labels: config.KubePolicyAppLabels,
|
||||
},
|
||||
|
@ -161,7 +215,8 @@ func (wrc *WebhookRegistrationClient) contructDebugMutatingWebhookConfig(caData
|
|||
config.MutatingWebhookName,
|
||||
url,
|
||||
caData,
|
||||
false),
|
||||
false,
|
||||
wrc.timeoutSeconds),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
@ -183,10 +238,10 @@ func (wrc *WebhookRegistrationClient) constructValidatingWebhookConfig(configura
|
|||
}
|
||||
|
||||
return &admregapi.ValidatingWebhookConfiguration{
|
||||
ObjectMeta: meta.ObjectMeta{
|
||||
ObjectMeta: v1.ObjectMeta{
|
||||
Name: config.ValidatingWebhookConfigurationName,
|
||||
Labels: config.KubePolicyAppLabels,
|
||||
OwnerReferences: []meta.OwnerReference{
|
||||
OwnerReferences: []v1.OwnerReference{
|
||||
wrc.constructOwner(),
|
||||
},
|
||||
},
|
||||
|
@ -195,7 +250,8 @@ func (wrc *WebhookRegistrationClient) constructValidatingWebhookConfig(configura
|
|||
config.ValidatingWebhookName,
|
||||
config.ValidatingWebhookServicePath,
|
||||
caData,
|
||||
true),
|
||||
true,
|
||||
wrc.timeoutSeconds),
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
|
@ -205,7 +261,7 @@ func (wrc *WebhookRegistrationClient) contructDebugValidatingWebhookConfig(caDat
|
|||
glog.V(3).Infof("Debug ValidatingWebhookConfig is registered with url %s\n", url)
|
||||
|
||||
return &admregapi.ValidatingWebhookConfiguration{
|
||||
ObjectMeta: meta.ObjectMeta{
|
||||
ObjectMeta: v1.ObjectMeta{
|
||||
Name: config.ValidatingWebhookConfigurationDebug,
|
||||
Labels: config.KubePolicyAppLabels,
|
||||
},
|
||||
|
@ -214,7 +270,8 @@ func (wrc *WebhookRegistrationClient) contructDebugValidatingWebhookConfig(caDat
|
|||
config.ValidatingWebhookName,
|
||||
url,
|
||||
caData,
|
||||
true),
|
||||
true,
|
||||
wrc.timeoutSeconds),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
@ -236,10 +293,10 @@ func (wrc *WebhookRegistrationClient) contructPolicyValidatingWebhookConfig() (*
|
|||
}
|
||||
|
||||
return &admregapi.ValidatingWebhookConfiguration{
|
||||
ObjectMeta: meta.ObjectMeta{
|
||||
ObjectMeta: v1.ObjectMeta{
|
||||
Name: config.PolicyValidatingWebhookConfigurationName,
|
||||
Labels: config.KubePolicyAppLabels,
|
||||
OwnerReferences: []meta.OwnerReference{
|
||||
OwnerReferences: []v1.OwnerReference{
|
||||
wrc.constructOwner(),
|
||||
},
|
||||
},
|
||||
|
@ -248,7 +305,8 @@ func (wrc *WebhookRegistrationClient) contructPolicyValidatingWebhookConfig() (*
|
|||
config.PolicyValidatingWebhookName,
|
||||
config.PolicyValidatingWebhookServicePath,
|
||||
caData,
|
||||
true),
|
||||
true,
|
||||
wrc.timeoutSeconds),
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
|
@ -258,7 +316,7 @@ func (wrc *WebhookRegistrationClient) contructDebugPolicyValidatingWebhookConfig
|
|||
glog.V(3).Infof("Debug PolicyValidatingWebhookConfig is registered with url %s\n", url)
|
||||
|
||||
return &admregapi.ValidatingWebhookConfiguration{
|
||||
ObjectMeta: meta.ObjectMeta{
|
||||
ObjectMeta: v1.ObjectMeta{
|
||||
Name: config.PolicyValidatingWebhookConfigurationDebug,
|
||||
Labels: config.KubePolicyAppLabels,
|
||||
},
|
||||
|
@ -267,12 +325,13 @@ func (wrc *WebhookRegistrationClient) contructDebugPolicyValidatingWebhookConfig
|
|||
config.PolicyValidatingWebhookName,
|
||||
url,
|
||||
caData,
|
||||
true),
|
||||
true,
|
||||
wrc.timeoutSeconds),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func constructWebhook(name, servicePath string, caData []byte, validation bool) admregapi.Webhook {
|
||||
func constructWebhook(name, servicePath string, caData []byte, validation bool, timeoutSeconds int32) admregapi.Webhook {
|
||||
resource := "*/*"
|
||||
apiGroups := "*"
|
||||
apiversions := "*"
|
||||
|
@ -317,10 +376,11 @@ func constructWebhook(name, servicePath string, caData []byte, validation bool)
|
|||
},
|
||||
},
|
||||
},
|
||||
TimeoutSeconds: &timeoutSeconds,
|
||||
}
|
||||
}
|
||||
|
||||
func constructDebugWebhook(name, url string, caData []byte, validation bool) admregapi.Webhook {
|
||||
func constructDebugWebhook(name, url string, caData []byte, validation bool, timeoutSeconds int32) admregapi.Webhook {
|
||||
resource := "*/*"
|
||||
apiGroups := "*"
|
||||
apiversions := "*"
|
||||
|
@ -361,18 +421,19 @@ func constructDebugWebhook(name, url string, caData []byte, validation bool) adm
|
|||
},
|
||||
},
|
||||
},
|
||||
TimeoutSeconds: &timeoutSeconds,
|
||||
}
|
||||
}
|
||||
|
||||
func (wrc *WebhookRegistrationClient) constructOwner() meta.OwnerReference {
|
||||
func (wrc *WebhookRegistrationClient) constructOwner() v1.OwnerReference {
|
||||
kubePolicyDeployment, err := wrc.client.GetKubePolicyDeployment()
|
||||
|
||||
if err != nil {
|
||||
glog.Errorf("Error when constructing OwnerReference, err: %v\n", err)
|
||||
return meta.OwnerReference{}
|
||||
return v1.OwnerReference{}
|
||||
}
|
||||
|
||||
return meta.OwnerReference{
|
||||
return v1.OwnerReference{
|
||||
APIVersion: config.DeploymentAPIVersion,
|
||||
Kind: config.DeploymentKind,
|
||||
Name: kubePolicyDeployment.ObjectMeta.Name,
|
||||
|
|
|
@ -23,7 +23,7 @@ func newEventInfoFromPolicyInfo(policyInfoList []info.PolicyInfo, onUpdate bool,
|
|||
if !onUpdate {
|
||||
// CREATE
|
||||
eventsInfo = append(eventsInfo,
|
||||
event.NewEvent(policyKind, "", pi.Name, event.RequestBlocked, event.FPolicyApplyBlockCreate, pi.RName, ruleNames))
|
||||
event.NewEvent(policyKind, "", pi.Name, event.RequestBlocked, event.FPolicyApplyBlockCreate, pi.RNamespace+"/"+pi.RName, ruleNames))
|
||||
|
||||
glog.V(3).Infof("Rule(s) %s of policy %s blocked resource creation, error: %s\n", ruleNames, pi.Name, msg)
|
||||
} else {
|
||||
|
@ -31,7 +31,7 @@ func newEventInfoFromPolicyInfo(policyInfoList []info.PolicyInfo, onUpdate bool,
|
|||
eventsInfo = append(eventsInfo,
|
||||
event.NewEvent(pi.RKind, pi.RNamespace, pi.RName, event.RequestBlocked, event.FPolicyApplyBlockUpdate, ruleNames, pi.Name))
|
||||
eventsInfo = append(eventsInfo,
|
||||
event.NewEvent(policyKind, "", pi.Name, event.RequestBlocked, event.FPolicyBlockResourceUpdate, pi.RName, ruleNames))
|
||||
event.NewEvent(policyKind, "", pi.Name, event.RequestBlocked, event.FPolicyBlockResourceUpdate, pi.RNamespace+"/"+pi.RName, ruleNames))
|
||||
glog.V(3).Infof("Request blocked events info has prepared for %s/%s and %s/%s\n", policyKind, pi.Name, pi.RKind, pi.RName)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -10,10 +10,12 @@ import (
|
|||
"net/http"
|
||||
"time"
|
||||
|
||||
"github.com/nirmata/kyverno/pkg/engine"
|
||||
|
||||
"github.com/golang/glog"
|
||||
kyvernoclient "github.com/nirmata/kyverno/pkg/client/clientset/versioned"
|
||||
kyvernoinformer "github.com/nirmata/kyverno/pkg/client/informers/externalversions/kyverno/v1alpha1"
|
||||
kyvernolister "github.com/nirmata/kyverno/pkg/client/listers/kyverno/v1alpha1"
|
||||
lister "github.com/nirmata/kyverno/pkg/client/listers/kyverno/v1alpha1"
|
||||
"github.com/nirmata/kyverno/pkg/config"
|
||||
client "github.com/nirmata/kyverno/pkg/dclient"
|
||||
"github.com/nirmata/kyverno/pkg/event"
|
||||
|
@ -26,15 +28,16 @@ import (
|
|||
// WebhookServer contains configured TLS server with MutationWebhook.
|
||||
// MutationWebhook gets policies from policyController and takes control of the cluster with kubeclient.
|
||||
type WebhookServer struct {
|
||||
server http.Server
|
||||
client *client.Client
|
||||
kyvernoClient *kyvernoclient.Clientset
|
||||
pLister kyvernolister.PolicyLister
|
||||
pvLister kyvernolister.PolicyViolationLister
|
||||
pListerSynced cache.InformerSynced
|
||||
pvListerSynced cache.InformerSynced
|
||||
eventGen event.Interface
|
||||
filterK8Resources []utils.K8Resource
|
||||
server http.Server
|
||||
client *client.Client
|
||||
kyvernoClient *kyvernoclient.Clientset
|
||||
pLister lister.PolicyLister
|
||||
pvLister lister.PolicyViolationLister
|
||||
pListerSynced cache.InformerSynced
|
||||
pvListerSynced cache.InformerSynced
|
||||
eventGen event.Interface
|
||||
webhookRegistrationClient *WebhookRegistrationClient
|
||||
filterK8Resources []utils.K8Resource
|
||||
}
|
||||
|
||||
// NewWebhookServer creates new instance of WebhookServer accordingly to given configuration
|
||||
|
@ -46,6 +49,7 @@ func NewWebhookServer(
|
|||
pInformer kyvernoinformer.PolicyInformer,
|
||||
pvInormer kyvernoinformer.PolicyViolationInformer,
|
||||
eventGen event.Interface,
|
||||
webhookRegistrationClient *WebhookRegistrationClient,
|
||||
filterK8Resources string) (*WebhookServer, error) {
|
||||
|
||||
if tlsPair == nil {
|
||||
|
@ -60,14 +64,16 @@ func NewWebhookServer(
|
|||
tlsConfig.Certificates = []tls.Certificate{pair}
|
||||
|
||||
ws := &WebhookServer{
|
||||
client: client,
|
||||
kyvernoClient: kyvernoClient,
|
||||
pLister: pInformer.Lister(),
|
||||
pvLister: pvInormer.Lister(),
|
||||
pListerSynced: pInformer.Informer().HasSynced,
|
||||
pvListerSynced: pInformer.Informer().HasSynced,
|
||||
eventGen: eventGen,
|
||||
filterK8Resources: utils.ParseKinds(filterK8Resources),
|
||||
|
||||
client: client,
|
||||
kyvernoClient: kyvernoClient,
|
||||
pLister: pInformer.Lister(),
|
||||
pvLister: pvInormer.Lister(),
|
||||
pListerSynced: pInformer.Informer().HasSynced,
|
||||
pvListerSynced: pInformer.Informer().HasSynced,
|
||||
eventGen: eventGen,
|
||||
webhookRegistrationClient: webhookRegistrationClient,
|
||||
filterK8Resources: utils.ParseKinds(filterK8Resources),
|
||||
}
|
||||
mux := http.NewServeMux()
|
||||
mux.HandleFunc(config.MutatingWebhookServicePath, ws.serve)
|
||||
|
@ -102,9 +108,7 @@ func (ws *WebhookServer) serve(w http.ResponseWriter, r *http.Request) {
|
|||
// Resource UPDATE
|
||||
switch r.URL.Path {
|
||||
case config.MutatingWebhookServicePath:
|
||||
admissionReview.Response = ws.HandleMutation(admissionReview.Request)
|
||||
case config.ValidatingWebhookServicePath:
|
||||
admissionReview.Response = ws.HandleValidation(admissionReview.Request)
|
||||
admissionReview.Response = ws.HandleAdmissionRequest(admissionReview.Request)
|
||||
case config.PolicyValidatingWebhookServicePath:
|
||||
admissionReview.Response = ws.HandlePolicyValidation(admissionReview.Request)
|
||||
}
|
||||
|
@ -124,6 +128,27 @@ func (ws *WebhookServer) serve(w http.ResponseWriter, r *http.Request) {
|
|||
}
|
||||
}
|
||||
|
||||
func (ws *WebhookServer) HandleAdmissionRequest(request *v1beta1.AdmissionRequest) *v1beta1.AdmissionResponse {
|
||||
var response *v1beta1.AdmissionResponse
|
||||
|
||||
allowed, engineResponse := ws.HandleMutation(request)
|
||||
if !allowed {
|
||||
// TODO: add failure message to response
|
||||
return &v1beta1.AdmissionResponse{
|
||||
Allowed: false,
|
||||
}
|
||||
}
|
||||
|
||||
response = ws.HandleValidation(request, engineResponse.PatchedResource)
|
||||
if response.Allowed && len(engineResponse.Patches) > 0 {
|
||||
patchType := v1beta1.PatchTypeJSONPatch
|
||||
response.Patch = engine.JoinPatches(engineResponse.Patches)
|
||||
response.PatchType = &patchType
|
||||
}
|
||||
|
||||
return response
|
||||
}
|
||||
|
||||
// RunAsync TLS server in separate thread and returns control immediately
|
||||
func (ws *WebhookServer) RunAsync() {
|
||||
go func(ws *WebhookServer) {
|
||||
|
|
|
@ -7,7 +7,6 @@ import (
|
|||
"github.com/golang/glog"
|
||||
kyverno "github.com/nirmata/kyverno/pkg/api/kyverno/v1alpha1"
|
||||
"github.com/nirmata/kyverno/pkg/info"
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
)
|
||||
|
||||
const policyKind = "Policy"
|
||||
|
@ -81,18 +80,10 @@ const (
|
|||
func toBlock(pis []info.PolicyInfo) bool {
|
||||
for _, pi := range pis {
|
||||
if pi.ValidationFailureAction != ReportViolation {
|
||||
glog.V(3).Infoln("ValidationFailureAction set to enforce, blocking resource ceation")
|
||||
return true
|
||||
}
|
||||
}
|
||||
glog.V(3).Infoln("ValidationFailureAction set to audit, allowing resource creation, reporting with violation")
|
||||
return false
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
|
|
@ -8,16 +8,17 @@ import (
|
|||
"github.com/nirmata/kyverno/pkg/utils"
|
||||
v1beta1 "k8s.io/api/admission/v1beta1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
"k8s.io/apimachinery/pkg/labels"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
)
|
||||
|
||||
// HandleValidation handles validating webhook admission request
|
||||
// If there are no errors in validating rule we apply generation rules
|
||||
func (ws *WebhookServer) HandleValidation(request *v1beta1.AdmissionRequest) *v1beta1.AdmissionResponse {
|
||||
func (ws *WebhookServer) HandleValidation(request *v1beta1.AdmissionRequest, resource unstructured.Unstructured) *v1beta1.AdmissionResponse {
|
||||
var policyInfos []info.PolicyInfo
|
||||
|
||||
glog.V(4).Infof("Receive request in validating webhook: Kind=%s, Namespace=%s Name=%s UID=%s patchOperation=%s",
|
||||
glog.V(5).Infof("Receive request in validating webhook: Kind=%s, Namespace=%s Name=%s UID=%s patchOperation=%s",
|
||||
request.Kind.Kind, request.Namespace, request.Name, request.UID, request.Operation)
|
||||
|
||||
policies, err := ws.pLister.List(labels.NewSelector())
|
||||
|
@ -31,10 +32,6 @@ func (ws *WebhookServer) HandleValidation(request *v1beta1.AdmissionRequest) *v1
|
|||
}
|
||||
}
|
||||
|
||||
resource, err := convertToUnstructured(request.Object.Raw)
|
||||
if err != nil {
|
||||
glog.Errorf("unable to convert raw resource to unstructured: %v", err)
|
||||
}
|
||||
//TODO: check if resource gvk is available in raw resource,
|
||||
// if not then set it from the api request
|
||||
resource.SetGroupVersionKind(schema.GroupVersionKind{Group: request.Kind.Group, Version: request.Kind.Version, Kind: request.Kind.Kind})
|
||||
|
@ -52,30 +49,28 @@ func (ws *WebhookServer) HandleValidation(request *v1beta1.AdmissionRequest) *v1
|
|||
glog.V(4).Infof("Handling validation for Kind=%s, Namespace=%s Name=%s UID=%s patchOperation=%s",
|
||||
resource.GetKind(), resource.GetNamespace(), resource.GetName(), request.UID, request.Operation)
|
||||
|
||||
glog.V(4).Infof("Applying policy %s with %d rules\n", policy.ObjectMeta.Name, len(policy.Spec.Rules))
|
||||
glog.V(4).Infof("Validating resource %s/%s/%s with policy %s with %d rules\n", resource.GetKind(), resource.GetNamespace(), resource.GetName(), policy.ObjectMeta.Name, len(policy.Spec.Rules))
|
||||
|
||||
ruleInfos, err := engine.Validate(*policy, *resource)
|
||||
if err != nil {
|
||||
// This is not policy error
|
||||
// but if unable to parse request raw resource
|
||||
// TODO : create event ? dont think so
|
||||
glog.Error(err)
|
||||
engineResponse := engine.Validate(*policy, resource)
|
||||
if len(engineResponse.RuleInfos) == 0 {
|
||||
continue
|
||||
}
|
||||
policyInfo.AddRuleInfos(ruleInfos)
|
||||
policyInfos = append(policyInfos, policyInfo)
|
||||
|
||||
if len(engineResponse.RuleInfos) > 0 {
|
||||
glog.V(4).Infof("Validation from policy %s has applied succesfully to %s %s/%s", policy.Name, request.Kind.Kind, resource.GetNamespace(), resource.GetName())
|
||||
}
|
||||
|
||||
policyInfo.AddRuleInfos(engineResponse.RuleInfos)
|
||||
|
||||
if !policyInfo.IsSuccessful() {
|
||||
glog.Infof("Failed to apply policy %s on resource %s/%s", policy.Name, resource.GetNamespace(), resource.GetName())
|
||||
glog.V(4).Info("Failed rule details")
|
||||
for _, r := range ruleInfos {
|
||||
glog.V(4).Infof("%s: %s\n", r.Name, r.Msgs)
|
||||
for _, r := range engineResponse.RuleInfos {
|
||||
glog.Warningf("%s: %s\n", r.Name, r.Msgs)
|
||||
}
|
||||
continue
|
||||
}
|
||||
if len(ruleInfos) > 0 {
|
||||
glog.V(4).Infof("Validation from policy %s has applied succesfully to %s %s/%s", policy.Name, request.Kind.Kind, resource.GetNamespace(), resource.GetName())
|
||||
}
|
||||
|
||||
policyInfos = append(policyInfos, policyInfo)
|
||||
|
||||
}
|
||||
|
||||
// ADD EVENTS
|
||||
|
@ -86,6 +81,10 @@ func (ws *WebhookServer) HandleValidation(request *v1beta1.AdmissionRequest) *v1
|
|||
ws.eventGen.Add(eventsInfo...)
|
||||
}
|
||||
|
||||
// If Validation fails then reject the request
|
||||
// violations are created if "audit" flag is set
|
||||
// and if there are any then we dont block the resource creation
|
||||
// Even if one the policy being applied
|
||||
ok, msg := isAdmSuccesful(policyInfos)
|
||||
if !ok && toBlock(policyInfos) {
|
||||
return &v1beta1.AdmissionResponse{
|
||||
|
@ -102,5 +101,4 @@ func (ws *WebhookServer) HandleValidation(request *v1beta1.AdmissionRequest) *v1
|
|||
return &v1beta1.AdmissionResponse{
|
||||
Allowed: true,
|
||||
}
|
||||
// Generation rules applied via generation controller
|
||||
}
|
||||
|
|
49
pkg/webhooks/webhookManager.go
Normal file
49
pkg/webhooks/webhookManager.go
Normal file
|
@ -0,0 +1,49 @@
|
|||
package webhooks
|
||||
|
||||
import (
|
||||
"github.com/golang/glog"
|
||||
kyverno "github.com/nirmata/kyverno/pkg/api/kyverno/v1alpha1"
|
||||
v1beta1 "k8s.io/api/admission/v1beta1"
|
||||
"k8s.io/apimachinery/pkg/labels"
|
||||
)
|
||||
|
||||
type policyType int
|
||||
|
||||
const (
|
||||
none policyType = iota
|
||||
mutate
|
||||
validate
|
||||
all
|
||||
)
|
||||
|
||||
func (ws *WebhookServer) manageWebhookConfigurations(policy kyverno.Policy, op v1beta1.Operation) {
|
||||
switch op {
|
||||
case v1beta1.Create:
|
||||
ws.registerWebhookConfigurations(policy)
|
||||
case v1beta1.Delete:
|
||||
ws.deregisterWebhookConfigurations(policy)
|
||||
}
|
||||
}
|
||||
|
||||
func (ws *WebhookServer) registerWebhookConfigurations(policy kyverno.Policy) error {
|
||||
if !ws.webhookRegistrationClient.MutationRegistered.IsSet() {
|
||||
if err := ws.webhookRegistrationClient.RegisterMutatingWebhook(); err != nil {
|
||||
return err
|
||||
}
|
||||
glog.Infof("Mutating webhook registered")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (ws *WebhookServer) deregisterWebhookConfigurations(policy kyverno.Policy) error {
|
||||
policies, _ := ws.pLister.List(labels.NewSelector())
|
||||
|
||||
// deregister webhook if no policy found in cluster
|
||||
if len(policies) == 1 {
|
||||
ws.webhookRegistrationClient.deregisterMutatingWebhook()
|
||||
glog.Infoln("Mutating webhook deregistered")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
|
@ -18,7 +18,7 @@ dep ensure -v || exit 2
|
|||
echo "# Building executable ${project_name}..."
|
||||
chmod +x scripts/update-codegen.sh
|
||||
scripts/update-codegen.sh
|
||||
CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o ${project_name} . || exit 3
|
||||
make build || exit 3
|
||||
|
||||
echo "# Building docker image ${hub_user_name}/${project_name}:${version}"
|
||||
cat <<EOF > Dockerfile
|
||||
|
|
41
test/generate-resource/generate.sh
Normal file
41
test/generate-resource/generate.sh
Normal file
|
@ -0,0 +1,41 @@
|
|||
#!/bin/bash
|
||||
|
||||
### To use this script to generate resource:
|
||||
### ./resource.sh --file=resource.yaml --replica=10
|
||||
|
||||
for i in "$@"
|
||||
do
|
||||
case $i in
|
||||
--file=*)
|
||||
file="${i#*=}"
|
||||
shift
|
||||
;;
|
||||
--replica=*)
|
||||
replica="${i#*=}"
|
||||
shift
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
if [ -z "${file}" ]; then
|
||||
echo -e "Please specify '--file' where resource is located."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ -z "${replica}" ]; then
|
||||
echo -e "Please specify '--replica' of the number of replicas you want to create."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "loading resource from ${file}"
|
||||
RESOURCE=$(cat ${file} | sed -n -e 's/^ name: //p')
|
||||
|
||||
echo "generating ${replica} replicas from resource $RESOURCE"
|
||||
|
||||
for i in $(seq 1 ${replica})
|
||||
do
|
||||
# echo `cat ${file} | sed "s/name: ${RESOURCE}/name: ${RESOURCE}-${i}/"`
|
||||
dstfile=`sed 's/.\{5\}$/-$i&/' <<< "${file}"`
|
||||
cat ${file} | sed "s/name: ${RESOURCE}/name: ${RESOURCE}-${i}/" > ${dstfile}
|
||||
done
|
||||
|
73
test/generate-resource/main.go
Normal file
73
test/generate-resource/main.go
Normal file
|
@ -0,0 +1,73 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"flag"
|
||||
"fmt"
|
||||
ioutil "io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
|
||||
kubepolicy "github.com/nirmata/kyverno/pkg/apis/policy/v1alpha1"
|
||||
yaml "k8s.io/apimachinery/pkg/util/yaml"
|
||||
)
|
||||
|
||||
var policyPath, replica string
|
||||
|
||||
func main() {
|
||||
generatePolicies()
|
||||
}
|
||||
|
||||
func generatePolicies() error {
|
||||
var policy *kubepolicy.Policy
|
||||
|
||||
file, err := ioutil.ReadFile(policyPath)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to load file: %v", err)
|
||||
}
|
||||
|
||||
fmt.Printf("Generating policies from %s\n", policyPath)
|
||||
|
||||
rawPolicy, err := yaml.ToJSON(file)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := json.Unmarshal(rawPolicy, &policy); err != nil {
|
||||
return fmt.Errorf("failed to decode policy %s, err: %v", policy.Name, err)
|
||||
}
|
||||
|
||||
oldName := policy.Name
|
||||
repl, _ := strconv.Atoi(replica)
|
||||
for i := 0; i < repl; i++ {
|
||||
newName := oldName + "-" + strconv.Itoa(i)
|
||||
data := bytes.Replace(file, []byte(oldName), []byte(newName), -1)
|
||||
|
||||
writeToFile(data, "./.policy/"+newName+".yaml")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func writeToFile(data []byte, filename string) {
|
||||
|
||||
dir := filepath.Dir(filename)
|
||||
if _, err := os.Stat(dir); os.IsNotExist(err) {
|
||||
if err = os.MkdirAll(dir, 0755); err != nil {
|
||||
fmt.Println(err)
|
||||
}
|
||||
}
|
||||
|
||||
if err := ioutil.WriteFile(filename, data, 0755); err != nil {
|
||||
fmt.Println(err)
|
||||
}
|
||||
}
|
||||
|
||||
func init() {
|
||||
flag.StringVar(&policyPath, "policyPath", "", "Path to a policy")
|
||||
flag.StringVar(&replica, "replica", "10", "the number of replicas to generate")
|
||||
|
||||
flag.Parse()
|
||||
}
|
5
vendor/github.com/evanphx/json-patch/go.mod
generated
vendored
Normal file
5
vendor/github.com/evanphx/json-patch/go.mod
generated
vendored
Normal file
|
@ -0,0 +1,5 @@
|
|||
module github.com/evanphx/json-patch
|
||||
|
||||
go 1.12
|
||||
|
||||
require github.com/pkg/errors v0.8.1
|
2
vendor/github.com/evanphx/json-patch/go.sum
generated
vendored
Normal file
2
vendor/github.com/evanphx/json-patch/go.sum
generated
vendored
Normal file
|
@ -0,0 +1,2 @@
|
|||
github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I=
|
||||
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
2
vendor/github.com/golang/groupcache/groupcache.go
generated
vendored
2
vendor/github.com/golang/groupcache/groupcache.go
generated
vendored
|
@ -245,7 +245,7 @@ func (g *Group) load(ctx Context, key string, dest Sink) (value ByteView, destPo
|
|||
// be only one entry for this key.
|
||||
//
|
||||
// Consider the following serialized event ordering for two
|
||||
// goroutines in which this callback gets called twice for hte
|
||||
// goroutines in which this callback gets called twice for the
|
||||
// same key:
|
||||
// 1: Get("key")
|
||||
// 2: Get("key")
|
||||
|
|
23
vendor/github.com/golang/protobuf/jsonpb/jsonpb.go
generated
vendored
23
vendor/github.com/golang/protobuf/jsonpb/jsonpb.go
generated
vendored
|
@ -57,6 +57,7 @@ import (
|
|||
)
|
||||
|
||||
const secondInNanos = int64(time.Second / time.Nanosecond)
|
||||
const maxSecondsInDuration = 315576000000
|
||||
|
||||
// Marshaler is a configurable object for converting between
|
||||
// protocol buffer objects and a JSON representation for them.
|
||||
|
@ -182,7 +183,12 @@ func (m *Marshaler) marshalObject(out *errWriter, v proto.Message, indent, typeU
|
|||
return fmt.Errorf("failed to marshal type URL %q to JSON: %v", typeURL, err)
|
||||
}
|
||||
js["@type"] = (*json.RawMessage)(&turl)
|
||||
if b, err = json.Marshal(js); err != nil {
|
||||
if m.Indent != "" {
|
||||
b, err = json.MarshalIndent(js, indent, m.Indent)
|
||||
} else {
|
||||
b, err = json.Marshal(js)
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
@ -206,19 +212,26 @@ func (m *Marshaler) marshalObject(out *errWriter, v proto.Message, indent, typeU
|
|||
// Any is a bit more involved.
|
||||
return m.marshalAny(out, v, indent)
|
||||
case "Duration":
|
||||
// "Generated output always contains 0, 3, 6, or 9 fractional digits,
|
||||
// depending on required precision."
|
||||
s, ns := s.Field(0).Int(), s.Field(1).Int()
|
||||
if s < -maxSecondsInDuration || s > maxSecondsInDuration {
|
||||
return fmt.Errorf("seconds out of range %v", s)
|
||||
}
|
||||
if ns <= -secondInNanos || ns >= secondInNanos {
|
||||
return fmt.Errorf("ns out of range (%v, %v)", -secondInNanos, secondInNanos)
|
||||
}
|
||||
if (s > 0 && ns < 0) || (s < 0 && ns > 0) {
|
||||
return errors.New("signs of seconds and nanos do not match")
|
||||
}
|
||||
if s < 0 {
|
||||
// Generated output always contains 0, 3, 6, or 9 fractional digits,
|
||||
// depending on required precision, followed by the suffix "s".
|
||||
f := "%d.%09d"
|
||||
if ns < 0 {
|
||||
ns = -ns
|
||||
if s == 0 {
|
||||
f = "-%d.%09d"
|
||||
}
|
||||
}
|
||||
x := fmt.Sprintf("%d.%09d", s, ns)
|
||||
x := fmt.Sprintf(f, s, ns)
|
||||
x = strings.TrimSuffix(x, "000")
|
||||
x = strings.TrimSuffix(x, "000")
|
||||
x = strings.TrimSuffix(x, ".000")
|
||||
|
|
57
vendor/github.com/golang/protobuf/jsonpb/jsonpb_test.go
generated
vendored
57
vendor/github.com/golang/protobuf/jsonpb/jsonpb_test.go
generated
vendored
|
@ -473,10 +473,17 @@ var marshalingTests = []struct {
|
|||
{"Any with message and indent", marshalerAllOptions, anySimple, anySimplePrettyJSON},
|
||||
{"Any with WKT", marshaler, anyWellKnown, anyWellKnownJSON},
|
||||
{"Any with WKT and indent", marshalerAllOptions, anyWellKnown, anyWellKnownPrettyJSON},
|
||||
{"Duration", marshaler, &pb.KnownTypes{Dur: &durpb.Duration{Seconds: 3}}, `{"dur":"3s"}`},
|
||||
{"Duration", marshaler, &pb.KnownTypes{Dur: &durpb.Duration{Seconds: 3, Nanos: 1e6}}, `{"dur":"3.001s"}`},
|
||||
{"Duration beyond float64 precision", marshaler, &pb.KnownTypes{Dur: &durpb.Duration{Seconds: 100000000, Nanos: 1}}, `{"dur":"100000000.000000001s"}`},
|
||||
{"negative Duration", marshaler, &pb.KnownTypes{Dur: &durpb.Duration{Seconds: -123, Nanos: -456}}, `{"dur":"-123.000000456s"}`},
|
||||
{"Duration empty", marshaler, &durpb.Duration{}, `"0s"`},
|
||||
{"Duration with secs", marshaler, &durpb.Duration{Seconds: 3}, `"3s"`},
|
||||
{"Duration with -secs", marshaler, &durpb.Duration{Seconds: -3}, `"-3s"`},
|
||||
{"Duration with nanos", marshaler, &durpb.Duration{Nanos: 1e6}, `"0.001s"`},
|
||||
{"Duration with -nanos", marshaler, &durpb.Duration{Nanos: -1e6}, `"-0.001s"`},
|
||||
{"Duration with large secs", marshaler, &durpb.Duration{Seconds: 1e10, Nanos: 1}, `"10000000000.000000001s"`},
|
||||
{"Duration with 6-digit nanos", marshaler, &durpb.Duration{Nanos: 1e4}, `"0.000010s"`},
|
||||
{"Duration with 3-digit nanos", marshaler, &durpb.Duration{Nanos: 1e6}, `"0.001s"`},
|
||||
{"Duration with -secs -nanos", marshaler, &durpb.Duration{Seconds: -123, Nanos: -450}, `"-123.000000450s"`},
|
||||
{"Duration max value", marshaler, &durpb.Duration{Seconds: 315576000000, Nanos: 999999999}, `"315576000000.999999999s"`},
|
||||
{"Duration min value", marshaler, &durpb.Duration{Seconds: -315576000000, Nanos: -999999999}, `"-315576000000.999999999s"`},
|
||||
{"Struct", marshaler, &pb.KnownTypes{St: &stpb.Struct{
|
||||
Fields: map[string]*stpb.Value{
|
||||
"one": {Kind: &stpb.Value_StringValue{"loneliest number"}},
|
||||
|
@ -549,15 +556,17 @@ func TestMarshalIllegalTime(t *testing.T) {
|
|||
pb proto.Message
|
||||
fail bool
|
||||
}{
|
||||
{&pb.KnownTypes{Dur: &durpb.Duration{Seconds: 1, Nanos: 0}}, false},
|
||||
{&pb.KnownTypes{Dur: &durpb.Duration{Seconds: -1, Nanos: 0}}, false},
|
||||
{&pb.KnownTypes{Dur: &durpb.Duration{Seconds: 1, Nanos: -1}}, true},
|
||||
{&pb.KnownTypes{Dur: &durpb.Duration{Seconds: -1, Nanos: 1}}, true},
|
||||
{&pb.KnownTypes{Dur: &durpb.Duration{Seconds: 1, Nanos: 1000000000}}, true},
|
||||
{&pb.KnownTypes{Dur: &durpb.Duration{Seconds: -1, Nanos: -1000000000}}, true},
|
||||
{&pb.KnownTypes{Ts: &tspb.Timestamp{Seconds: 1, Nanos: 1}}, false},
|
||||
{&pb.KnownTypes{Ts: &tspb.Timestamp{Seconds: 1, Nanos: -1}}, true},
|
||||
{&pb.KnownTypes{Ts: &tspb.Timestamp{Seconds: 1, Nanos: 1000000000}}, true},
|
||||
{&durpb.Duration{Seconds: 1, Nanos: 0}, false},
|
||||
{&durpb.Duration{Seconds: -1, Nanos: 0}, false},
|
||||
{&durpb.Duration{Seconds: 1, Nanos: -1}, true},
|
||||
{&durpb.Duration{Seconds: -1, Nanos: 1}, true},
|
||||
{&durpb.Duration{Seconds: 315576000001}, true},
|
||||
{&durpb.Duration{Seconds: -315576000001}, true},
|
||||
{&durpb.Duration{Seconds: 1, Nanos: 1000000000}, true},
|
||||
{&durpb.Duration{Seconds: -1, Nanos: -1000000000}, true},
|
||||
{&tspb.Timestamp{Seconds: 1, Nanos: 1}, false},
|
||||
{&tspb.Timestamp{Seconds: 1, Nanos: -1}, true},
|
||||
{&tspb.Timestamp{Seconds: 1, Nanos: 1000000000}, true},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
_, err := marshaler.MarshalToString(tt.pb)
|
||||
|
@ -598,6 +607,28 @@ func TestMarshalAnyJSONPBMarshaler(t *testing.T) {
|
|||
if str != expected {
|
||||
t.Errorf("marshalling JSON produced incorrect output: got %s, wanted %s", str, expected)
|
||||
}
|
||||
|
||||
// Do it again, but this time with indentation:
|
||||
|
||||
marshaler := Marshaler{Indent: " "}
|
||||
str, err = marshaler.MarshalToString(a)
|
||||
if err != nil {
|
||||
t.Errorf("an unexpected error occurred when marshalling Any to JSON: %v", err)
|
||||
}
|
||||
// same as expected above, but pretty-printed w/ indentation
|
||||
expected = `{
|
||||
"@type": "type.googleapis.com/` + dynamicMessageName + `",
|
||||
"baz": [
|
||||
0,
|
||||
1,
|
||||
2,
|
||||
3
|
||||
],
|
||||
"foo": "bar"
|
||||
}`
|
||||
if str != expected {
|
||||
t.Errorf("marshalling JSON produced incorrect output: got %s, wanted %s", str, expected)
|
||||
}
|
||||
}
|
||||
|
||||
func TestMarshalWithCustomValidation(t *testing.T) {
|
||||
|
|
5
vendor/github.com/golang/protobuf/proto/properties.go
generated
vendored
5
vendor/github.com/golang/protobuf/proto/properties.go
generated
vendored
|
@ -38,7 +38,6 @@ package proto
|
|||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
"reflect"
|
||||
"sort"
|
||||
"strconv"
|
||||
|
@ -194,7 +193,7 @@ func (p *Properties) Parse(s string) {
|
|||
// "bytes,49,opt,name=foo,def=hello!"
|
||||
fields := strings.Split(s, ",") // breaks def=, but handled below.
|
||||
if len(fields) < 2 {
|
||||
fmt.Fprintf(os.Stderr, "proto: tag has too few fields: %q\n", s)
|
||||
log.Printf("proto: tag has too few fields: %q", s)
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -214,7 +213,7 @@ func (p *Properties) Parse(s string) {
|
|||
p.WireType = WireBytes
|
||||
// no numeric converter for non-numeric types
|
||||
default:
|
||||
fmt.Fprintf(os.Stderr, "proto: tag has unknown wire type: %q\n", s)
|
||||
log.Printf("proto: tag has unknown wire type: %q", s)
|
||||
return
|
||||
}
|
||||
|
||||
|
|
61
vendor/github.com/golang/protobuf/protoc-gen-go/grpc/grpc.go
generated
vendored
61
vendor/github.com/golang/protobuf/protoc-gen-go/grpc/grpc.go
generated
vendored
|
@ -54,6 +54,8 @@ const generatedCodeVersion = 4
|
|||
const (
|
||||
contextPkgPath = "context"
|
||||
grpcPkgPath = "google.golang.org/grpc"
|
||||
codePkgPath = "google.golang.org/grpc/codes"
|
||||
statusPkgPath = "google.golang.org/grpc/status"
|
||||
)
|
||||
|
||||
func init() {
|
||||
|
@ -216,6 +218,12 @@ func (g *grpc) generateService(file *generator.FileDescriptor, service *pb.Servi
|
|||
g.P("}")
|
||||
g.P()
|
||||
|
||||
// Server Unimplemented struct for forward compatability.
|
||||
if deprecated {
|
||||
g.P(deprecationComment)
|
||||
}
|
||||
g.generateUnimplementedServer(servName, service)
|
||||
|
||||
// Server registration.
|
||||
if deprecated {
|
||||
g.P(deprecationComment)
|
||||
|
@ -269,6 +277,35 @@ func (g *grpc) generateService(file *generator.FileDescriptor, service *pb.Servi
|
|||
g.P()
|
||||
}
|
||||
|
||||
// generateUnimplementedServer creates the unimplemented server struct
|
||||
func (g *grpc) generateUnimplementedServer(servName string, service *pb.ServiceDescriptorProto) {
|
||||
serverType := servName + "Server"
|
||||
g.P("// Unimplemented", serverType, " can be embedded to have forward compatible implementations.")
|
||||
g.P("type Unimplemented", serverType, " struct {")
|
||||
g.P("}")
|
||||
g.P()
|
||||
// Unimplemented<service_name>Server's concrete methods
|
||||
for _, method := range service.Method {
|
||||
g.generateServerMethodConcrete(servName, method)
|
||||
}
|
||||
g.P()
|
||||
}
|
||||
|
||||
// generateServerMethodConcrete returns unimplemented methods which ensure forward compatibility
|
||||
func (g *grpc) generateServerMethodConcrete(servName string, method *pb.MethodDescriptorProto) {
|
||||
header := g.generateServerSignatureWithParamNames(servName, method)
|
||||
g.P("func (*Unimplemented", servName, "Server) ", header, " {")
|
||||
var nilArg string
|
||||
if !method.GetServerStreaming() && !method.GetClientStreaming() {
|
||||
nilArg = "nil, "
|
||||
}
|
||||
methName := generator.CamelCase(method.GetName())
|
||||
statusPkg := string(g.gen.AddImport(statusPkgPath))
|
||||
codePkg := string(g.gen.AddImport(codePkgPath))
|
||||
g.P("return ", nilArg, statusPkg, `.Errorf(`, codePkg, `.Unimplemented, "method `, methName, ` not implemented")`)
|
||||
g.P("}")
|
||||
}
|
||||
|
||||
// generateClientSignature returns the client-side signature for a method.
|
||||
func (g *grpc) generateClientSignature(servName string, method *pb.MethodDescriptorProto) string {
|
||||
origMethName := method.GetName()
|
||||
|
@ -368,6 +405,30 @@ func (g *grpc) generateClientMethod(servName, fullServName, serviceDescVar strin
|
|||
}
|
||||
}
|
||||
|
||||
// generateServerSignatureWithParamNames returns the server-side signature for a method with parameter names.
|
||||
func (g *grpc) generateServerSignatureWithParamNames(servName string, method *pb.MethodDescriptorProto) string {
|
||||
origMethName := method.GetName()
|
||||
methName := generator.CamelCase(origMethName)
|
||||
if reservedClientName[methName] {
|
||||
methName += "_"
|
||||
}
|
||||
|
||||
var reqArgs []string
|
||||
ret := "error"
|
||||
if !method.GetServerStreaming() && !method.GetClientStreaming() {
|
||||
reqArgs = append(reqArgs, "ctx "+contextPkg+".Context")
|
||||
ret = "(*" + g.typeName(method.GetOutputType()) + ", error)"
|
||||
}
|
||||
if !method.GetClientStreaming() {
|
||||
reqArgs = append(reqArgs, "req *"+g.typeName(method.GetInputType()))
|
||||
}
|
||||
if method.GetServerStreaming() || method.GetClientStreaming() {
|
||||
reqArgs = append(reqArgs, "srv "+servName+"_"+generator.CamelCase(origMethName)+"Server")
|
||||
}
|
||||
|
||||
return methName + "(" + strings.Join(reqArgs, ", ") + ") " + ret
|
||||
}
|
||||
|
||||
// generateServerSignature returns the server-side signature for a method.
|
||||
func (g *grpc) generateServerSignature(servName string, method *pb.MethodDescriptorProto) string {
|
||||
origMethName := method.GetName()
|
||||
|
|
11
vendor/github.com/golang/protobuf/protoc-gen-go/testdata/deprecated/deprecated.pb.go
generated
vendored
11
vendor/github.com/golang/protobuf/protoc-gen-go/testdata/deprecated/deprecated.pb.go
generated
vendored
|
@ -10,6 +10,8 @@ import (
|
|||
fmt "fmt"
|
||||
proto "github.com/golang/protobuf/proto"
|
||||
grpc "google.golang.org/grpc"
|
||||
codes "google.golang.org/grpc/codes"
|
||||
status "google.golang.org/grpc/status"
|
||||
math "math"
|
||||
)
|
||||
|
||||
|
@ -235,6 +237,15 @@ type DeprecatedServiceServer interface {
|
|||
DeprecatedCall(context.Context, *DeprecatedRequest) (*DeprecatedResponse, error)
|
||||
}
|
||||
|
||||
// Deprecated: Do not use.
|
||||
// UnimplementedDeprecatedServiceServer can be embedded to have forward compatible implementations.
|
||||
type UnimplementedDeprecatedServiceServer struct {
|
||||
}
|
||||
|
||||
func (*UnimplementedDeprecatedServiceServer) DeprecatedCall(ctx context.Context, req *DeprecatedRequest) (*DeprecatedResponse, error) {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method DeprecatedCall not implemented")
|
||||
}
|
||||
|
||||
// Deprecated: Do not use.
|
||||
func RegisterDeprecatedServiceServer(s *grpc.Server, srv DeprecatedServiceServer) {
|
||||
s.RegisterService(&_DeprecatedService_serviceDesc, srv)
|
||||
|
|
19
vendor/github.com/golang/protobuf/protoc-gen-go/testdata/grpc/grpc.pb.go
generated
vendored
19
vendor/github.com/golang/protobuf/protoc-gen-go/testdata/grpc/grpc.pb.go
generated
vendored
|
@ -8,6 +8,8 @@ import (
|
|||
fmt "fmt"
|
||||
proto "github.com/golang/protobuf/proto"
|
||||
grpc "google.golang.org/grpc"
|
||||
codes "google.golang.org/grpc/codes"
|
||||
status "google.golang.org/grpc/status"
|
||||
math "math"
|
||||
)
|
||||
|
||||
|
@ -321,6 +323,23 @@ type TestServer interface {
|
|||
Bidi(Test_BidiServer) error
|
||||
}
|
||||
|
||||
// UnimplementedTestServer can be embedded to have forward compatible implementations.
|
||||
type UnimplementedTestServer struct {
|
||||
}
|
||||
|
||||
func (*UnimplementedTestServer) UnaryCall(ctx context.Context, req *SimpleRequest) (*SimpleResponse, error) {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method UnaryCall not implemented")
|
||||
}
|
||||
func (*UnimplementedTestServer) Downstream(req *SimpleRequest, srv Test_DownstreamServer) error {
|
||||
return status.Errorf(codes.Unimplemented, "method Downstream not implemented")
|
||||
}
|
||||
func (*UnimplementedTestServer) Upstream(srv Test_UpstreamServer) error {
|
||||
return status.Errorf(codes.Unimplemented, "method Upstream not implemented")
|
||||
}
|
||||
func (*UnimplementedTestServer) Bidi(srv Test_BidiServer) error {
|
||||
return status.Errorf(codes.Unimplemented, "method Bidi not implemented")
|
||||
}
|
||||
|
||||
func RegisterTestServer(s *grpc.Server, srv TestServer) {
|
||||
s.RegisterService(&_Test_serviceDesc, srv)
|
||||
}
|
||||
|
|
79
vendor/github.com/golang/protobuf/protoc-gen-go/testdata/grpc/grpc_empty.pb.go
generated
vendored
Normal file
79
vendor/github.com/golang/protobuf/protoc-gen-go/testdata/grpc/grpc_empty.pb.go
generated
vendored
Normal file
|
@ -0,0 +1,79 @@
|
|||
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||
// source: grpc/grpc_empty.proto
|
||||
|
||||
package testing
|
||||
|
||||
import (
|
||||
context "context"
|
||||
fmt "fmt"
|
||||
proto "github.com/golang/protobuf/proto"
|
||||
grpc "google.golang.org/grpc"
|
||||
math "math"
|
||||
)
|
||||
|
||||
// Reference imports to suppress errors if they are not otherwise used.
|
||||
var _ = proto.Marshal
|
||||
var _ = fmt.Errorf
|
||||
var _ = math.Inf
|
||||
|
||||
// This is a compile-time assertion to ensure that this generated file
|
||||
// is compatible with the proto package it is being compiled against.
|
||||
// A compilation error at this line likely means your copy of the
|
||||
// proto package needs to be updated.
|
||||
const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package
|
||||
|
||||
func init() { proto.RegisterFile("grpc/grpc_empty.proto", fileDescriptor_c580a37f1c90e9b1) }
|
||||
|
||||
var fileDescriptor_c580a37f1c90e9b1 = []byte{
|
||||
// 125 bytes of a gzipped FileDescriptorProto
|
||||
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x12, 0x4d, 0x2f, 0x2a, 0x48,
|
||||
0xd6, 0x07, 0x11, 0xf1, 0xa9, 0xb9, 0x05, 0x25, 0x95, 0x7a, 0x05, 0x45, 0xf9, 0x25, 0xf9, 0x42,
|
||||
0x3c, 0x20, 0x11, 0xbd, 0x92, 0xd4, 0xe2, 0x92, 0xcc, 0xbc, 0x74, 0x23, 0x3e, 0x2e, 0x1e, 0x57,
|
||||
0x90, 0x64, 0x70, 0x6a, 0x51, 0x59, 0x66, 0x72, 0xaa, 0x93, 0x43, 0x94, 0x5d, 0x7a, 0x66, 0x49,
|
||||
0x46, 0x69, 0x92, 0x5e, 0x72, 0x7e, 0xae, 0x7e, 0x7a, 0x7e, 0x4e, 0x62, 0x5e, 0xba, 0x3e, 0x58,
|
||||
0x63, 0x52, 0x69, 0x1a, 0x84, 0x91, 0xac, 0x9b, 0x9e, 0x9a, 0xa7, 0x9b, 0x9e, 0xaf, 0x0f, 0x32,
|
||||
0x23, 0x25, 0xb1, 0x24, 0x11, 0x6c, 0x87, 0x35, 0xd4, 0xc4, 0x24, 0x36, 0xb0, 0x22, 0x63, 0x40,
|
||||
0x00, 0x00, 0x00, 0xff, 0xff, 0x93, 0x1d, 0xf2, 0x47, 0x7f, 0x00, 0x00, 0x00,
|
||||
}
|
||||
|
||||
// Reference imports to suppress errors if they are not otherwise used.
|
||||
var _ context.Context
|
||||
var _ grpc.ClientConn
|
||||
|
||||
// This is a compile-time assertion to ensure that this generated file
|
||||
// is compatible with the grpc package it is being compiled against.
|
||||
const _ = grpc.SupportPackageIsVersion4
|
||||
|
||||
// EmptyServiceClient is the client API for EmptyService service.
|
||||
//
|
||||
// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#ClientConn.NewStream.
|
||||
type EmptyServiceClient interface {
|
||||
}
|
||||
|
||||
type emptyServiceClient struct {
|
||||
cc *grpc.ClientConn
|
||||
}
|
||||
|
||||
func NewEmptyServiceClient(cc *grpc.ClientConn) EmptyServiceClient {
|
||||
return &emptyServiceClient{cc}
|
||||
}
|
||||
|
||||
// EmptyServiceServer is the server API for EmptyService service.
|
||||
type EmptyServiceServer interface {
|
||||
}
|
||||
|
||||
// UnimplementedEmptyServiceServer can be embedded to have forward compatible implementations.
|
||||
type UnimplementedEmptyServiceServer struct {
|
||||
}
|
||||
|
||||
func RegisterEmptyServiceServer(s *grpc.Server, srv EmptyServiceServer) {
|
||||
s.RegisterService(&_EmptyService_serviceDesc, srv)
|
||||
}
|
||||
|
||||
var _EmptyService_serviceDesc = grpc.ServiceDesc{
|
||||
ServiceName: "grpc.testing.EmptyService",
|
||||
HandlerType: (*EmptyServiceServer)(nil),
|
||||
Methods: []grpc.MethodDesc{},
|
||||
Streams: []grpc.StreamDesc{},
|
||||
Metadata: "grpc/grpc_empty.proto",
|
||||
}
|
38
vendor/github.com/golang/protobuf/protoc-gen-go/testdata/grpc/grpc_empty.proto
generated
vendored
Normal file
38
vendor/github.com/golang/protobuf/protoc-gen-go/testdata/grpc/grpc_empty.proto
generated
vendored
Normal file
|
@ -0,0 +1,38 @@
|
|||
// Go support for Protocol Buffers - Google's data interchange format
|
||||
//
|
||||
// Copyright 2019 The Go Authors. All rights reserved.
|
||||
// https://github.com/golang/protobuf
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following disclaimer
|
||||
// in the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from
|
||||
// this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
syntax = "proto3";
|
||||
|
||||
package grpc.testing;
|
||||
|
||||
option go_package = "github.com/golang/protobuf/protoc-gen-go/testdata/grpc;testing";
|
||||
|
||||
service EmptyService {}
|
6
vendor/github.com/googleapis/gnostic/.travis-install.sh
generated
vendored
6
vendor/github.com/googleapis/gnostic/.travis-install.sh
generated
vendored
|
@ -13,12 +13,6 @@
|
|||
cd
|
||||
mkdir -p local
|
||||
|
||||
# Install swift
|
||||
SWIFT_URL=https://swift.org/builds/swift-4.0-branch/ubuntu1404/swift-4.0-DEVELOPMENT-SNAPSHOT-2017-09-01-a/swift-4.0-DEVELOPMENT-SNAPSHOT-2017-09-01-a-ubuntu14.04.tar.gz
|
||||
echo $SWIFT_URL
|
||||
curl -fSsL $SWIFT_URL -o swift.tar.gz
|
||||
tar -xzf swift.tar.gz --strip-components=2 --directory=local
|
||||
|
||||
# Install protoc
|
||||
PROTOC_URL=https://github.com/google/protobuf/releases/download/v3.4.0/protoc-3.4.0-linux-x86_64.zip
|
||||
echo $PROTOC_URL
|
||||
|
|
18
vendor/github.com/googleapis/gnostic/.travis.yml
generated
vendored
18
vendor/github.com/googleapis/gnostic/.travis.yml
generated
vendored
|
@ -29,21 +29,3 @@ install:
|
|||
|
||||
script:
|
||||
- go test . -v
|
||||
- pushd plugins/gnostic-go-generator/examples/v2.0/bookstore
|
||||
- make test
|
||||
- popd
|
||||
- pushd plugins/gnostic-go-generator/examples/v2.0/sample
|
||||
- make test
|
||||
- popd
|
||||
- pushd plugins/gnostic-go-generator/examples/v3.0/bookstore
|
||||
- make test
|
||||
- popd
|
||||
- export PATH=.:$HOME/local/bin:$PATH
|
||||
- export LD_LIBRARY_PATH=$HOME/local/lib
|
||||
- pushd plugins/gnostic-swift-generator
|
||||
- make install
|
||||
- cd examples/bookstore
|
||||
- make
|
||||
- .build/debug/Server &
|
||||
- make test
|
||||
|
||||
|
|
4
vendor/github.com/googleapis/gnostic/Makefile
generated
vendored
4
vendor/github.com/googleapis/gnostic/Makefile
generated
vendored
|
@ -8,9 +8,5 @@ build:
|
|||
cd apps/petstore-builder; go get; go install
|
||||
cd plugins/gnostic-summary; go get; go install
|
||||
cd plugins/gnostic-analyze; go get; go install
|
||||
cd plugins/gnostic-go-generator; go get; go install
|
||||
rm -f $(GOPATH)/bin/gnostic-go-client $(GOPATH)/bin/gnostic-go-server
|
||||
ln -s $(GOPATH)/bin/gnostic-go-generator $(GOPATH)/bin/gnostic-go-client
|
||||
ln -s $(GOPATH)/bin/gnostic-go-generator $(GOPATH)/bin/gnostic-go-server
|
||||
cd extensions/sample; make
|
||||
|
||||
|
|
295
vendor/github.com/googleapis/gnostic/OpenAPIv2/OpenAPIv2.go
generated
vendored
295
vendor/github.com/googleapis/gnostic/OpenAPIv2/OpenAPIv2.go
generated
vendored
|
@ -7105,15 +7105,15 @@ func (m *Any) ToRawInfo() interface{} {
|
|||
// ToRawInfo returns a description of ApiKeySecurity suitable for JSON or YAML export.
|
||||
func (m *ApiKeySecurity) ToRawInfo() interface{} {
|
||||
info := yaml.MapSlice{}
|
||||
if m.Type != "" {
|
||||
info = append(info, yaml.MapItem{Key: "type", Value: m.Type})
|
||||
}
|
||||
if m.Name != "" {
|
||||
info = append(info, yaml.MapItem{Key: "name", Value: m.Name})
|
||||
}
|
||||
if m.In != "" {
|
||||
info = append(info, yaml.MapItem{Key: "in", Value: m.In})
|
||||
if m == nil {
|
||||
return info
|
||||
}
|
||||
// always include this required field.
|
||||
info = append(info, yaml.MapItem{Key: "type", Value: m.Type})
|
||||
// always include this required field.
|
||||
info = append(info, yaml.MapItem{Key: "name", Value: m.Name})
|
||||
// always include this required field.
|
||||
info = append(info, yaml.MapItem{Key: "in", Value: m.In})
|
||||
if m.Description != "" {
|
||||
info = append(info, yaml.MapItem{Key: "description", Value: m.Description})
|
||||
}
|
||||
|
@ -7129,9 +7129,11 @@ func (m *ApiKeySecurity) ToRawInfo() interface{} {
|
|||
// ToRawInfo returns a description of BasicAuthenticationSecurity suitable for JSON or YAML export.
|
||||
func (m *BasicAuthenticationSecurity) ToRawInfo() interface{} {
|
||||
info := yaml.MapSlice{}
|
||||
if m.Type != "" {
|
||||
info = append(info, yaml.MapItem{Key: "type", Value: m.Type})
|
||||
if m == nil {
|
||||
return info
|
||||
}
|
||||
// always include this required field.
|
||||
info = append(info, yaml.MapItem{Key: "type", Value: m.Type})
|
||||
if m.Description != "" {
|
||||
info = append(info, yaml.MapItem{Key: "description", Value: m.Description})
|
||||
}
|
||||
|
@ -7147,21 +7149,21 @@ func (m *BasicAuthenticationSecurity) ToRawInfo() interface{} {
|
|||
// ToRawInfo returns a description of BodyParameter suitable for JSON or YAML export.
|
||||
func (m *BodyParameter) ToRawInfo() interface{} {
|
||||
info := yaml.MapSlice{}
|
||||
if m == nil {
|
||||
return info
|
||||
}
|
||||
if m.Description != "" {
|
||||
info = append(info, yaml.MapItem{Key: "description", Value: m.Description})
|
||||
}
|
||||
if m.Name != "" {
|
||||
info = append(info, yaml.MapItem{Key: "name", Value: m.Name})
|
||||
}
|
||||
if m.In != "" {
|
||||
info = append(info, yaml.MapItem{Key: "in", Value: m.In})
|
||||
}
|
||||
// always include this required field.
|
||||
info = append(info, yaml.MapItem{Key: "name", Value: m.Name})
|
||||
// always include this required field.
|
||||
info = append(info, yaml.MapItem{Key: "in", Value: m.In})
|
||||
if m.Required != false {
|
||||
info = append(info, yaml.MapItem{Key: "required", Value: m.Required})
|
||||
}
|
||||
if m.Schema != nil {
|
||||
info = append(info, yaml.MapItem{Key: "schema", Value: m.Schema.ToRawInfo()})
|
||||
}
|
||||
// always include this required field.
|
||||
info = append(info, yaml.MapItem{Key: "schema", Value: m.Schema.ToRawInfo()})
|
||||
// &{Name:schema Type:Schema StringEnumValues:[] MapType: Repeated:false Pattern: Implicit:false Description:}
|
||||
if m.VendorExtension != nil {
|
||||
for _, item := range m.VendorExtension {
|
||||
|
@ -7175,6 +7177,9 @@ func (m *BodyParameter) ToRawInfo() interface{} {
|
|||
// ToRawInfo returns a description of Contact suitable for JSON or YAML export.
|
||||
func (m *Contact) ToRawInfo() interface{} {
|
||||
info := yaml.MapSlice{}
|
||||
if m == nil {
|
||||
return info
|
||||
}
|
||||
if m.Name != "" {
|
||||
info = append(info, yaml.MapItem{Key: "name", Value: m.Name})
|
||||
}
|
||||
|
@ -7196,6 +7201,9 @@ func (m *Contact) ToRawInfo() interface{} {
|
|||
// ToRawInfo returns a description of Default suitable for JSON or YAML export.
|
||||
func (m *Default) ToRawInfo() interface{} {
|
||||
info := yaml.MapSlice{}
|
||||
if m == nil {
|
||||
return info
|
||||
}
|
||||
if m.AdditionalProperties != nil {
|
||||
for _, item := range m.AdditionalProperties {
|
||||
info = append(info, yaml.MapItem{Key: item.Name, Value: item.Value.ToRawInfo()})
|
||||
|
@ -7208,6 +7216,9 @@ func (m *Default) ToRawInfo() interface{} {
|
|||
// ToRawInfo returns a description of Definitions suitable for JSON or YAML export.
|
||||
func (m *Definitions) ToRawInfo() interface{} {
|
||||
info := yaml.MapSlice{}
|
||||
if m == nil {
|
||||
return info
|
||||
}
|
||||
if m.AdditionalProperties != nil {
|
||||
for _, item := range m.AdditionalProperties {
|
||||
info = append(info, yaml.MapItem{Key: item.Name, Value: item.Value.ToRawInfo()})
|
||||
|
@ -7220,12 +7231,13 @@ func (m *Definitions) ToRawInfo() interface{} {
|
|||
// ToRawInfo returns a description of Document suitable for JSON or YAML export.
|
||||
func (m *Document) ToRawInfo() interface{} {
|
||||
info := yaml.MapSlice{}
|
||||
if m.Swagger != "" {
|
||||
info = append(info, yaml.MapItem{Key: "swagger", Value: m.Swagger})
|
||||
}
|
||||
if m.Info != nil {
|
||||
info = append(info, yaml.MapItem{Key: "info", Value: m.Info.ToRawInfo()})
|
||||
if m == nil {
|
||||
return info
|
||||
}
|
||||
// always include this required field.
|
||||
info = append(info, yaml.MapItem{Key: "swagger", Value: m.Swagger})
|
||||
// always include this required field.
|
||||
info = append(info, yaml.MapItem{Key: "info", Value: m.Info.ToRawInfo()})
|
||||
// &{Name:info Type:Info StringEnumValues:[] MapType: Repeated:false Pattern: Implicit:false Description:}
|
||||
if m.Host != "" {
|
||||
info = append(info, yaml.MapItem{Key: "host", Value: m.Host})
|
||||
|
@ -7242,9 +7254,8 @@ func (m *Document) ToRawInfo() interface{} {
|
|||
if len(m.Produces) != 0 {
|
||||
info = append(info, yaml.MapItem{Key: "produces", Value: m.Produces})
|
||||
}
|
||||
if m.Paths != nil {
|
||||
info = append(info, yaml.MapItem{Key: "paths", Value: m.Paths.ToRawInfo()})
|
||||
}
|
||||
// always include this required field.
|
||||
info = append(info, yaml.MapItem{Key: "paths", Value: m.Paths.ToRawInfo()})
|
||||
// &{Name:paths Type:Paths StringEnumValues:[] MapType: Repeated:false Pattern: Implicit:false Description:}
|
||||
if m.Definitions != nil {
|
||||
info = append(info, yaml.MapItem{Key: "definitions", Value: m.Definitions.ToRawInfo()})
|
||||
|
@ -7294,6 +7305,9 @@ func (m *Document) ToRawInfo() interface{} {
|
|||
// ToRawInfo returns a description of Examples suitable for JSON or YAML export.
|
||||
func (m *Examples) ToRawInfo() interface{} {
|
||||
info := yaml.MapSlice{}
|
||||
if m == nil {
|
||||
return info
|
||||
}
|
||||
if m.AdditionalProperties != nil {
|
||||
for _, item := range m.AdditionalProperties {
|
||||
info = append(info, yaml.MapItem{Key: item.Name, Value: item.Value.ToRawInfo()})
|
||||
|
@ -7306,12 +7320,14 @@ func (m *Examples) ToRawInfo() interface{} {
|
|||
// ToRawInfo returns a description of ExternalDocs suitable for JSON or YAML export.
|
||||
func (m *ExternalDocs) ToRawInfo() interface{} {
|
||||
info := yaml.MapSlice{}
|
||||
if m == nil {
|
||||
return info
|
||||
}
|
||||
if m.Description != "" {
|
||||
info = append(info, yaml.MapItem{Key: "description", Value: m.Description})
|
||||
}
|
||||
if m.Url != "" {
|
||||
info = append(info, yaml.MapItem{Key: "url", Value: m.Url})
|
||||
}
|
||||
// always include this required field.
|
||||
info = append(info, yaml.MapItem{Key: "url", Value: m.Url})
|
||||
if m.VendorExtension != nil {
|
||||
for _, item := range m.VendorExtension {
|
||||
info = append(info, yaml.MapItem{Key: item.Name, Value: item.Value.ToRawInfo()})
|
||||
|
@ -7324,6 +7340,9 @@ func (m *ExternalDocs) ToRawInfo() interface{} {
|
|||
// ToRawInfo returns a description of FileSchema suitable for JSON or YAML export.
|
||||
func (m *FileSchema) ToRawInfo() interface{} {
|
||||
info := yaml.MapSlice{}
|
||||
if m == nil {
|
||||
return info
|
||||
}
|
||||
if m.Format != "" {
|
||||
info = append(info, yaml.MapItem{Key: "format", Value: m.Format})
|
||||
}
|
||||
|
@ -7340,9 +7359,8 @@ func (m *FileSchema) ToRawInfo() interface{} {
|
|||
if len(m.Required) != 0 {
|
||||
info = append(info, yaml.MapItem{Key: "required", Value: m.Required})
|
||||
}
|
||||
if m.Type != "" {
|
||||
info = append(info, yaml.MapItem{Key: "type", Value: m.Type})
|
||||
}
|
||||
// always include this required field.
|
||||
info = append(info, yaml.MapItem{Key: "type", Value: m.Type})
|
||||
if m.ReadOnly != false {
|
||||
info = append(info, yaml.MapItem{Key: "readOnly", Value: m.ReadOnly})
|
||||
}
|
||||
|
@ -7366,6 +7384,9 @@ func (m *FileSchema) ToRawInfo() interface{} {
|
|||
// ToRawInfo returns a description of FormDataParameterSubSchema suitable for JSON or YAML export.
|
||||
func (m *FormDataParameterSubSchema) ToRawInfo() interface{} {
|
||||
info := yaml.MapSlice{}
|
||||
if m == nil {
|
||||
return info
|
||||
}
|
||||
if m.Required != false {
|
||||
info = append(info, yaml.MapItem{Key: "required", Value: m.Required})
|
||||
}
|
||||
|
@ -7451,9 +7472,11 @@ func (m *FormDataParameterSubSchema) ToRawInfo() interface{} {
|
|||
// ToRawInfo returns a description of Header suitable for JSON or YAML export.
|
||||
func (m *Header) ToRawInfo() interface{} {
|
||||
info := yaml.MapSlice{}
|
||||
if m.Type != "" {
|
||||
info = append(info, yaml.MapItem{Key: "type", Value: m.Type})
|
||||
if m == nil {
|
||||
return info
|
||||
}
|
||||
// always include this required field.
|
||||
info = append(info, yaml.MapItem{Key: "type", Value: m.Type})
|
||||
if m.Format != "" {
|
||||
info = append(info, yaml.MapItem{Key: "format", Value: m.Format})
|
||||
}
|
||||
|
@ -7524,6 +7547,9 @@ func (m *Header) ToRawInfo() interface{} {
|
|||
// ToRawInfo returns a description of HeaderParameterSubSchema suitable for JSON or YAML export.
|
||||
func (m *HeaderParameterSubSchema) ToRawInfo() interface{} {
|
||||
info := yaml.MapSlice{}
|
||||
if m == nil {
|
||||
return info
|
||||
}
|
||||
if m.Required != false {
|
||||
info = append(info, yaml.MapItem{Key: "required", Value: m.Required})
|
||||
}
|
||||
|
@ -7606,6 +7632,9 @@ func (m *HeaderParameterSubSchema) ToRawInfo() interface{} {
|
|||
// ToRawInfo returns a description of Headers suitable for JSON or YAML export.
|
||||
func (m *Headers) ToRawInfo() interface{} {
|
||||
info := yaml.MapSlice{}
|
||||
if m == nil {
|
||||
return info
|
||||
}
|
||||
if m.AdditionalProperties != nil {
|
||||
for _, item := range m.AdditionalProperties {
|
||||
info = append(info, yaml.MapItem{Key: item.Name, Value: item.Value.ToRawInfo()})
|
||||
|
@ -7618,12 +7647,13 @@ func (m *Headers) ToRawInfo() interface{} {
|
|||
// ToRawInfo returns a description of Info suitable for JSON or YAML export.
|
||||
func (m *Info) ToRawInfo() interface{} {
|
||||
info := yaml.MapSlice{}
|
||||
if m.Title != "" {
|
||||
info = append(info, yaml.MapItem{Key: "title", Value: m.Title})
|
||||
}
|
||||
if m.Version != "" {
|
||||
info = append(info, yaml.MapItem{Key: "version", Value: m.Version})
|
||||
if m == nil {
|
||||
return info
|
||||
}
|
||||
// always include this required field.
|
||||
info = append(info, yaml.MapItem{Key: "title", Value: m.Title})
|
||||
// always include this required field.
|
||||
info = append(info, yaml.MapItem{Key: "version", Value: m.Version})
|
||||
if m.Description != "" {
|
||||
info = append(info, yaml.MapItem{Key: "description", Value: m.Description})
|
||||
}
|
||||
|
@ -7650,6 +7680,9 @@ func (m *Info) ToRawInfo() interface{} {
|
|||
// ToRawInfo returns a description of ItemsItem suitable for JSON or YAML export.
|
||||
func (m *ItemsItem) ToRawInfo() interface{} {
|
||||
info := yaml.MapSlice{}
|
||||
if m == nil {
|
||||
return info
|
||||
}
|
||||
if len(m.Schema) != 0 {
|
||||
items := make([]interface{}, 0)
|
||||
for _, item := range m.Schema {
|
||||
|
@ -7664,9 +7697,11 @@ func (m *ItemsItem) ToRawInfo() interface{} {
|
|||
// ToRawInfo returns a description of JsonReference suitable for JSON or YAML export.
|
||||
func (m *JsonReference) ToRawInfo() interface{} {
|
||||
info := yaml.MapSlice{}
|
||||
if m.XRef != "" {
|
||||
info = append(info, yaml.MapItem{Key: "$ref", Value: m.XRef})
|
||||
if m == nil {
|
||||
return info
|
||||
}
|
||||
// always include this required field.
|
||||
info = append(info, yaml.MapItem{Key: "$ref", Value: m.XRef})
|
||||
if m.Description != "" {
|
||||
info = append(info, yaml.MapItem{Key: "description", Value: m.Description})
|
||||
}
|
||||
|
@ -7676,9 +7711,11 @@ func (m *JsonReference) ToRawInfo() interface{} {
|
|||
// ToRawInfo returns a description of License suitable for JSON or YAML export.
|
||||
func (m *License) ToRawInfo() interface{} {
|
||||
info := yaml.MapSlice{}
|
||||
if m.Name != "" {
|
||||
info = append(info, yaml.MapItem{Key: "name", Value: m.Name})
|
||||
if m == nil {
|
||||
return info
|
||||
}
|
||||
// always include this required field.
|
||||
info = append(info, yaml.MapItem{Key: "name", Value: m.Name})
|
||||
if m.Url != "" {
|
||||
info = append(info, yaml.MapItem{Key: "url", Value: m.Url})
|
||||
}
|
||||
|
@ -7694,6 +7731,9 @@ func (m *License) ToRawInfo() interface{} {
|
|||
// ToRawInfo returns a description of NamedAny suitable for JSON or YAML export.
|
||||
func (m *NamedAny) ToRawInfo() interface{} {
|
||||
info := yaml.MapSlice{}
|
||||
if m == nil {
|
||||
return info
|
||||
}
|
||||
if m.Name != "" {
|
||||
info = append(info, yaml.MapItem{Key: "name", Value: m.Name})
|
||||
}
|
||||
|
@ -7704,6 +7744,9 @@ func (m *NamedAny) ToRawInfo() interface{} {
|
|||
// ToRawInfo returns a description of NamedHeader suitable for JSON or YAML export.
|
||||
func (m *NamedHeader) ToRawInfo() interface{} {
|
||||
info := yaml.MapSlice{}
|
||||
if m == nil {
|
||||
return info
|
||||
}
|
||||
if m.Name != "" {
|
||||
info = append(info, yaml.MapItem{Key: "name", Value: m.Name})
|
||||
}
|
||||
|
@ -7714,6 +7757,9 @@ func (m *NamedHeader) ToRawInfo() interface{} {
|
|||
// ToRawInfo returns a description of NamedParameter suitable for JSON or YAML export.
|
||||
func (m *NamedParameter) ToRawInfo() interface{} {
|
||||
info := yaml.MapSlice{}
|
||||
if m == nil {
|
||||
return info
|
||||
}
|
||||
if m.Name != "" {
|
||||
info = append(info, yaml.MapItem{Key: "name", Value: m.Name})
|
||||
}
|
||||
|
@ -7724,6 +7770,9 @@ func (m *NamedParameter) ToRawInfo() interface{} {
|
|||
// ToRawInfo returns a description of NamedPathItem suitable for JSON or YAML export.
|
||||
func (m *NamedPathItem) ToRawInfo() interface{} {
|
||||
info := yaml.MapSlice{}
|
||||
if m == nil {
|
||||
return info
|
||||
}
|
||||
if m.Name != "" {
|
||||
info = append(info, yaml.MapItem{Key: "name", Value: m.Name})
|
||||
}
|
||||
|
@ -7734,6 +7783,9 @@ func (m *NamedPathItem) ToRawInfo() interface{} {
|
|||
// ToRawInfo returns a description of NamedResponse suitable for JSON or YAML export.
|
||||
func (m *NamedResponse) ToRawInfo() interface{} {
|
||||
info := yaml.MapSlice{}
|
||||
if m == nil {
|
||||
return info
|
||||
}
|
||||
if m.Name != "" {
|
||||
info = append(info, yaml.MapItem{Key: "name", Value: m.Name})
|
||||
}
|
||||
|
@ -7744,6 +7796,9 @@ func (m *NamedResponse) ToRawInfo() interface{} {
|
|||
// ToRawInfo returns a description of NamedResponseValue suitable for JSON or YAML export.
|
||||
func (m *NamedResponseValue) ToRawInfo() interface{} {
|
||||
info := yaml.MapSlice{}
|
||||
if m == nil {
|
||||
return info
|
||||
}
|
||||
if m.Name != "" {
|
||||
info = append(info, yaml.MapItem{Key: "name", Value: m.Name})
|
||||
}
|
||||
|
@ -7754,6 +7809,9 @@ func (m *NamedResponseValue) ToRawInfo() interface{} {
|
|||
// ToRawInfo returns a description of NamedSchema suitable for JSON or YAML export.
|
||||
func (m *NamedSchema) ToRawInfo() interface{} {
|
||||
info := yaml.MapSlice{}
|
||||
if m == nil {
|
||||
return info
|
||||
}
|
||||
if m.Name != "" {
|
||||
info = append(info, yaml.MapItem{Key: "name", Value: m.Name})
|
||||
}
|
||||
|
@ -7764,6 +7822,9 @@ func (m *NamedSchema) ToRawInfo() interface{} {
|
|||
// ToRawInfo returns a description of NamedSecurityDefinitionsItem suitable for JSON or YAML export.
|
||||
func (m *NamedSecurityDefinitionsItem) ToRawInfo() interface{} {
|
||||
info := yaml.MapSlice{}
|
||||
if m == nil {
|
||||
return info
|
||||
}
|
||||
if m.Name != "" {
|
||||
info = append(info, yaml.MapItem{Key: "name", Value: m.Name})
|
||||
}
|
||||
|
@ -7774,6 +7835,9 @@ func (m *NamedSecurityDefinitionsItem) ToRawInfo() interface{} {
|
|||
// ToRawInfo returns a description of NamedString suitable for JSON or YAML export.
|
||||
func (m *NamedString) ToRawInfo() interface{} {
|
||||
info := yaml.MapSlice{}
|
||||
if m == nil {
|
||||
return info
|
||||
}
|
||||
if m.Name != "" {
|
||||
info = append(info, yaml.MapItem{Key: "name", Value: m.Name})
|
||||
}
|
||||
|
@ -7786,6 +7850,9 @@ func (m *NamedString) ToRawInfo() interface{} {
|
|||
// ToRawInfo returns a description of NamedStringArray suitable for JSON or YAML export.
|
||||
func (m *NamedStringArray) ToRawInfo() interface{} {
|
||||
info := yaml.MapSlice{}
|
||||
if m == nil {
|
||||
return info
|
||||
}
|
||||
if m.Name != "" {
|
||||
info = append(info, yaml.MapItem{Key: "name", Value: m.Name})
|
||||
}
|
||||
|
@ -7823,22 +7890,21 @@ func (m *NonBodyParameter) ToRawInfo() interface{} {
|
|||
// ToRawInfo returns a description of Oauth2AccessCodeSecurity suitable for JSON or YAML export.
|
||||
func (m *Oauth2AccessCodeSecurity) ToRawInfo() interface{} {
|
||||
info := yaml.MapSlice{}
|
||||
if m.Type != "" {
|
||||
info = append(info, yaml.MapItem{Key: "type", Value: m.Type})
|
||||
}
|
||||
if m.Flow != "" {
|
||||
info = append(info, yaml.MapItem{Key: "flow", Value: m.Flow})
|
||||
if m == nil {
|
||||
return info
|
||||
}
|
||||
// always include this required field.
|
||||
info = append(info, yaml.MapItem{Key: "type", Value: m.Type})
|
||||
// always include this required field.
|
||||
info = append(info, yaml.MapItem{Key: "flow", Value: m.Flow})
|
||||
if m.Scopes != nil {
|
||||
info = append(info, yaml.MapItem{Key: "scopes", Value: m.Scopes.ToRawInfo()})
|
||||
}
|
||||
// &{Name:scopes Type:Oauth2Scopes StringEnumValues:[] MapType: Repeated:false Pattern: Implicit:false Description:}
|
||||
if m.AuthorizationUrl != "" {
|
||||
info = append(info, yaml.MapItem{Key: "authorizationUrl", Value: m.AuthorizationUrl})
|
||||
}
|
||||
if m.TokenUrl != "" {
|
||||
info = append(info, yaml.MapItem{Key: "tokenUrl", Value: m.TokenUrl})
|
||||
}
|
||||
// always include this required field.
|
||||
info = append(info, yaml.MapItem{Key: "authorizationUrl", Value: m.AuthorizationUrl})
|
||||
// always include this required field.
|
||||
info = append(info, yaml.MapItem{Key: "tokenUrl", Value: m.TokenUrl})
|
||||
if m.Description != "" {
|
||||
info = append(info, yaml.MapItem{Key: "description", Value: m.Description})
|
||||
}
|
||||
|
@ -7854,19 +7920,19 @@ func (m *Oauth2AccessCodeSecurity) ToRawInfo() interface{} {
|
|||
// ToRawInfo returns a description of Oauth2ApplicationSecurity suitable for JSON or YAML export.
|
||||
func (m *Oauth2ApplicationSecurity) ToRawInfo() interface{} {
|
||||
info := yaml.MapSlice{}
|
||||
if m.Type != "" {
|
||||
info = append(info, yaml.MapItem{Key: "type", Value: m.Type})
|
||||
}
|
||||
if m.Flow != "" {
|
||||
info = append(info, yaml.MapItem{Key: "flow", Value: m.Flow})
|
||||
if m == nil {
|
||||
return info
|
||||
}
|
||||
// always include this required field.
|
||||
info = append(info, yaml.MapItem{Key: "type", Value: m.Type})
|
||||
// always include this required field.
|
||||
info = append(info, yaml.MapItem{Key: "flow", Value: m.Flow})
|
||||
if m.Scopes != nil {
|
||||
info = append(info, yaml.MapItem{Key: "scopes", Value: m.Scopes.ToRawInfo()})
|
||||
}
|
||||
// &{Name:scopes Type:Oauth2Scopes StringEnumValues:[] MapType: Repeated:false Pattern: Implicit:false Description:}
|
||||
if m.TokenUrl != "" {
|
||||
info = append(info, yaml.MapItem{Key: "tokenUrl", Value: m.TokenUrl})
|
||||
}
|
||||
// always include this required field.
|
||||
info = append(info, yaml.MapItem{Key: "tokenUrl", Value: m.TokenUrl})
|
||||
if m.Description != "" {
|
||||
info = append(info, yaml.MapItem{Key: "description", Value: m.Description})
|
||||
}
|
||||
|
@ -7882,19 +7948,19 @@ func (m *Oauth2ApplicationSecurity) ToRawInfo() interface{} {
|
|||
// ToRawInfo returns a description of Oauth2ImplicitSecurity suitable for JSON or YAML export.
|
||||
func (m *Oauth2ImplicitSecurity) ToRawInfo() interface{} {
|
||||
info := yaml.MapSlice{}
|
||||
if m.Type != "" {
|
||||
info = append(info, yaml.MapItem{Key: "type", Value: m.Type})
|
||||
}
|
||||
if m.Flow != "" {
|
||||
info = append(info, yaml.MapItem{Key: "flow", Value: m.Flow})
|
||||
if m == nil {
|
||||
return info
|
||||
}
|
||||
// always include this required field.
|
||||
info = append(info, yaml.MapItem{Key: "type", Value: m.Type})
|
||||
// always include this required field.
|
||||
info = append(info, yaml.MapItem{Key: "flow", Value: m.Flow})
|
||||
if m.Scopes != nil {
|
||||
info = append(info, yaml.MapItem{Key: "scopes", Value: m.Scopes.ToRawInfo()})
|
||||
}
|
||||
// &{Name:scopes Type:Oauth2Scopes StringEnumValues:[] MapType: Repeated:false Pattern: Implicit:false Description:}
|
||||
if m.AuthorizationUrl != "" {
|
||||
info = append(info, yaml.MapItem{Key: "authorizationUrl", Value: m.AuthorizationUrl})
|
||||
}
|
||||
// always include this required field.
|
||||
info = append(info, yaml.MapItem{Key: "authorizationUrl", Value: m.AuthorizationUrl})
|
||||
if m.Description != "" {
|
||||
info = append(info, yaml.MapItem{Key: "description", Value: m.Description})
|
||||
}
|
||||
|
@ -7910,19 +7976,19 @@ func (m *Oauth2ImplicitSecurity) ToRawInfo() interface{} {
|
|||
// ToRawInfo returns a description of Oauth2PasswordSecurity suitable for JSON or YAML export.
|
||||
func (m *Oauth2PasswordSecurity) ToRawInfo() interface{} {
|
||||
info := yaml.MapSlice{}
|
||||
if m.Type != "" {
|
||||
info = append(info, yaml.MapItem{Key: "type", Value: m.Type})
|
||||
}
|
||||
if m.Flow != "" {
|
||||
info = append(info, yaml.MapItem{Key: "flow", Value: m.Flow})
|
||||
if m == nil {
|
||||
return info
|
||||
}
|
||||
// always include this required field.
|
||||
info = append(info, yaml.MapItem{Key: "type", Value: m.Type})
|
||||
// always include this required field.
|
||||
info = append(info, yaml.MapItem{Key: "flow", Value: m.Flow})
|
||||
if m.Scopes != nil {
|
||||
info = append(info, yaml.MapItem{Key: "scopes", Value: m.Scopes.ToRawInfo()})
|
||||
}
|
||||
// &{Name:scopes Type:Oauth2Scopes StringEnumValues:[] MapType: Repeated:false Pattern: Implicit:false Description:}
|
||||
if m.TokenUrl != "" {
|
||||
info = append(info, yaml.MapItem{Key: "tokenUrl", Value: m.TokenUrl})
|
||||
}
|
||||
// always include this required field.
|
||||
info = append(info, yaml.MapItem{Key: "tokenUrl", Value: m.TokenUrl})
|
||||
if m.Description != "" {
|
||||
info = append(info, yaml.MapItem{Key: "description", Value: m.Description})
|
||||
}
|
||||
|
@ -7938,6 +8004,9 @@ func (m *Oauth2PasswordSecurity) ToRawInfo() interface{} {
|
|||
// ToRawInfo returns a description of Oauth2Scopes suitable for JSON or YAML export.
|
||||
func (m *Oauth2Scopes) ToRawInfo() interface{} {
|
||||
info := yaml.MapSlice{}
|
||||
if m == nil {
|
||||
return info
|
||||
}
|
||||
// &{Name:additionalProperties Type:NamedString StringEnumValues:[] MapType:string Repeated:true Pattern: Implicit:true Description:}
|
||||
return info
|
||||
}
|
||||
|
@ -7945,6 +8014,9 @@ func (m *Oauth2Scopes) ToRawInfo() interface{} {
|
|||
// ToRawInfo returns a description of Operation suitable for JSON or YAML export.
|
||||
func (m *Operation) ToRawInfo() interface{} {
|
||||
info := yaml.MapSlice{}
|
||||
if m == nil {
|
||||
return info
|
||||
}
|
||||
if len(m.Tags) != 0 {
|
||||
info = append(info, yaml.MapItem{Key: "tags", Value: m.Tags})
|
||||
}
|
||||
|
@ -7975,9 +8047,8 @@ func (m *Operation) ToRawInfo() interface{} {
|
|||
info = append(info, yaml.MapItem{Key: "parameters", Value: items})
|
||||
}
|
||||
// &{Name:parameters Type:ParametersItem StringEnumValues:[] MapType: Repeated:true Pattern: Implicit:false Description:The parameters needed to send a valid API call.}
|
||||
if m.Responses != nil {
|
||||
info = append(info, yaml.MapItem{Key: "responses", Value: m.Responses.ToRawInfo()})
|
||||
}
|
||||
// always include this required field.
|
||||
info = append(info, yaml.MapItem{Key: "responses", Value: m.Responses.ToRawInfo()})
|
||||
// &{Name:responses Type:Responses StringEnumValues:[] MapType: Repeated:false Pattern: Implicit:false Description:}
|
||||
if len(m.Schemes) != 0 {
|
||||
info = append(info, yaml.MapItem{Key: "schemes", Value: m.Schemes})
|
||||
|
@ -8022,6 +8093,9 @@ func (m *Parameter) ToRawInfo() interface{} {
|
|||
// ToRawInfo returns a description of ParameterDefinitions suitable for JSON or YAML export.
|
||||
func (m *ParameterDefinitions) ToRawInfo() interface{} {
|
||||
info := yaml.MapSlice{}
|
||||
if m == nil {
|
||||
return info
|
||||
}
|
||||
if m.AdditionalProperties != nil {
|
||||
for _, item := range m.AdditionalProperties {
|
||||
info = append(info, yaml.MapItem{Key: item.Name, Value: item.Value.ToRawInfo()})
|
||||
|
@ -8051,6 +8125,9 @@ func (m *ParametersItem) ToRawInfo() interface{} {
|
|||
// ToRawInfo returns a description of PathItem suitable for JSON or YAML export.
|
||||
func (m *PathItem) ToRawInfo() interface{} {
|
||||
info := yaml.MapSlice{}
|
||||
if m == nil {
|
||||
return info
|
||||
}
|
||||
if m.XRef != "" {
|
||||
info = append(info, yaml.MapItem{Key: "$ref", Value: m.XRef})
|
||||
}
|
||||
|
@ -8102,9 +8179,11 @@ func (m *PathItem) ToRawInfo() interface{} {
|
|||
// ToRawInfo returns a description of PathParameterSubSchema suitable for JSON or YAML export.
|
||||
func (m *PathParameterSubSchema) ToRawInfo() interface{} {
|
||||
info := yaml.MapSlice{}
|
||||
if m.Required != false {
|
||||
info = append(info, yaml.MapItem{Key: "required", Value: m.Required})
|
||||
if m == nil {
|
||||
return info
|
||||
}
|
||||
// always include this required field.
|
||||
info = append(info, yaml.MapItem{Key: "required", Value: m.Required})
|
||||
if m.In != "" {
|
||||
info = append(info, yaml.MapItem{Key: "in", Value: m.In})
|
||||
}
|
||||
|
@ -8184,6 +8263,9 @@ func (m *PathParameterSubSchema) ToRawInfo() interface{} {
|
|||
// ToRawInfo returns a description of Paths suitable for JSON or YAML export.
|
||||
func (m *Paths) ToRawInfo() interface{} {
|
||||
info := yaml.MapSlice{}
|
||||
if m == nil {
|
||||
return info
|
||||
}
|
||||
if m.VendorExtension != nil {
|
||||
for _, item := range m.VendorExtension {
|
||||
info = append(info, yaml.MapItem{Key: item.Name, Value: item.Value.ToRawInfo()})
|
||||
|
@ -8202,6 +8284,9 @@ func (m *Paths) ToRawInfo() interface{} {
|
|||
// ToRawInfo returns a description of PrimitivesItems suitable for JSON or YAML export.
|
||||
func (m *PrimitivesItems) ToRawInfo() interface{} {
|
||||
info := yaml.MapSlice{}
|
||||
if m == nil {
|
||||
return info
|
||||
}
|
||||
if m.Type != "" {
|
||||
info = append(info, yaml.MapItem{Key: "type", Value: m.Type})
|
||||
}
|
||||
|
@ -8272,6 +8357,9 @@ func (m *PrimitivesItems) ToRawInfo() interface{} {
|
|||
// ToRawInfo returns a description of Properties suitable for JSON or YAML export.
|
||||
func (m *Properties) ToRawInfo() interface{} {
|
||||
info := yaml.MapSlice{}
|
||||
if m == nil {
|
||||
return info
|
||||
}
|
||||
if m.AdditionalProperties != nil {
|
||||
for _, item := range m.AdditionalProperties {
|
||||
info = append(info, yaml.MapItem{Key: item.Name, Value: item.Value.ToRawInfo()})
|
||||
|
@ -8284,6 +8372,9 @@ func (m *Properties) ToRawInfo() interface{} {
|
|||
// ToRawInfo returns a description of QueryParameterSubSchema suitable for JSON or YAML export.
|
||||
func (m *QueryParameterSubSchema) ToRawInfo() interface{} {
|
||||
info := yaml.MapSlice{}
|
||||
if m == nil {
|
||||
return info
|
||||
}
|
||||
if m.Required != false {
|
||||
info = append(info, yaml.MapItem{Key: "required", Value: m.Required})
|
||||
}
|
||||
|
@ -8369,9 +8460,11 @@ func (m *QueryParameterSubSchema) ToRawInfo() interface{} {
|
|||
// ToRawInfo returns a description of Response suitable for JSON or YAML export.
|
||||
func (m *Response) ToRawInfo() interface{} {
|
||||
info := yaml.MapSlice{}
|
||||
if m.Description != "" {
|
||||
info = append(info, yaml.MapItem{Key: "description", Value: m.Description})
|
||||
if m == nil {
|
||||
return info
|
||||
}
|
||||
// always include this required field.
|
||||
info = append(info, yaml.MapItem{Key: "description", Value: m.Description})
|
||||
if m.Schema != nil {
|
||||
info = append(info, yaml.MapItem{Key: "schema", Value: m.Schema.ToRawInfo()})
|
||||
}
|
||||
|
@ -8396,6 +8489,9 @@ func (m *Response) ToRawInfo() interface{} {
|
|||
// ToRawInfo returns a description of ResponseDefinitions suitable for JSON or YAML export.
|
||||
func (m *ResponseDefinitions) ToRawInfo() interface{} {
|
||||
info := yaml.MapSlice{}
|
||||
if m == nil {
|
||||
return info
|
||||
}
|
||||
if m.AdditionalProperties != nil {
|
||||
for _, item := range m.AdditionalProperties {
|
||||
info = append(info, yaml.MapItem{Key: item.Name, Value: item.Value.ToRawInfo()})
|
||||
|
@ -8425,6 +8521,9 @@ func (m *ResponseValue) ToRawInfo() interface{} {
|
|||
// ToRawInfo returns a description of Responses suitable for JSON or YAML export.
|
||||
func (m *Responses) ToRawInfo() interface{} {
|
||||
info := yaml.MapSlice{}
|
||||
if m == nil {
|
||||
return info
|
||||
}
|
||||
if m.ResponseCode != nil {
|
||||
for _, item := range m.ResponseCode {
|
||||
info = append(info, yaml.MapItem{Key: item.Name, Value: item.Value.ToRawInfo()})
|
||||
|
@ -8443,6 +8542,9 @@ func (m *Responses) ToRawInfo() interface{} {
|
|||
// ToRawInfo returns a description of Schema suitable for JSON or YAML export.
|
||||
func (m *Schema) ToRawInfo() interface{} {
|
||||
info := yaml.MapSlice{}
|
||||
if m == nil {
|
||||
return info
|
||||
}
|
||||
if m.XRef != "" {
|
||||
info = append(info, yaml.MapItem{Key: "$ref", Value: m.XRef})
|
||||
}
|
||||
|
@ -8588,6 +8690,9 @@ func (m *SchemaItem) ToRawInfo() interface{} {
|
|||
// ToRawInfo returns a description of SecurityDefinitions suitable for JSON or YAML export.
|
||||
func (m *SecurityDefinitions) ToRawInfo() interface{} {
|
||||
info := yaml.MapSlice{}
|
||||
if m == nil {
|
||||
return info
|
||||
}
|
||||
if m.AdditionalProperties != nil {
|
||||
for _, item := range m.AdditionalProperties {
|
||||
info = append(info, yaml.MapItem{Key: item.Name, Value: item.Value.ToRawInfo()})
|
||||
|
@ -8637,6 +8742,9 @@ func (m *SecurityDefinitionsItem) ToRawInfo() interface{} {
|
|||
// ToRawInfo returns a description of SecurityRequirement suitable for JSON or YAML export.
|
||||
func (m *SecurityRequirement) ToRawInfo() interface{} {
|
||||
info := yaml.MapSlice{}
|
||||
if m == nil {
|
||||
return info
|
||||
}
|
||||
if m.AdditionalProperties != nil {
|
||||
for _, item := range m.AdditionalProperties {
|
||||
info = append(info, yaml.MapItem{Key: item.Name, Value: item.Value.ToRawInfo()})
|
||||
|
@ -8654,9 +8762,11 @@ func (m *StringArray) ToRawInfo() interface{} {
|
|||
// ToRawInfo returns a description of Tag suitable for JSON or YAML export.
|
||||
func (m *Tag) ToRawInfo() interface{} {
|
||||
info := yaml.MapSlice{}
|
||||
if m.Name != "" {
|
||||
info = append(info, yaml.MapItem{Key: "name", Value: m.Name})
|
||||
if m == nil {
|
||||
return info
|
||||
}
|
||||
// always include this required field.
|
||||
info = append(info, yaml.MapItem{Key: "name", Value: m.Name})
|
||||
if m.Description != "" {
|
||||
info = append(info, yaml.MapItem{Key: "description", Value: m.Description})
|
||||
}
|
||||
|
@ -8676,6 +8786,9 @@ func (m *Tag) ToRawInfo() interface{} {
|
|||
// ToRawInfo returns a description of TypeItem suitable for JSON or YAML export.
|
||||
func (m *TypeItem) ToRawInfo() interface{} {
|
||||
info := yaml.MapSlice{}
|
||||
if m == nil {
|
||||
return info
|
||||
}
|
||||
if len(m.Value) != 0 {
|
||||
info = append(info, yaml.MapItem{Key: "value", Value: m.Value})
|
||||
}
|
||||
|
@ -8685,6 +8798,9 @@ func (m *TypeItem) ToRawInfo() interface{} {
|
|||
// ToRawInfo returns a description of VendorExtension suitable for JSON or YAML export.
|
||||
func (m *VendorExtension) ToRawInfo() interface{} {
|
||||
info := yaml.MapSlice{}
|
||||
if m == nil {
|
||||
return info
|
||||
}
|
||||
if m.AdditionalProperties != nil {
|
||||
for _, item := range m.AdditionalProperties {
|
||||
info = append(info, yaml.MapItem{Key: item.Name, Value: item.Value.ToRawInfo()})
|
||||
|
@ -8697,6 +8813,9 @@ func (m *VendorExtension) ToRawInfo() interface{} {
|
|||
// ToRawInfo returns a description of Xml suitable for JSON or YAML export.
|
||||
func (m *Xml) ToRawInfo() interface{} {
|
||||
info := yaml.MapSlice{}
|
||||
if m == nil {
|
||||
return info
|
||||
}
|
||||
if m.Name != "" {
|
||||
info = append(info, yaml.MapItem{Key: "name", Value: m.Name})
|
||||
}
|
||||
|
|
262
vendor/github.com/googleapis/gnostic/OpenAPIv3/OpenAPIv3.go
generated
vendored
262
vendor/github.com/googleapis/gnostic/OpenAPIv3/OpenAPIv3.go
generated
vendored
|
@ -6714,6 +6714,9 @@ func (m *AnyOrExpression) ToRawInfo() interface{} {
|
|||
// ToRawInfo returns a description of AnysOrExpressions suitable for JSON or YAML export.
|
||||
func (m *AnysOrExpressions) ToRawInfo() interface{} {
|
||||
info := yaml.MapSlice{}
|
||||
if m == nil {
|
||||
return info
|
||||
}
|
||||
if m.AdditionalProperties != nil {
|
||||
for _, item := range m.AdditionalProperties {
|
||||
info = append(info, yaml.MapItem{Key: item.Name, Value: item.Value.ToRawInfo()})
|
||||
|
@ -6726,6 +6729,9 @@ func (m *AnysOrExpressions) ToRawInfo() interface{} {
|
|||
// ToRawInfo returns a description of Callback suitable for JSON or YAML export.
|
||||
func (m *Callback) ToRawInfo() interface{} {
|
||||
info := yaml.MapSlice{}
|
||||
if m == nil {
|
||||
return info
|
||||
}
|
||||
if m.Path != nil {
|
||||
for _, item := range m.Path {
|
||||
info = append(info, yaml.MapItem{Key: item.Name, Value: item.Value.ToRawInfo()})
|
||||
|
@ -6761,6 +6767,9 @@ func (m *CallbackOrReference) ToRawInfo() interface{} {
|
|||
// ToRawInfo returns a description of CallbacksOrReferences suitable for JSON or YAML export.
|
||||
func (m *CallbacksOrReferences) ToRawInfo() interface{} {
|
||||
info := yaml.MapSlice{}
|
||||
if m == nil {
|
||||
return info
|
||||
}
|
||||
if m.AdditionalProperties != nil {
|
||||
for _, item := range m.AdditionalProperties {
|
||||
info = append(info, yaml.MapItem{Key: item.Name, Value: item.Value.ToRawInfo()})
|
||||
|
@ -6773,6 +6782,9 @@ func (m *CallbacksOrReferences) ToRawInfo() interface{} {
|
|||
// ToRawInfo returns a description of Components suitable for JSON or YAML export.
|
||||
func (m *Components) ToRawInfo() interface{} {
|
||||
info := yaml.MapSlice{}
|
||||
if m == nil {
|
||||
return info
|
||||
}
|
||||
if m.Schemas != nil {
|
||||
info = append(info, yaml.MapItem{Key: "schemas", Value: m.Schemas.ToRawInfo()})
|
||||
}
|
||||
|
@ -6821,6 +6833,9 @@ func (m *Components) ToRawInfo() interface{} {
|
|||
// ToRawInfo returns a description of Contact suitable for JSON or YAML export.
|
||||
func (m *Contact) ToRawInfo() interface{} {
|
||||
info := yaml.MapSlice{}
|
||||
if m == nil {
|
||||
return info
|
||||
}
|
||||
if m.Name != "" {
|
||||
info = append(info, yaml.MapItem{Key: "name", Value: m.Name})
|
||||
}
|
||||
|
@ -6861,9 +6876,11 @@ func (m *DefaultType) ToRawInfo() interface{} {
|
|||
// ToRawInfo returns a description of Discriminator suitable for JSON or YAML export.
|
||||
func (m *Discriminator) ToRawInfo() interface{} {
|
||||
info := yaml.MapSlice{}
|
||||
if m.PropertyName != "" {
|
||||
info = append(info, yaml.MapItem{Key: "propertyName", Value: m.PropertyName})
|
||||
if m == nil {
|
||||
return info
|
||||
}
|
||||
// always include this required field.
|
||||
info = append(info, yaml.MapItem{Key: "propertyName", Value: m.PropertyName})
|
||||
if m.Mapping != nil {
|
||||
info = append(info, yaml.MapItem{Key: "mapping", Value: m.Mapping.ToRawInfo()})
|
||||
}
|
||||
|
@ -6874,12 +6891,13 @@ func (m *Discriminator) ToRawInfo() interface{} {
|
|||
// ToRawInfo returns a description of Document suitable for JSON or YAML export.
|
||||
func (m *Document) ToRawInfo() interface{} {
|
||||
info := yaml.MapSlice{}
|
||||
if m.Openapi != "" {
|
||||
info = append(info, yaml.MapItem{Key: "openapi", Value: m.Openapi})
|
||||
}
|
||||
if m.Info != nil {
|
||||
info = append(info, yaml.MapItem{Key: "info", Value: m.Info.ToRawInfo()})
|
||||
if m == nil {
|
||||
return info
|
||||
}
|
||||
// always include this required field.
|
||||
info = append(info, yaml.MapItem{Key: "openapi", Value: m.Openapi})
|
||||
// always include this required field.
|
||||
info = append(info, yaml.MapItem{Key: "info", Value: m.Info.ToRawInfo()})
|
||||
// &{Name:info Type:Info StringEnumValues:[] MapType: Repeated:false Pattern: Implicit:false Description:}
|
||||
if len(m.Servers) != 0 {
|
||||
items := make([]interface{}, 0)
|
||||
|
@ -6889,9 +6907,8 @@ func (m *Document) ToRawInfo() interface{} {
|
|||
info = append(info, yaml.MapItem{Key: "servers", Value: items})
|
||||
}
|
||||
// &{Name:servers Type:Server StringEnumValues:[] MapType: Repeated:true Pattern: Implicit:false Description:}
|
||||
if m.Paths != nil {
|
||||
info = append(info, yaml.MapItem{Key: "paths", Value: m.Paths.ToRawInfo()})
|
||||
}
|
||||
// always include this required field.
|
||||
info = append(info, yaml.MapItem{Key: "paths", Value: m.Paths.ToRawInfo()})
|
||||
// &{Name:paths Type:Paths StringEnumValues:[] MapType: Repeated:false Pattern: Implicit:false Description:}
|
||||
if m.Components != nil {
|
||||
info = append(info, yaml.MapItem{Key: "components", Value: m.Components.ToRawInfo()})
|
||||
|
@ -6929,6 +6946,9 @@ func (m *Document) ToRawInfo() interface{} {
|
|||
// ToRawInfo returns a description of Encoding suitable for JSON or YAML export.
|
||||
func (m *Encoding) ToRawInfo() interface{} {
|
||||
info := yaml.MapSlice{}
|
||||
if m == nil {
|
||||
return info
|
||||
}
|
||||
if m.ContentType != "" {
|
||||
info = append(info, yaml.MapItem{Key: "contentType", Value: m.ContentType})
|
||||
}
|
||||
|
@ -6957,6 +6977,9 @@ func (m *Encoding) ToRawInfo() interface{} {
|
|||
// ToRawInfo returns a description of Encodings suitable for JSON or YAML export.
|
||||
func (m *Encodings) ToRawInfo() interface{} {
|
||||
info := yaml.MapSlice{}
|
||||
if m == nil {
|
||||
return info
|
||||
}
|
||||
if m.AdditionalProperties != nil {
|
||||
for _, item := range m.AdditionalProperties {
|
||||
info = append(info, yaml.MapItem{Key: item.Name, Value: item.Value.ToRawInfo()})
|
||||
|
@ -6969,6 +6992,9 @@ func (m *Encodings) ToRawInfo() interface{} {
|
|||
// ToRawInfo returns a description of Example suitable for JSON or YAML export.
|
||||
func (m *Example) ToRawInfo() interface{} {
|
||||
info := yaml.MapSlice{}
|
||||
if m == nil {
|
||||
return info
|
||||
}
|
||||
if m.Summary != "" {
|
||||
info = append(info, yaml.MapItem{Key: "summary", Value: m.Summary})
|
||||
}
|
||||
|
@ -7008,6 +7034,9 @@ func (m *ExampleOrReference) ToRawInfo() interface{} {
|
|||
// ToRawInfo returns a description of ExamplesOrReferences suitable for JSON or YAML export.
|
||||
func (m *ExamplesOrReferences) ToRawInfo() interface{} {
|
||||
info := yaml.MapSlice{}
|
||||
if m == nil {
|
||||
return info
|
||||
}
|
||||
if m.AdditionalProperties != nil {
|
||||
for _, item := range m.AdditionalProperties {
|
||||
info = append(info, yaml.MapItem{Key: item.Name, Value: item.Value.ToRawInfo()})
|
||||
|
@ -7020,6 +7049,9 @@ func (m *ExamplesOrReferences) ToRawInfo() interface{} {
|
|||
// ToRawInfo returns a description of Expression suitable for JSON or YAML export.
|
||||
func (m *Expression) ToRawInfo() interface{} {
|
||||
info := yaml.MapSlice{}
|
||||
if m == nil {
|
||||
return info
|
||||
}
|
||||
if m.AdditionalProperties != nil {
|
||||
for _, item := range m.AdditionalProperties {
|
||||
info = append(info, yaml.MapItem{Key: item.Name, Value: item.Value.ToRawInfo()})
|
||||
|
@ -7032,12 +7064,14 @@ func (m *Expression) ToRawInfo() interface{} {
|
|||
// ToRawInfo returns a description of ExternalDocs suitable for JSON or YAML export.
|
||||
func (m *ExternalDocs) ToRawInfo() interface{} {
|
||||
info := yaml.MapSlice{}
|
||||
if m == nil {
|
||||
return info
|
||||
}
|
||||
if m.Description != "" {
|
||||
info = append(info, yaml.MapItem{Key: "description", Value: m.Description})
|
||||
}
|
||||
if m.Url != "" {
|
||||
info = append(info, yaml.MapItem{Key: "url", Value: m.Url})
|
||||
}
|
||||
// always include this required field.
|
||||
info = append(info, yaml.MapItem{Key: "url", Value: m.Url})
|
||||
if m.SpecificationExtension != nil {
|
||||
for _, item := range m.SpecificationExtension {
|
||||
info = append(info, yaml.MapItem{Key: item.Name, Value: item.Value.ToRawInfo()})
|
||||
|
@ -7050,6 +7084,9 @@ func (m *ExternalDocs) ToRawInfo() interface{} {
|
|||
// ToRawInfo returns a description of Header suitable for JSON or YAML export.
|
||||
func (m *Header) ToRawInfo() interface{} {
|
||||
info := yaml.MapSlice{}
|
||||
if m == nil {
|
||||
return info
|
||||
}
|
||||
if m.Description != "" {
|
||||
info = append(info, yaml.MapItem{Key: "description", Value: m.Description})
|
||||
}
|
||||
|
@ -7116,6 +7153,9 @@ func (m *HeaderOrReference) ToRawInfo() interface{} {
|
|||
// ToRawInfo returns a description of HeadersOrReferences suitable for JSON or YAML export.
|
||||
func (m *HeadersOrReferences) ToRawInfo() interface{} {
|
||||
info := yaml.MapSlice{}
|
||||
if m == nil {
|
||||
return info
|
||||
}
|
||||
if m.AdditionalProperties != nil {
|
||||
for _, item := range m.AdditionalProperties {
|
||||
info = append(info, yaml.MapItem{Key: item.Name, Value: item.Value.ToRawInfo()})
|
||||
|
@ -7128,9 +7168,11 @@ func (m *HeadersOrReferences) ToRawInfo() interface{} {
|
|||
// ToRawInfo returns a description of Info suitable for JSON or YAML export.
|
||||
func (m *Info) ToRawInfo() interface{} {
|
||||
info := yaml.MapSlice{}
|
||||
if m.Title != "" {
|
||||
info = append(info, yaml.MapItem{Key: "title", Value: m.Title})
|
||||
if m == nil {
|
||||
return info
|
||||
}
|
||||
// always include this required field.
|
||||
info = append(info, yaml.MapItem{Key: "title", Value: m.Title})
|
||||
if m.Description != "" {
|
||||
info = append(info, yaml.MapItem{Key: "description", Value: m.Description})
|
||||
}
|
||||
|
@ -7145,9 +7187,8 @@ func (m *Info) ToRawInfo() interface{} {
|
|||
info = append(info, yaml.MapItem{Key: "license", Value: m.License.ToRawInfo()})
|
||||
}
|
||||
// &{Name:license Type:License StringEnumValues:[] MapType: Repeated:false Pattern: Implicit:false Description:}
|
||||
if m.Version != "" {
|
||||
info = append(info, yaml.MapItem{Key: "version", Value: m.Version})
|
||||
}
|
||||
// always include this required field.
|
||||
info = append(info, yaml.MapItem{Key: "version", Value: m.Version})
|
||||
if m.SpecificationExtension != nil {
|
||||
for _, item := range m.SpecificationExtension {
|
||||
info = append(info, yaml.MapItem{Key: item.Name, Value: item.Value.ToRawInfo()})
|
||||
|
@ -7160,6 +7201,9 @@ func (m *Info) ToRawInfo() interface{} {
|
|||
// ToRawInfo returns a description of ItemsItem suitable for JSON or YAML export.
|
||||
func (m *ItemsItem) ToRawInfo() interface{} {
|
||||
info := yaml.MapSlice{}
|
||||
if m == nil {
|
||||
return info
|
||||
}
|
||||
if len(m.SchemaOrReference) != 0 {
|
||||
items := make([]interface{}, 0)
|
||||
for _, item := range m.SchemaOrReference {
|
||||
|
@ -7174,9 +7218,11 @@ func (m *ItemsItem) ToRawInfo() interface{} {
|
|||
// ToRawInfo returns a description of License suitable for JSON or YAML export.
|
||||
func (m *License) ToRawInfo() interface{} {
|
||||
info := yaml.MapSlice{}
|
||||
if m.Name != "" {
|
||||
info = append(info, yaml.MapItem{Key: "name", Value: m.Name})
|
||||
if m == nil {
|
||||
return info
|
||||
}
|
||||
// always include this required field.
|
||||
info = append(info, yaml.MapItem{Key: "name", Value: m.Name})
|
||||
if m.Url != "" {
|
||||
info = append(info, yaml.MapItem{Key: "url", Value: m.Url})
|
||||
}
|
||||
|
@ -7192,6 +7238,9 @@ func (m *License) ToRawInfo() interface{} {
|
|||
// ToRawInfo returns a description of Link suitable for JSON or YAML export.
|
||||
func (m *Link) ToRawInfo() interface{} {
|
||||
info := yaml.MapSlice{}
|
||||
if m == nil {
|
||||
return info
|
||||
}
|
||||
if m.OperationRef != "" {
|
||||
info = append(info, yaml.MapItem{Key: "operationRef", Value: m.OperationRef})
|
||||
}
|
||||
|
@ -7242,6 +7291,9 @@ func (m *LinkOrReference) ToRawInfo() interface{} {
|
|||
// ToRawInfo returns a description of LinksOrReferences suitable for JSON or YAML export.
|
||||
func (m *LinksOrReferences) ToRawInfo() interface{} {
|
||||
info := yaml.MapSlice{}
|
||||
if m == nil {
|
||||
return info
|
||||
}
|
||||
if m.AdditionalProperties != nil {
|
||||
for _, item := range m.AdditionalProperties {
|
||||
info = append(info, yaml.MapItem{Key: item.Name, Value: item.Value.ToRawInfo()})
|
||||
|
@ -7254,6 +7306,9 @@ func (m *LinksOrReferences) ToRawInfo() interface{} {
|
|||
// ToRawInfo returns a description of MediaType suitable for JSON or YAML export.
|
||||
func (m *MediaType) ToRawInfo() interface{} {
|
||||
info := yaml.MapSlice{}
|
||||
if m == nil {
|
||||
return info
|
||||
}
|
||||
if m.Schema != nil {
|
||||
info = append(info, yaml.MapItem{Key: "schema", Value: m.Schema.ToRawInfo()})
|
||||
}
|
||||
|
@ -7282,6 +7337,9 @@ func (m *MediaType) ToRawInfo() interface{} {
|
|||
// ToRawInfo returns a description of MediaTypes suitable for JSON or YAML export.
|
||||
func (m *MediaTypes) ToRawInfo() interface{} {
|
||||
info := yaml.MapSlice{}
|
||||
if m == nil {
|
||||
return info
|
||||
}
|
||||
if m.AdditionalProperties != nil {
|
||||
for _, item := range m.AdditionalProperties {
|
||||
info = append(info, yaml.MapItem{Key: item.Name, Value: item.Value.ToRawInfo()})
|
||||
|
@ -7294,6 +7352,9 @@ func (m *MediaTypes) ToRawInfo() interface{} {
|
|||
// ToRawInfo returns a description of NamedAny suitable for JSON or YAML export.
|
||||
func (m *NamedAny) ToRawInfo() interface{} {
|
||||
info := yaml.MapSlice{}
|
||||
if m == nil {
|
||||
return info
|
||||
}
|
||||
if m.Name != "" {
|
||||
info = append(info, yaml.MapItem{Key: "name", Value: m.Name})
|
||||
}
|
||||
|
@ -7304,6 +7365,9 @@ func (m *NamedAny) ToRawInfo() interface{} {
|
|||
// ToRawInfo returns a description of NamedAnyOrExpression suitable for JSON or YAML export.
|
||||
func (m *NamedAnyOrExpression) ToRawInfo() interface{} {
|
||||
info := yaml.MapSlice{}
|
||||
if m == nil {
|
||||
return info
|
||||
}
|
||||
if m.Name != "" {
|
||||
info = append(info, yaml.MapItem{Key: "name", Value: m.Name})
|
||||
}
|
||||
|
@ -7314,6 +7378,9 @@ func (m *NamedAnyOrExpression) ToRawInfo() interface{} {
|
|||
// ToRawInfo returns a description of NamedCallbackOrReference suitable for JSON or YAML export.
|
||||
func (m *NamedCallbackOrReference) ToRawInfo() interface{} {
|
||||
info := yaml.MapSlice{}
|
||||
if m == nil {
|
||||
return info
|
||||
}
|
||||
if m.Name != "" {
|
||||
info = append(info, yaml.MapItem{Key: "name", Value: m.Name})
|
||||
}
|
||||
|
@ -7324,6 +7391,9 @@ func (m *NamedCallbackOrReference) ToRawInfo() interface{} {
|
|||
// ToRawInfo returns a description of NamedEncoding suitable for JSON or YAML export.
|
||||
func (m *NamedEncoding) ToRawInfo() interface{} {
|
||||
info := yaml.MapSlice{}
|
||||
if m == nil {
|
||||
return info
|
||||
}
|
||||
if m.Name != "" {
|
||||
info = append(info, yaml.MapItem{Key: "name", Value: m.Name})
|
||||
}
|
||||
|
@ -7334,6 +7404,9 @@ func (m *NamedEncoding) ToRawInfo() interface{} {
|
|||
// ToRawInfo returns a description of NamedExampleOrReference suitable for JSON or YAML export.
|
||||
func (m *NamedExampleOrReference) ToRawInfo() interface{} {
|
||||
info := yaml.MapSlice{}
|
||||
if m == nil {
|
||||
return info
|
||||
}
|
||||
if m.Name != "" {
|
||||
info = append(info, yaml.MapItem{Key: "name", Value: m.Name})
|
||||
}
|
||||
|
@ -7344,6 +7417,9 @@ func (m *NamedExampleOrReference) ToRawInfo() interface{} {
|
|||
// ToRawInfo returns a description of NamedHeaderOrReference suitable for JSON or YAML export.
|
||||
func (m *NamedHeaderOrReference) ToRawInfo() interface{} {
|
||||
info := yaml.MapSlice{}
|
||||
if m == nil {
|
||||
return info
|
||||
}
|
||||
if m.Name != "" {
|
||||
info = append(info, yaml.MapItem{Key: "name", Value: m.Name})
|
||||
}
|
||||
|
@ -7354,6 +7430,9 @@ func (m *NamedHeaderOrReference) ToRawInfo() interface{} {
|
|||
// ToRawInfo returns a description of NamedLinkOrReference suitable for JSON or YAML export.
|
||||
func (m *NamedLinkOrReference) ToRawInfo() interface{} {
|
||||
info := yaml.MapSlice{}
|
||||
if m == nil {
|
||||
return info
|
||||
}
|
||||
if m.Name != "" {
|
||||
info = append(info, yaml.MapItem{Key: "name", Value: m.Name})
|
||||
}
|
||||
|
@ -7364,6 +7443,9 @@ func (m *NamedLinkOrReference) ToRawInfo() interface{} {
|
|||
// ToRawInfo returns a description of NamedMediaType suitable for JSON or YAML export.
|
||||
func (m *NamedMediaType) ToRawInfo() interface{} {
|
||||
info := yaml.MapSlice{}
|
||||
if m == nil {
|
||||
return info
|
||||
}
|
||||
if m.Name != "" {
|
||||
info = append(info, yaml.MapItem{Key: "name", Value: m.Name})
|
||||
}
|
||||
|
@ -7374,6 +7456,9 @@ func (m *NamedMediaType) ToRawInfo() interface{} {
|
|||
// ToRawInfo returns a description of NamedParameterOrReference suitable for JSON or YAML export.
|
||||
func (m *NamedParameterOrReference) ToRawInfo() interface{} {
|
||||
info := yaml.MapSlice{}
|
||||
if m == nil {
|
||||
return info
|
||||
}
|
||||
if m.Name != "" {
|
||||
info = append(info, yaml.MapItem{Key: "name", Value: m.Name})
|
||||
}
|
||||
|
@ -7384,6 +7469,9 @@ func (m *NamedParameterOrReference) ToRawInfo() interface{} {
|
|||
// ToRawInfo returns a description of NamedPathItem suitable for JSON or YAML export.
|
||||
func (m *NamedPathItem) ToRawInfo() interface{} {
|
||||
info := yaml.MapSlice{}
|
||||
if m == nil {
|
||||
return info
|
||||
}
|
||||
if m.Name != "" {
|
||||
info = append(info, yaml.MapItem{Key: "name", Value: m.Name})
|
||||
}
|
||||
|
@ -7394,6 +7482,9 @@ func (m *NamedPathItem) ToRawInfo() interface{} {
|
|||
// ToRawInfo returns a description of NamedRequestBodyOrReference suitable for JSON or YAML export.
|
||||
func (m *NamedRequestBodyOrReference) ToRawInfo() interface{} {
|
||||
info := yaml.MapSlice{}
|
||||
if m == nil {
|
||||
return info
|
||||
}
|
||||
if m.Name != "" {
|
||||
info = append(info, yaml.MapItem{Key: "name", Value: m.Name})
|
||||
}
|
||||
|
@ -7404,6 +7495,9 @@ func (m *NamedRequestBodyOrReference) ToRawInfo() interface{} {
|
|||
// ToRawInfo returns a description of NamedResponseOrReference suitable for JSON or YAML export.
|
||||
func (m *NamedResponseOrReference) ToRawInfo() interface{} {
|
||||
info := yaml.MapSlice{}
|
||||
if m == nil {
|
||||
return info
|
||||
}
|
||||
if m.Name != "" {
|
||||
info = append(info, yaml.MapItem{Key: "name", Value: m.Name})
|
||||
}
|
||||
|
@ -7414,6 +7508,9 @@ func (m *NamedResponseOrReference) ToRawInfo() interface{} {
|
|||
// ToRawInfo returns a description of NamedSchemaOrReference suitable for JSON or YAML export.
|
||||
func (m *NamedSchemaOrReference) ToRawInfo() interface{} {
|
||||
info := yaml.MapSlice{}
|
||||
if m == nil {
|
||||
return info
|
||||
}
|
||||
if m.Name != "" {
|
||||
info = append(info, yaml.MapItem{Key: "name", Value: m.Name})
|
||||
}
|
||||
|
@ -7424,6 +7521,9 @@ func (m *NamedSchemaOrReference) ToRawInfo() interface{} {
|
|||
// ToRawInfo returns a description of NamedSecuritySchemeOrReference suitable for JSON or YAML export.
|
||||
func (m *NamedSecuritySchemeOrReference) ToRawInfo() interface{} {
|
||||
info := yaml.MapSlice{}
|
||||
if m == nil {
|
||||
return info
|
||||
}
|
||||
if m.Name != "" {
|
||||
info = append(info, yaml.MapItem{Key: "name", Value: m.Name})
|
||||
}
|
||||
|
@ -7434,6 +7534,9 @@ func (m *NamedSecuritySchemeOrReference) ToRawInfo() interface{} {
|
|||
// ToRawInfo returns a description of NamedServerVariable suitable for JSON or YAML export.
|
||||
func (m *NamedServerVariable) ToRawInfo() interface{} {
|
||||
info := yaml.MapSlice{}
|
||||
if m == nil {
|
||||
return info
|
||||
}
|
||||
if m.Name != "" {
|
||||
info = append(info, yaml.MapItem{Key: "name", Value: m.Name})
|
||||
}
|
||||
|
@ -7444,6 +7547,9 @@ func (m *NamedServerVariable) ToRawInfo() interface{} {
|
|||
// ToRawInfo returns a description of NamedString suitable for JSON or YAML export.
|
||||
func (m *NamedString) ToRawInfo() interface{} {
|
||||
info := yaml.MapSlice{}
|
||||
if m == nil {
|
||||
return info
|
||||
}
|
||||
if m.Name != "" {
|
||||
info = append(info, yaml.MapItem{Key: "name", Value: m.Name})
|
||||
}
|
||||
|
@ -7456,6 +7562,9 @@ func (m *NamedString) ToRawInfo() interface{} {
|
|||
// ToRawInfo returns a description of OauthFlow suitable for JSON or YAML export.
|
||||
func (m *OauthFlow) ToRawInfo() interface{} {
|
||||
info := yaml.MapSlice{}
|
||||
if m == nil {
|
||||
return info
|
||||
}
|
||||
if m.AuthorizationUrl != "" {
|
||||
info = append(info, yaml.MapItem{Key: "authorizationUrl", Value: m.AuthorizationUrl})
|
||||
}
|
||||
|
@ -7481,6 +7590,9 @@ func (m *OauthFlow) ToRawInfo() interface{} {
|
|||
// ToRawInfo returns a description of OauthFlows suitable for JSON or YAML export.
|
||||
func (m *OauthFlows) ToRawInfo() interface{} {
|
||||
info := yaml.MapSlice{}
|
||||
if m == nil {
|
||||
return info
|
||||
}
|
||||
if m.Implicit != nil {
|
||||
info = append(info, yaml.MapItem{Key: "implicit", Value: m.Implicit.ToRawInfo()})
|
||||
}
|
||||
|
@ -7509,6 +7621,9 @@ func (m *OauthFlows) ToRawInfo() interface{} {
|
|||
// ToRawInfo returns a description of Object suitable for JSON or YAML export.
|
||||
func (m *Object) ToRawInfo() interface{} {
|
||||
info := yaml.MapSlice{}
|
||||
if m == nil {
|
||||
return info
|
||||
}
|
||||
if m.AdditionalProperties != nil {
|
||||
for _, item := range m.AdditionalProperties {
|
||||
info = append(info, yaml.MapItem{Key: item.Name, Value: item.Value.ToRawInfo()})
|
||||
|
@ -7521,6 +7636,9 @@ func (m *Object) ToRawInfo() interface{} {
|
|||
// ToRawInfo returns a description of Operation suitable for JSON or YAML export.
|
||||
func (m *Operation) ToRawInfo() interface{} {
|
||||
info := yaml.MapSlice{}
|
||||
if m == nil {
|
||||
return info
|
||||
}
|
||||
if len(m.Tags) != 0 {
|
||||
info = append(info, yaml.MapItem{Key: "tags", Value: m.Tags})
|
||||
}
|
||||
|
@ -7549,9 +7667,8 @@ func (m *Operation) ToRawInfo() interface{} {
|
|||
info = append(info, yaml.MapItem{Key: "requestBody", Value: m.RequestBody.ToRawInfo()})
|
||||
}
|
||||
// &{Name:requestBody Type:RequestBodyOrReference StringEnumValues:[] MapType: Repeated:false Pattern: Implicit:false Description:}
|
||||
if m.Responses != nil {
|
||||
info = append(info, yaml.MapItem{Key: "responses", Value: m.Responses.ToRawInfo()})
|
||||
}
|
||||
// always include this required field.
|
||||
info = append(info, yaml.MapItem{Key: "responses", Value: m.Responses.ToRawInfo()})
|
||||
// &{Name:responses Type:Responses StringEnumValues:[] MapType: Repeated:false Pattern: Implicit:false Description:}
|
||||
if m.Callbacks != nil {
|
||||
info = append(info, yaml.MapItem{Key: "callbacks", Value: m.Callbacks.ToRawInfo()})
|
||||
|
@ -7588,12 +7705,13 @@ func (m *Operation) ToRawInfo() interface{} {
|
|||
// ToRawInfo returns a description of Parameter suitable for JSON or YAML export.
|
||||
func (m *Parameter) ToRawInfo() interface{} {
|
||||
info := yaml.MapSlice{}
|
||||
if m.Name != "" {
|
||||
info = append(info, yaml.MapItem{Key: "name", Value: m.Name})
|
||||
}
|
||||
if m.In != "" {
|
||||
info = append(info, yaml.MapItem{Key: "in", Value: m.In})
|
||||
if m == nil {
|
||||
return info
|
||||
}
|
||||
// always include this required field.
|
||||
info = append(info, yaml.MapItem{Key: "name", Value: m.Name})
|
||||
// always include this required field.
|
||||
info = append(info, yaml.MapItem{Key: "in", Value: m.In})
|
||||
if m.Description != "" {
|
||||
info = append(info, yaml.MapItem{Key: "description", Value: m.Description})
|
||||
}
|
||||
|
@ -7660,6 +7778,9 @@ func (m *ParameterOrReference) ToRawInfo() interface{} {
|
|||
// ToRawInfo returns a description of ParametersOrReferences suitable for JSON or YAML export.
|
||||
func (m *ParametersOrReferences) ToRawInfo() interface{} {
|
||||
info := yaml.MapSlice{}
|
||||
if m == nil {
|
||||
return info
|
||||
}
|
||||
if m.AdditionalProperties != nil {
|
||||
for _, item := range m.AdditionalProperties {
|
||||
info = append(info, yaml.MapItem{Key: item.Name, Value: item.Value.ToRawInfo()})
|
||||
|
@ -7672,6 +7793,9 @@ func (m *ParametersOrReferences) ToRawInfo() interface{} {
|
|||
// ToRawInfo returns a description of PathItem suitable for JSON or YAML export.
|
||||
func (m *PathItem) ToRawInfo() interface{} {
|
||||
info := yaml.MapSlice{}
|
||||
if m == nil {
|
||||
return info
|
||||
}
|
||||
if m.XRef != "" {
|
||||
info = append(info, yaml.MapItem{Key: "$ref", Value: m.XRef})
|
||||
}
|
||||
|
@ -7741,6 +7865,9 @@ func (m *PathItem) ToRawInfo() interface{} {
|
|||
// ToRawInfo returns a description of Paths suitable for JSON or YAML export.
|
||||
func (m *Paths) ToRawInfo() interface{} {
|
||||
info := yaml.MapSlice{}
|
||||
if m == nil {
|
||||
return info
|
||||
}
|
||||
if m.Path != nil {
|
||||
for _, item := range m.Path {
|
||||
info = append(info, yaml.MapItem{Key: item.Name, Value: item.Value.ToRawInfo()})
|
||||
|
@ -7759,6 +7886,9 @@ func (m *Paths) ToRawInfo() interface{} {
|
|||
// ToRawInfo returns a description of Properties suitable for JSON or YAML export.
|
||||
func (m *Properties) ToRawInfo() interface{} {
|
||||
info := yaml.MapSlice{}
|
||||
if m == nil {
|
||||
return info
|
||||
}
|
||||
if m.AdditionalProperties != nil {
|
||||
for _, item := range m.AdditionalProperties {
|
||||
info = append(info, yaml.MapItem{Key: item.Name, Value: item.Value.ToRawInfo()})
|
||||
|
@ -7771,15 +7901,20 @@ func (m *Properties) ToRawInfo() interface{} {
|
|||
// ToRawInfo returns a description of Reference suitable for JSON or YAML export.
|
||||
func (m *Reference) ToRawInfo() interface{} {
|
||||
info := yaml.MapSlice{}
|
||||
if m.XRef != "" {
|
||||
info = append(info, yaml.MapItem{Key: "$ref", Value: m.XRef})
|
||||
if m == nil {
|
||||
return info
|
||||
}
|
||||
// always include this required field.
|
||||
info = append(info, yaml.MapItem{Key: "$ref", Value: m.XRef})
|
||||
return info
|
||||
}
|
||||
|
||||
// ToRawInfo returns a description of RequestBodiesOrReferences suitable for JSON or YAML export.
|
||||
func (m *RequestBodiesOrReferences) ToRawInfo() interface{} {
|
||||
info := yaml.MapSlice{}
|
||||
if m == nil {
|
||||
return info
|
||||
}
|
||||
if m.AdditionalProperties != nil {
|
||||
for _, item := range m.AdditionalProperties {
|
||||
info = append(info, yaml.MapItem{Key: item.Name, Value: item.Value.ToRawInfo()})
|
||||
|
@ -7792,12 +7927,14 @@ func (m *RequestBodiesOrReferences) ToRawInfo() interface{} {
|
|||
// ToRawInfo returns a description of RequestBody suitable for JSON or YAML export.
|
||||
func (m *RequestBody) ToRawInfo() interface{} {
|
||||
info := yaml.MapSlice{}
|
||||
if m == nil {
|
||||
return info
|
||||
}
|
||||
if m.Description != "" {
|
||||
info = append(info, yaml.MapItem{Key: "description", Value: m.Description})
|
||||
}
|
||||
if m.Content != nil {
|
||||
info = append(info, yaml.MapItem{Key: "content", Value: m.Content.ToRawInfo()})
|
||||
}
|
||||
// always include this required field.
|
||||
info = append(info, yaml.MapItem{Key: "content", Value: m.Content.ToRawInfo()})
|
||||
// &{Name:content Type:MediaTypes StringEnumValues:[] MapType: Repeated:false Pattern: Implicit:false Description:}
|
||||
if m.Required != false {
|
||||
info = append(info, yaml.MapItem{Key: "required", Value: m.Required})
|
||||
|
@ -7831,9 +7968,11 @@ func (m *RequestBodyOrReference) ToRawInfo() interface{} {
|
|||
// ToRawInfo returns a description of Response suitable for JSON or YAML export.
|
||||
func (m *Response) ToRawInfo() interface{} {
|
||||
info := yaml.MapSlice{}
|
||||
if m.Description != "" {
|
||||
info = append(info, yaml.MapItem{Key: "description", Value: m.Description})
|
||||
if m == nil {
|
||||
return info
|
||||
}
|
||||
// always include this required field.
|
||||
info = append(info, yaml.MapItem{Key: "description", Value: m.Description})
|
||||
if m.Headers != nil {
|
||||
info = append(info, yaml.MapItem{Key: "headers", Value: m.Headers.ToRawInfo()})
|
||||
}
|
||||
|
@ -7875,6 +8014,9 @@ func (m *ResponseOrReference) ToRawInfo() interface{} {
|
|||
// ToRawInfo returns a description of Responses suitable for JSON or YAML export.
|
||||
func (m *Responses) ToRawInfo() interface{} {
|
||||
info := yaml.MapSlice{}
|
||||
if m == nil {
|
||||
return info
|
||||
}
|
||||
if m.Default != nil {
|
||||
info = append(info, yaml.MapItem{Key: "default", Value: m.Default.ToRawInfo()})
|
||||
}
|
||||
|
@ -7897,6 +8039,9 @@ func (m *Responses) ToRawInfo() interface{} {
|
|||
// ToRawInfo returns a description of ResponsesOrReferences suitable for JSON or YAML export.
|
||||
func (m *ResponsesOrReferences) ToRawInfo() interface{} {
|
||||
info := yaml.MapSlice{}
|
||||
if m == nil {
|
||||
return info
|
||||
}
|
||||
if m.AdditionalProperties != nil {
|
||||
for _, item := range m.AdditionalProperties {
|
||||
info = append(info, yaml.MapItem{Key: item.Name, Value: item.Value.ToRawInfo()})
|
||||
|
@ -7909,6 +8054,9 @@ func (m *ResponsesOrReferences) ToRawInfo() interface{} {
|
|||
// ToRawInfo returns a description of Schema suitable for JSON or YAML export.
|
||||
func (m *Schema) ToRawInfo() interface{} {
|
||||
info := yaml.MapSlice{}
|
||||
if m == nil {
|
||||
return info
|
||||
}
|
||||
if m.Nullable != false {
|
||||
info = append(info, yaml.MapItem{Key: "nullable", Value: m.Nullable})
|
||||
}
|
||||
|
@ -8076,6 +8224,9 @@ func (m *SchemaOrReference) ToRawInfo() interface{} {
|
|||
// ToRawInfo returns a description of SchemasOrReferences suitable for JSON or YAML export.
|
||||
func (m *SchemasOrReferences) ToRawInfo() interface{} {
|
||||
info := yaml.MapSlice{}
|
||||
if m == nil {
|
||||
return info
|
||||
}
|
||||
if m.AdditionalProperties != nil {
|
||||
for _, item := range m.AdditionalProperties {
|
||||
info = append(info, yaml.MapItem{Key: item.Name, Value: item.Value.ToRawInfo()})
|
||||
|
@ -8088,15 +8239,20 @@ func (m *SchemasOrReferences) ToRawInfo() interface{} {
|
|||
// ToRawInfo returns a description of SecurityRequirement suitable for JSON or YAML export.
|
||||
func (m *SecurityRequirement) ToRawInfo() interface{} {
|
||||
info := yaml.MapSlice{}
|
||||
if m == nil {
|
||||
return info
|
||||
}
|
||||
return info
|
||||
}
|
||||
|
||||
// ToRawInfo returns a description of SecurityScheme suitable for JSON or YAML export.
|
||||
func (m *SecurityScheme) ToRawInfo() interface{} {
|
||||
info := yaml.MapSlice{}
|
||||
if m.Type != "" {
|
||||
info = append(info, yaml.MapItem{Key: "type", Value: m.Type})
|
||||
if m == nil {
|
||||
return info
|
||||
}
|
||||
// always include this required field.
|
||||
info = append(info, yaml.MapItem{Key: "type", Value: m.Type})
|
||||
if m.Description != "" {
|
||||
info = append(info, yaml.MapItem{Key: "description", Value: m.Description})
|
||||
}
|
||||
|
@ -8148,6 +8304,9 @@ func (m *SecuritySchemeOrReference) ToRawInfo() interface{} {
|
|||
// ToRawInfo returns a description of SecuritySchemesOrReferences suitable for JSON or YAML export.
|
||||
func (m *SecuritySchemesOrReferences) ToRawInfo() interface{} {
|
||||
info := yaml.MapSlice{}
|
||||
if m == nil {
|
||||
return info
|
||||
}
|
||||
if m.AdditionalProperties != nil {
|
||||
for _, item := range m.AdditionalProperties {
|
||||
info = append(info, yaml.MapItem{Key: item.Name, Value: item.Value.ToRawInfo()})
|
||||
|
@ -8160,9 +8319,11 @@ func (m *SecuritySchemesOrReferences) ToRawInfo() interface{} {
|
|||
// ToRawInfo returns a description of Server suitable for JSON or YAML export.
|
||||
func (m *Server) ToRawInfo() interface{} {
|
||||
info := yaml.MapSlice{}
|
||||
if m.Url != "" {
|
||||
info = append(info, yaml.MapItem{Key: "url", Value: m.Url})
|
||||
if m == nil {
|
||||
return info
|
||||
}
|
||||
// always include this required field.
|
||||
info = append(info, yaml.MapItem{Key: "url", Value: m.Url})
|
||||
if m.Description != "" {
|
||||
info = append(info, yaml.MapItem{Key: "description", Value: m.Description})
|
||||
}
|
||||
|
@ -8182,12 +8343,14 @@ func (m *Server) ToRawInfo() interface{} {
|
|||
// ToRawInfo returns a description of ServerVariable suitable for JSON or YAML export.
|
||||
func (m *ServerVariable) ToRawInfo() interface{} {
|
||||
info := yaml.MapSlice{}
|
||||
if m == nil {
|
||||
return info
|
||||
}
|
||||
if len(m.Enum) != 0 {
|
||||
info = append(info, yaml.MapItem{Key: "enum", Value: m.Enum})
|
||||
}
|
||||
if m.Default != "" {
|
||||
info = append(info, yaml.MapItem{Key: "default", Value: m.Default})
|
||||
}
|
||||
// always include this required field.
|
||||
info = append(info, yaml.MapItem{Key: "default", Value: m.Default})
|
||||
if m.Description != "" {
|
||||
info = append(info, yaml.MapItem{Key: "description", Value: m.Description})
|
||||
}
|
||||
|
@ -8203,6 +8366,9 @@ func (m *ServerVariable) ToRawInfo() interface{} {
|
|||
// ToRawInfo returns a description of ServerVariables suitable for JSON or YAML export.
|
||||
func (m *ServerVariables) ToRawInfo() interface{} {
|
||||
info := yaml.MapSlice{}
|
||||
if m == nil {
|
||||
return info
|
||||
}
|
||||
if m.AdditionalProperties != nil {
|
||||
for _, item := range m.AdditionalProperties {
|
||||
info = append(info, yaml.MapItem{Key: item.Name, Value: item.Value.ToRawInfo()})
|
||||
|
@ -8239,6 +8405,9 @@ func (m *StringArray) ToRawInfo() interface{} {
|
|||
// ToRawInfo returns a description of Strings suitable for JSON or YAML export.
|
||||
func (m *Strings) ToRawInfo() interface{} {
|
||||
info := yaml.MapSlice{}
|
||||
if m == nil {
|
||||
return info
|
||||
}
|
||||
// &{Name:additionalProperties Type:NamedString StringEnumValues:[] MapType:string Repeated:true Pattern: Implicit:true Description:}
|
||||
return info
|
||||
}
|
||||
|
@ -8246,9 +8415,11 @@ func (m *Strings) ToRawInfo() interface{} {
|
|||
// ToRawInfo returns a description of Tag suitable for JSON or YAML export.
|
||||
func (m *Tag) ToRawInfo() interface{} {
|
||||
info := yaml.MapSlice{}
|
||||
if m.Name != "" {
|
||||
info = append(info, yaml.MapItem{Key: "name", Value: m.Name})
|
||||
if m == nil {
|
||||
return info
|
||||
}
|
||||
// always include this required field.
|
||||
info = append(info, yaml.MapItem{Key: "name", Value: m.Name})
|
||||
if m.Description != "" {
|
||||
info = append(info, yaml.MapItem{Key: "description", Value: m.Description})
|
||||
}
|
||||
|
@ -8268,6 +8439,9 @@ func (m *Tag) ToRawInfo() interface{} {
|
|||
// ToRawInfo returns a description of Xml suitable for JSON or YAML export.
|
||||
func (m *Xml) ToRawInfo() interface{} {
|
||||
info := yaml.MapSlice{}
|
||||
if m == nil {
|
||||
return info
|
||||
}
|
||||
if m.Name != "" {
|
||||
info = append(info, yaml.MapItem{Key: "name", Value: m.Name})
|
||||
}
|
||||
|
|
9
vendor/github.com/googleapis/gnostic/README.md
generated
vendored
9
vendor/github.com/googleapis/gnostic/README.md
generated
vendored
|
@ -93,12 +93,9 @@ Protocol Buffer description. This is mainly for use in testing and debugging.
|
|||
go install github.com/googleapis/gnostic/apps/report
|
||||
report petstore.pb
|
||||
|
||||
8. **gnostic** supports plugins. This builds and runs a sample plugin
|
||||
that reports some basic information about an API. The "-" causes the plugin to
|
||||
write its output to stdout.
|
||||
|
||||
go install github.com/googleapis/gnostic/plugins/gnostic-go-sample
|
||||
gnostic examples/v2.0/json/petstore.json --go-sample-out=-
|
||||
8. **gnostic** supports plugins. Some are already implemented in the `plugins` directory.
|
||||
Others, like [gnostic-go-generator](https://github.com/googleapis/gnostic-go-generator),
|
||||
are separated into their own repositories.
|
||||
|
||||
## Copyright
|
||||
|
||||
|
|
6
vendor/github.com/googleapis/gnostic/apps/disco/openapiv3.go
generated
vendored
6
vendor/github.com/googleapis/gnostic/apps/disco/openapiv3.go
generated
vendored
|
@ -285,8 +285,10 @@ func OpenAPIv3(api *discovery.Document) (*openapi3.Document, error) {
|
|||
|
||||
d.Components = &openapi3.Components{}
|
||||
d.Components.Schemas = &openapi3.SchemasOrReferences{}
|
||||
for _, pair := range api.Schemas.AdditionalProperties {
|
||||
addOpenAPI3SchemaForSchema(d, pair.Name, pair.Value)
|
||||
if api.Schemas != nil {
|
||||
for _, pair := range api.Schemas.AdditionalProperties {
|
||||
addOpenAPI3SchemaForSchema(d, pair.Name, pair.Value)
|
||||
}
|
||||
}
|
||||
|
||||
d.Paths = &openapi3.Paths{}
|
||||
|
|
16
vendor/github.com/googleapis/gnostic/apps/report/main.go
generated
vendored
16
vendor/github.com/googleapis/gnostic/apps/report/main.go
generated
vendored
|
@ -20,6 +20,7 @@ import (
|
|||
"flag"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"os"
|
||||
|
||||
"github.com/golang/protobuf/proto"
|
||||
|
@ -28,18 +29,17 @@ import (
|
|||
pb "github.com/googleapis/gnostic/OpenAPIv2"
|
||||
)
|
||||
|
||||
func readDocumentFromFileWithName(filename string) *pb.Document {
|
||||
func readDocumentFromFileWithName(filename string) (*pb.Document, error) {
|
||||
data, err := ioutil.ReadFile(filename)
|
||||
if err != nil {
|
||||
fmt.Printf("File error: %v\n", err)
|
||||
os.Exit(1)
|
||||
return nil, err
|
||||
}
|
||||
document := &pb.Document{}
|
||||
err = proto.Unmarshal(data, document)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
return nil, err
|
||||
}
|
||||
return document
|
||||
return document, nil
|
||||
}
|
||||
|
||||
func printDocument(code *printer.Code, document *pb.Document) {
|
||||
|
@ -229,8 +229,12 @@ func main() {
|
|||
return
|
||||
}
|
||||
|
||||
document := readDocumentFromFileWithName(args[0])
|
||||
document, err := readDocumentFromFileWithName(args[0])
|
||||
|
||||
if err != nil {
|
||||
log.Printf("Error reading %s. This sample expects OpenAPI v2.", args[0])
|
||||
os.Exit(-1)
|
||||
}
|
||||
code := &printer.Code{}
|
||||
code.Print("API REPORT")
|
||||
code.Print("----------")
|
||||
|
|
84
vendor/github.com/googleapis/gnostic/compiler/reader.go
generated
vendored
84
vendor/github.com/googleapis/gnostic/compiler/reader.go
generated
vendored
|
@ -17,13 +17,14 @@ package compiler
|
|||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"gopkg.in/yaml.v2"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
yaml "gopkg.in/yaml.v2"
|
||||
)
|
||||
|
||||
var fileCache map[string][]byte
|
||||
|
@ -31,6 +32,8 @@ var infoCache map[string]interface{}
|
|||
var count int64
|
||||
|
||||
var verboseReader = false
|
||||
var fileCacheEnable = true
|
||||
var infoCacheEnable = true
|
||||
|
||||
func initializeFileCache() {
|
||||
if fileCache == nil {
|
||||
|
@ -44,29 +47,56 @@ func initializeInfoCache() {
|
|||
}
|
||||
}
|
||||
|
||||
func DisableFileCache() {
|
||||
fileCacheEnable = false
|
||||
}
|
||||
|
||||
func DisableInfoCache() {
|
||||
infoCacheEnable = false
|
||||
}
|
||||
|
||||
func RemoveFromFileCache(fileurl string) {
|
||||
if !fileCacheEnable {
|
||||
return
|
||||
}
|
||||
initializeFileCache()
|
||||
delete(fileCache, fileurl)
|
||||
}
|
||||
|
||||
func RemoveFromInfoCache(filename string) {
|
||||
if !infoCacheEnable {
|
||||
return
|
||||
}
|
||||
initializeInfoCache()
|
||||
delete(infoCache, filename)
|
||||
}
|
||||
|
||||
// FetchFile gets a specified file from the local filesystem or a remote location.
|
||||
func FetchFile(fileurl string) ([]byte, error) {
|
||||
var bytes []byte
|
||||
initializeFileCache()
|
||||
bytes, ok := fileCache[fileurl]
|
||||
if ok {
|
||||
if verboseReader {
|
||||
log.Printf("Cache hit %s", fileurl)
|
||||
if fileCacheEnable {
|
||||
bytes, ok := fileCache[fileurl]
|
||||
if ok {
|
||||
if verboseReader {
|
||||
log.Printf("Cache hit %s", fileurl)
|
||||
}
|
||||
return bytes, nil
|
||||
}
|
||||
if verboseReader {
|
||||
log.Printf("Fetching %s", fileurl)
|
||||
}
|
||||
return bytes, nil
|
||||
}
|
||||
if verboseReader {
|
||||
log.Printf("Fetching %s", fileurl)
|
||||
}
|
||||
response, err := http.Get(fileurl)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer response.Body.Close()
|
||||
if response.StatusCode != 200 {
|
||||
return nil, errors.New(fmt.Sprintf("Error downloading %s: %s", fileurl, response.Status))
|
||||
}
|
||||
defer response.Body.Close()
|
||||
bytes, err = ioutil.ReadAll(response.Body)
|
||||
if err == nil {
|
||||
if fileCacheEnable && err == nil {
|
||||
fileCache[fileurl] = bytes
|
||||
}
|
||||
return bytes, err
|
||||
|
@ -95,22 +125,24 @@ func ReadBytesForFile(filename string) ([]byte, error) {
|
|||
// ReadInfoFromBytes unmarshals a file as a yaml.MapSlice.
|
||||
func ReadInfoFromBytes(filename string, bytes []byte) (interface{}, error) {
|
||||
initializeInfoCache()
|
||||
cachedInfo, ok := infoCache[filename]
|
||||
if ok {
|
||||
if verboseReader {
|
||||
log.Printf("Cache hit info for file %s", filename)
|
||||
if infoCacheEnable {
|
||||
cachedInfo, ok := infoCache[filename]
|
||||
if ok {
|
||||
if verboseReader {
|
||||
log.Printf("Cache hit info for file %s", filename)
|
||||
}
|
||||
return cachedInfo, nil
|
||||
}
|
||||
if verboseReader {
|
||||
log.Printf("Reading info for file %s", filename)
|
||||
}
|
||||
return cachedInfo, nil
|
||||
}
|
||||
if verboseReader {
|
||||
log.Printf("Reading info for file %s", filename)
|
||||
}
|
||||
var info yaml.MapSlice
|
||||
err := yaml.Unmarshal(bytes, &info)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(filename) > 0 {
|
||||
if infoCacheEnable && len(filename) > 0 {
|
||||
infoCache[filename] = info
|
||||
}
|
||||
return info, nil
|
||||
|
@ -119,7 +151,7 @@ func ReadInfoFromBytes(filename string, bytes []byte) (interface{}, error) {
|
|||
// ReadInfoForRef reads a file and return the fragment needed to resolve a $ref.
|
||||
func ReadInfoForRef(basefile string, ref string) (interface{}, error) {
|
||||
initializeInfoCache()
|
||||
{
|
||||
if infoCacheEnable {
|
||||
info, ok := infoCache[ref]
|
||||
if ok {
|
||||
if verboseReader {
|
||||
|
@ -127,9 +159,9 @@ func ReadInfoForRef(basefile string, ref string) (interface{}, error) {
|
|||
}
|
||||
return info, nil
|
||||
}
|
||||
}
|
||||
if verboseReader {
|
||||
log.Printf("Reading info for ref %s#%s", basefile, ref)
|
||||
if verboseReader {
|
||||
log.Printf("Reading info for ref %s#%s", basefile, ref)
|
||||
}
|
||||
}
|
||||
count = count + 1
|
||||
basedir, _ := filepath.Split(basefile)
|
||||
|
@ -170,6 +202,8 @@ func ReadInfoForRef(basefile string, ref string) (interface{}, error) {
|
|||
}
|
||||
}
|
||||
}
|
||||
infoCache[ref] = info
|
||||
if infoCacheEnable {
|
||||
infoCache[ref] = info
|
||||
}
|
||||
return info, nil
|
||||
}
|
||||
|
|
82
vendor/github.com/googleapis/gnostic/compiler/reader_test.go
generated
vendored
Normal file
82
vendor/github.com/googleapis/gnostic/compiler/reader_test.go
generated
vendored
Normal file
|
@ -0,0 +1,82 @@
|
|||
package compiler
|
||||
|
||||
import (
|
||||
. "gopkg.in/check.v1"
|
||||
"io"
|
||||
"net/http"
|
||||
"testing"
|
||||
)
|
||||
|
||||
// Hook up gocheck into the "go test" runner.
|
||||
func Test(t *testing.T) {
|
||||
TestingT(t)
|
||||
}
|
||||
|
||||
var mockSever *http.Server
|
||||
|
||||
type ReaderTestingSuite struct{}
|
||||
|
||||
var _ = Suite(&ReaderTestingSuite{})
|
||||
|
||||
|
||||
func (s *ReaderTestingSuite) SetUpSuite(c *C) {
|
||||
mockSever = &http.Server{Addr: "127.0.0.1:8080", Handler:
|
||||
http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
yamlBytes, err := ReadBytesForFile("testdata/petstore.yaml")
|
||||
c.Assert(err, IsNil)
|
||||
io.WriteString(w, string(yamlBytes))
|
||||
|
||||
})}
|
||||
go func() {
|
||||
mockSever.ListenAndServe()
|
||||
}()
|
||||
}
|
||||
|
||||
func (s *ReaderTestingSuite) TearDownSuite(c *C) {
|
||||
mockSever.Close()
|
||||
}
|
||||
|
||||
func (s *ReaderTestingSuite) TestRemoveFromInfoCache(c *C) {
|
||||
fileName := "testdata/petstore.yaml"
|
||||
yamlBytes, err := ReadBytesForFile(fileName)
|
||||
c.Assert(err, IsNil)
|
||||
c.Assert(len(yamlBytes) > 0, Equals, true)
|
||||
petstore, err := ReadInfoFromBytes(fileName, yamlBytes)
|
||||
c.Assert(err, IsNil)
|
||||
c.Assert(petstore, NotNil)
|
||||
c.Assert(len(infoCache), Equals, 1)
|
||||
RemoveFromInfoCache(fileName)
|
||||
c.Assert(len(infoCache), Equals, 0)
|
||||
}
|
||||
|
||||
func (s *ReaderTestingSuite) TestDisableInfoCache(c *C) {
|
||||
fileName := "testdata/petstore.yaml"
|
||||
yamlBytes, err := ReadBytesForFile(fileName)
|
||||
c.Assert(err, IsNil)
|
||||
c.Assert(len(yamlBytes) > 0, Equals, true)
|
||||
DisableInfoCache()
|
||||
petstore, err := ReadInfoFromBytes(fileName, yamlBytes)
|
||||
c.Assert(err, IsNil)
|
||||
c.Assert(petstore, NotNil)
|
||||
c.Assert(len(infoCache), Equals, 0)
|
||||
}
|
||||
|
||||
|
||||
func (s *ReaderTestingSuite) TestRemoveFromFileCache(c *C) {
|
||||
fileUrl := "http://127.0.0.1:8080/petstore"
|
||||
yamlBytes, err := FetchFile(fileUrl)
|
||||
c.Assert(err, IsNil)
|
||||
c.Assert(len(yamlBytes) > 0, Equals, true)
|
||||
c.Assert(len(fileCache), Equals, 1)
|
||||
RemoveFromFileCache(fileUrl)
|
||||
c.Assert(len(fileCache), Equals, 0)
|
||||
}
|
||||
|
||||
func (s *ReaderTestingSuite) TestDisableFileCache(c *C) {
|
||||
DisableFileCache()
|
||||
fileUrl := "http://127.0.0.1:8080/petstore"
|
||||
yamlBytes, err := FetchFile(fileUrl)
|
||||
c.Assert(err, IsNil)
|
||||
c.Assert(len(yamlBytes) > 0, Equals, true)
|
||||
c.Assert(len(fileCache), Equals, 0)
|
||||
}
|
110
vendor/github.com/googleapis/gnostic/compiler/testdata/petstore.yaml
generated
vendored
Normal file
110
vendor/github.com/googleapis/gnostic/compiler/testdata/petstore.yaml
generated
vendored
Normal file
|
@ -0,0 +1,110 @@
|
|||
openapi: "3.0"
|
||||
info:
|
||||
version: 1.0.0
|
||||
title: OpenAPI Petstore
|
||||
license:
|
||||
name: MIT
|
||||
servers:
|
||||
- url: https://petstore.openapis.org/v1
|
||||
description: Development server
|
||||
paths:
|
||||
/pets:
|
||||
get:
|
||||
summary: List all pets
|
||||
operationId: listPets
|
||||
tags:
|
||||
- pets
|
||||
parameters:
|
||||
- name: limit
|
||||
in: query
|
||||
description: How many items to return at one time (max 100)
|
||||
required: false
|
||||
schema:
|
||||
type: integer
|
||||
format: int32
|
||||
responses:
|
||||
"200":
|
||||
description: An paged array of pets
|
||||
headers:
|
||||
x-next:
|
||||
schema:
|
||||
type: string
|
||||
description: A link to the next page of responses
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/Pets'
|
||||
default:
|
||||
description: unexpected error
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/Error'
|
||||
post:
|
||||
summary: Create a pet
|
||||
operationId: createPets
|
||||
tags:
|
||||
- pets
|
||||
responses:
|
||||
"201":
|
||||
description: Null response
|
||||
default:
|
||||
description: unexpected error
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/Error'
|
||||
/pets/{petId}:
|
||||
get:
|
||||
summary: Info for a specific pet
|
||||
operationId: showPetById
|
||||
tags:
|
||||
- pets
|
||||
parameters:
|
||||
- name: petId
|
||||
in: path
|
||||
required: true
|
||||
description: The id of the pet to retrieve
|
||||
schema:
|
||||
type: string
|
||||
responses:
|
||||
"200":
|
||||
description: Expected response to a valid request
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/Pets'
|
||||
default:
|
||||
description: unexpected error
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/Error'
|
||||
components:
|
||||
schemas:
|
||||
Pet:
|
||||
required:
|
||||
- id
|
||||
- name
|
||||
properties:
|
||||
id:
|
||||
type: integer
|
||||
format: int64
|
||||
name:
|
||||
type: string
|
||||
tag:
|
||||
type: string
|
||||
Pets:
|
||||
type: array
|
||||
items:
|
||||
$ref: '#/components/schemas/Pet'
|
||||
Error:
|
||||
required:
|
||||
- code
|
||||
- message
|
||||
properties:
|
||||
code:
|
||||
type: integer
|
||||
format: int32
|
||||
message:
|
||||
type: string
|
6
vendor/github.com/googleapis/gnostic/examples/v2.0/yaml/empty-v2.yaml
generated
vendored
Normal file
6
vendor/github.com/googleapis/gnostic/examples/v2.0/yaml/empty-v2.yaml
generated
vendored
Normal file
|
@ -0,0 +1,6 @@
|
|||
swagger: "2.0"
|
||||
info:
|
||||
version: ""
|
||||
title: ""
|
||||
description: ""
|
||||
paths:
|
6
vendor/github.com/googleapis/gnostic/examples/v3.0/yaml/empty-v3.yaml
generated
vendored
Normal file
6
vendor/github.com/googleapis/gnostic/examples/v3.0/yaml/empty-v3.yaml
generated
vendored
Normal file
|
@ -0,0 +1,6 @@
|
|||
openapi: "3.0"
|
||||
info:
|
||||
version:
|
||||
title:
|
||||
description:
|
||||
paths:
|
164
vendor/github.com/googleapis/gnostic/extensions/extension.pb.go
generated
vendored
164
vendor/github.com/googleapis/gnostic/extensions/extension.pb.go
generated
vendored
|
@ -1,24 +1,12 @@
|
|||
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||
// source: extension.proto
|
||||
|
||||
/*
|
||||
Package openapiextension_v1 is a generated protocol buffer package.
|
||||
|
||||
It is generated from these files:
|
||||
extension.proto
|
||||
|
||||
It has these top-level messages:
|
||||
Version
|
||||
ExtensionHandlerRequest
|
||||
ExtensionHandlerResponse
|
||||
Wrapper
|
||||
*/
|
||||
package openapiextension_v1
|
||||
|
||||
import proto "github.com/golang/protobuf/proto"
|
||||
import fmt "fmt"
|
||||
import math "math"
|
||||
import google_protobuf "github.com/golang/protobuf/ptypes/any"
|
||||
import any "github.com/golang/protobuf/ptypes/any"
|
||||
|
||||
// Reference imports to suppress errors if they are not otherwise used.
|
||||
var _ = proto.Marshal
|
||||
|
@ -33,18 +21,40 @@ const _ = proto.ProtoPackageIsVersion2 // please upgrade the proto package
|
|||
|
||||
// The version number of OpenAPI compiler.
|
||||
type Version struct {
|
||||
Major int32 `protobuf:"varint,1,opt,name=major" json:"major,omitempty"`
|
||||
Minor int32 `protobuf:"varint,2,opt,name=minor" json:"minor,omitempty"`
|
||||
Patch int32 `protobuf:"varint,3,opt,name=patch" json:"patch,omitempty"`
|
||||
Major int32 `protobuf:"varint,1,opt,name=major,proto3" json:"major,omitempty"`
|
||||
Minor int32 `protobuf:"varint,2,opt,name=minor,proto3" json:"minor,omitempty"`
|
||||
Patch int32 `protobuf:"varint,3,opt,name=patch,proto3" json:"patch,omitempty"`
|
||||
// A suffix for alpha, beta or rc release, e.g., "alpha-1", "rc2". It should
|
||||
// be empty for mainline stable releases.
|
||||
Suffix string `protobuf:"bytes,4,opt,name=suffix" json:"suffix,omitempty"`
|
||||
Suffix string `protobuf:"bytes,4,opt,name=suffix,proto3" json:"suffix,omitempty"`
|
||||
XXX_NoUnkeyedLiteral struct{} `json:"-"`
|
||||
XXX_unrecognized []byte `json:"-"`
|
||||
XXX_sizecache int32 `json:"-"`
|
||||
}
|
||||
|
||||
func (m *Version) Reset() { *m = Version{} }
|
||||
func (m *Version) String() string { return proto.CompactTextString(m) }
|
||||
func (*Version) ProtoMessage() {}
|
||||
func (*Version) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{0} }
|
||||
func (m *Version) Reset() { *m = Version{} }
|
||||
func (m *Version) String() string { return proto.CompactTextString(m) }
|
||||
func (*Version) ProtoMessage() {}
|
||||
func (*Version) Descriptor() ([]byte, []int) {
|
||||
return fileDescriptor_extension_d25f09c742c58c90, []int{0}
|
||||
}
|
||||
func (m *Version) XXX_Unmarshal(b []byte) error {
|
||||
return xxx_messageInfo_Version.Unmarshal(m, b)
|
||||
}
|
||||
func (m *Version) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
|
||||
return xxx_messageInfo_Version.Marshal(b, m, deterministic)
|
||||
}
|
||||
func (dst *Version) XXX_Merge(src proto.Message) {
|
||||
xxx_messageInfo_Version.Merge(dst, src)
|
||||
}
|
||||
func (m *Version) XXX_Size() int {
|
||||
return xxx_messageInfo_Version.Size(m)
|
||||
}
|
||||
func (m *Version) XXX_DiscardUnknown() {
|
||||
xxx_messageInfo_Version.DiscardUnknown(m)
|
||||
}
|
||||
|
||||
var xxx_messageInfo_Version proto.InternalMessageInfo
|
||||
|
||||
func (m *Version) GetMajor() int32 {
|
||||
if m != nil {
|
||||
|
@ -78,15 +88,37 @@ func (m *Version) GetSuffix() string {
|
|||
type ExtensionHandlerRequest struct {
|
||||
// The OpenAPI descriptions that were explicitly listed on the command line.
|
||||
// The specifications will appear in the order they are specified to gnostic.
|
||||
Wrapper *Wrapper `protobuf:"bytes,1,opt,name=wrapper" json:"wrapper,omitempty"`
|
||||
Wrapper *Wrapper `protobuf:"bytes,1,opt,name=wrapper,proto3" json:"wrapper,omitempty"`
|
||||
// The version number of openapi compiler.
|
||||
CompilerVersion *Version `protobuf:"bytes,3,opt,name=compiler_version,json=compilerVersion" json:"compiler_version,omitempty"`
|
||||
CompilerVersion *Version `protobuf:"bytes,3,opt,name=compiler_version,json=compilerVersion,proto3" json:"compiler_version,omitempty"`
|
||||
XXX_NoUnkeyedLiteral struct{} `json:"-"`
|
||||
XXX_unrecognized []byte `json:"-"`
|
||||
XXX_sizecache int32 `json:"-"`
|
||||
}
|
||||
|
||||
func (m *ExtensionHandlerRequest) Reset() { *m = ExtensionHandlerRequest{} }
|
||||
func (m *ExtensionHandlerRequest) String() string { return proto.CompactTextString(m) }
|
||||
func (*ExtensionHandlerRequest) ProtoMessage() {}
|
||||
func (*ExtensionHandlerRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{1} }
|
||||
func (m *ExtensionHandlerRequest) Reset() { *m = ExtensionHandlerRequest{} }
|
||||
func (m *ExtensionHandlerRequest) String() string { return proto.CompactTextString(m) }
|
||||
func (*ExtensionHandlerRequest) ProtoMessage() {}
|
||||
func (*ExtensionHandlerRequest) Descriptor() ([]byte, []int) {
|
||||
return fileDescriptor_extension_d25f09c742c58c90, []int{1}
|
||||
}
|
||||
func (m *ExtensionHandlerRequest) XXX_Unmarshal(b []byte) error {
|
||||
return xxx_messageInfo_ExtensionHandlerRequest.Unmarshal(m, b)
|
||||
}
|
||||
func (m *ExtensionHandlerRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
|
||||
return xxx_messageInfo_ExtensionHandlerRequest.Marshal(b, m, deterministic)
|
||||
}
|
||||
func (dst *ExtensionHandlerRequest) XXX_Merge(src proto.Message) {
|
||||
xxx_messageInfo_ExtensionHandlerRequest.Merge(dst, src)
|
||||
}
|
||||
func (m *ExtensionHandlerRequest) XXX_Size() int {
|
||||
return xxx_messageInfo_ExtensionHandlerRequest.Size(m)
|
||||
}
|
||||
func (m *ExtensionHandlerRequest) XXX_DiscardUnknown() {
|
||||
xxx_messageInfo_ExtensionHandlerRequest.DiscardUnknown(m)
|
||||
}
|
||||
|
||||
var xxx_messageInfo_ExtensionHandlerRequest proto.InternalMessageInfo
|
||||
|
||||
func (m *ExtensionHandlerRequest) GetWrapper() *Wrapper {
|
||||
if m != nil {
|
||||
|
@ -105,7 +137,7 @@ func (m *ExtensionHandlerRequest) GetCompilerVersion() *Version {
|
|||
// The extensions writes an encoded ExtensionHandlerResponse to stdout.
|
||||
type ExtensionHandlerResponse struct {
|
||||
// true if the extension is handled by the extension handler; false otherwise
|
||||
Handled bool `protobuf:"varint,1,opt,name=handled" json:"handled,omitempty"`
|
||||
Handled bool `protobuf:"varint,1,opt,name=handled,proto3" json:"handled,omitempty"`
|
||||
// Error message. If non-empty, the extension handling failed.
|
||||
// The extension handler process should exit with status code zero
|
||||
// even if it reports an error in this way.
|
||||
|
@ -115,15 +147,37 @@ type ExtensionHandlerResponse struct {
|
|||
// itself -- such as the input Document being unparseable -- should be
|
||||
// reported by writing a message to stderr and exiting with a non-zero
|
||||
// status code.
|
||||
Error []string `protobuf:"bytes,2,rep,name=error" json:"error,omitempty"`
|
||||
Error []string `protobuf:"bytes,2,rep,name=error,proto3" json:"error,omitempty"`
|
||||
// text output
|
||||
Value *google_protobuf.Any `protobuf:"bytes,3,opt,name=value" json:"value,omitempty"`
|
||||
Value *any.Any `protobuf:"bytes,3,opt,name=value,proto3" json:"value,omitempty"`
|
||||
XXX_NoUnkeyedLiteral struct{} `json:"-"`
|
||||
XXX_unrecognized []byte `json:"-"`
|
||||
XXX_sizecache int32 `json:"-"`
|
||||
}
|
||||
|
||||
func (m *ExtensionHandlerResponse) Reset() { *m = ExtensionHandlerResponse{} }
|
||||
func (m *ExtensionHandlerResponse) String() string { return proto.CompactTextString(m) }
|
||||
func (*ExtensionHandlerResponse) ProtoMessage() {}
|
||||
func (*ExtensionHandlerResponse) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{2} }
|
||||
func (m *ExtensionHandlerResponse) Reset() { *m = ExtensionHandlerResponse{} }
|
||||
func (m *ExtensionHandlerResponse) String() string { return proto.CompactTextString(m) }
|
||||
func (*ExtensionHandlerResponse) ProtoMessage() {}
|
||||
func (*ExtensionHandlerResponse) Descriptor() ([]byte, []int) {
|
||||
return fileDescriptor_extension_d25f09c742c58c90, []int{2}
|
||||
}
|
||||
func (m *ExtensionHandlerResponse) XXX_Unmarshal(b []byte) error {
|
||||
return xxx_messageInfo_ExtensionHandlerResponse.Unmarshal(m, b)
|
||||
}
|
||||
func (m *ExtensionHandlerResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
|
||||
return xxx_messageInfo_ExtensionHandlerResponse.Marshal(b, m, deterministic)
|
||||
}
|
||||
func (dst *ExtensionHandlerResponse) XXX_Merge(src proto.Message) {
|
||||
xxx_messageInfo_ExtensionHandlerResponse.Merge(dst, src)
|
||||
}
|
||||
func (m *ExtensionHandlerResponse) XXX_Size() int {
|
||||
return xxx_messageInfo_ExtensionHandlerResponse.Size(m)
|
||||
}
|
||||
func (m *ExtensionHandlerResponse) XXX_DiscardUnknown() {
|
||||
xxx_messageInfo_ExtensionHandlerResponse.DiscardUnknown(m)
|
||||
}
|
||||
|
||||
var xxx_messageInfo_ExtensionHandlerResponse proto.InternalMessageInfo
|
||||
|
||||
func (m *ExtensionHandlerResponse) GetHandled() bool {
|
||||
if m != nil {
|
||||
|
@ -139,7 +193,7 @@ func (m *ExtensionHandlerResponse) GetError() []string {
|
|||
return nil
|
||||
}
|
||||
|
||||
func (m *ExtensionHandlerResponse) GetValue() *google_protobuf.Any {
|
||||
func (m *ExtensionHandlerResponse) GetValue() *any.Any {
|
||||
if m != nil {
|
||||
return m.Value
|
||||
}
|
||||
|
@ -148,17 +202,39 @@ func (m *ExtensionHandlerResponse) GetValue() *google_protobuf.Any {
|
|||
|
||||
type Wrapper struct {
|
||||
// version of the OpenAPI specification in which this extension was written.
|
||||
Version string `protobuf:"bytes,1,opt,name=version" json:"version,omitempty"`
|
||||
Version string `protobuf:"bytes,1,opt,name=version,proto3" json:"version,omitempty"`
|
||||
// Name of the extension
|
||||
ExtensionName string `protobuf:"bytes,2,opt,name=extension_name,json=extensionName" json:"extension_name,omitempty"`
|
||||
ExtensionName string `protobuf:"bytes,2,opt,name=extension_name,json=extensionName,proto3" json:"extension_name,omitempty"`
|
||||
// Must be a valid yaml for the proto
|
||||
Yaml string `protobuf:"bytes,3,opt,name=yaml" json:"yaml,omitempty"`
|
||||
Yaml string `protobuf:"bytes,3,opt,name=yaml,proto3" json:"yaml,omitempty"`
|
||||
XXX_NoUnkeyedLiteral struct{} `json:"-"`
|
||||
XXX_unrecognized []byte `json:"-"`
|
||||
XXX_sizecache int32 `json:"-"`
|
||||
}
|
||||
|
||||
func (m *Wrapper) Reset() { *m = Wrapper{} }
|
||||
func (m *Wrapper) String() string { return proto.CompactTextString(m) }
|
||||
func (*Wrapper) ProtoMessage() {}
|
||||
func (*Wrapper) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{3} }
|
||||
func (m *Wrapper) Reset() { *m = Wrapper{} }
|
||||
func (m *Wrapper) String() string { return proto.CompactTextString(m) }
|
||||
func (*Wrapper) ProtoMessage() {}
|
||||
func (*Wrapper) Descriptor() ([]byte, []int) {
|
||||
return fileDescriptor_extension_d25f09c742c58c90, []int{3}
|
||||
}
|
||||
func (m *Wrapper) XXX_Unmarshal(b []byte) error {
|
||||
return xxx_messageInfo_Wrapper.Unmarshal(m, b)
|
||||
}
|
||||
func (m *Wrapper) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
|
||||
return xxx_messageInfo_Wrapper.Marshal(b, m, deterministic)
|
||||
}
|
||||
func (dst *Wrapper) XXX_Merge(src proto.Message) {
|
||||
xxx_messageInfo_Wrapper.Merge(dst, src)
|
||||
}
|
||||
func (m *Wrapper) XXX_Size() int {
|
||||
return xxx_messageInfo_Wrapper.Size(m)
|
||||
}
|
||||
func (m *Wrapper) XXX_DiscardUnknown() {
|
||||
xxx_messageInfo_Wrapper.DiscardUnknown(m)
|
||||
}
|
||||
|
||||
var xxx_messageInfo_Wrapper proto.InternalMessageInfo
|
||||
|
||||
func (m *Wrapper) GetVersion() string {
|
||||
if m != nil {
|
||||
|
@ -188,9 +264,9 @@ func init() {
|
|||
proto.RegisterType((*Wrapper)(nil), "openapiextension.v1.Wrapper")
|
||||
}
|
||||
|
||||
func init() { proto.RegisterFile("extension.proto", fileDescriptor0) }
|
||||
func init() { proto.RegisterFile("extension.proto", fileDescriptor_extension_d25f09c742c58c90) }
|
||||
|
||||
var fileDescriptor0 = []byte{
|
||||
var fileDescriptor_extension_d25f09c742c58c90 = []byte{
|
||||
// 357 bytes of a gzipped FileDescriptorProto
|
||||
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x74, 0x91, 0x4d, 0x4b, 0xc3, 0x40,
|
||||
0x18, 0x84, 0x49, 0xbf, 0x62, 0x56, 0x6c, 0x65, 0x2d, 0x1a, 0xc5, 0x43, 0x09, 0x08, 0x45, 0x64,
|
||||
|
|
27
vendor/github.com/googleapis/gnostic/generate-gnostic/generate-compiler.go
generated
vendored
27
vendor/github.com/googleapis/gnostic/generate-gnostic/generate-compiler.go
generated
vendored
|
@ -798,14 +798,17 @@ func (domain *Domain) generateToRawInfoMethodForType(code *printer.Code, typeNam
|
|||
code.Print("return nil")
|
||||
} else {
|
||||
code.Print("info := yaml.MapSlice{}")
|
||||
code.Print("if m == nil {return info}")
|
||||
for _, propertyModel := range typeModel.Properties {
|
||||
isRequired := typeModel.IsRequired(propertyModel.Name)
|
||||
switch propertyModel.Type {
|
||||
case "string":
|
||||
propertyName := propertyModel.Name
|
||||
if !propertyModel.Repeated {
|
||||
code.Print("if m.%s != \"\" {", propertyModel.FieldName())
|
||||
code.PrintIf(isRequired, "// always include this required field.")
|
||||
code.PrintIf(!isRequired, "if m.%s != \"\" {", propertyModel.FieldName())
|
||||
code.Print("info = append(info, yaml.MapItem{Key:\"%s\", Value:m.%s})", propertyName, propertyModel.FieldName())
|
||||
code.Print("}")
|
||||
code.PrintIf(!isRequired, "}")
|
||||
} else {
|
||||
code.Print("if len(m.%s) != 0 {", propertyModel.FieldName())
|
||||
code.Print("info = append(info, yaml.MapItem{Key:\"%s\", Value:m.%s})", propertyName, propertyModel.FieldName())
|
||||
|
@ -814,9 +817,10 @@ func (domain *Domain) generateToRawInfoMethodForType(code *printer.Code, typeNam
|
|||
case "bool":
|
||||
propertyName := propertyModel.Name
|
||||
if !propertyModel.Repeated {
|
||||
code.Print("if m.%s != false {", propertyModel.FieldName())
|
||||
code.PrintIf(isRequired, "// always include this required field.")
|
||||
code.PrintIf(!isRequired, "if m.%s != false {", propertyModel.FieldName())
|
||||
code.Print("info = append(info, yaml.MapItem{Key:\"%s\", Value:m.%s})", propertyName, propertyModel.FieldName())
|
||||
code.Print("}")
|
||||
code.PrintIf(!isRequired, "}")
|
||||
} else {
|
||||
code.Print("if len(m.%s) != 0 {", propertyModel.FieldName())
|
||||
code.Print("info = append(info, yaml.MapItem{Key:\"%s\", Value:m.%s})", propertyName, propertyModel.FieldName())
|
||||
|
@ -825,9 +829,10 @@ func (domain *Domain) generateToRawInfoMethodForType(code *printer.Code, typeNam
|
|||
case "int":
|
||||
propertyName := propertyModel.Name
|
||||
if !propertyModel.Repeated {
|
||||
code.Print("if m.%s != 0 {", propertyModel.FieldName())
|
||||
code.PrintIf(isRequired, "// always include this required field.")
|
||||
code.PrintIf(!isRequired, "if m.%s != 0 {", propertyModel.FieldName())
|
||||
code.Print("info = append(info, yaml.MapItem{Key:\"%s\", Value:m.%s})", propertyName, propertyModel.FieldName())
|
||||
code.Print("}")
|
||||
code.PrintIf(!isRequired, "}")
|
||||
} else {
|
||||
code.Print("if len(m.%s) != 0 {", propertyModel.FieldName())
|
||||
code.Print("info = append(info, yaml.MapItem{Key:\"%s\", Value:m.%s})", propertyName, propertyModel.FieldName())
|
||||
|
@ -836,9 +841,10 @@ func (domain *Domain) generateToRawInfoMethodForType(code *printer.Code, typeNam
|
|||
case "float":
|
||||
propertyName := propertyModel.Name
|
||||
if !propertyModel.Repeated {
|
||||
code.Print("if m.%s != 0.0 {", propertyModel.FieldName())
|
||||
code.PrintIf(isRequired, "// always include this required field.")
|
||||
code.PrintIf(!isRequired, "if m.%s != 0.0 {", propertyModel.FieldName())
|
||||
code.Print("info = append(info, yaml.MapItem{Key:\"%s\", Value:m.%s})", propertyName, propertyModel.FieldName())
|
||||
code.Print("}")
|
||||
code.PrintIf(!isRequired, "}")
|
||||
} else {
|
||||
code.Print("if len(m.%s) != 0 {", propertyModel.FieldName())
|
||||
code.Print("info = append(info, yaml.MapItem{Key:\"%s\", Value:m.%s})", propertyName, propertyModel.FieldName())
|
||||
|
@ -849,7 +855,8 @@ func (domain *Domain) generateToRawInfoMethodForType(code *printer.Code, typeNam
|
|||
if propertyName == "value" {
|
||||
code.Print("// %+v", propertyModel)
|
||||
} else if !propertyModel.Repeated {
|
||||
code.Print("if m.%s != nil {", propertyModel.FieldName())
|
||||
code.PrintIf(isRequired, "// always include this required field.")
|
||||
code.PrintIf(!isRequired, "if m.%s != nil {", propertyModel.FieldName())
|
||||
if propertyModel.Type == "TypeItem" {
|
||||
code.Print("if len(m.Type.Value) == 1 {")
|
||||
code.Print("info = append(info, yaml.MapItem{Key:\"type\", Value:m.Type.Value[0]})")
|
||||
|
@ -870,7 +877,7 @@ func (domain *Domain) generateToRawInfoMethodForType(code *printer.Code, typeNam
|
|||
code.Print("info = append(info, yaml.MapItem{Key:\"%s\", Value:m.%s.ToRawInfo()})",
|
||||
propertyName, propertyModel.FieldName())
|
||||
}
|
||||
code.Print("}")
|
||||
code.PrintIf(!isRequired, "}")
|
||||
code.Print("// %+v", propertyModel)
|
||||
} else if propertyModel.MapType == "string" {
|
||||
code.Print("// %+v", propertyModel)
|
||||
|
|
9
vendor/github.com/googleapis/gnostic/generate-gnostic/types.go
generated
vendored
9
vendor/github.com/googleapis/gnostic/generate-gnostic/types.go
generated
vendored
|
@ -130,3 +130,12 @@ func NewTypeModel() *TypeModel {
|
|||
typeModel.Properties = make([]*TypeProperty, 0)
|
||||
return typeModel
|
||||
}
|
||||
|
||||
func (typeModel *TypeModel) IsRequired(propertyName string) bool {
|
||||
for _, requiredName := range typeModel.Required {
|
||||
if requiredName == propertyName {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
|
6
vendor/github.com/googleapis/gnostic/gnostic.go
generated
vendored
6
vendor/github.com/googleapis/gnostic/gnostic.go
generated
vendored
|
@ -186,8 +186,10 @@ func (p *pluginCall) perform(document proto.Message, sourceFormat int, sourceNam
|
|||
// any logging messages are written to stderr only.
|
||||
return nil, errors.New("Invalid plugin response (plugins must write log messages to stderr, not stdout).")
|
||||
}
|
||||
plugins.HandleResponse(response, outputLocation)
|
||||
return response.Messages, nil
|
||||
|
||||
err = plugins.HandleResponse(response, outputLocation)
|
||||
|
||||
return response.Messages, err
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
|
|
31
vendor/github.com/googleapis/gnostic/gnostic_test.go
generated
vendored
31
vendor/github.com/googleapis/gnostic/gnostic_test.go
generated
vendored
|
@ -10,17 +10,18 @@ import (
|
|||
)
|
||||
|
||||
func testCompiler(t *testing.T, inputFile string, referenceFile string, expectErrors bool) {
|
||||
textFile := strings.Replace(filepath.Base(inputFile), filepath.Ext(inputFile), ".text", 1)
|
||||
outputFormat := filepath.Ext(referenceFile)[1:]
|
||||
outputFile := strings.Replace(filepath.Base(inputFile), filepath.Ext(inputFile), "."+outputFormat, 1)
|
||||
errorsFile := strings.Replace(filepath.Base(inputFile), filepath.Ext(inputFile), ".errors", 1)
|
||||
// remove any preexisting output files
|
||||
os.Remove(textFile)
|
||||
os.Remove(outputFile)
|
||||
os.Remove(errorsFile)
|
||||
// run the compiler
|
||||
var err error
|
||||
var cmd = exec.Command(
|
||||
"gnostic",
|
||||
inputFile,
|
||||
"--text-out=.",
|
||||
"--"+outputFormat+"-out=.",
|
||||
"--errors-out=.",
|
||||
"--resolve-refs")
|
||||
//t.Log(cmd.Args)
|
||||
|
@ -30,19 +31,19 @@ func testCompiler(t *testing.T, inputFile string, referenceFile string, expectEr
|
|||
t.FailNow()
|
||||
}
|
||||
// verify the output against a reference
|
||||
var outputFile string
|
||||
var testFile string
|
||||
if expectErrors {
|
||||
outputFile = errorsFile
|
||||
testFile = errorsFile
|
||||
} else {
|
||||
outputFile = textFile
|
||||
testFile = outputFile
|
||||
}
|
||||
err = exec.Command("diff", outputFile, referenceFile).Run()
|
||||
err = exec.Command("diff", testFile, referenceFile).Run()
|
||||
if err != nil {
|
||||
t.Logf("Diff failed: %+v", err)
|
||||
t.FailNow()
|
||||
} else {
|
||||
// if the test succeeded, clean up
|
||||
os.Remove(textFile)
|
||||
os.Remove(outputFile)
|
||||
os.Remove(errorsFile)
|
||||
}
|
||||
}
|
||||
|
@ -451,3 +452,17 @@ func TestPetstoreJSON_30(t *testing.T) {
|
|||
"examples/v3.0/json/petstore.json",
|
||||
"test/v3.0/petstore.text")
|
||||
}
|
||||
|
||||
// Test that empty required fields are exported.
|
||||
|
||||
func TestEmptyRequiredFields_v2(t *testing.T) {
|
||||
testNormal(t,
|
||||
"examples/v2.0/yaml/empty-v2.yaml",
|
||||
"test/v2.0/json/empty-v2.json")
|
||||
}
|
||||
|
||||
func TestEmptyRequiredFields_v3(t *testing.T) {
|
||||
testNormal(t,
|
||||
"examples/v3.0/yaml/empty-v3.yaml",
|
||||
"test/v3.0/json/empty-v3.json")
|
||||
}
|
||||
|
|
4
vendor/github.com/googleapis/gnostic/plugins/environment.go
generated
vendored
4
vendor/github.com/googleapis/gnostic/plugins/environment.go
generated
vendored
|
@ -105,7 +105,7 @@ When the -plugin option is specified, these flags are ignored.`)
|
|||
env.Request.AddModel("openapi.v2.Document", documentv2)
|
||||
// include experimental API surface model
|
||||
surfaceModel, err := surface.NewModelFromOpenAPI2(documentv2)
|
||||
if err != nil {
|
||||
if err == nil {
|
||||
env.Request.AddModel("surface.v1.Model", surfaceModel)
|
||||
}
|
||||
return env, err
|
||||
|
@ -117,7 +117,7 @@ When the -plugin option is specified, these flags are ignored.`)
|
|||
env.Request.AddModel("openapi.v3.Document", documentv3)
|
||||
// include experimental API surface model
|
||||
surfaceModel, err := surface.NewModelFromOpenAPI3(documentv3)
|
||||
if err != nil {
|
||||
if err == nil {
|
||||
env.Request.AddModel("surface.v1.Model", surfaceModel)
|
||||
}
|
||||
return env, err
|
||||
|
|
9
vendor/github.com/googleapis/gnostic/plugins/gnostic-go-generator/Makefile
generated
vendored
9
vendor/github.com/googleapis/gnostic/plugins/gnostic-go-generator/Makefile
generated
vendored
|
@ -1,9 +0,0 @@
|
|||
|
||||
build:
|
||||
go get golang.org/x/tools/cmd/goimports
|
||||
go install github.com/googleapis/gnostic
|
||||
go install github.com/googleapis/gnostic/plugins/gnostic-go-generator
|
||||
rm -f $(GOPATH)/bin/gnostic-go-client $(GOPATH)/bin/gnostic-go-server
|
||||
ln -s $(GOPATH)/bin/gnostic-go-generator $(GOPATH)/bin/gnostic-go-client
|
||||
ln -s $(GOPATH)/bin/gnostic-go-generator $(GOPATH)/bin/gnostic-go-server
|
||||
|
18
vendor/github.com/googleapis/gnostic/plugins/gnostic-go-generator/README.md
generated
vendored
18
vendor/github.com/googleapis/gnostic/plugins/gnostic-go-generator/README.md
generated
vendored
|
@ -1,18 +0,0 @@
|
|||
# Go Generator Plugin
|
||||
|
||||
This directory contains a `gnostic` plugin that can be used to generate a Go client library and scaffolding for a Go server for an API with an OpenAPI description.
|
||||
|
||||
The plugin can be invoked like this:
|
||||
|
||||
gnostic bookstore.json --go-generator-out=bookstore
|
||||
|
||||
`bookstore` is the name of a directory where the generated code will be written.
|
||||
`bookstore` will also be the package name used for generated code.
|
||||
|
||||
By default, both client and server code will be generated. If the `gnostic-go-generator` binary is also linked from the names `gnostic-go-client` and `gnostic-go-server`, then only client or only server code can be generated as follows:
|
||||
|
||||
gnostic bookstore.json --go-client-out=bookstore
|
||||
|
||||
gnostic bookstore.json --go-server-out=bookstore
|
||||
|
||||
For example usage, see the [examples/v2.0/bookstore](examples/v2.0/bookstore) directory.
|
|
@ -1,31 +0,0 @@
|
|||
# googleauth
|
||||
|
||||
This directory contains support code that can be used to get an OAuth2 token for a Google API user.
|
||||
|
||||
It is designed to work on computers with attached displays.
|
||||
Use it to write command-line tools and test programs that call Google APIs.
|
||||
|
||||
## Instructions
|
||||
|
||||
Import this package and make the following call to request a token.
|
||||
|
||||
client, err := googleauth.NewOAuth2Client(scopes)
|
||||
|
||||
`scopes` should be a string containing the OAuth scopes needed by the APIs to be called.
|
||||
For example, the URL Shortener API would require "https://www.googleapis.com/auth/urlshortener".
|
||||
|
||||
This call will then open a local browser that will redirect to a Google signin page
|
||||
with information about the app that is requesting a token.
|
||||
|
||||
## Application Credentials
|
||||
|
||||
To use this package, you need to download a "client secrets" file and
|
||||
save it as `client_secrets.json` in the directory where your tool is run.
|
||||
|
||||
To get this file, visit the {{ Google Cloud Console }}{{ https://cloud.google.com/console }}
|
||||
and create a project. Then go to the API Manager to enable the APIs that you want to use
|
||||
and create OAuth2 credentials. You'll then be able to download these credentials
|
||||
as JSON. Save this file as `client_secrets.json`
|
||||
|
||||
For more information about the `client_secrets.json` file format, please visit:
|
||||
https://developers.google.com/api-client-library/python/guide/aaa_client_secrets
|
|
@ -1,220 +0,0 @@
|
|||
//
|
||||
// Copyright 2017 Google Inc. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
|
||||
package googleauth
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"flag"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net"
|
||||
"net/http"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
|
||||
"golang.org/x/net/context"
|
||||
"golang.org/x/oauth2"
|
||||
)
|
||||
|
||||
const missingClientSecretsMessage = `
|
||||
Please configure OAuth 2.0
|
||||
|
||||
To make this sample run, you need to populate the client_secrets.json file
|
||||
found at:
|
||||
|
||||
%v
|
||||
|
||||
with information from the {{ Google Cloud Console }}
|
||||
{{ https://cloud.google.com/console }}
|
||||
|
||||
For more information about the client_secrets.json file format, please visit:
|
||||
https://developers.google.com/api-client-library/python/guide/aaa_client_secrets
|
||||
`
|
||||
|
||||
var (
|
||||
clientSecretsFile = flag.String("secrets", "client_secrets.json", "Client Secrets configuration")
|
||||
cacheFile = flag.String("cache", "request.token", "Token cache file")
|
||||
)
|
||||
|
||||
// ClientConfig is a data structure definition for the client_secrets.json file.
|
||||
// The code unmarshals the JSON configuration file into this structure.
|
||||
type ClientConfig struct {
|
||||
ClientID string `json:"client_id"`
|
||||
ClientSecret string `json:"client_secret"`
|
||||
RedirectURIs []string `json:"redirect_uris"`
|
||||
AuthURI string `json:"auth_uri"`
|
||||
TokenURI string `json:"token_uri"`
|
||||
}
|
||||
|
||||
// Config is a root-level configuration object.
|
||||
type Config struct {
|
||||
Installed ClientConfig `json:"installed"`
|
||||
Web ClientConfig `json:"web"`
|
||||
}
|
||||
|
||||
// openURL opens a browser window to the specified location.
|
||||
// This code originally appeared at:
|
||||
// http://stackoverflow.com/questions/10377243/how-can-i-launch-a-process-that-is-not-a-file-in-go
|
||||
func openURL(url string) error {
|
||||
var err error
|
||||
switch runtime.GOOS {
|
||||
case "linux":
|
||||
err = exec.Command("xdg-open", url).Start()
|
||||
case "windows":
|
||||
err = exec.Command("rundll32", "url.dll,FileProtocolHandler", "http://localhost:4001/").Start()
|
||||
case "darwin":
|
||||
err = exec.Command("open", url).Start()
|
||||
default:
|
||||
err = fmt.Errorf("Cannot open URL %s on this platform", url)
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
// readConfig reads the configuration from clientSecretsFile.
|
||||
// It returns an oauth configuration object for use with the Google API client.
|
||||
func readConfig(scopes []string) (*oauth2.Config, error) {
|
||||
// Read the secrets file
|
||||
data, err := ioutil.ReadFile(*clientSecretsFile)
|
||||
if err != nil {
|
||||
pwd, _ := os.Getwd()
|
||||
fullPath := filepath.Join(pwd, *clientSecretsFile)
|
||||
return nil, fmt.Errorf(missingClientSecretsMessage, fullPath)
|
||||
}
|
||||
|
||||
cfg := new(Config)
|
||||
err = json.Unmarshal(data, &cfg)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var redirectURI string
|
||||
if len(cfg.Web.RedirectURIs) > 0 {
|
||||
redirectURI = cfg.Web.RedirectURIs[0]
|
||||
} else if len(cfg.Installed.RedirectURIs) > 0 {
|
||||
redirectURI = cfg.Installed.RedirectURIs[0]
|
||||
} else {
|
||||
return nil, errors.New("Must specify a redirect URI in config file or when creating OAuth client")
|
||||
}
|
||||
|
||||
return &oauth2.Config{
|
||||
ClientID: cfg.Installed.ClientID,
|
||||
ClientSecret: cfg.Installed.ClientSecret,
|
||||
Scopes: scopes,
|
||||
Endpoint: oauth2.Endpoint{cfg.Installed.AuthURI, cfg.Installed.TokenURI},
|
||||
RedirectURL: redirectURI,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// startWebServer starts a web server that listens on http://localhost:8080.
|
||||
// The webserver waits for an oauth code in the three-legged auth flow.
|
||||
func startWebServer() (codeCh chan string, err error) {
|
||||
listener, err := net.Listen("tcp", "localhost:8080")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
codeCh = make(chan string)
|
||||
go http.Serve(listener, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
code := r.FormValue("code")
|
||||
codeCh <- code // send code to OAuth flow
|
||||
listener.Close()
|
||||
w.Header().Set("Content-Type", "text/plain")
|
||||
fmt.Fprintf(w, "Received code: %v\r\nYou can now safely close this browser window.", code)
|
||||
}))
|
||||
return codeCh, nil
|
||||
}
|
||||
|
||||
// NewOAuth2Client takes the user through the three-legged OAuth flow.
|
||||
// It opens a browser in the native OS or outputs a URL, then blocks until
|
||||
// the redirect completes to the /oauth2callback URI.
|
||||
// It returns an instance of an HTTP client that can be passed to the
|
||||
// constructor of an OAuth client.
|
||||
// scopes is a variable number of OAuth scopes
|
||||
func NewOAuth2Client(scopes ...string) (*http.Client, error) {
|
||||
var ctx context.Context
|
||||
tokenSource, err := NewOAuth2TokenSource(scopes...)
|
||||
if err == nil {
|
||||
return oauth2.NewClient(ctx, tokenSource), nil
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// NewOAuth2TokenSource takes the user through the three-legged OAuth flow.
|
||||
// It opens a browser in the native OS or outputs a URL, then blocks until
|
||||
// the redirect completes to the /oauth2callback URI.
|
||||
// It returns an instance of an OAuth token source that can be passed to the
|
||||
// constructor of an OAuth client.
|
||||
// scopes is a variable number of OAuth scopes
|
||||
func NewOAuth2TokenSource(scopes ...string) (oauth2.TokenSource, error) {
|
||||
config, err := readConfig(scopes)
|
||||
if err != nil {
|
||||
msg := fmt.Sprintf("Cannot read configuration file: %v", err)
|
||||
return nil, errors.New(msg)
|
||||
}
|
||||
|
||||
var ctx context.Context
|
||||
|
||||
// Try to read the token from the cache file.
|
||||
// If an error occurs, do the three-legged OAuth flow because
|
||||
// the token is invalid or doesn't exist.
|
||||
//token, err := config.TokenCache.Token()
|
||||
|
||||
var token *oauth2.Token
|
||||
|
||||
data, err := ioutil.ReadFile(*cacheFile)
|
||||
if err == nil {
|
||||
err = json.Unmarshal(data, &token)
|
||||
}
|
||||
if (err != nil) || !token.Valid() {
|
||||
// Start web server.
|
||||
// This is how this program receives the authorization code
|
||||
// when the browser redirects.
|
||||
codeCh, err := startWebServer()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Open url in browser
|
||||
url := config.AuthCodeURL("")
|
||||
err = openURL(url)
|
||||
if err != nil {
|
||||
fmt.Println("Visit the URL below to get a code.",
|
||||
" This program will pause until the site is visted.")
|
||||
} else {
|
||||
fmt.Println("Your browser has been opened to an authorization URL.",
|
||||
" This program will resume once authorization has been provided.\n")
|
||||
}
|
||||
fmt.Println(url)
|
||||
|
||||
// Wait for the web server to get the code.
|
||||
code := <-codeCh
|
||||
|
||||
// This code caches the authorization code on the local
|
||||
// filesystem, if necessary, as long as the TokenCache
|
||||
// attribute in the config is set.
|
||||
token, err = config.Exchange(ctx, code)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
data, err := json.Marshal(token)
|
||||
ioutil.WriteFile(*cacheFile, data, 0644)
|
||||
}
|
||||
return oauth2.StaticTokenSource(token), nil
|
||||
}
|
|
@ -1,4 +0,0 @@
|
|||
|
||||
all:
|
||||
gnostic swagger.yaml --go-client-out=apis_guru
|
||||
go install
|
|
@ -1,45 +0,0 @@
|
|||
// +build ignore
|
||||
// This file is omitted when getting with `go get github.com/googleapis/gnostic/...`
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"sort"
|
||||
|
||||
"github.com/googleapis/gnostic/plugins/gnostic-go-generator/examples/v2.0/apis_guru/apis_guru"
|
||||
)
|
||||
|
||||
func main() {
|
||||
c := apis_guru.NewClient("http://api.apis.guru/v2")
|
||||
|
||||
metrics, err := c.GetMetrics()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
fmt.Printf("%+v\n", metrics)
|
||||
|
||||
apis, err := c.ListAPIs()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
keys := make([]string, 0)
|
||||
for key, _ := range *apis.OK {
|
||||
keys = append(keys, key)
|
||||
}
|
||||
sort.Strings(keys)
|
||||
|
||||
for _, key := range keys {
|
||||
api := (*apis.OK)[key]
|
||||
versions := make([]string, 0)
|
||||
for key, _ := range api.Versions {
|
||||
versions = append(versions, key)
|
||||
}
|
||||
sort.Strings(versions)
|
||||
fmt.Printf("[%s]:%+v\n", key, versions)
|
||||
}
|
||||
|
||||
api := (*apis.OK)["xkcd.com"].Versions["1.0.0"]
|
||||
fmt.Printf("%+v\n", api.SwaggerUrl)
|
||||
}
|
|
@ -1,186 +0,0 @@
|
|||
swagger: '2.0'
|
||||
schemes:
|
||||
- https
|
||||
host: api.apis.guru
|
||||
basePath: /v2/
|
||||
info:
|
||||
contact:
|
||||
email: founders@apis.guru
|
||||
name: APIs.guru
|
||||
url: 'http://APIs.guru'
|
||||
description: |
|
||||
Wikipedia for Web APIs. Repository of API specs in OpenAPI(fka Swagger) 2.0 format.
|
||||
|
||||
**Warning**: If you want to be notified about changes in advance please subscribe to our [Gitter channel](https://gitter.im/APIs-guru/api-models).
|
||||
|
||||
Client sample: [[Demo]](https://apis.guru/simple-ui) [[Repo]](https://github.com/APIs-guru/simple-ui)
|
||||
license:
|
||||
name: CC0 1.0
|
||||
url: 'https://github.com/APIs-guru/api-models#licenses'
|
||||
title: APIs.guru
|
||||
version: '2.0'
|
||||
x-logo:
|
||||
url: 'https://apis.guru/branding/logo_vertical.svg'
|
||||
externalDocs:
|
||||
url: 'https://github.com/APIs-guru/api-models/blob/master/API.md'
|
||||
produces:
|
||||
- application/json
|
||||
security: []
|
||||
paths:
|
||||
/list.json:
|
||||
get:
|
||||
description: |
|
||||
List all APIs in the directory.
|
||||
Returns links to OpenAPI specification for each API in the directory.
|
||||
If API exist in multiply versions `preferred` one is explicitly marked.
|
||||
|
||||
Some basic info from OpenAPI spec is cached inside each object.
|
||||
This allows to generate some simple views without need to fetch OpenAPI spec for each API.
|
||||
operationId: listAPIs
|
||||
responses:
|
||||
'200':
|
||||
description: OK
|
||||
schema:
|
||||
$ref: '#/definitions/APIs'
|
||||
summary: List all APIs
|
||||
/metrics.json:
|
||||
get:
|
||||
description: |
|
||||
Some basic metrics for the entire directory.
|
||||
Just stunning numbers to put on a front page and are intended purely for WoW effect :)
|
||||
operationId: getMetrics
|
||||
responses:
|
||||
'200':
|
||||
description: OK
|
||||
schema:
|
||||
$ref: '#/definitions/Metrics'
|
||||
summary: Get basic metrics
|
||||
definitions:
|
||||
API:
|
||||
additionalProperties: false
|
||||
description: Meta information about API
|
||||
properties:
|
||||
added:
|
||||
description: Timestamp when the API was first added to the directory
|
||||
format: date-time
|
||||
type: string
|
||||
preferred:
|
||||
description: Recommended version
|
||||
type: string
|
||||
versions:
|
||||
additionalProperties:
|
||||
$ref: '#/definitions/ApiVersion'
|
||||
description: List of supported versions of the API
|
||||
minProperties: 1
|
||||
type: object
|
||||
required:
|
||||
- added
|
||||
- preferred
|
||||
- versions
|
||||
type: object
|
||||
APIs:
|
||||
additionalProperties:
|
||||
$ref: '#/definitions/API'
|
||||
description: |
|
||||
List of API details.
|
||||
It is a JSON object with API IDs(`<provider>[:<service>]`) as keys.
|
||||
example:
|
||||
'googleapis.com:drive':
|
||||
added: '2015-02-22T20:00:45.000Z'
|
||||
preferred: v3
|
||||
versions:
|
||||
v2:
|
||||
added: '2015-02-22T20:00:45.000Z'
|
||||
info:
|
||||
title: Drive
|
||||
version: v2
|
||||
x-apiClientRegistration:
|
||||
url: 'https://console.developers.google.com'
|
||||
x-logo:
|
||||
url: 'https://api.apis.guru/v2/cache/logo/https_www.gstatic.com_images_icons_material_product_2x_drive_32dp.png'
|
||||
x-origin:
|
||||
format: google
|
||||
url: 'https://www.googleapis.com/discovery/v1/apis/drive/v2/rest'
|
||||
version: v1
|
||||
x-preferred: false
|
||||
x-providerName: googleapis.com
|
||||
x-serviceName: drive
|
||||
swaggerUrl: 'https://api.apis.guru/v2/specs/googleapis.com/drive/v2/swagger.json'
|
||||
swaggerYamlUrl: 'https://api.apis.guru/v2/specs/googleapis.com/drive/v2/swagger.yaml'
|
||||
updated: '2016-06-17T00:21:44.000Z'
|
||||
v3:
|
||||
added: '2015-12-12T00:25:13.000Z'
|
||||
info:
|
||||
title: Drive
|
||||
version: v3
|
||||
x-apiClientRegistration:
|
||||
url: 'https://console.developers.google.com'
|
||||
x-logo:
|
||||
url: 'https://api.apis.guru/v2/cache/logo/https_www.gstatic.com_images_icons_material_product_2x_drive_32dp.png'
|
||||
x-origin:
|
||||
format: google
|
||||
url: 'https://www.googleapis.com/discovery/v1/apis/drive/v3/rest'
|
||||
version: v1
|
||||
x-preferred: true
|
||||
x-providerName: googleapis.com
|
||||
x-serviceName: drive
|
||||
swaggerUrl: 'https://api.apis.guru/v2/specs/googleapis.com/drive/v3/swagger.json'
|
||||
swaggerYamlUrl: 'https://api.apis.guru/v2/specs/googleapis.com/drive/v3/swagger.yaml'
|
||||
updated: '2016-06-17T00:21:44.000Z'
|
||||
minProperties: 1
|
||||
type: object
|
||||
ApiVersion:
|
||||
additionalProperties: false
|
||||
properties:
|
||||
added:
|
||||
description: Timestamp when the version was added
|
||||
format: date-time
|
||||
type: string
|
||||
info:
|
||||
description: Copy of `info` section from Swagger spec
|
||||
minProperties: 1
|
||||
type: object
|
||||
swaggerUrl:
|
||||
description: URL to Swagger spec in JSON format
|
||||
format: url
|
||||
type: string
|
||||
swaggerYamlUrl:
|
||||
description: URL to Swagger spec in YAML format
|
||||
format: url
|
||||
type: string
|
||||
updated:
|
||||
description: Timestamp when the version was updated
|
||||
format: date-time
|
||||
type: string
|
||||
required:
|
||||
- added
|
||||
- updated
|
||||
- swaggerUrl
|
||||
- swaggerYamlUrl
|
||||
- info
|
||||
type: object
|
||||
Metrics:
|
||||
additionalProperties: false
|
||||
description: List of basic metrics
|
||||
example:
|
||||
numAPIs: 238
|
||||
numEndpoints: 6448
|
||||
numSpecs: 302
|
||||
properties:
|
||||
numAPIs:
|
||||
description: Number of APIs
|
||||
minimum: 1
|
||||
type: integer
|
||||
numEndpoints:
|
||||
description: Total number of endpoints inside all specifications
|
||||
minimum: 1
|
||||
type: integer
|
||||
numSpecs:
|
||||
description: Number of API specifications including different versions of the same API
|
||||
minimum: 1
|
||||
type: integer
|
||||
required:
|
||||
- numSpecs
|
||||
- numAPIs
|
||||
- numEndpoints
|
||||
type: object
|
|
@ -1,20 +0,0 @@
|
|||
build:
|
||||
go get golang.org/x/tools/cmd/goimports
|
||||
go install github.com/googleapis/gnostic
|
||||
go install github.com/googleapis/gnostic/plugins/gnostic-go-generator
|
||||
rm -f $(GOPATH)/bin/gnostic-go-client $(GOPATH)/bin/gnostic-go-server
|
||||
ln -s $(GOPATH)/bin/gnostic-go-generator $(GOPATH)/bin/gnostic-go-client
|
||||
ln -s $(GOPATH)/bin/gnostic-go-generator $(GOPATH)/bin/gnostic-go-server
|
||||
|
||||
all: build
|
||||
gnostic bookstore.json --go-generator-out=bookstore
|
||||
|
||||
clean:
|
||||
rm -rf bookstore bookstore.text service/service
|
||||
|
||||
test: all
|
||||
killall service; true # ignore errors due to no matching processes
|
||||
cd service; go get .; go build; ./service &
|
||||
go test
|
||||
killall service
|
||||
|
|
@ -1,23 +0,0 @@
|
|||
# Bookstore Example
|
||||
|
||||
This directory contains an OpenAPI description of a simple bookstore API.
|
||||
|
||||
Use this example to try the `gnostic-go-generator` plugin, which implements
|
||||
`gnostic-go-client` and `gnostic-go-server` for generating API client and
|
||||
server code, respectively.
|
||||
|
||||
Run "make all" to build and install `gnostic` and the Go plugins.
|
||||
It will generate both client and server code. The API client and
|
||||
server code will be in the `bookstore` package.
|
||||
|
||||
The `service` directory contains additional code that completes the server.
|
||||
To build and run the service, `cd service` and do the following:
|
||||
|
||||
go get .
|
||||
go build
|
||||
./service &
|
||||
|
||||
To test the service with the generated client, go back up to the top-level
|
||||
directory and run `go test`. The test in `bookstore_test.go` uses client
|
||||
code generated in `bookstore` to verify the service.
|
||||
|
|
@ -1,357 +0,0 @@
|
|||
{
|
||||
"swagger": "2.0",
|
||||
"info": {
|
||||
"description": "A simple Bookstore API example.",
|
||||
"title": "Bookstore",
|
||||
"version": "1.0.0"
|
||||
},
|
||||
"host": "generated-bookstore.appspot.com",
|
||||
"basePath": "/",
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"schemes": [
|
||||
"https"
|
||||
],
|
||||
"paths": {
|
||||
"/shelves": {
|
||||
"get": {
|
||||
"description": "Return all shelves in the bookstore.",
|
||||
"operationId": "listShelves",
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "List of shelves in the bookstore.",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/listShelvesResponse"
|
||||
}
|
||||
}
|
||||
},
|
||||
"security": [
|
||||
|
||||
]
|
||||
},
|
||||
"post": {
|
||||
"description": "Create a new shelf in the bookstore.",
|
||||
"operationId": "createShelf",
|
||||
"parameters": [
|
||||
{
|
||||
"description": "A shelf resource to create.",
|
||||
"in": "body",
|
||||
"name": "shelf",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"$ref": "#/definitions/shelf"
|
||||
}
|
||||
}
|
||||
],
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "A newly created shelf resource.",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/shelf"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"delete": {
|
||||
"description": "Delete all shelves.",
|
||||
"operationId": "deleteShelves",
|
||||
"responses": {
|
||||
"default": {
|
||||
"description": "An empty response body."
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/shelves/{shelf}": {
|
||||
"get": {
|
||||
"description": "Get a single shelf resource with the given ID.",
|
||||
"operationId": "getShelf",
|
||||
"parameters": [
|
||||
{
|
||||
"description": "ID of the shelf to get.",
|
||||
"format": "int64",
|
||||
"in": "path",
|
||||
"name": "shelf",
|
||||
"required": true,
|
||||
"type": "integer"
|
||||
}
|
||||
],
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "A shelf resource.",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/shelf"
|
||||
}
|
||||
},
|
||||
"default": {
|
||||
"description": "unexpected error",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/error"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"delete": {
|
||||
"description": "Delete a single shelf with the given ID.",
|
||||
"operationId": "deleteShelf",
|
||||
"parameters": [
|
||||
{
|
||||
"description": "ID of the shelf to delete.",
|
||||
"format": "int64",
|
||||
"in": "path",
|
||||
"name": "shelf",
|
||||
"required": true,
|
||||
"type": "integer"
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"default": {
|
||||
"description": "An empty response body."
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/shelves/{shelf}/books": {
|
||||
"get": {
|
||||
"description": "Return all books in a shelf with the given ID.",
|
||||
"operationId": "listBooks",
|
||||
"parameters": [
|
||||
{
|
||||
"description": "ID of the shelf whose books should be returned.",
|
||||
"format": "int64",
|
||||
"in": "path",
|
||||
"name": "shelf",
|
||||
"required": true,
|
||||
"type": "integer"
|
||||
}
|
||||
],
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "List of books on the specified shelf.",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/listBooksResponse"
|
||||
}
|
||||
},
|
||||
"default": {
|
||||
"description": "unexpected error",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/error"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"post": {
|
||||
"description": "Create a new book on the shelf.",
|
||||
"operationId": "createBook",
|
||||
"parameters": [
|
||||
{
|
||||
"description": "ID of the shelf where the book should be created.",
|
||||
"format": "int64",
|
||||
"in": "path",
|
||||
"name": "shelf",
|
||||
"required": true,
|
||||
"type": "integer"
|
||||
},
|
||||
{
|
||||
"description": "Book to create.",
|
||||
"in": "body",
|
||||
"name": "book",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"$ref": "#/definitions/book"
|
||||
}
|
||||
}
|
||||
],
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "A newly created book resource.",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/book"
|
||||
}
|
||||
},
|
||||
"default": {
|
||||
"description": "unexpected error",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/error"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/shelves/{shelf}/books/{book}": {
|
||||
"get": {
|
||||
"description": "Get a single book with a given ID from a shelf.",
|
||||
"operationId": "getBook",
|
||||
"parameters": [
|
||||
{
|
||||
"description": "ID of the shelf from which to get the book.",
|
||||
"format": "int64",
|
||||
"in": "path",
|
||||
"name": "shelf",
|
||||
"required": true,
|
||||
"type": "integer"
|
||||
},
|
||||
{
|
||||
"description": "ID of the book to get from the shelf.",
|
||||
"format": "int64",
|
||||
"in": "path",
|
||||
"name": "book",
|
||||
"required": true,
|
||||
"type": "integer"
|
||||
}
|
||||
],
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "A book resource.",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/book"
|
||||
}
|
||||
},
|
||||
"default": {
|
||||
"description": "unexpected error",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/error"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"delete": {
|
||||
"description": "Delete a single book with a given ID from a shelf.",
|
||||
"operationId": "deleteBook",
|
||||
"parameters": [
|
||||
{
|
||||
"description": "ID of the shelf from which to delete the book.",
|
||||
"format": "int64",
|
||||
"in": "path",
|
||||
"name": "shelf",
|
||||
"required": true,
|
||||
"type": "integer"
|
||||
},
|
||||
{
|
||||
"description": "ID of the book to delete from the shelf.",
|
||||
"format": "int64",
|
||||
"in": "path",
|
||||
"name": "book",
|
||||
"required": true,
|
||||
"type": "integer"
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"default": {
|
||||
"description": "An empty response body."
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"definitions": {
|
||||
"book": {
|
||||
"properties": {
|
||||
"author": {
|
||||
"type": "string"
|
||||
},
|
||||
"name": {
|
||||
"type": "string"
|
||||
},
|
||||
"title": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"name",
|
||||
"author",
|
||||
"title"
|
||||
]
|
||||
},
|
||||
"listBooksResponse": {
|
||||
"properties": {
|
||||
"books": {
|
||||
"items": {
|
||||
"$ref": "#/definitions/book"
|
||||
},
|
||||
"type": "array"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"books"
|
||||
],
|
||||
"type": "object"
|
||||
},
|
||||
"listShelvesResponse": {
|
||||
"properties": {
|
||||
"shelves": {
|
||||
"items": {
|
||||
"$ref": "#/definitions/shelf"
|
||||
},
|
||||
"type": "array"
|
||||
}
|
||||
},
|
||||
"type": "object"
|
||||
},
|
||||
"shelf": {
|
||||
"properties": {
|
||||
"name": {
|
||||
"type": "string"
|
||||
},
|
||||
"theme": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"name",
|
||||
"theme"
|
||||
]
|
||||
},
|
||||
"error": {
|
||||
"required": [
|
||||
"code",
|
||||
"message"
|
||||
],
|
||||
"properties": {
|
||||
"code": {
|
||||
"type": "integer",
|
||||
"format": "int32"
|
||||
},
|
||||
"message": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"security": [
|
||||
{
|
||||
"api_key": [
|
||||
|
||||
]
|
||||
}
|
||||
],
|
||||
"securityDefinitions": {
|
||||
"api_key": {
|
||||
"in": "query",
|
||||
"name": "key",
|
||||
"type": "apiKey"
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,19 +0,0 @@
|
|||
/*
|
||||
Copyright 2017 Google Inc. All Rights Reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
// Package bookstore exists to allow this repo to work with recursive go get.
|
||||
// It will be filled in with auto generated code.
|
||||
package bookstore
|
|
@ -1,239 +0,0 @@
|
|||
/*
|
||||
Copyright 2017 Google Inc. All Rights Reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/googleapis/gnostic/plugins/gnostic-go-generator/examples/v2.0/bookstore/bookstore"
|
||||
)
|
||||
|
||||
const service = "http://localhost:8080"
|
||||
|
||||
//const service = "http://generated-bookstore.appspot.com"
|
||||
|
||||
func TestBookstore(t *testing.T) {
|
||||
// create a client
|
||||
b := bookstore.NewClient(service, nil)
|
||||
// reset the service by deleting all shelves
|
||||
{
|
||||
err := b.DeleteShelves()
|
||||
if err != nil {
|
||||
t.Log("delete shelves failed")
|
||||
t.Fail()
|
||||
}
|
||||
}
|
||||
// verify that the service has no shelves
|
||||
{
|
||||
response, err := b.ListShelves()
|
||||
if err != nil {
|
||||
t.Log("list shelves failed")
|
||||
t.Fail()
|
||||
}
|
||||
if (response == nil) || (response.OK == nil) || (response.OK.Shelves != nil) {
|
||||
t.Log(fmt.Sprintf("list shelves failed %+v", response.OK))
|
||||
t.Log(fmt.Sprintf("list shelves failed len=%d", len(response.OK.Shelves)))
|
||||
t.Fail()
|
||||
}
|
||||
}
|
||||
// attempting to get a shelf should return an error
|
||||
{
|
||||
_, err := b.GetShelf(1)
|
||||
if err == nil {
|
||||
t.Log("get shelf failed to return an error")
|
||||
t.Fail()
|
||||
}
|
||||
}
|
||||
// attempting to get a book should return an error
|
||||
{
|
||||
_, err := b.GetBook(1, 2)
|
||||
if err == nil {
|
||||
t.Log("get book failed to return an error")
|
||||
t.Fail()
|
||||
}
|
||||
}
|
||||
// add a shelf
|
||||
{
|
||||
var shelf bookstore.Shelf
|
||||
shelf.Theme = "mysteries"
|
||||
response, err := b.CreateShelf(shelf)
|
||||
if err != nil {
|
||||
t.Log("create shelf mysteries failed")
|
||||
t.Fail()
|
||||
}
|
||||
if (response.OK.Name != "shelves/1") ||
|
||||
(response.OK.Theme != "mysteries") {
|
||||
t.Log("create shelf mysteries failed")
|
||||
t.Fail()
|
||||
}
|
||||
}
|
||||
// add another shelf
|
||||
{
|
||||
var shelf bookstore.Shelf
|
||||
shelf.Theme = "comedies"
|
||||
response, err := b.CreateShelf(shelf)
|
||||
if err != nil {
|
||||
t.Log("create shelf comedies failed")
|
||||
t.Fail()
|
||||
}
|
||||
if (response.OK.Name != "shelves/2") ||
|
||||
(response.OK.Theme != "comedies") {
|
||||
t.Log("create shelf comedies failed")
|
||||
t.Fail()
|
||||
}
|
||||
}
|
||||
// get the first shelf that was added
|
||||
{
|
||||
response, err := b.GetShelf(1)
|
||||
if err != nil {
|
||||
t.Log("get shelf mysteries failed")
|
||||
t.Fail()
|
||||
}
|
||||
if (response.OK.Name != "shelves/1") ||
|
||||
(response.OK.Theme != "mysteries") {
|
||||
t.Log("get shelf mysteries failed")
|
||||
t.Fail()
|
||||
}
|
||||
}
|
||||
// list shelves and verify that there are 2
|
||||
{
|
||||
response, err := b.ListShelves()
|
||||
if err != nil {
|
||||
t.Log("list shelves failed")
|
||||
t.Fail()
|
||||
}
|
||||
if len(response.OK.Shelves) != 2 {
|
||||
t.Log("list shelves failed")
|
||||
t.Fail()
|
||||
}
|
||||
}
|
||||
// delete a shelf
|
||||
{
|
||||
err := b.DeleteShelf(2)
|
||||
if err != nil {
|
||||
t.Log("delete shelf failed")
|
||||
t.Fail()
|
||||
}
|
||||
}
|
||||
// list shelves and verify that there is only 1
|
||||
{
|
||||
response, err := b.ListShelves()
|
||||
if err != nil {
|
||||
t.Log("list shelves failed")
|
||||
t.Fail()
|
||||
}
|
||||
if len(response.OK.Shelves) != 1 {
|
||||
t.Log("list shelves failed")
|
||||
t.Fail()
|
||||
}
|
||||
}
|
||||
// list books on a shelf, verify that there are none
|
||||
{
|
||||
response, err := b.ListBooks(1)
|
||||
if err != nil {
|
||||
t.Log("list books failed")
|
||||
t.Fail()
|
||||
}
|
||||
if len(response.OK.Books) != 0 {
|
||||
t.Log("list books failed")
|
||||
t.Fail()
|
||||
}
|
||||
}
|
||||
// create a book
|
||||
{
|
||||
var book bookstore.Book
|
||||
book.Author = "Agatha Christie"
|
||||
book.Title = "And Then There Were None"
|
||||
_, err := b.CreateBook(1, book)
|
||||
if err != nil {
|
||||
t.Log("create book failed")
|
||||
t.Fail()
|
||||
}
|
||||
}
|
||||
// create another book
|
||||
{
|
||||
var book bookstore.Book
|
||||
book.Author = "Agatha Christie"
|
||||
book.Title = "Murder on the Orient Express"
|
||||
_, err := b.CreateBook(1, book)
|
||||
if err != nil {
|
||||
t.Log("create book failed")
|
||||
t.Fail()
|
||||
}
|
||||
}
|
||||
// get the first book that was added
|
||||
{
|
||||
_, err := b.GetBook(1, 1)
|
||||
if err != nil {
|
||||
t.Log("get book failed")
|
||||
t.Fail()
|
||||
}
|
||||
}
|
||||
// list the books on a shelf and verify that there are 2
|
||||
{
|
||||
response, err := b.ListBooks(1)
|
||||
if err != nil {
|
||||
t.Log("list books failed")
|
||||
t.Fail()
|
||||
}
|
||||
if len(response.OK.Books) != 2 {
|
||||
t.Log("list books failed")
|
||||
t.Fail()
|
||||
}
|
||||
}
|
||||
// delete a book
|
||||
{
|
||||
err := b.DeleteBook(1, 2)
|
||||
if err != nil {
|
||||
t.Log("delete book failed")
|
||||
t.Fail()
|
||||
}
|
||||
}
|
||||
// list the books on a shelf and verify that is only 1
|
||||
{
|
||||
response, err := b.ListBooks(1)
|
||||
if err != nil {
|
||||
t.Log("list books failed")
|
||||
t.Fail()
|
||||
}
|
||||
if len(response.OK.Books) != 1 {
|
||||
t.Log("list books failed")
|
||||
t.Fail()
|
||||
}
|
||||
}
|
||||
// verify the handling of a badly-formed request
|
||||
{
|
||||
req, err := http.NewRequest("POST", service+"/shelves", strings.NewReader(""))
|
||||
if err != nil {
|
||||
t.Log("bad request failed")
|
||||
return
|
||||
}
|
||||
resp, err := http.DefaultClient.Do(req)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
// we expect a 400 (Bad Request) code
|
||||
if resp.StatusCode != 400 {
|
||||
t.Log("bad request failed")
|
||||
t.Fail()
|
||||
}
|
||||
return
|
||||
}
|
||||
}
|
|
@ -1,9 +0,0 @@
|
|||
application: bookstore
|
||||
version: 1
|
||||
runtime: go
|
||||
api_version: go1
|
||||
handlers:
|
||||
- url: /.*
|
||||
script: _go_app
|
||||
- url: /
|
||||
static_dir: static
|
|
@ -1,27 +0,0 @@
|
|||
/*
|
||||
Copyright 2017 Google Inc. All Rights Reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"github.com/googleapis/gnostic/plugins/gnostic-go-generator/examples/v2.0/bookstore/bookstore"
|
||||
)
|
||||
|
||||
// init() is called when the package is loaded
|
||||
// this allows this app to be trivially deployed to Google App Engine, which does not call main()
|
||||
func init() {
|
||||
bookstore.Initialize(NewService())
|
||||
}
|
|
@ -1,34 +0,0 @@
|
|||
// +build !appengine
|
||||
|
||||
// This file is omitted when the app is built for Google App Engine
|
||||
|
||||
/*
|
||||
Copyright 2017 Google Inc. All Rights Reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"log"
|
||||
|
||||
"github.com/googleapis/gnostic/plugins/gnostic-go-generator/examples/v2.0/bookstore/bookstore"
|
||||
)
|
||||
|
||||
func main() {
|
||||
err := bookstore.ServeHTTP(":8080")
|
||||
if err != nil {
|
||||
log.Printf("%v", err)
|
||||
}
|
||||
}
|
|
@ -1,195 +0,0 @@
|
|||
/*
|
||||
Copyright 2017 Google Inc. All Rights Reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"sync"
|
||||
|
||||
"github.com/googleapis/gnostic/plugins/gnostic-go-generator/examples/v2.0/bookstore/bookstore"
|
||||
)
|
||||
|
||||
//
|
||||
// The Service type implements a bookstore service.
|
||||
// All objects are managed in an in-memory non-persistent store.
|
||||
//
|
||||
type Service struct {
|
||||
// shelves are stored in a map keyed by shelf id
|
||||
// books are stored in a two level map, keyed first by shelf id and then by book id
|
||||
Shelves map[int64]*bookstore.Shelf
|
||||
Books map[int64]map[int64]*bookstore.Book
|
||||
LastShelfID int64 // the id of the last shelf that was added
|
||||
LastBookID int64 // the id of the last book that was added
|
||||
Mutex sync.Mutex // global mutex to synchronize service access
|
||||
}
|
||||
|
||||
func NewService() *Service {
|
||||
return &Service{
|
||||
Shelves: make(map[int64]*bookstore.Shelf),
|
||||
Books: make(map[int64]map[int64]*bookstore.Book),
|
||||
}
|
||||
}
|
||||
|
||||
func (service *Service) ListShelves(responses *bookstore.ListShelvesResponses) (err error) {
|
||||
service.Mutex.Lock()
|
||||
defer service.Mutex.Unlock()
|
||||
// copy shelf ids from Shelves map keys
|
||||
shelves := make([]bookstore.Shelf, 0, len(service.Shelves))
|
||||
for _, shelf := range service.Shelves {
|
||||
shelves = append(shelves, *shelf)
|
||||
}
|
||||
response := &bookstore.ListShelvesResponse{}
|
||||
response.Shelves = shelves
|
||||
(*responses).OK = response
|
||||
return err
|
||||
}
|
||||
|
||||
func (service *Service) CreateShelf(parameters *bookstore.CreateShelfParameters, responses *bookstore.CreateShelfResponses) (err error) {
|
||||
service.Mutex.Lock()
|
||||
defer service.Mutex.Unlock()
|
||||
// assign an id and name to a shelf and add it to the Shelves map.
|
||||
shelf := parameters.Shelf
|
||||
service.LastShelfID++
|
||||
sid := service.LastShelfID
|
||||
shelf.Name = fmt.Sprintf("shelves/%d", sid)
|
||||
service.Shelves[sid] = &shelf
|
||||
(*responses).OK = &shelf
|
||||
return err
|
||||
}
|
||||
|
||||
func (service *Service) DeleteShelves() (err error) {
|
||||
service.Mutex.Lock()
|
||||
defer service.Mutex.Unlock()
|
||||
// delete everything by reinitializing the Shelves and Books maps.
|
||||
service.Shelves = make(map[int64]*bookstore.Shelf)
|
||||
service.Books = make(map[int64]map[int64]*bookstore.Book)
|
||||
service.LastShelfID = 0
|
||||
service.LastBookID = 0
|
||||
return nil
|
||||
}
|
||||
|
||||
func (service *Service) GetShelf(parameters *bookstore.GetShelfParameters, responses *bookstore.GetShelfResponses) (err error) {
|
||||
service.Mutex.Lock()
|
||||
defer service.Mutex.Unlock()
|
||||
// look up a shelf from the Shelves map.
|
||||
shelf, err := service.getShelf(parameters.Shelf)
|
||||
if err != nil {
|
||||
(*responses).Default = &bookstore.Error{Code: int32(http.StatusNotFound), Message: err.Error()}
|
||||
return nil
|
||||
} else {
|
||||
(*responses).OK = shelf
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func (service *Service) DeleteShelf(parameters *bookstore.DeleteShelfParameters) (err error) {
|
||||
service.Mutex.Lock()
|
||||
defer service.Mutex.Unlock()
|
||||
// delete a shelf by removing the shelf from the Shelves map and the associated books from the Books map.
|
||||
delete(service.Shelves, parameters.Shelf)
|
||||
delete(service.Books, parameters.Shelf)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (service *Service) ListBooks(parameters *bookstore.ListBooksParameters, responses *bookstore.ListBooksResponses) (err error) {
|
||||
service.Mutex.Lock()
|
||||
defer service.Mutex.Unlock()
|
||||
// list the books in a shelf
|
||||
_, err = service.getShelf(parameters.Shelf)
|
||||
if err != nil {
|
||||
(*responses).Default = &bookstore.Error{Code: int32(http.StatusNotFound), Message: err.Error()}
|
||||
return nil
|
||||
}
|
||||
shelfBooks := service.Books[parameters.Shelf]
|
||||
books := make([]bookstore.Book, 0, len(shelfBooks))
|
||||
for _, book := range shelfBooks {
|
||||
books = append(books, *book)
|
||||
}
|
||||
response := &bookstore.ListBooksResponse{}
|
||||
response.Books = books
|
||||
(*responses).OK = response
|
||||
return nil
|
||||
}
|
||||
|
||||
func (service *Service) CreateBook(parameters *bookstore.CreateBookParameters, responses *bookstore.CreateBookResponses) (err error) {
|
||||
service.Mutex.Lock()
|
||||
defer service.Mutex.Unlock()
|
||||
// return "not found" if the shelf doesn't exist
|
||||
shelf, err := service.getShelf(parameters.Shelf)
|
||||
if err != nil {
|
||||
(*responses).Default = &bookstore.Error{Code: int32(http.StatusNotFound), Message: err.Error()}
|
||||
return nil
|
||||
}
|
||||
// assign an id and name to a book and add it to the Books map.
|
||||
service.LastBookID++
|
||||
bid := service.LastBookID
|
||||
book := parameters.Book
|
||||
book.Name = fmt.Sprintf("%s/books/%d", shelf.Name, bid)
|
||||
if service.Books[parameters.Shelf] == nil {
|
||||
service.Books[parameters.Shelf] = make(map[int64]*bookstore.Book)
|
||||
}
|
||||
service.Books[parameters.Shelf][bid] = &book
|
||||
(*responses).OK = &book
|
||||
return err
|
||||
}
|
||||
|
||||
func (service *Service) GetBook(parameters *bookstore.GetBookParameters, responses *bookstore.GetBookResponses) (err error) {
|
||||
service.Mutex.Lock()
|
||||
defer service.Mutex.Unlock()
|
||||
// get a book from the Books map
|
||||
book, err := service.getBook(parameters.Shelf, parameters.Book)
|
||||
if err != nil {
|
||||
(*responses).Default = &bookstore.Error{Code: int32(http.StatusNotFound), Message: err.Error()}
|
||||
} else {
|
||||
(*responses).OK = book
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (service *Service) DeleteBook(parameters *bookstore.DeleteBookParameters) (err error) {
|
||||
service.Mutex.Lock()
|
||||
defer service.Mutex.Unlock()
|
||||
// delete a book by removing the book from the Books map.
|
||||
delete(service.Books[parameters.Shelf], parameters.Book)
|
||||
return nil
|
||||
}
|
||||
|
||||
// internal helpers
|
||||
|
||||
func (service *Service) getShelf(sid int64) (shelf *bookstore.Shelf, err error) {
|
||||
shelf, ok := service.Shelves[sid]
|
||||
if !ok {
|
||||
return nil, errors.New(fmt.Sprintf("Couldn't find shelf %d", sid))
|
||||
} else {
|
||||
return shelf, nil
|
||||
}
|
||||
}
|
||||
|
||||
func (service *Service) getBook(sid int64, bid int64) (book *bookstore.Book, err error) {
|
||||
_, err = service.getShelf(sid)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
book, ok := service.Books[sid][bid]
|
||||
if !ok {
|
||||
return nil, errors.New(fmt.Sprintf("Couldn't find book %d on shelf %d", bid, sid))
|
||||
} else {
|
||||
return book, nil
|
||||
}
|
||||
}
|
|
@ -1,20 +0,0 @@
|
|||
build:
|
||||
go get golang.org/x/tools/cmd/goimports
|
||||
go install github.com/googleapis/gnostic
|
||||
go install github.com/googleapis/gnostic/plugins/gnostic-go-generator
|
||||
rm -f $(GOPATH)/bin/gnostic-go-client $(GOPATH)/bin/gnostic-go-server
|
||||
ln -s $(GOPATH)/bin/gnostic-go-generator $(GOPATH)/bin/gnostic-go-client
|
||||
ln -s $(GOPATH)/bin/gnostic-go-generator $(GOPATH)/bin/gnostic-go-server
|
||||
|
||||
all: build
|
||||
gnostic sample.yaml --go-generator-out=sample
|
||||
|
||||
clean:
|
||||
rm -rf sample service/service
|
||||
|
||||
test: all
|
||||
killall service; true # ignore errors due to no matching processes
|
||||
cd service; go get .; go build; ./service &
|
||||
go test
|
||||
killall service
|
||||
|
|
@ -1,24 +0,0 @@
|
|||
# API Sample
|
||||
|
||||
This directory contains an OpenAPI description of a sample API
|
||||
that exercises various OpenAPI features.
|
||||
|
||||
Use this example to try the `gnostic-go-generator` plugin, which implements
|
||||
`gnostic-go-client` and `gnostic-go-server` for generating API client and
|
||||
server code, respectively.
|
||||
|
||||
Run "make all" to build and install `gnostic` and the Go plugins.
|
||||
It will generate both client and server code. The API client and
|
||||
server code will be in the `sample` package.
|
||||
|
||||
The `service` directory contains additional code that completes the server.
|
||||
To build and run the service, `cd service` and do the following:
|
||||
|
||||
go get .
|
||||
go build
|
||||
./service &
|
||||
|
||||
To test the service with the generated client, go back up to the top-level
|
||||
directory and run `go test`. The test in `sample_test.go` uses client
|
||||
code generated in `sample` to verify the service.
|
||||
|
|
@ -1,67 +0,0 @@
|
|||
swagger: '2.0'
|
||||
schemes:
|
||||
- https
|
||||
host: sample.io
|
||||
basePath: /
|
||||
info:
|
||||
title: sample.io
|
||||
version: '1.0'
|
||||
consumes:
|
||||
- application/json
|
||||
produces:
|
||||
- application/json;charset=UTF-8
|
||||
securityDefinitions:
|
||||
api_key:
|
||||
in: query
|
||||
name: key
|
||||
type: apiKey
|
||||
paths:
|
||||
/sample/{id}:
|
||||
get:
|
||||
operationId: "GetSample"
|
||||
parameters:
|
||||
- description: identifier
|
||||
in: path
|
||||
name: id
|
||||
required: true
|
||||
type: string
|
||||
responses:
|
||||
'200':
|
||||
description: sample response
|
||||
schema:
|
||||
$ref: '#/definitions/Sample'
|
||||
'401':
|
||||
description: User doesn't have a valid session.
|
||||
schema:
|
||||
$ref: '#/definitions/APIError'
|
||||
'404':
|
||||
description: Unable to find supplied extractor ID.
|
||||
schema:
|
||||
$ref: '#/definitions/APIError'
|
||||
security:
|
||||
- api_key: []
|
||||
summary: Get a sample response
|
||||
tags:
|
||||
- sample
|
||||
- demo
|
||||
definitions:
|
||||
APIError:
|
||||
properties:
|
||||
code:
|
||||
description: Internal error code
|
||||
format: int
|
||||
type: integer
|
||||
message:
|
||||
description: A message containing a brief description of the error
|
||||
type: string
|
||||
type: object
|
||||
Sample:
|
||||
properties:
|
||||
id:
|
||||
type: string
|
||||
thing:
|
||||
type: object
|
||||
count:
|
||||
format: int32
|
||||
type: integer
|
||||
type: object
|
|
@ -1,19 +0,0 @@
|
|||
/*
|
||||
Copyright 2017 Google Inc. All Rights Reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
// Package sample exists to allow this repo to work with recursive go get.
|
||||
// It will be filled in with auto generated code.
|
||||
package sample
|
|
@ -1,68 +0,0 @@
|
|||
/*
|
||||
Copyright 2018 Google Inc. All Rights Reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/googleapis/gnostic/plugins/gnostic-go-generator/examples/v2.0/sample/sample"
|
||||
)
|
||||
|
||||
const service = "http://localhost:8080"
|
||||
|
||||
func TestSample(t *testing.T) {
|
||||
// create a client
|
||||
s := sample.NewClient(service, nil)
|
||||
// verify a sample request
|
||||
{
|
||||
message := "hello world"
|
||||
response, err := s.GetSample(message)
|
||||
if err != nil {
|
||||
t.Log("get sample failed")
|
||||
t.Fail()
|
||||
}
|
||||
if response.OK.Id != message || response.OK.Count != int32(len(message)) {
|
||||
t.Log(fmt.Sprintf("get sample received %+v", response.OK))
|
||||
t.Fail()
|
||||
}
|
||||
if (response == nil) || (response.OK == nil) {
|
||||
t.Log(fmt.Sprintf("get sample failed %+v", response.OK))
|
||||
t.Fail()
|
||||
}
|
||||
}
|
||||
// verify the handling of an invalid request
|
||||
{
|
||||
req, err := http.NewRequest("GET", service+"/unsupported", strings.NewReader(""))
|
||||
if err != nil {
|
||||
t.Log("bad request failed")
|
||||
return
|
||||
}
|
||||
resp, err := http.DefaultClient.Do(req)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
// we expect a 404 (Not Found) code
|
||||
if resp.StatusCode != 404 {
|
||||
t.Log("bad request failed")
|
||||
t.Fail()
|
||||
}
|
||||
return
|
||||
}
|
||||
}
|
|
@ -1,9 +0,0 @@
|
|||
application: sample
|
||||
version: 1
|
||||
runtime: go
|
||||
api_version: go1
|
||||
handlers:
|
||||
- url: /.*
|
||||
script: _go_app
|
||||
- url: /
|
||||
static_dir: static
|
|
@ -1,27 +0,0 @@
|
|||
/*
|
||||
Copyright 2018 Google Inc. All Rights Reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"github.com/googleapis/gnostic/plugins/gnostic-go-generator/examples/v2.0/sample/sample"
|
||||
)
|
||||
|
||||
// init() is called when the package is loaded
|
||||
// this allows this app to be trivially deployed to Google App Engine, which does not call main()
|
||||
func init() {
|
||||
sample.Initialize(NewService())
|
||||
}
|
|
@ -1,34 +0,0 @@
|
|||
// +build !appengine
|
||||
|
||||
// This file is omitted when the app is built for Google App Engine
|
||||
|
||||
/*
|
||||
Copyright 2018 Google Inc. All Rights Reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"log"
|
||||
|
||||
"github.com/googleapis/gnostic/plugins/gnostic-go-generator/examples/v2.0/sample/sample"
|
||||
)
|
||||
|
||||
func main() {
|
||||
err := sample.ServeHTTP(":8080")
|
||||
if err != nil {
|
||||
log.Printf("%v", err)
|
||||
}
|
||||
}
|
|
@ -1,38 +0,0 @@
|
|||
/*
|
||||
Copyright 2018 Google Inc. All Rights Reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"github.com/googleapis/gnostic/plugins/gnostic-go-generator/examples/v2.0/sample/sample"
|
||||
)
|
||||
|
||||
//
|
||||
// The Service type implements a sample service.
|
||||
//
|
||||
type Service struct{}
|
||||
|
||||
func NewService() *Service {
|
||||
return &Service{}
|
||||
}
|
||||
|
||||
func (service *Service) GetSample(parameters *sample.GetSampleParameters, responses *sample.GetSampleResponses) (err error) {
|
||||
(*responses).OK = &sample.Sample{
|
||||
Id: parameters.Id,
|
||||
Thing: map[string]interface{}{"thing": 123},
|
||||
Count: int32(len(parameters.Id))}
|
||||
return err
|
||||
}
|
|
@ -1,3 +0,0 @@
|
|||
all:
|
||||
gnostic swagger.json --go-client-out=xkcd
|
||||
go install
|
|
@ -1,23 +0,0 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/googleapis/gnostic/plugins/gnostic-go-generator/examples/v2.0/xkcd/xkcd"
|
||||
)
|
||||
|
||||
func main() {
|
||||
c := xkcd.NewClient("http://xkcd.com")
|
||||
|
||||
comic, err := c.Get_info_0_json()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
fmt.Printf("%+v\n", comic)
|
||||
|
||||
comic, err = c.Get_comicId_info_0_json(1800)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
fmt.Printf("%+v\n", comic)
|
||||
}
|
|
@ -1,111 +0,0 @@
|
|||
{
|
||||
"swagger": "2.0",
|
||||
"schemes": [
|
||||
"http"
|
||||
],
|
||||
"host": "xkcd.com",
|
||||
"basePath": "/",
|
||||
"info": {
|
||||
"description": "Webcomic of romance, sarcasm, math, and language.",
|
||||
"title": "XKCD",
|
||||
"version": "1.0.0",
|
||||
"x-apisguru-categories": [
|
||||
"media"
|
||||
],
|
||||
"x-logo": {
|
||||
"url": "https://api.apis.guru/v2/cache/logo/http_imgs.xkcd.com_static_terrible_small_logo.png"
|
||||
},
|
||||
"x-origin": {
|
||||
"format": "swagger",
|
||||
"url": "https://raw.githubusercontent.com/APIs-guru/unofficial_openapi_specs/master/xkcd.com/1.0.0/swagger.yaml",
|
||||
"version": "2.0"
|
||||
},
|
||||
"x-preferred": true,
|
||||
"x-providerName": "xkcd.com",
|
||||
"x-tags": [
|
||||
"humor",
|
||||
"comics"
|
||||
],
|
||||
"x-unofficialSpec": true
|
||||
},
|
||||
"externalDocs": {
|
||||
"url": "https://xkcd.com/json.html"
|
||||
},
|
||||
"securityDefinitions": {},
|
||||
"paths": {
|
||||
"/info.0.json": {
|
||||
"get": {
|
||||
"description": "Fetch current comic and metadata.\n",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/comic"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/{comicId}/info.0.json": {
|
||||
"get": {
|
||||
"description": "Fetch comics and metadata by comic id.\n",
|
||||
"parameters": [
|
||||
{
|
||||
"in": "path",
|
||||
"name": "comicId",
|
||||
"required": true,
|
||||
"type": "number"
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/comic"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"definitions": {
|
||||
"comic": {
|
||||
"properties": {
|
||||
"alt": {
|
||||
"type": "string"
|
||||
},
|
||||
"day": {
|
||||
"type": "string"
|
||||
},
|
||||
"img": {
|
||||
"type": "string"
|
||||
},
|
||||
"link": {
|
||||
"type": "string"
|
||||
},
|
||||
"month": {
|
||||
"type": "string"
|
||||
},
|
||||
"news": {
|
||||
"type": "string"
|
||||
},
|
||||
"num": {
|
||||
"type": "number"
|
||||
},
|
||||
"safe_title": {
|
||||
"type": "string"
|
||||
},
|
||||
"title": {
|
||||
"type": "string"
|
||||
},
|
||||
"transcript": {
|
||||
"type": "string"
|
||||
},
|
||||
"year": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"type": "object"
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,19 +0,0 @@
|
|||
/*
|
||||
Copyright 2017 Google Inc. All Rights Reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
// Package xkcd exists to allow this repo to work with recursive go get.
|
||||
// It will be filled in with auto generated code.
|
||||
package xkcd
|
|
@ -1,20 +0,0 @@
|
|||
build:
|
||||
go get golang.org/x/tools/cmd/goimports
|
||||
go install github.com/googleapis/gnostic
|
||||
go install github.com/googleapis/gnostic/plugins/gnostic-go-generator
|
||||
rm -f $(GOPATH)/bin/gnostic-go-client $(GOPATH)/bin/gnostic-go-server
|
||||
ln -s $(GOPATH)/bin/gnostic-go-generator $(GOPATH)/bin/gnostic-go-client
|
||||
ln -s $(GOPATH)/bin/gnostic-go-generator $(GOPATH)/bin/gnostic-go-server
|
||||
|
||||
all: build
|
||||
gnostic bookstore.json --go-generator-out=bookstore
|
||||
|
||||
clean:
|
||||
rm -rf bookstore bookstore.text service/service
|
||||
|
||||
test: all
|
||||
killall service; true # ignore errors due to no matching processes
|
||||
cd service; go get .; go build; ./service &
|
||||
go test
|
||||
killall service
|
||||
|
|
@ -1,23 +0,0 @@
|
|||
# Bookstore Example
|
||||
|
||||
This directory contains an OpenAPI description of a simple bookstore API.
|
||||
|
||||
Use this example to try the `gnostic-go-generator` plugin, which implements
|
||||
`gnostic-go-client` and `gnostic-go-server` for generating API client and
|
||||
server code, respectively.
|
||||
|
||||
Run "make all" to build and install `gnostic` and the Go plugins.
|
||||
It will generate both client and server code. The API client and
|
||||
server code will be in the `bookstore` package.
|
||||
|
||||
The `service` directory contains additional code that completes the server.
|
||||
To build and run the service, `cd service` and do the following:
|
||||
|
||||
go get .
|
||||
go build
|
||||
./service &
|
||||
|
||||
To test the service with the generated client, go back up to the top-level
|
||||
directory and run `go test`. The test in `bookstore_test.go` uses client
|
||||
code generated in `bookstore` to verify the service.
|
||||
|
|
@ -1,392 +0,0 @@
|
|||
{
|
||||
"openapi": "3.0.0",
|
||||
"servers": [
|
||||
{
|
||||
"url": "https://generated-bookstore.appspot.com/"
|
||||
}
|
||||
],
|
||||
"info": {
|
||||
"description": "A simple Bookstore API example.",
|
||||
"title": "Bookstore",
|
||||
"version": "1.0.0"
|
||||
},
|
||||
"paths": {
|
||||
"/shelves": {
|
||||
"get": {
|
||||
"description": "Return all shelves in the bookstore.",
|
||||
"operationId": "listShelves",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "List of shelves in the bookstore.",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/listShelvesResponse"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"security": []
|
||||
},
|
||||
"post": {
|
||||
"description": "Create a new shelf in the bookstore.",
|
||||
"operationId": "createShelf",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "A newly created shelf resource.",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/shelf"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"requestBody": {
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/shelf"
|
||||
}
|
||||
}
|
||||
},
|
||||
"description": "A shelf resource to create.",
|
||||
"required": true
|
||||
}
|
||||
},
|
||||
"delete": {
|
||||
"description": "Delete all shelves.",
|
||||
"operationId": "deleteShelves",
|
||||
"responses": {
|
||||
"default": {
|
||||
"description": "An empty response body."
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/shelves/{shelf}": {
|
||||
"get": {
|
||||
"description": "Get a single shelf resource with the given ID.",
|
||||
"operationId": "getShelf",
|
||||
"parameters": [
|
||||
{
|
||||
"description": "ID of the shelf to get.",
|
||||
"in": "path",
|
||||
"name": "shelf",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"type": "integer",
|
||||
"format": "int64"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "A shelf resource.",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/shelf"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"default": {
|
||||
"description": "unexpected error",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/error"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"delete": {
|
||||
"description": "Delete a single shelf with the given ID.",
|
||||
"operationId": "deleteShelf",
|
||||
"parameters": [
|
||||
{
|
||||
"description": "ID of the shelf to delete.",
|
||||
"in": "path",
|
||||
"name": "shelf",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"type": "integer",
|
||||
"format": "int64"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"default": {
|
||||
"description": "An empty response body."
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/shelves/{shelf}/books": {
|
||||
"get": {
|
||||
"description": "Return all books in a shelf with the given ID.",
|
||||
"operationId": "listBooks",
|
||||
"parameters": [
|
||||
{
|
||||
"description": "ID of the shelf whose books should be returned.",
|
||||
"in": "path",
|
||||
"name": "shelf",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"type": "integer",
|
||||
"format": "int64"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "List of books on the specified shelf.",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/listBooksResponse"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"default": {
|
||||
"description": "unexpected error",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/error"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"post": {
|
||||
"description": "Create a new book on the shelf.",
|
||||
"operationId": "createBook",
|
||||
"parameters": [
|
||||
{
|
||||
"description": "ID of the shelf where the book should be created.",
|
||||
"in": "path",
|
||||
"name": "shelf",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"type": "integer",
|
||||
"format": "int64"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "A newly created book resource.",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/book"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"default": {
|
||||
"description": "unexpected error",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/error"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"requestBody": {
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/book"
|
||||
}
|
||||
}
|
||||
},
|
||||
"description": "Book to create.",
|
||||
"required": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"/shelves/{shelf}/books/{book}": {
|
||||
"get": {
|
||||
"description": "Get a single book with a given ID from a shelf.",
|
||||
"operationId": "getBook",
|
||||
"parameters": [
|
||||
{
|
||||
"description": "ID of the shelf from which to get the book.",
|
||||
"in": "path",
|
||||
"name": "shelf",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"type": "integer",
|
||||
"format": "int64"
|
||||
}
|
||||
},
|
||||
{
|
||||
"description": "ID of the book to get from the shelf.",
|
||||
"in": "path",
|
||||
"name": "book",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"type": "integer",
|
||||
"format": "int64"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "A book resource.",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/book"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"default": {
|
||||
"description": "unexpected error",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/error"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"delete": {
|
||||
"description": "Delete a single book with a given ID from a shelf.",
|
||||
"operationId": "deleteBook",
|
||||
"parameters": [
|
||||
{
|
||||
"description": "ID of the shelf from which to delete the book.",
|
||||
"in": "path",
|
||||
"name": "shelf",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"type": "integer",
|
||||
"format": "int64"
|
||||
}
|
||||
},
|
||||
{
|
||||
"description": "ID of the book to delete from the shelf.",
|
||||
"in": "path",
|
||||
"name": "book",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"type": "integer",
|
||||
"format": "int64"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"default": {
|
||||
"description": "An empty response body."
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"security": [
|
||||
{
|
||||
"api_key": []
|
||||
}
|
||||
],
|
||||
"components": {
|
||||
"schemas": {
|
||||
"book": {
|
||||
"properties": {
|
||||
"author": {
|
||||
"type": "string"
|
||||
},
|
||||
"name": {
|
||||
"type": "string"
|
||||
},
|
||||
"title": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"name",
|
||||
"author",
|
||||
"title"
|
||||
],
|
||||
"type": "object"
|
||||
},
|
||||
"listBooksResponse": {
|
||||
"properties": {
|
||||
"books": {
|
||||
"items": {
|
||||
"$ref": "#/components/schemas/book"
|
||||
},
|
||||
"type": "array"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"books"
|
||||
],
|
||||
"type": "object"
|
||||
},
|
||||
"listShelvesResponse": {
|
||||
"properties": {
|
||||
"shelves": {
|
||||
"items": {
|
||||
"$ref": "#/components/schemas/shelf"
|
||||
},
|
||||
"type": "array"
|
||||
}
|
||||
},
|
||||
"type": "object"
|
||||
},
|
||||
"shelf": {
|
||||
"properties": {
|
||||
"name": {
|
||||
"type": "string"
|
||||
},
|
||||
"theme": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"name",
|
||||
"theme"
|
||||
],
|
||||
"type": "object"
|
||||
},
|
||||
"error": {
|
||||
"required": [
|
||||
"code",
|
||||
"message"
|
||||
],
|
||||
"properties": {
|
||||
"code": {
|
||||
"type": "integer",
|
||||
"format": "int32"
|
||||
},
|
||||
"message": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"type": "object"
|
||||
}
|
||||
},
|
||||
"securitySchemes": {
|
||||
"api_key": {
|
||||
"in": "query",
|
||||
"name": "key",
|
||||
"type": "apiKey"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,19 +0,0 @@
|
|||
/*
|
||||
Copyright 2017 Google Inc. All Rights Reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
// Package bookstore exists to allow this repo to work with recursive go get.
|
||||
// It will be filled in with auto generated code.
|
||||
package bookstore
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Reference in a new issue