From 7e0098146b290818d69785b2e083d1393defcc78 Mon Sep 17 00:00:00 2001 From: GLYASAI Date: Thu, 6 Feb 2020 22:16:00 +0800 Subject: [PATCH 01/47] discovery registry IP --- cmd/node/option/conf.go | 8 ++-- cmd/node/server/server.go | 16 +++---- node/initiate/host.go | 86 ++++++++++++++++++++++++++++------ node/kubecache/kube_cache.go | 90 ++++++++++++++++++++++++++++++++---- 4 files changed, 166 insertions(+), 34 deletions(-) diff --git a/cmd/node/option/conf.go b/cmd/node/option/conf.go index 877cf2d27..632cf73a1 100644 --- a/cmd/node/option/conf.go +++ b/cmd/node/option/conf.go @@ -121,8 +121,9 @@ type Conf struct { // ImageGCPeriod is the period for performing image garbage collection. ImageGCPeriod time.Duration - ImageRepositoryIPAddress string - ImageRepositoryHost string + // Namespace for Rainbond application. + RbdNamespace string + ImageRepositoryHost string } //StatsdConfig StatsdConfig @@ -182,8 +183,9 @@ func (a *Conf) AddFlags(fs *pflag.FlagSet) { fs.DurationVar(&a.ImageGCPeriod, "image-gc-period", 5*time.Minute, "ImageGCPeriod is the period for performing image garbage collection. Examples: '10s', '5m' or '2h45m'.") fs.Int32Var(&a.ImageGCHighThresholdPercent, "image-gc-high-threshold", 90, "The percent of disk usage after which image garbage collection is always run. Values must be within the range [0, 100], To disable image garbage collection, set to 100. ") fs.Int32Var(&a.ImageGCLowThresholdPercent, "image-gc-low-threshold", 75, "The percent of disk usage before which image garbage collection is never run. Lowest disk usage to garbage collect to. Values must be within the range [0, 100] and should not be larger than that of --image-gc-high-threshold.") - fs.StringVar(&a.ImageRepositoryIPAddress, "image-repo-ip", "", "The ip address of image repository") + fs.StringVar(&a.RbdNamespace, "rbd-ns", "rbd-system", "The namespace of rainbond applications.") fs.StringVar(&a.ImageRepositoryHost, "image-repo-host", "goodrain.me", "The host of image repository") + } //SetLog 设置log diff --git a/cmd/node/server/server.go b/cmd/node/server/server.go index 06d9d0a64..9f0e77de6 100644 --- a/cmd/node/server/server.go +++ b/cmd/node/server/server.go @@ -21,6 +21,8 @@ package server import ( "context" "fmt" + "github.com/eapache/channels" + "github.com/goodrain/rainbond/node/initiate" "os" "os/signal" "syscall" @@ -30,7 +32,6 @@ import ( "github.com/goodrain/rainbond/node/api" "github.com/goodrain/rainbond/node/api/controller" "github.com/goodrain/rainbond/node/core/store" - "github.com/goodrain/rainbond/node/initiate" "github.com/goodrain/rainbond/node/kubecache" "github.com/goodrain/rainbond/node/masterserver" "github.com/goodrain/rainbond/node/nodem" @@ -64,11 +65,6 @@ func Run(c *option.Conf) error { return fmt.Errorf("config parse error:%s", err.Error()) } - hostManager := initiate.NewHostManager(c.ImageRepositoryIPAddress, c.ImageRepositoryHost) - if err := hostManager.CleanupAndFlush(); err != nil { - logrus.Errorf("error writing image repository resolve: %v", err) - } - nodemanager, err := nodem.NewNodeManager(c) if err != nil { return fmt.Errorf("create node manager failed: %s", err) @@ -87,13 +83,17 @@ func Run(c *option.Conf) error { } defer eventLog.CloseManager() logrus.Debug("create and start event log client success") - kubecli, err := kubecache.NewKubeClient(c) + + registryUpdateCh := channels.NewRingChannel(1024) + kubecli, err := kubecache.NewKubeClient(c, registryUpdateCh) if err != nil { return err } defer kubecli.Stop() - logrus.Debug("create and start kube cache moudle success") + hostManager := initiate.NewHostManager(c, kubecli, registryUpdateCh) + hostManager.Start() + defer hostManager.Stop() logrus.Debugf("rbd-namespace=%s; rbd-docker-secret=%s", os.Getenv("RBD_NAMESPACE"), os.Getenv("RBD_DOCKER_SECRET")) // sync docker inscure registries cert info into all rainbond node diff --git a/node/initiate/host.go b/node/initiate/host.go index 4fb096538..6f47a14ef 100644 --- a/node/initiate/host.go +++ b/node/initiate/host.go @@ -27,6 +27,14 @@ import ( "net" "os" "strings" + + "github.com/eapache/channels" + "github.com/goodrain/rainbond/cmd/node/option" + "github.com/goodrain/rainbond/node/kubecache" + + "github.com/Sirupsen/logrus" + corev1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/labels" ) const ( @@ -36,28 +44,77 @@ const ( eol = "\n" ) +var ( + // ErrRegistryAddressNotFound record not found error, happens when haven't find any matched registry address. + ErrRegistryAddressNotFound = errors.New("registry address not found") +) + // HostManager is responsible for writing the resolution of the private image repository domain name to /etc/hosts. -type HostManager struct { - // private image repository domain name - Domain string - // private image repository IP address - IP string +type HostManager interface { + Start() + Stop() } // NewHostManager creates a new HostManager. -func NewHostManager(ip, domain string) *HostManager { - return &HostManager{ - Domain: domain, - IP: ip, +func NewHostManager(cfg *option.Conf, kubecli kubecache.KubeClient, registryUpdateCh *channels.RingChannel) HostManager { + return &hostManager{ + registryUpdateCh: registryUpdateCh, + stopCh: make(chan struct{}), + cfg: cfg, + kubecli: kubecli, } } -// CleanupAndFlush cleanup old content and write new ones. -func (h *HostManager) CleanupAndFlush() error { - if h.IP == "" { - return nil +type hostManager struct { + registryUpdateCh *channels.RingChannel + stopCh chan struct{} + + cfg *option.Conf + kubecli kubecache.KubeClient +} + +func (h *hostManager) Start() { + for { + select { + case <-h.registryUpdateCh.Out(): + // ignore all error. If an error occurs, do not write the hosts file + ep, err := h.getEndpointForRegistry() + if err != nil { + logrus.Warningf("finding endpoint for registry: %v", err) + break + } + if err := h.CleanupAndFlush(ep, h.cfg.ImageRepositoryHost); err != nil { + logrus.Warningf("clean and flush /etc/hosts: %v", err) + } + case <-h.stopCh: + return + } + } +} + +func (h *hostManager) Stop() { + close(h.stopCh) +} + +func (h *hostManager) getEndpointForRegistry() (string, error) { + selector, _ := labels.Parse("name=rbd-hub") + pods, err := h.kubecli.GetPodsBySelector(h.cfg.RbdNamespace, selector) + if err != nil { + return "", err + } + for _, po := range pods { + for _, cdt := range po.Status.Conditions { + if cdt.Type == corev1.PodReady && cdt.Status == corev1.ConditionTrue { + return po.Status.HostIP, nil + } + } } + return "", ErrRegistryAddressNotFound +} + +// CleanupAndFlush cleanup old content and write new ones. +func (h *hostManager) CleanupAndFlush(ip, domain string) error { hosts, err := NewHosts() if err != nil { return fmt.Errorf("error creating hosts: %v", err) @@ -68,9 +125,8 @@ func (h *HostManager) CleanupAndFlush() error { } lines := []string{ - "\n", startOfSection, - h.IP + " " + h.Domain, + ip + " " + domain, endOfSection, } hosts.AddLines(lines...) diff --git a/node/kubecache/kube_cache.go b/node/kubecache/kube_cache.go index 16d1f4fc3..348068e71 100644 --- a/node/kubecache/kube_cache.go +++ b/node/kubecache/kube_cache.go @@ -20,6 +20,9 @@ package kubecache import ( "fmt" + "github.com/eapache/channels" + "k8s.io/apimachinery/pkg/labels" + "k8s.io/client-go/tools/cache" "math" "strings" "time" @@ -31,26 +34,53 @@ import ( "github.com/Sirupsen/logrus" k8sutil "github.com/goodrain/rainbond/util/k8s" + corev1 "k8s.io/api/core/v1" v1 "k8s.io/api/core/v1" "k8s.io/api/policy/v1beta1" apierrors "k8s.io/apimachinery/pkg/api/errors" "k8s.io/apimachinery/pkg/api/resource" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/fields" - "k8s.io/apimachinery/pkg/labels" "k8s.io/apimachinery/pkg/types" "k8s.io/apimachinery/pkg/util/wait" "k8s.io/client-go/informers" "k8s.io/client-go/kubernetes" ) +// EventType - +type EventType string + const ( //EvictionKind EvictionKind EvictionKind = "Eviction" //EvictionSubresource EvictionSubresource EvictionSubresource = "pods/eviction" + // CreateEvent event associated with new objects in an informer + CreateEvent EventType = "CREATE" + // UpdateEvent event associated with an object update in an informer + UpdateEvent EventType = "UPDATE" + // DeleteEvent event associated when an object is removed from an informer + DeleteEvent EventType = "DELETE" ) +// Event holds the context of an event. +type Event struct { + Type EventType + Obj interface{} +} + +type l map[string]string + +func (l l) contains(k, v string) bool { + if l == nil { + return false + } + if val, ok := l[k]; !ok || val != v { + return false + } + return true +} + //KubeClient KubeClient type KubeClient interface { GetKubeClient() kubernetes.Interface @@ -58,6 +88,7 @@ type KubeClient interface { DownK8sNode(nodename string) error GetAllPods() (pods []*v1.Pod, err error) GetPods(namespace string) (pods []*v1.Pod, err error) + GetPodsBySelector(namespace string, selector labels.Selector) (pods []*v1.Pod, err error) GetNodeByName(nodename string) (*v1.Node, error) GetNodes() ([]*v1.Node, error) GetNode(nodeName string) (*v1.Node, error) @@ -72,7 +103,7 @@ type KubeClient interface { } //NewKubeClient NewKubeClient -func NewKubeClient(cfg *conf.Conf) (KubeClient, error) { +func NewKubeClient(cfg *conf.Conf, registryUpdateCh *channels.RingChannel) (KubeClient, error) { config, err := k8sutil.NewRestConfig(cfg.K8SConfPath) if err != nil { return nil, err @@ -86,15 +117,53 @@ func NewKubeClient(cfg *conf.Conf) (KubeClient, error) { } stop := make(chan struct{}) - sharedInformers := informers.NewFilteredSharedInformerFactory(cli, cfg.MinResyncPeriod, v1.NamespaceAll, - func(options *metav1.ListOptions) { - //options.LabelSelector = "creator=Rainbond" - }) - sharedInformers.Core().V1().Services().Informer() + sharedInformers := informers.NewSharedInformerFactoryWithOptions(cli, cfg.MinResyncPeriod) + + // Pod Event Handler + podEventHandler := cache.ResourceEventHandlerFuncs{ + AddFunc: func(obj interface{}) { + ep := obj.(*corev1.Pod) + if !l(ep.Labels).contains("name", "rbd-hub") { + return + } + registryUpdateCh.In() <- Event{ + Type: CreateEvent, + Obj: obj, + } + }, + DeleteFunc: func(obj interface{}) { + ep := obj.(*corev1.Pod) + if !l(ep.Labels).contains("name", "rbd-hub") { + return + } + registryUpdateCh.In() <- Event{ + Type: DeleteEvent, + Obj: obj, + } + }, + UpdateFunc: func(old, cur interface{}) { + oldPod := old.(*corev1.Pod) + curPod := cur.(*corev1.Pod) + + if oldPod.Status.Phase == curPod.Status.Phase { + return + } + + if !l(curPod.Labels).contains("name", "rbd-hub") { + return + } + registryUpdateCh.In() <- Event{ + Type: UpdateEvent, + Obj: cur, + } + }, + } + sharedInformers.Core().V1().Endpoints().Informer() + sharedInformers.Core().V1().Services().Informer() sharedInformers.Core().V1().ConfigMaps().Informer() sharedInformers.Core().V1().Nodes().Informer() - sharedInformers.Core().V1().Pods().Informer() + sharedInformers.Core().V1().Pods().Informer().AddEventHandler(podEventHandler) sharedInformers.Start(stop) return &kubeClient{ kubeclient: cli, @@ -107,6 +176,7 @@ type kubeClient struct { kubeclient kubernetes.Interface sharedInformers informers.SharedInformerFactory stop chan struct{} + updateCh *channels.RingChannel } func (k *kubeClient) Stop() { @@ -453,6 +523,10 @@ func (k *kubeClient) UpK8sNode(rainbondNode *client.HostNode) (*v1.Node, error) return node, nil } +func (k *kubeClient) GetPodsBySelector(namespace string, selector labels.Selector) ([]*v1.Pod, error) { + return k.sharedInformers.Core().V1().Pods().Lister().Pods(namespace).List(selector) +} + func (k *kubeClient) GetEndpoints(namespace string, selector labels.Selector) ([]*v1.Endpoints, error) { return k.sharedInformers.Core().V1().Endpoints().Lister().Endpoints(namespace).List(selector) } From 3766f72f075d7e97a3a291936947cea33593ce32 Mon Sep 17 00:00:00 2001 From: barnettZQG Date: Fri, 7 Feb 2020 11:10:17 +0800 Subject: [PATCH 02/47] update grctl entrypoint shell and copy api client pem --- hack/contrib/docker/grctl/Dockerfile | 2 +- hack/contrib/docker/grctl/entrypoint.sh | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/hack/contrib/docker/grctl/Dockerfile b/hack/contrib/docker/grctl/Dockerfile index e3d320cb6..c10fcf5a3 100644 --- a/hack/contrib/docker/grctl/Dockerfile +++ b/hack/contrib/docker/grctl/Dockerfile @@ -1,6 +1,6 @@ FROM goodrainapps/alpine:3.4 COPY . /run RUN chmod +x /run/rainbond-grctl /run/entrypoint.sh -VOLUME [ "/rootfs" ] +VOLUME [ "/rootfs/root","/rootfs/path","/ssl" ] ENV RELEASE_DESC=__RELEASE_DESC__ ENTRYPOINT ["/run/entrypoint.sh"] \ No newline at end of file diff --git a/hack/contrib/docker/grctl/entrypoint.sh b/hack/contrib/docker/grctl/entrypoint.sh index d6a6d611a..578dc6cf5 100644 --- a/hack/contrib/docker/grctl/entrypoint.sh +++ b/hack/contrib/docker/grctl/entrypoint.sh @@ -6,6 +6,9 @@ elif [ "$1" = "version" ];then elif [ "$1" = "install" ];then cp -a /run/rainbond-grctl /rootfs/path mkdir -p /rootfs/root/.rbd + cp -a /etc/goodrain/region.goodrain.me/ssl/ca.pem /ssl/ca.pem + cp -a /etc/goodrain/region.goodrain.me/ssl/client.pem /ssl/client.pem + cp -a /etc/goodrain/region.goodrain.me/ssl/client.key.pem /ssl/client.key.pem cp -a /run/grctl.yaml /rootfs/root/.rbd/grctl.yaml exec /run/rainbond-grctl install else From a639e422001c6101ca0fd80bde3778f50a48973a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=87=A1=E7=BE=8A=E7=BE=8A?= Date: Fri, 7 Feb 2020 17:55:55 +0800 Subject: [PATCH 03/47] fix issue/650 --- gateway/store/store.go | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/gateway/store/store.go b/gateway/store/store.go index 784b26e40..43715efd7 100644 --- a/gateway/store/store.go +++ b/gateway/store/store.go @@ -20,6 +20,8 @@ package store import ( "bytes" + "crypto/x509" + "encoding/pem" "fmt" "io/ioutil" "net" @@ -817,7 +819,6 @@ func (s *k8sStore) syncSecret(secrKey string) { logrus.Errorf("fail to get certificate pem: %v", err) return } - old, exists := s.sslStore.Get(secrKey) if exists { oldSSLCert := old.(*v1.SSLCert) @@ -858,9 +859,22 @@ func (s *k8sStore) getCertificatePem(secrKey string) (*v1.SSLCert, error) { if e := ioutil.WriteFile(filename, buffer.Bytes(), 0666); e != nil { return nil, fmt.Errorf("cant not write data to %s: %v", filename, e) } + fileContent, err := ioutil.ReadFile(filename) + if err != nil { + return nil, fmt.Errorf("read certificate file failed: %s", err.Error()) + } + pemContent, _ := pem.Decode(fileContent) + certificate, err := x509.ParseCertificate(pemContent.Bytes) + if err != nil { + return nil, fmt.Errorf("generate certificate object failed: %s", err.Error()) + } return &v1.SSLCert{ CertificatePem: filename, + Certificate: certificate, + CertificateStr: string(certificate.Raw), + PrivateKey: string(key), + CN: []string{certificate.Subject.CommonName}, }, nil } From 18303b886bed68d5295f556987d3f4789e39e804 Mon Sep 17 00:00:00 2001 From: GLYASAI Date: Fri, 7 Feb 2020 21:47:23 +0800 Subject: [PATCH 04/47] discover by k8s --- discover.v2/discover.go | 2 + discover.v2/k8s_discover.go | 166 ++++ discover.v2/k8s_discover_test.go | 105 +++ .../github.com/evanphx/json-patch/.travis.yml | 16 + vendor/github.com/evanphx/json-patch/LICENSE | 25 + .../github.com/evanphx/json-patch/README.md | 297 +++++++ .../github.com/evanphx/json-patch/errors.go | 38 + vendor/github.com/evanphx/json-patch/merge.go | 383 +++++++++ vendor/github.com/evanphx/json-patch/patch.go | 776 ++++++++++++++++++ 9 files changed, 1808 insertions(+) create mode 100644 discover.v2/k8s_discover.go create mode 100644 discover.v2/k8s_discover_test.go create mode 100644 vendor/github.com/evanphx/json-patch/.travis.yml create mode 100644 vendor/github.com/evanphx/json-patch/LICENSE create mode 100644 vendor/github.com/evanphx/json-patch/README.md create mode 100644 vendor/github.com/evanphx/json-patch/errors.go create mode 100644 vendor/github.com/evanphx/json-patch/merge.go create mode 100644 vendor/github.com/evanphx/json-patch/patch.go diff --git a/discover.v2/discover.go b/discover.v2/discover.go index a10fe5ee8..4d2c60ab6 100644 --- a/discover.v2/discover.go +++ b/discover.v2/discover.go @@ -52,7 +52,9 @@ type Callback interface { //Discover 后端服务自动发现 type Discover interface { + // Add project to cache if not exists, then watch the endpoints. AddProject(name string, callback Callback) + // Update a project. AddUpdateProject(name string, callback CallbackUpdate) Stop() } diff --git a/discover.v2/k8s_discover.go b/discover.v2/k8s_discover.go new file mode 100644 index 000000000..e5629ec85 --- /dev/null +++ b/discover.v2/k8s_discover.go @@ -0,0 +1,166 @@ +package discover + +import ( + "context" + "github.com/Sirupsen/logrus" + corev1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/watch" + "k8s.io/client-go/kubernetes" + "sync" + "time" + + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + + "github.com/goodrain/rainbond/cmd/node/option" + "github.com/goodrain/rainbond/discover/config" +) + +type k8sDiscover struct { + ctx context.Context + cancel context.CancelFunc + lock sync.Mutex + clientset kubernetes.Interface + cfg *option.Conf + projects map[string]CallbackUpdate +} + +func NewK8sDiscover(ctx context.Context, clientset kubernetes.Interface, cfg *option.Conf) Discover { + ctx, cancel := context.WithCancel(ctx) + return &k8sDiscover{ + ctx: ctx, + cancel: cancel, + clientset: clientset, + cfg: cfg, + projects: make(map[string]CallbackUpdate), + } +} + +func (k *k8sDiscover) Stop() { + k.cancel() +} + +func (k *k8sDiscover) AddProject(name string, callback Callback) { + k.lock.Lock() + defer k.lock.Unlock() + + if _, ok := k.projects[name]; !ok { + cal := &defaultCallBackUpdate{ + callback: callback, + endpoints: make(map[string]*config.Endpoint), + } + k.projects[name] = cal + go k.discover(name, cal) + } +} + +func (k *k8sDiscover) AddUpdateProject(name string, callback CallbackUpdate) { + k.lock.Lock() + defer k.lock.Unlock() + + if _, ok := k.projects[name]; !ok { + k.projects[name] = callback + go k.discover(name, callback) + } +} + +func (k *k8sDiscover) discover(name string, callback CallbackUpdate) { + ctx, cancel := context.WithCancel(k.ctx) + defer cancel() + + endpoints := k.list(name) + if len(endpoints) > 0 { + callback.UpdateEndpoints(config.SYNC, endpoints...) + } + + w, err := k.clientset.CoreV1().Pods(k.cfg.RbdNamespace).Watch(metav1.ListOptions{ + LabelSelector: "name=" + name, + }) + if err != nil { + k.rewatchWithErr(name, callback, err) + return + } + for { + select { + case <-ctx.Done(): + return + case event := <-w.ResultChan(): + pod := event.Object.(*corev1.Pod) + if !isPodReady(pod) { + continue + } + ep := endpointForPod(pod) + switch event.Type { + case watch.Deleted: + callback.UpdateEndpoints(config.DELETE, ep) + case watch.Added: + callback.UpdateEndpoints(config.ADD, ep) + case watch.Modified: + callback.UpdateEndpoints(config.UPDATE, ep) + case watch.Error: + k.rewatchWithErr(name, callback, err) + } + } + } +} + +func (k *k8sDiscover) removeProject(name string) { + k.lock.Lock() + defer k.lock.Unlock() + if _, ok := k.projects[name]; ok { + delete(k.projects, name) + } +} + +func (k *k8sDiscover) rewatchWithErr(name string, callback CallbackUpdate, err error) { + logrus.Debugf("name: %s; monitor discover get watch error: %s, remove this watch target first, and then sleep 10 sec, we will re-watch it", name, err.Error()) + callback.Error(err) + k.removeProject(name) + time.Sleep(10 * time.Second) + k.AddUpdateProject(name, callback) +} + +func (k *k8sDiscover) list(name string) []*config.Endpoint { + podList, err := k.clientset.CoreV1().Pods(k.cfg.RbdNamespace).List(metav1.ListOptions{ + LabelSelector: "name=" + name, + }) + if err != nil { + logrus.Warningf("list pods for %s: %v", name, err) + return nil + } + + var endpoints []*config.Endpoint + var notReadyEp *config.Endpoint + for _, pod := range podList.Items { + ep := endpointForPod(&pod) + if isPodReady(&pod) { + endpoints = append(endpoints, ep) + continue + } + if notReadyEp == nil { + notReadyEp = endpointForPod(&pod) + } + } + + // If there are no ready endpoints, a not ready endpoint is used + if len(endpoints) == 0 && notReadyEp != nil { + endpoints = append(endpoints, notReadyEp) + } + + return endpoints +} + +func endpointForPod(pod *corev1.Pod) *config.Endpoint { + return &config.Endpoint{ + Name: pod.Name, + URL: pod.Status.PodIP, + } +} + +func isPodReady(pod *corev1.Pod) bool { + for _, cond := range pod.Status.Conditions { + if cond.Type == corev1.PodReady && cond.Status == corev1.ConditionTrue { + return true + } + } + return false +} diff --git a/discover.v2/k8s_discover_test.go b/discover.v2/k8s_discover_test.go new file mode 100644 index 000000000..83ba08831 --- /dev/null +++ b/discover.v2/k8s_discover_test.go @@ -0,0 +1,105 @@ +package discover + +import ( + "context" + "fmt" + "github.com/goodrain/rainbond/cmd/node/option" + "github.com/goodrain/rainbond/discover/config" + "k8s.io/client-go/kubernetes/fake" + "testing" + "time" + + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + + corev1 "k8s.io/api/core/v1" +) + +func TestK8sDiscover_AddProject(t *testing.T) { + tests := []struct { + name string + }{ + { + name: "ok", + }, + } + + for idx := range tests { + tc := tests[idx] + t.Run(tc.name, func(t *testing.T) { + ctx := context.Background() + cfg := &option.Conf{RbdNamespace: ""} + + pod := &corev1.Pod{ + ObjectMeta: metav1.ObjectMeta{ + Name: "rbd-gateway-abcde", + Labels: map[string]string{ + "name": "rbd-gateway", + }, + }, + Status: corev1.PodStatus{ + PodIP: "172.20.0.20", + }, + } + clientset := fake.NewSimpleClientset(pod) + + discover := NewK8sDiscover(ctx, clientset, cfg) + defer discover.Stop() + + callback := &testCallback{ + epCh: make(chan []*config.Endpoint), + errCh: make(chan error), + } + discover.AddProject("rbd-gateway", callback) + + go func() { + for { + select { + case endpoints := <-callback.epCh: + for _, ep := range endpoints { + fmt.Printf("%#v\n", ep) + } + case err := <-callback.errCh: + t.Errorf("received unexpected error from callback: %v", err) + return + default: + + } + } + }() + + time.Sleep(2 * time.Second) + + pod.Status.Conditions = append(pod.Status.Conditions, corev1.PodCondition{ + Type: corev1.PodReady, + Status: corev1.ConditionTrue, + }) + pod.Status.PodIP = "172.20.0.50" + _, err := clientset.CoreV1().Pods("").Update(pod) + if err != nil { + t.Error(err) + } + + time.Sleep(1 * time.Second) + + err = clientset.CoreV1().Pods("").Delete(pod.Name, &metav1.DeleteOptions{}) + if err != nil { + t.Error(err) + } + + time.Sleep(30 * time.Second) + }) + } +} + +type testCallback struct { + epCh chan []*config.Endpoint + errCh chan error +} + +func (t *testCallback) UpdateEndpoints(endpoints ...*config.Endpoint) { + t.epCh <- endpoints +} + +func (t *testCallback) Error(err error) { + fmt.Println(err) +} diff --git a/vendor/github.com/evanphx/json-patch/.travis.yml b/vendor/github.com/evanphx/json-patch/.travis.yml new file mode 100644 index 000000000..2092c72c4 --- /dev/null +++ b/vendor/github.com/evanphx/json-patch/.travis.yml @@ -0,0 +1,16 @@ +language: go + +go: + - 1.8 + - 1.7 + +install: + - if ! go get code.google.com/p/go.tools/cmd/cover; then go get golang.org/x/tools/cmd/cover; fi + - go get github.com/jessevdk/go-flags + +script: + - go get + - go test -cover ./... + +notifications: + email: false diff --git a/vendor/github.com/evanphx/json-patch/LICENSE b/vendor/github.com/evanphx/json-patch/LICENSE new file mode 100644 index 000000000..0eb9b72d8 --- /dev/null +++ b/vendor/github.com/evanphx/json-patch/LICENSE @@ -0,0 +1,25 @@ +Copyright (c) 2014, Evan Phoenix +All rights reserved. + +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 the Evan Phoenix 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. diff --git a/vendor/github.com/evanphx/json-patch/README.md b/vendor/github.com/evanphx/json-patch/README.md new file mode 100644 index 000000000..9c7f87f7c --- /dev/null +++ b/vendor/github.com/evanphx/json-patch/README.md @@ -0,0 +1,297 @@ +# JSON-Patch +`jsonpatch` is a library which provides functionallity for both applying +[RFC6902 JSON patches](http://tools.ietf.org/html/rfc6902) against documents, as +well as for calculating & applying [RFC7396 JSON merge patches](https://tools.ietf.org/html/rfc7396). + +[![GoDoc](https://godoc.org/github.com/evanphx/json-patch?status.svg)](http://godoc.org/github.com/evanphx/json-patch) +[![Build Status](https://travis-ci.org/evanphx/json-patch.svg?branch=master)](https://travis-ci.org/evanphx/json-patch) +[![Report Card](https://goreportcard.com/badge/github.com/evanphx/json-patch)](https://goreportcard.com/report/github.com/evanphx/json-patch) + +# Get It! + +**Latest and greatest**: +```bash +go get -u github.com/evanphx/json-patch +``` + +**Stable Versions**: +* Version 4: `go get -u gopkg.in/evanphx/json-patch.v4` + +(previous versions below `v3` are unavailable) + +# Use It! +* [Create and apply a merge patch](#create-and-apply-a-merge-patch) +* [Create and apply a JSON Patch](#create-and-apply-a-json-patch) +* [Comparing JSON documents](#comparing-json-documents) +* [Combine merge patches](#combine-merge-patches) + + +# Configuration + +* There is a global configuration variable `jsonpatch.SupportNegativeIndices`. + This defaults to `true` and enables the non-standard practice of allowing + negative indices to mean indices starting at the end of an array. This + functionality can be disabled by setting `jsonpatch.SupportNegativeIndices = + false`. + +* There is a global configuration variable `jsonpatch.AccumulatedCopySizeLimit`, + which limits the total size increase in bytes caused by "copy" operations in a + patch. It defaults to 0, which means there is no limit. + +## Create and apply a merge patch +Given both an original JSON document and a modified JSON document, you can create +a [Merge Patch](https://tools.ietf.org/html/rfc7396) document. + +It can describe the changes needed to convert from the original to the +modified JSON document. + +Once you have a merge patch, you can apply it to other JSON documents using the +`jsonpatch.MergePatch(document, patch)` function. + +```go +package main + +import ( + "fmt" + + jsonpatch "github.com/evanphx/json-patch" +) + +func main() { + // Let's create a merge patch from these two documents... + original := []byte(`{"name": "John", "age": 24, "height": 3.21}`) + target := []byte(`{"name": "Jane", "age": 24}`) + + patch, err := jsonpatch.CreateMergePatch(original, target) + if err != nil { + panic(err) + } + + // Now lets apply the patch against a different JSON document... + + alternative := []byte(`{"name": "Tina", "age": 28, "height": 3.75}`) + modifiedAlternative, err := jsonpatch.MergePatch(alternative, patch) + + fmt.Printf("patch document: %s\n", patch) + fmt.Printf("updated alternative doc: %s\n", modifiedAlternative) +} +``` + +When ran, you get the following output: + +```bash +$ go run main.go +patch document: {"height":null,"name":"Jane"} +updated tina doc: {"age":28,"name":"Jane"} +``` + +## Create and apply a JSON Patch +You can create patch objects using `DecodePatch([]byte)`, which can then +be applied against JSON documents. + +The following is an example of creating a patch from two operations, and +applying it against a JSON document. + +```go +package main + +import ( + "fmt" + + jsonpatch "github.com/evanphx/json-patch" +) + +func main() { + original := []byte(`{"name": "John", "age": 24, "height": 3.21}`) + patchJSON := []byte(`[ + {"op": "replace", "path": "/name", "value": "Jane"}, + {"op": "remove", "path": "/height"} + ]`) + + patch, err := jsonpatch.DecodePatch(patchJSON) + if err != nil { + panic(err) + } + + modified, err := patch.Apply(original) + if err != nil { + panic(err) + } + + fmt.Printf("Original document: %s\n", original) + fmt.Printf("Modified document: %s\n", modified) +} +``` + +When ran, you get the following output: + +```bash +$ go run main.go +Original document: {"name": "John", "age": 24, "height": 3.21} +Modified document: {"age":24,"name":"Jane"} +``` + +## Comparing JSON documents +Due to potential whitespace and ordering differences, one cannot simply compare +JSON strings or byte-arrays directly. + +As such, you can instead use `jsonpatch.Equal(document1, document2)` to +determine if two JSON documents are _structurally_ equal. This ignores +whitespace differences, and key-value ordering. + +```go +package main + +import ( + "fmt" + + jsonpatch "github.com/evanphx/json-patch" +) + +func main() { + original := []byte(`{"name": "John", "age": 24, "height": 3.21}`) + similar := []byte(` + { + "age": 24, + "height": 3.21, + "name": "John" + } + `) + different := []byte(`{"name": "Jane", "age": 20, "height": 3.37}`) + + if jsonpatch.Equal(original, similar) { + fmt.Println(`"original" is structurally equal to "similar"`) + } + + if !jsonpatch.Equal(original, different) { + fmt.Println(`"original" is _not_ structurally equal to "similar"`) + } +} +``` + +When ran, you get the following output: +```bash +$ go run main.go +"original" is structurally equal to "similar" +"original" is _not_ structurally equal to "similar" +``` + +## Combine merge patches +Given two JSON merge patch documents, it is possible to combine them into a +single merge patch which can describe both set of changes. + +The resulting merge patch can be used such that applying it results in a +document structurally similar as merging each merge patch to the document +in succession. + +```go +package main + +import ( + "fmt" + + jsonpatch "github.com/evanphx/json-patch" +) + +func main() { + original := []byte(`{"name": "John", "age": 24, "height": 3.21}`) + + nameAndHeight := []byte(`{"height":null,"name":"Jane"}`) + ageAndEyes := []byte(`{"age":4.23,"eyes":"blue"}`) + + // Let's combine these merge patch documents... + combinedPatch, err := jsonpatch.MergeMergePatches(nameAndHeight, ageAndEyes) + if err != nil { + panic(err) + } + + // Apply each patch individual against the original document + withoutCombinedPatch, err := jsonpatch.MergePatch(original, nameAndHeight) + if err != nil { + panic(err) + } + + withoutCombinedPatch, err = jsonpatch.MergePatch(withoutCombinedPatch, ageAndEyes) + if err != nil { + panic(err) + } + + // Apply the combined patch against the original document + + withCombinedPatch, err := jsonpatch.MergePatch(original, combinedPatch) + if err != nil { + panic(err) + } + + // Do both result in the same thing? They should! + if jsonpatch.Equal(withCombinedPatch, withoutCombinedPatch) { + fmt.Println("Both JSON documents are structurally the same!") + } + + fmt.Printf("combined merge patch: %s", combinedPatch) +} +``` + +When ran, you get the following output: +```bash +$ go run main.go +Both JSON documents are structurally the same! +combined merge patch: {"age":4.23,"eyes":"blue","height":null,"name":"Jane"} +``` + +# CLI for comparing JSON documents +You can install the commandline program `json-patch`. + +This program can take multiple JSON patch documents as arguments, +and fed a JSON document from `stdin`. It will apply the patch(es) against +the document and output the modified doc. + +**patch.1.json** +```json +[ + {"op": "replace", "path": "/name", "value": "Jane"}, + {"op": "remove", "path": "/height"} +] +``` + +**patch.2.json** +```json +[ + {"op": "add", "path": "/address", "value": "123 Main St"}, + {"op": "replace", "path": "/age", "value": "21"} +] +``` + +**document.json** +```json +{ + "name": "John", + "age": 24, + "height": 3.21 +} +``` + +You can then run: + +```bash +$ go install github.com/evanphx/json-patch/cmd/json-patch +$ cat document.json | json-patch -p patch.1.json -p patch.2.json +{"address":"123 Main St","age":"21","name":"Jane"} +``` + +# Help It! +Contributions are welcomed! Leave [an issue](https://github.com/evanphx/json-patch/issues) +or [create a PR](https://github.com/evanphx/json-patch/compare). + + +Before creating a pull request, we'd ask that you make sure tests are passing +and that you have added new tests when applicable. + +Contributors can run tests using: + +```bash +go test -cover ./... +``` + +Builds for pull requests are tested automatically +using [TravisCI](https://travis-ci.org/evanphx/json-patch). diff --git a/vendor/github.com/evanphx/json-patch/errors.go b/vendor/github.com/evanphx/json-patch/errors.go new file mode 100644 index 000000000..75304b443 --- /dev/null +++ b/vendor/github.com/evanphx/json-patch/errors.go @@ -0,0 +1,38 @@ +package jsonpatch + +import "fmt" + +// AccumulatedCopySizeError is an error type returned when the accumulated size +// increase caused by copy operations in a patch operation has exceeded the +// limit. +type AccumulatedCopySizeError struct { + limit int64 + accumulated int64 +} + +// NewAccumulatedCopySizeError returns an AccumulatedCopySizeError. +func NewAccumulatedCopySizeError(l, a int64) *AccumulatedCopySizeError { + return &AccumulatedCopySizeError{limit: l, accumulated: a} +} + +// Error implements the error interface. +func (a *AccumulatedCopySizeError) Error() string { + return fmt.Sprintf("Unable to complete the copy, the accumulated size increase of copy is %d, exceeding the limit %d", a.accumulated, a.limit) +} + +// ArraySizeError is an error type returned when the array size has exceeded +// the limit. +type ArraySizeError struct { + limit int + size int +} + +// NewArraySizeError returns an ArraySizeError. +func NewArraySizeError(l, s int) *ArraySizeError { + return &ArraySizeError{limit: l, size: s} +} + +// Error implements the error interface. +func (a *ArraySizeError) Error() string { + return fmt.Sprintf("Unable to create array of size %d, limit is %d", a.size, a.limit) +} diff --git a/vendor/github.com/evanphx/json-patch/merge.go b/vendor/github.com/evanphx/json-patch/merge.go new file mode 100644 index 000000000..6806c4c20 --- /dev/null +++ b/vendor/github.com/evanphx/json-patch/merge.go @@ -0,0 +1,383 @@ +package jsonpatch + +import ( + "bytes" + "encoding/json" + "fmt" + "reflect" +) + +func merge(cur, patch *lazyNode, mergeMerge bool) *lazyNode { + curDoc, err := cur.intoDoc() + + if err != nil { + pruneNulls(patch) + return patch + } + + patchDoc, err := patch.intoDoc() + + if err != nil { + return patch + } + + mergeDocs(curDoc, patchDoc, mergeMerge) + + return cur +} + +func mergeDocs(doc, patch *partialDoc, mergeMerge bool) { + for k, v := range *patch { + if v == nil { + if mergeMerge { + (*doc)[k] = nil + } else { + delete(*doc, k) + } + } else { + cur, ok := (*doc)[k] + + if !ok || cur == nil { + pruneNulls(v) + (*doc)[k] = v + } else { + (*doc)[k] = merge(cur, v, mergeMerge) + } + } + } +} + +func pruneNulls(n *lazyNode) { + sub, err := n.intoDoc() + + if err == nil { + pruneDocNulls(sub) + } else { + ary, err := n.intoAry() + + if err == nil { + pruneAryNulls(ary) + } + } +} + +func pruneDocNulls(doc *partialDoc) *partialDoc { + for k, v := range *doc { + if v == nil { + delete(*doc, k) + } else { + pruneNulls(v) + } + } + + return doc +} + +func pruneAryNulls(ary *partialArray) *partialArray { + newAry := []*lazyNode{} + + for _, v := range *ary { + if v != nil { + pruneNulls(v) + newAry = append(newAry, v) + } + } + + *ary = newAry + + return ary +} + +var errBadJSONDoc = fmt.Errorf("Invalid JSON Document") +var errBadJSONPatch = fmt.Errorf("Invalid JSON Patch") +var errBadMergeTypes = fmt.Errorf("Mismatched JSON Documents") + +// MergeMergePatches merges two merge patches together, such that +// applying this resulting merged merge patch to a document yields the same +// as merging each merge patch to the document in succession. +func MergeMergePatches(patch1Data, patch2Data []byte) ([]byte, error) { + return doMergePatch(patch1Data, patch2Data, true) +} + +// MergePatch merges the patchData into the docData. +func MergePatch(docData, patchData []byte) ([]byte, error) { + return doMergePatch(docData, patchData, false) +} + +func doMergePatch(docData, patchData []byte, mergeMerge bool) ([]byte, error) { + doc := &partialDoc{} + + docErr := json.Unmarshal(docData, doc) + + patch := &partialDoc{} + + patchErr := json.Unmarshal(patchData, patch) + + if _, ok := docErr.(*json.SyntaxError); ok { + return nil, errBadJSONDoc + } + + if _, ok := patchErr.(*json.SyntaxError); ok { + return nil, errBadJSONPatch + } + + if docErr == nil && *doc == nil { + return nil, errBadJSONDoc + } + + if patchErr == nil && *patch == nil { + return nil, errBadJSONPatch + } + + if docErr != nil || patchErr != nil { + // Not an error, just not a doc, so we turn straight into the patch + if patchErr == nil { + if mergeMerge { + doc = patch + } else { + doc = pruneDocNulls(patch) + } + } else { + patchAry := &partialArray{} + patchErr = json.Unmarshal(patchData, patchAry) + + if patchErr != nil { + return nil, errBadJSONPatch + } + + pruneAryNulls(patchAry) + + out, patchErr := json.Marshal(patchAry) + + if patchErr != nil { + return nil, errBadJSONPatch + } + + return out, nil + } + } else { + mergeDocs(doc, patch, mergeMerge) + } + + return json.Marshal(doc) +} + +// resemblesJSONArray indicates whether the byte-slice "appears" to be +// a JSON array or not. +// False-positives are possible, as this function does not check the internal +// structure of the array. It only checks that the outer syntax is present and +// correct. +func resemblesJSONArray(input []byte) bool { + input = bytes.TrimSpace(input) + + hasPrefix := bytes.HasPrefix(input, []byte("[")) + hasSuffix := bytes.HasSuffix(input, []byte("]")) + + return hasPrefix && hasSuffix +} + +// CreateMergePatch will return a merge patch document capable of converting +// the original document(s) to the modified document(s). +// The parameters can be bytes of either two JSON Documents, or two arrays of +// JSON documents. +// The merge patch returned follows the specification defined at http://tools.ietf.org/html/draft-ietf-appsawg-json-merge-patch-07 +func CreateMergePatch(originalJSON, modifiedJSON []byte) ([]byte, error) { + originalResemblesArray := resemblesJSONArray(originalJSON) + modifiedResemblesArray := resemblesJSONArray(modifiedJSON) + + // Do both byte-slices seem like JSON arrays? + if originalResemblesArray && modifiedResemblesArray { + return createArrayMergePatch(originalJSON, modifiedJSON) + } + + // Are both byte-slices are not arrays? Then they are likely JSON objects... + if !originalResemblesArray && !modifiedResemblesArray { + return createObjectMergePatch(originalJSON, modifiedJSON) + } + + // None of the above? Then return an error because of mismatched types. + return nil, errBadMergeTypes +} + +// createObjectMergePatch will return a merge-patch document capable of +// converting the original document to the modified document. +func createObjectMergePatch(originalJSON, modifiedJSON []byte) ([]byte, error) { + originalDoc := map[string]interface{}{} + modifiedDoc := map[string]interface{}{} + + err := json.Unmarshal(originalJSON, &originalDoc) + if err != nil { + return nil, errBadJSONDoc + } + + err = json.Unmarshal(modifiedJSON, &modifiedDoc) + if err != nil { + return nil, errBadJSONDoc + } + + dest, err := getDiff(originalDoc, modifiedDoc) + if err != nil { + return nil, err + } + + return json.Marshal(dest) +} + +// createArrayMergePatch will return an array of merge-patch documents capable +// of converting the original document to the modified document for each +// pair of JSON documents provided in the arrays. +// Arrays of mismatched sizes will result in an error. +func createArrayMergePatch(originalJSON, modifiedJSON []byte) ([]byte, error) { + originalDocs := []json.RawMessage{} + modifiedDocs := []json.RawMessage{} + + err := json.Unmarshal(originalJSON, &originalDocs) + if err != nil { + return nil, errBadJSONDoc + } + + err = json.Unmarshal(modifiedJSON, &modifiedDocs) + if err != nil { + return nil, errBadJSONDoc + } + + total := len(originalDocs) + if len(modifiedDocs) != total { + return nil, errBadJSONDoc + } + + result := []json.RawMessage{} + for i := 0; i < len(originalDocs); i++ { + original := originalDocs[i] + modified := modifiedDocs[i] + + patch, err := createObjectMergePatch(original, modified) + if err != nil { + return nil, err + } + + result = append(result, json.RawMessage(patch)) + } + + return json.Marshal(result) +} + +// Returns true if the array matches (must be json types). +// As is idiomatic for go, an empty array is not the same as a nil array. +func matchesArray(a, b []interface{}) bool { + if len(a) != len(b) { + return false + } + if (a == nil && b != nil) || (a != nil && b == nil) { + return false + } + for i := range a { + if !matchesValue(a[i], b[i]) { + return false + } + } + return true +} + +// Returns true if the values matches (must be json types) +// The types of the values must match, otherwise it will always return false +// If two map[string]interface{} are given, all elements must match. +func matchesValue(av, bv interface{}) bool { + if reflect.TypeOf(av) != reflect.TypeOf(bv) { + return false + } + switch at := av.(type) { + case string: + bt := bv.(string) + if bt == at { + return true + } + case float64: + bt := bv.(float64) + if bt == at { + return true + } + case bool: + bt := bv.(bool) + if bt == at { + return true + } + case nil: + // Both nil, fine. + return true + case map[string]interface{}: + bt := bv.(map[string]interface{}) + for key := range at { + if !matchesValue(at[key], bt[key]) { + return false + } + } + for key := range bt { + if !matchesValue(at[key], bt[key]) { + return false + } + } + return true + case []interface{}: + bt := bv.([]interface{}) + return matchesArray(at, bt) + } + return false +} + +// getDiff returns the (recursive) difference between a and b as a map[string]interface{}. +func getDiff(a, b map[string]interface{}) (map[string]interface{}, error) { + into := map[string]interface{}{} + for key, bv := range b { + av, ok := a[key] + // value was added + if !ok { + into[key] = bv + continue + } + // If types have changed, replace completely + if reflect.TypeOf(av) != reflect.TypeOf(bv) { + into[key] = bv + continue + } + // Types are the same, compare values + switch at := av.(type) { + case map[string]interface{}: + bt := bv.(map[string]interface{}) + dst := make(map[string]interface{}, len(bt)) + dst, err := getDiff(at, bt) + if err != nil { + return nil, err + } + if len(dst) > 0 { + into[key] = dst + } + case string, float64, bool: + if !matchesValue(av, bv) { + into[key] = bv + } + case []interface{}: + bt := bv.([]interface{}) + if !matchesArray(at, bt) { + into[key] = bv + } + case nil: + switch bv.(type) { + case nil: + // Both nil, fine. + default: + into[key] = bv + } + default: + panic(fmt.Sprintf("Unknown type:%T in key %s", av, key)) + } + } + // Now add all deleted values as nil + for key := range a { + _, found := b[key] + if !found { + into[key] = nil + } + } + return into, nil +} diff --git a/vendor/github.com/evanphx/json-patch/patch.go b/vendor/github.com/evanphx/json-patch/patch.go new file mode 100644 index 000000000..1b5f95e61 --- /dev/null +++ b/vendor/github.com/evanphx/json-patch/patch.go @@ -0,0 +1,776 @@ +package jsonpatch + +import ( + "bytes" + "encoding/json" + "fmt" + "strconv" + "strings" + + "github.com/pkg/errors" +) + +const ( + eRaw = iota + eDoc + eAry +) + +var ( + // SupportNegativeIndices decides whether to support non-standard practice of + // allowing negative indices to mean indices starting at the end of an array. + // Default to true. + SupportNegativeIndices bool = true + // AccumulatedCopySizeLimit limits the total size increase in bytes caused by + // "copy" operations in a patch. + AccumulatedCopySizeLimit int64 = 0 +) + +var ( + ErrTestFailed = errors.New("test failed") + ErrMissing = errors.New("missing value") + ErrUnknownType = errors.New("unknown object type") + ErrInvalid = errors.New("invalid state detected") + ErrInvalidIndex = errors.New("invalid index referenced") +) + +type lazyNode struct { + raw *json.RawMessage + doc partialDoc + ary partialArray + which int +} + +// Operation is a single JSON-Patch step, such as a single 'add' operation. +type Operation map[string]*json.RawMessage + +// Patch is an ordered collection of Operations. +type Patch []Operation + +type partialDoc map[string]*lazyNode +type partialArray []*lazyNode + +type container interface { + get(key string) (*lazyNode, error) + set(key string, val *lazyNode) error + add(key string, val *lazyNode) error + remove(key string) error +} + +func newLazyNode(raw *json.RawMessage) *lazyNode { + return &lazyNode{raw: raw, doc: nil, ary: nil, which: eRaw} +} + +func (n *lazyNode) MarshalJSON() ([]byte, error) { + switch n.which { + case eRaw: + return json.Marshal(n.raw) + case eDoc: + return json.Marshal(n.doc) + case eAry: + return json.Marshal(n.ary) + default: + return nil, ErrUnknownType + } +} + +func (n *lazyNode) UnmarshalJSON(data []byte) error { + dest := make(json.RawMessage, len(data)) + copy(dest, data) + n.raw = &dest + n.which = eRaw + return nil +} + +func deepCopy(src *lazyNode) (*lazyNode, int, error) { + if src == nil { + return nil, 0, nil + } + a, err := src.MarshalJSON() + if err != nil { + return nil, 0, err + } + sz := len(a) + ra := make(json.RawMessage, sz) + copy(ra, a) + return newLazyNode(&ra), sz, nil +} + +func (n *lazyNode) intoDoc() (*partialDoc, error) { + if n.which == eDoc { + return &n.doc, nil + } + + if n.raw == nil { + return nil, ErrInvalid + } + + err := json.Unmarshal(*n.raw, &n.doc) + + if err != nil { + return nil, err + } + + n.which = eDoc + return &n.doc, nil +} + +func (n *lazyNode) intoAry() (*partialArray, error) { + if n.which == eAry { + return &n.ary, nil + } + + if n.raw == nil { + return nil, ErrInvalid + } + + err := json.Unmarshal(*n.raw, &n.ary) + + if err != nil { + return nil, err + } + + n.which = eAry + return &n.ary, nil +} + +func (n *lazyNode) compact() []byte { + buf := &bytes.Buffer{} + + if n.raw == nil { + return nil + } + + err := json.Compact(buf, *n.raw) + + if err != nil { + return *n.raw + } + + return buf.Bytes() +} + +func (n *lazyNode) tryDoc() bool { + if n.raw == nil { + return false + } + + err := json.Unmarshal(*n.raw, &n.doc) + + if err != nil { + return false + } + + n.which = eDoc + return true +} + +func (n *lazyNode) tryAry() bool { + if n.raw == nil { + return false + } + + err := json.Unmarshal(*n.raw, &n.ary) + + if err != nil { + return false + } + + n.which = eAry + return true +} + +func (n *lazyNode) equal(o *lazyNode) bool { + if n.which == eRaw { + if !n.tryDoc() && !n.tryAry() { + if o.which != eRaw { + return false + } + + return bytes.Equal(n.compact(), o.compact()) + } + } + + if n.which == eDoc { + if o.which == eRaw { + if !o.tryDoc() { + return false + } + } + + if o.which != eDoc { + return false + } + + for k, v := range n.doc { + ov, ok := o.doc[k] + + if !ok { + return false + } + + if v == nil && ov == nil { + continue + } + + if !v.equal(ov) { + return false + } + } + + return true + } + + if o.which != eAry && !o.tryAry() { + return false + } + + if len(n.ary) != len(o.ary) { + return false + } + + for idx, val := range n.ary { + if !val.equal(o.ary[idx]) { + return false + } + } + + return true +} + +// Kind reads the "op" field of the Operation. +func (o Operation) Kind() string { + if obj, ok := o["op"]; ok && obj != nil { + var op string + + err := json.Unmarshal(*obj, &op) + + if err != nil { + return "unknown" + } + + return op + } + + return "unknown" +} + +// Path reads the "path" field of the Operation. +func (o Operation) Path() (string, error) { + if obj, ok := o["path"]; ok && obj != nil { + var op string + + err := json.Unmarshal(*obj, &op) + + if err != nil { + return "unknown", err + } + + return op, nil + } + + return "unknown", errors.Wrapf(ErrMissing, "operation missing path field") +} + +// From reads the "from" field of the Operation. +func (o Operation) From() (string, error) { + if obj, ok := o["from"]; ok && obj != nil { + var op string + + err := json.Unmarshal(*obj, &op) + + if err != nil { + return "unknown", err + } + + return op, nil + } + + return "unknown", errors.Wrapf(ErrMissing, "operation, missing from field") +} + +func (o Operation) value() *lazyNode { + if obj, ok := o["value"]; ok { + return newLazyNode(obj) + } + + return nil +} + +// ValueInterface decodes the operation value into an interface. +func (o Operation) ValueInterface() (interface{}, error) { + if obj, ok := o["value"]; ok && obj != nil { + var v interface{} + + err := json.Unmarshal(*obj, &v) + + if err != nil { + return nil, err + } + + return v, nil + } + + return nil, errors.Wrapf(ErrMissing, "operation, missing value field") +} + +func isArray(buf []byte) bool { +Loop: + for _, c := range buf { + switch c { + case ' ': + case '\n': + case '\t': + continue + case '[': + return true + default: + break Loop + } + } + + return false +} + +func findObject(pd *container, path string) (container, string) { + doc := *pd + + split := strings.Split(path, "/") + + if len(split) < 2 { + return nil, "" + } + + parts := split[1 : len(split)-1] + + key := split[len(split)-1] + + var err error + + for _, part := range parts { + + next, ok := doc.get(decodePatchKey(part)) + + if next == nil || ok != nil { + return nil, "" + } + + if isArray(*next.raw) { + doc, err = next.intoAry() + + if err != nil { + return nil, "" + } + } else { + doc, err = next.intoDoc() + + if err != nil { + return nil, "" + } + } + } + + return doc, decodePatchKey(key) +} + +func (d *partialDoc) set(key string, val *lazyNode) error { + (*d)[key] = val + return nil +} + +func (d *partialDoc) add(key string, val *lazyNode) error { + (*d)[key] = val + return nil +} + +func (d *partialDoc) get(key string) (*lazyNode, error) { + return (*d)[key], nil +} + +func (d *partialDoc) remove(key string) error { + _, ok := (*d)[key] + if !ok { + return errors.Wrapf(ErrMissing, "Unable to remove nonexistent key: %s", key) + } + + delete(*d, key) + return nil +} + +// set should only be used to implement the "replace" operation, so "key" must +// be an already existing index in "d". +func (d *partialArray) set(key string, val *lazyNode) error { + idx, err := strconv.Atoi(key) + if err != nil { + return err + } + (*d)[idx] = val + return nil +} + +func (d *partialArray) add(key string, val *lazyNode) error { + if key == "-" { + *d = append(*d, val) + return nil + } + + idx, err := strconv.Atoi(key) + if err != nil { + return errors.Wrapf(err, "value was not a proper array index: '%s'", key) + } + + sz := len(*d) + 1 + + ary := make([]*lazyNode, sz) + + cur := *d + + if idx >= len(ary) { + return errors.Wrapf(ErrInvalidIndex, "Unable to access invalid index: %d", idx) + } + + if SupportNegativeIndices { + if idx < -len(ary) { + return errors.Wrapf(ErrInvalidIndex, "Unable to access invalid index: %d", idx) + } + + if idx < 0 { + idx += len(ary) + } + } + + copy(ary[0:idx], cur[0:idx]) + ary[idx] = val + copy(ary[idx+1:], cur[idx:]) + + *d = ary + return nil +} + +func (d *partialArray) get(key string) (*lazyNode, error) { + idx, err := strconv.Atoi(key) + + if err != nil { + return nil, err + } + + if idx >= len(*d) { + return nil, errors.Wrapf(ErrInvalidIndex, "Unable to access invalid index: %d", idx) + } + + return (*d)[idx], nil +} + +func (d *partialArray) remove(key string) error { + idx, err := strconv.Atoi(key) + if err != nil { + return err + } + + cur := *d + + if idx >= len(cur) { + return errors.Wrapf(ErrInvalidIndex, "Unable to access invalid index: %d", idx) + } + + if SupportNegativeIndices { + if idx < -len(cur) { + return errors.Wrapf(ErrInvalidIndex, "Unable to access invalid index: %d", idx) + } + + if idx < 0 { + idx += len(cur) + } + } + + ary := make([]*lazyNode, len(cur)-1) + + copy(ary[0:idx], cur[0:idx]) + copy(ary[idx:], cur[idx+1:]) + + *d = ary + return nil + +} + +func (p Patch) add(doc *container, op Operation) error { + path, err := op.Path() + if err != nil { + return errors.Wrapf(ErrMissing, "add operation failed to decode path") + } + + con, key := findObject(doc, path) + + if con == nil { + return errors.Wrapf(ErrMissing, "add operation does not apply: doc is missing path: \"%s\"", path) + } + + err = con.add(key, op.value()) + if err != nil { + return errors.Wrapf(err, "error in add for path: '%s'", path) + } + + return nil +} + +func (p Patch) remove(doc *container, op Operation) error { + path, err := op.Path() + if err != nil { + return errors.Wrapf(ErrMissing, "remove operation failed to decode path") + } + + con, key := findObject(doc, path) + + if con == nil { + return errors.Wrapf(ErrMissing, "remove operation does not apply: doc is missing path: \"%s\"", path) + } + + err = con.remove(key) + if err != nil { + return errors.Wrapf(err, "error in remove for path: '%s'", path) + } + + return nil +} + +func (p Patch) replace(doc *container, op Operation) error { + path, err := op.Path() + if err != nil { + return errors.Wrapf(err, "replace operation failed to decode path") + } + + con, key := findObject(doc, path) + + if con == nil { + return errors.Wrapf(ErrMissing, "replace operation does not apply: doc is missing path: %s", path) + } + + _, ok := con.get(key) + if ok != nil { + return errors.Wrapf(ErrMissing, "replace operation does not apply: doc is missing key: %s", path) + } + + err = con.set(key, op.value()) + if err != nil { + return errors.Wrapf(err, "error in remove for path: '%s'", path) + } + + return nil +} + +func (p Patch) move(doc *container, op Operation) error { + from, err := op.From() + if err != nil { + return errors.Wrapf(err, "move operation failed to decode from") + } + + con, key := findObject(doc, from) + + if con == nil { + return errors.Wrapf(ErrMissing, "move operation does not apply: doc is missing from path: %s", from) + } + + val, err := con.get(key) + if err != nil { + return errors.Wrapf(err, "error in move for path: '%s'", key) + } + + err = con.remove(key) + if err != nil { + return errors.Wrapf(err, "error in move for path: '%s'", key) + } + + path, err := op.Path() + if err != nil { + return errors.Wrapf(err, "move operation failed to decode path") + } + + con, key = findObject(doc, path) + + if con == nil { + return errors.Wrapf(ErrMissing, "move operation does not apply: doc is missing destination path: %s", path) + } + + err = con.add(key, val) + if err != nil { + return errors.Wrapf(err, "error in move for path: '%s'", path) + } + + return nil +} + +func (p Patch) test(doc *container, op Operation) error { + path, err := op.Path() + if err != nil { + return errors.Wrapf(err, "test operation failed to decode path") + } + + con, key := findObject(doc, path) + + if con == nil { + return errors.Wrapf(ErrMissing, "test operation does not apply: is missing path: %s", path) + } + + val, err := con.get(key) + if err != nil { + return errors.Wrapf(err, "error in test for path: '%s'", path) + } + + if val == nil { + if op.value().raw == nil { + return nil + } + return errors.Wrapf(ErrTestFailed, "testing value %s failed", path) + } else if op.value() == nil { + return errors.Wrapf(ErrTestFailed, "testing value %s failed", path) + } + + if val.equal(op.value()) { + return nil + } + + return errors.Wrapf(ErrTestFailed, "testing value %s failed", path) +} + +func (p Patch) copy(doc *container, op Operation, accumulatedCopySize *int64) error { + from, err := op.From() + if err != nil { + return errors.Wrapf(err, "copy operation failed to decode from") + } + + con, key := findObject(doc, from) + + if con == nil { + return errors.Wrapf(ErrMissing, "copy operation does not apply: doc is missing from path: %s", from) + } + + val, err := con.get(key) + if err != nil { + return errors.Wrapf(err, "error in copy for from: '%s'", from) + } + + path, err := op.Path() + if err != nil { + return errors.Wrapf(ErrMissing, "copy operation failed to decode path") + } + + con, key = findObject(doc, path) + + if con == nil { + return errors.Wrapf(ErrMissing, "copy operation does not apply: doc is missing destination path: %s", path) + } + + valCopy, sz, err := deepCopy(val) + if err != nil { + return errors.Wrapf(err, "error while performing deep copy") + } + + (*accumulatedCopySize) += int64(sz) + if AccumulatedCopySizeLimit > 0 && *accumulatedCopySize > AccumulatedCopySizeLimit { + return NewAccumulatedCopySizeError(AccumulatedCopySizeLimit, *accumulatedCopySize) + } + + err = con.add(key, valCopy) + if err != nil { + return errors.Wrapf(err, "error while adding value during copy") + } + + return nil +} + +// Equal indicates if 2 JSON documents have the same structural equality. +func Equal(a, b []byte) bool { + ra := make(json.RawMessage, len(a)) + copy(ra, a) + la := newLazyNode(&ra) + + rb := make(json.RawMessage, len(b)) + copy(rb, b) + lb := newLazyNode(&rb) + + return la.equal(lb) +} + +// DecodePatch decodes the passed JSON document as an RFC 6902 patch. +func DecodePatch(buf []byte) (Patch, error) { + var p Patch + + err := json.Unmarshal(buf, &p) + + if err != nil { + return nil, err + } + + return p, nil +} + +// Apply mutates a JSON document according to the patch, and returns the new +// document. +func (p Patch) Apply(doc []byte) ([]byte, error) { + return p.ApplyIndent(doc, "") +} + +// ApplyIndent mutates a JSON document according to the patch, and returns the new +// document indented. +func (p Patch) ApplyIndent(doc []byte, indent string) ([]byte, error) { + var pd container + if doc[0] == '[' { + pd = &partialArray{} + } else { + pd = &partialDoc{} + } + + err := json.Unmarshal(doc, pd) + + if err != nil { + return nil, err + } + + err = nil + + var accumulatedCopySize int64 + + for _, op := range p { + switch op.Kind() { + case "add": + err = p.add(&pd, op) + case "remove": + err = p.remove(&pd, op) + case "replace": + err = p.replace(&pd, op) + case "move": + err = p.move(&pd, op) + case "test": + err = p.test(&pd, op) + case "copy": + err = p.copy(&pd, op, &accumulatedCopySize) + default: + err = fmt.Errorf("Unexpected kind: %s", op.Kind()) + } + + if err != nil { + return nil, err + } + } + + if indent != "" { + return json.MarshalIndent(pd, "", indent) + } + + return json.Marshal(pd) +} + +// From http://tools.ietf.org/html/rfc6901#section-4 : +// +// Evaluation of each reference token begins by decoding any escaped +// character sequence. This is performed by first transforming any +// occurrence of the sequence '~1' to '/', and then transforming any +// occurrence of the sequence '~0' to '~'. + +var ( + rfc6901Decoder = strings.NewReplacer("~1", "/", "~0", "~") +) + +func decodePatchKey(k string) string { + return rfc6901Decoder.Replace(k) +} From 471dd166738ed95a56ecdb2e1714cf281d68bdb0 Mon Sep 17 00:00:00 2001 From: GLYASAI Date: Sat, 8 Feb 2020 07:49:29 +0800 Subject: [PATCH 05/47] discover ip from k8s and update /etc/hosts --- cmd/node/option/conf.go | 3 +- cmd/node/server/server.go | 58 ++++++++------ discover.v2/k8s_discover.go | 15 ++-- discover.v2/k8s_discover_test.go | 43 ++++++++++- node/initiate/host.go | 129 +++++++++++++------------------ node/initiate/hosts_test.go | 41 ++++++++++ node/kubecache/kube_cache.go | 68 +--------------- node/nodem/envoy/server_v2.go | 6 +- 8 files changed, 189 insertions(+), 174 deletions(-) diff --git a/cmd/node/option/conf.go b/cmd/node/option/conf.go index 632cf73a1..d1d31336a 100644 --- a/cmd/node/option/conf.go +++ b/cmd/node/option/conf.go @@ -124,6 +124,7 @@ type Conf struct { // Namespace for Rainbond application. RbdNamespace string ImageRepositoryHost string + HostsFile string } //StatsdConfig StatsdConfig @@ -185,7 +186,7 @@ func (a *Conf) AddFlags(fs *pflag.FlagSet) { fs.Int32Var(&a.ImageGCLowThresholdPercent, "image-gc-low-threshold", 75, "The percent of disk usage before which image garbage collection is never run. Lowest disk usage to garbage collect to. Values must be within the range [0, 100] and should not be larger than that of --image-gc-high-threshold.") fs.StringVar(&a.RbdNamespace, "rbd-ns", "rbd-system", "The namespace of rainbond applications.") fs.StringVar(&a.ImageRepositoryHost, "image-repo-host", "goodrain.me", "The host of image repository") - + fs.StringVar(&a.HostsFile, "hostsfile", "/tmp/hosts", "/etc/hosts mapped path in the container. eg. /etc/hosts:/tmp/hosts. Do not set hostsfile to /etc/hosts") } //SetLog 设置log diff --git a/cmd/node/server/server.go b/cmd/node/server/server.go index 9f0e77de6..12bf90a03 100644 --- a/cmd/node/server/server.go +++ b/cmd/node/server/server.go @@ -21,8 +21,9 @@ package server import ( "context" "fmt" - "github.com/eapache/channels" + "github.com/goodrain/rainbond/discover.v2" "github.com/goodrain/rainbond/node/initiate" + "k8s.io/client-go/kubernetes" "os" "os/signal" "syscall" @@ -38,12 +39,13 @@ import ( "github.com/goodrain/rainbond/node/nodem/docker" "github.com/goodrain/rainbond/node/nodem/envoy" etcdutil "github.com/goodrain/rainbond/util/etcd" + k8sutil "github.com/goodrain/rainbond/util/k8s" "github.com/Sirupsen/logrus" ) //Run start run -func Run(c *option.Conf) error { +func Run(cfg *option.Conf) error { var stoped = make(chan struct{}) stopfunc := func() error { close(stoped) @@ -54,18 +56,29 @@ func Run(c *option.Conf) error { defer cancel() etcdClientArgs := &etcdutil.ClientArgs{ - Endpoints: c.EtcdEndpoints, - CaFile: c.EtcdCaFile, - CertFile: c.EtcdCertFile, - KeyFile: c.EtcdKeyFile, - DialTimeout: c.EtcdDialTimeout, + Endpoints: cfg.EtcdEndpoints, + CaFile: cfg.EtcdCaFile, + CertFile: cfg.EtcdCertFile, + KeyFile: cfg.EtcdKeyFile, + DialTimeout: cfg.EtcdDialTimeout, } - - if err := c.ParseClient(ctx, etcdClientArgs); err != nil { + if err := cfg.ParseClient(ctx, etcdClientArgs); err != nil { return fmt.Errorf("config parse error:%s", err.Error()) } - nodemanager, err := nodem.NewNodeManager(c) + config, err := k8sutil.NewRestConfig(cfg.K8SConfPath) + if err != nil { + return err + } + clientset, err := kubernetes.NewForConfig(config) + if err != nil { + return err + } + + k8sDiscover := discover.NewK8sDiscover(ctx, clientset, cfg) + defer k8sDiscover.Stop() + + nodemanager, err := nodem.NewNodeManager(cfg) if err != nil { return fmt.Errorf("create node manager failed: %s", err) } @@ -74,7 +87,7 @@ func Run(c *option.Conf) error { } err = eventLog.NewManager(eventLog.EventConfig{ - EventLogServers: c.EventLogServer, + EventLogServers: cfg.EventLogServer, DiscoverArgs: etcdClientArgs, }) if err != nil { @@ -84,26 +97,27 @@ func Run(c *option.Conf) error { defer eventLog.CloseManager() logrus.Debug("create and start event log client success") - registryUpdateCh := channels.NewRingChannel(1024) - kubecli, err := kubecache.NewKubeClient(c, registryUpdateCh) + kubecli, err := kubecache.NewKubeClient(cfg, clientset) if err != nil { return err } defer kubecli.Stop() - hostManager := initiate.NewHostManager(c, kubecli, registryUpdateCh) + hostManager, err := initiate.NewHostManager(cfg, k8sDiscover) + if err != nil { + return fmt.Errorf("create new host manager: %v", err) + } hostManager.Start() - defer hostManager.Stop() logrus.Debugf("rbd-namespace=%s; rbd-docker-secret=%s", os.Getenv("RBD_NAMESPACE"), os.Getenv("RBD_DOCKER_SECRET")) // sync docker inscure registries cert info into all rainbond node - if err = docker.SyncDockerCertFromSecret(kubecli.GetKubeClient(), os.Getenv("RBD_NAMESPACE"), os.Getenv("RBD_DOCKER_SECRET")); err != nil { // TODO fanyangyang namespace secretname + if err = docker.SyncDockerCertFromSecret(clientset, os.Getenv("RBD_NAMESPACE"), os.Getenv("RBD_DOCKER_SECRET")); err != nil { // TODO fanyangyang namespace secretname return fmt.Errorf("sync docker cert from secret error: %s", err.Error()) } // init etcd client - if err = store.NewClient(ctx, c, etcdClientArgs); err != nil { - return fmt.Errorf("Connect to ETCD %s failed: %s", c.EtcdEndpoints, err) + if err = store.NewClient(ctx, cfg, etcdClientArgs); err != nil { + return fmt.Errorf("Connect to ETCD %s failed: %s", cfg.EtcdEndpoints, err) } errChan := make(chan error, 3) if err := nodemanager.Start(errChan); err != nil { @@ -114,7 +128,7 @@ func Run(c *option.Conf) error { //master服务在node服务之后启动 var ms *masterserver.MasterServer - if c.RunMode == "master" { + if cfg.RunMode == "master" { ms, err = masterserver.NewMasterServer(nodemanager.GetCurrentNode(), kubecli) if err != nil { logrus.Errorf(err.Error()) @@ -129,7 +143,7 @@ func Run(c *option.Conf) error { logrus.Debug("create and start master server moudle success") } //create api manager - apiManager := api.NewManager(*c, nodemanager.GetCurrentNode(), ms, kubecli) + apiManager := api.NewManager(*cfg, nodemanager.GetCurrentNode(), ms, kubecli) if err := apiManager.Start(errChan); err != nil { return err } @@ -139,7 +153,7 @@ func Run(c *option.Conf) error { defer apiManager.Stop() //create service mesh controller - grpcserver, err := envoy.CreateDiscoverServerManager(kubecli, *c) + grpcserver, err := envoy.CreateDiscoverServerManager(clientset, *cfg) if err != nil { return err } @@ -165,7 +179,7 @@ func Run(c *option.Conf) error { logrus.Info("See you next time!") return nil } - err := initService(c, startfunc, stopfunc) + err := initService(cfg, startfunc, stopfunc) if err != nil { return err } diff --git a/discover.v2/k8s_discover.go b/discover.v2/k8s_discover.go index e5629ec85..a1bcc9000 100644 --- a/discover.v2/k8s_discover.go +++ b/discover.v2/k8s_discover.go @@ -85,17 +85,15 @@ func (k *k8sDiscover) discover(name string, callback CallbackUpdate) { return case event := <-w.ResultChan(): pod := event.Object.(*corev1.Pod) - if !isPodReady(pod) { - continue - } ep := endpointForPod(pod) switch event.Type { case watch.Deleted: callback.UpdateEndpoints(config.DELETE, ep) - case watch.Added: - callback.UpdateEndpoints(config.ADD, ep) - case watch.Modified: - callback.UpdateEndpoints(config.UPDATE, ep) + case watch.Added, watch.Modified: + if !isPodReady(pod) { + continue + } + callback.UpdateEndpoints(config.SYNC, ep) case watch.Error: k.rewatchWithErr(name, callback, err) } @@ -157,6 +155,9 @@ func endpointForPod(pod *corev1.Pod) *config.Endpoint { } func isPodReady(pod *corev1.Pod) bool { + if pod.ObjectMeta.DeletionTimestamp != nil { + return false + } for _, cond := range pod.Status.Conditions { if cond.Type == corev1.PodReady && cond.Status == corev1.ConditionTrue { return true diff --git a/discover.v2/k8s_discover_test.go b/discover.v2/k8s_discover_test.go index 83ba08831..9d88eddd4 100644 --- a/discover.v2/k8s_discover_test.go +++ b/discover.v2/k8s_discover_test.go @@ -5,13 +5,15 @@ import ( "fmt" "github.com/goodrain/rainbond/cmd/node/option" "github.com/goodrain/rainbond/discover/config" + "k8s.io/client-go/kubernetes" "k8s.io/client-go/kubernetes/fake" "testing" "time" + corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - corev1 "k8s.io/api/core/v1" + k8sutil "github.com/goodrain/rainbond/util/k8s" ) func TestK8sDiscover_AddProject(t *testing.T) { @@ -103,3 +105,42 @@ func (t *testCallback) UpdateEndpoints(endpoints ...*config.Endpoint) { func (t *testCallback) Error(err error) { fmt.Println(err) } + +func TestK8sDiscover_AddProject2(t *testing.T) { + c, err := k8sutil.NewRestConfig("/Users/abewang/.kube/config") + if err != nil { + t.Error(err) + t.FailNow() + } + clientset, err := kubernetes.NewForConfig(c) + if err != nil { + t.Error(err) + t.FailNow() + } + + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + cfg := &option.Conf{RbdNamespace: "rbd-system"} + discover := NewK8sDiscover(ctx, clientset, cfg) + defer discover.Stop() + callback := &testCallback{ + epCh: make(chan []*config.Endpoint), + errCh: make(chan error), + } + discover.AddProject("rbd-gateway", callback) + + for { + select { + case endpoints := <-callback.epCh: + for _, ep := range endpoints { + fmt.Printf("%#v\n", ep) + } + case err := <-callback.errCh: + t.Errorf("received unexpected error from callback: %v", err) + return + default: + + } + } +} diff --git a/node/initiate/host.go b/node/initiate/host.go index 6f47a14ef..c147f0117 100644 --- a/node/initiate/host.go +++ b/node/initiate/host.go @@ -22,19 +22,17 @@ package initiate import ( "bufio" + "context" "errors" "fmt" + "github.com/Sirupsen/logrus" + "github.com/goodrain/rainbond/discover.v2" + "github.com/goodrain/rainbond/discover/config" "net" "os" "strings" - "github.com/eapache/channels" "github.com/goodrain/rainbond/cmd/node/option" - "github.com/goodrain/rainbond/node/kubecache" - - "github.com/Sirupsen/logrus" - corev1 "k8s.io/api/core/v1" - "k8s.io/apimachinery/pkg/labels" ) const ( @@ -52,86 +50,69 @@ var ( // HostManager is responsible for writing the resolution of the private image repository domain name to /etc/hosts. type HostManager interface { Start() - Stop() } // NewHostManager creates a new HostManager. -func NewHostManager(cfg *option.Conf, kubecli kubecache.KubeClient, registryUpdateCh *channels.RingChannel) HostManager { - return &hostManager{ - registryUpdateCh: registryUpdateCh, - stopCh: make(chan struct{}), - cfg: cfg, - kubecli: kubecli, +func NewHostManager(cfg *option.Conf, discover discover.Discover) (HostManager, error) { + hosts, err := NewHosts(cfg.HostsFile) + if err != nil { + return nil, err } + callback := &hostCallback{ + cfg: cfg, + hosts: hosts, + } + return &hostManager{ + cfg: cfg, + discover: discover, + hostCallback: callback, + }, nil } type hostManager struct { - registryUpdateCh *channels.RingChannel - stopCh chan struct{} - - cfg *option.Conf - kubecli kubecache.KubeClient + ctx context.Context + cfg *option.Conf + discover discover.Discover + hostCallback *hostCallback } func (h *hostManager) Start() { - for { - select { - case <-h.registryUpdateCh.Out(): - // ignore all error. If an error occurs, do not write the hosts file - ep, err := h.getEndpointForRegistry() - if err != nil { - logrus.Warningf("finding endpoint for registry: %v", err) - break - } - if err := h.CleanupAndFlush(ep, h.cfg.ImageRepositoryHost); err != nil { - logrus.Warningf("clean and flush /etc/hosts: %v", err) - } - case <-h.stopCh: - return + if h.cfg.ImageRepositoryHost == "" { + // no need to write hosts file + return + } + h.discover.AddProject("rbd-gateway", h.hostCallback) +} + +type hostCallback struct { + cfg *option.Conf + hosts Hosts +} + +func (h *hostCallback) UpdateEndpoints(endpoints ...*config.Endpoint) { + logrus.Info("hostCallback; update endpoints") + if err := h.hosts.Cleanup(); err != nil { + logrus.Warningf("cleanup hosts file: %v", err) + return + } + + if len(endpoints) > 0 { + logrus.Infof("found endpints: %d; endpoint selected: %#v", len(endpoints), *endpoints[0]) + lines := []string{ + startOfSection, + endpoints[0].URL + " " + h.cfg.ImageRepositoryHost, + endOfSection, } + h.hosts.AddLines(lines...) + } + + if err := h.hosts.Flush(); err != nil { + logrus.Warningf("flush hosts file: %v", err) } } -func (h *hostManager) Stop() { - close(h.stopCh) -} - -func (h *hostManager) getEndpointForRegistry() (string, error) { - selector, _ := labels.Parse("name=rbd-hub") - pods, err := h.kubecli.GetPodsBySelector(h.cfg.RbdNamespace, selector) - if err != nil { - return "", err - } - for _, po := range pods { - for _, cdt := range po.Status.Conditions { - if cdt.Type == corev1.PodReady && cdt.Status == corev1.ConditionTrue { - return po.Status.HostIP, nil - } - } - } - - return "", ErrRegistryAddressNotFound -} - -// CleanupAndFlush cleanup old content and write new ones. -func (h *hostManager) CleanupAndFlush(ip, domain string) error { - hosts, err := NewHosts() - if err != nil { - return fmt.Errorf("error creating hosts: %v", err) - } - - if err := hosts.Cleanup(); err != nil { - return fmt.Errorf("error cleanup hosts: %v", err) - } - - lines := []string{ - startOfSection, - ip + " " + domain, - endOfSection, - } - hosts.AddLines(lines...) - - return hosts.Flush() +func (h *hostCallback) Error(err error) { + logrus.Warningf("unexpected error from host callback: %v", err) } // HostsLine represents a single line in the hosts file. @@ -177,8 +158,8 @@ type Hosts struct { } // NewHosts return a new instance of ``Hosts``. -func NewHosts() (Hosts, error) { - hosts := Hosts{Path: "/etc/hosts"} +func NewHosts(hostsFile string) (Hosts, error) { + hosts := Hosts{Path: hostsFile} err := hosts.load() if err != nil { diff --git a/node/initiate/hosts_test.go b/node/initiate/hosts_test.go index 61a8ab46a..a61cbd915 100644 --- a/node/initiate/hosts_test.go +++ b/node/initiate/hosts_test.go @@ -1,8 +1,15 @@ package initiate import ( + "context" + "fmt" + "github.com/goodrain/rainbond/cmd/node/option" + "github.com/goodrain/rainbond/discover.v2" + "k8s.io/client-go/kubernetes" "reflect" "testing" + + k8sutil "github.com/goodrain/rainbond/util/k8s" ) func TestHosts_Cleanup(t *testing.T) { @@ -81,3 +88,37 @@ func TestHosts_Add(t *testing.T) { }) } } + +func TestHostManager_Start(t *testing.T) { + config, err := k8sutil.NewRestConfig("/Users/abewang/.kube/config") + if err != nil { + t.Error(err) + t.FailNow() + } + clientset, err := kubernetes.NewForConfig(config) + if err != nil { + t.Error(err) + t.FailNow() + } + + ctx := context.Background() + cfg := &option.Conf{ + RbdNamespace: "rbd-system", + ImageRepositoryHost: "goodrain.me", + } + k8sDiscover := discover.NewK8sDiscover(ctx, clientset, cfg) + defer k8sDiscover.Stop() + + hostManager, err := NewHostManager(cfg, k8sDiscover) + if err != nil { + t.Error(err) + t.FailNow() + } + hostManager.Start() + + fmt.Println("oook") + + select { + + } +} diff --git a/node/kubecache/kube_cache.go b/node/kubecache/kube_cache.go index 348068e71..4b7fcf9d9 100644 --- a/node/kubecache/kube_cache.go +++ b/node/kubecache/kube_cache.go @@ -22,7 +22,6 @@ import ( "fmt" "github.com/eapache/channels" "k8s.io/apimachinery/pkg/labels" - "k8s.io/client-go/tools/cache" "math" "strings" "time" @@ -33,8 +32,6 @@ import ( "github.com/goodrain/rainbond/node/nodem/client" "github.com/Sirupsen/logrus" - k8sutil "github.com/goodrain/rainbond/util/k8s" - corev1 "k8s.io/api/core/v1" v1 "k8s.io/api/core/v1" "k8s.io/api/policy/v1beta1" apierrors "k8s.io/apimachinery/pkg/api/errors" @@ -83,7 +80,6 @@ func (l l) contains(k, v string) bool { //KubeClient KubeClient type KubeClient interface { - GetKubeClient() kubernetes.Interface UpK8sNode(*client.HostNode) (*v1.Node, error) DownK8sNode(nodename string) error GetAllPods() (pods []*v1.Pod, err error) @@ -103,70 +99,17 @@ type KubeClient interface { } //NewKubeClient NewKubeClient -func NewKubeClient(cfg *conf.Conf, registryUpdateCh *channels.RingChannel) (KubeClient, error) { - config, err := k8sutil.NewRestConfig(cfg.K8SConfPath) - if err != nil { - return nil, err - } - config.QPS = 50 - config.Burst = 100 - - cli, err := kubernetes.NewForConfig(config) - if err != nil { - return nil, err - } - +func NewKubeClient(cfg *conf.Conf, clientset kubernetes.Interface) (KubeClient, error) { stop := make(chan struct{}) - sharedInformers := informers.NewSharedInformerFactoryWithOptions(cli, cfg.MinResyncPeriod) - - // Pod Event Handler - podEventHandler := cache.ResourceEventHandlerFuncs{ - AddFunc: func(obj interface{}) { - ep := obj.(*corev1.Pod) - if !l(ep.Labels).contains("name", "rbd-hub") { - return - } - registryUpdateCh.In() <- Event{ - Type: CreateEvent, - Obj: obj, - } - }, - DeleteFunc: func(obj interface{}) { - ep := obj.(*corev1.Pod) - if !l(ep.Labels).contains("name", "rbd-hub") { - return - } - registryUpdateCh.In() <- Event{ - Type: DeleteEvent, - Obj: obj, - } - }, - UpdateFunc: func(old, cur interface{}) { - oldPod := old.(*corev1.Pod) - curPod := cur.(*corev1.Pod) - - if oldPod.Status.Phase == curPod.Status.Phase { - return - } - - if !l(curPod.Labels).contains("name", "rbd-hub") { - return - } - registryUpdateCh.In() <- Event{ - Type: UpdateEvent, - Obj: cur, - } - }, - } + sharedInformers := informers.NewSharedInformerFactoryWithOptions(clientset, cfg.MinResyncPeriod) sharedInformers.Core().V1().Endpoints().Informer() sharedInformers.Core().V1().Services().Informer() sharedInformers.Core().V1().ConfigMaps().Informer() sharedInformers.Core().V1().Nodes().Informer() - sharedInformers.Core().V1().Pods().Informer().AddEventHandler(podEventHandler) + sharedInformers.Core().V1().Pods().Informer() sharedInformers.Start(stop) return &kubeClient{ - kubeclient: cli, stop: stop, sharedInformers: sharedInformers, }, nil @@ -185,11 +128,6 @@ func (k *kubeClient) Stop() { } } -//GetKubeClient get kube client -func (k *kubeClient) GetKubeClient() kubernetes.Interface { - return k.kubeclient -} - //GetNodeByName get node func (k *kubeClient) GetNodeByName(nodename string) (*v1.Node, error) { return k.sharedInformers.Core().V1().Nodes().Lister().Get(nodename) diff --git a/node/nodem/envoy/server_v2.go b/node/nodem/envoy/server_v2.go index 5ed3b8e73..b14917a19 100644 --- a/node/nodem/envoy/server_v2.go +++ b/node/nodem/envoy/server_v2.go @@ -28,8 +28,6 @@ import ( "sync/atomic" "time" - "github.com/goodrain/rainbond/node/kubecache" - "k8s.io/apimachinery/pkg/labels" "github.com/Sirupsen/logrus" @@ -256,13 +254,13 @@ func (d *DiscoverServerManager) setSnapshot(nc *NodeConfig) error { } //CreateDiscoverServerManager create discover server manager -func CreateDiscoverServerManager(client kubecache.KubeClient, conf option.Conf) (*DiscoverServerManager, error) { +func CreateDiscoverServerManager(clientset kubernetes.Interface, conf option.Conf) (*DiscoverServerManager, error) { configcache := cache.NewSnapshotCache(false, Hasher{}, logrus.WithField("module", "config-cache")) ctx, cancel := context.WithCancel(context.Background()) dsm := &DiscoverServerManager{ server: server.NewServer(configcache, nil), cacheManager: configcache, - kubecli: client.GetKubeClient(), + kubecli: clientset, conf: conf, eventChan: make(chan *Event, 100), pool: &sync.Pool{ From 8ddbfe7554e99e7e76a7cfe3d3fe186be734bc25 Mon Sep 17 00:00:00 2001 From: GLYASAI Date: Sat, 8 Feb 2020 08:09:10 +0800 Subject: [PATCH 06/47] change hostsfile --- cmd/node/option/conf.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmd/node/option/conf.go b/cmd/node/option/conf.go index d1d31336a..69aaa2a8e 100644 --- a/cmd/node/option/conf.go +++ b/cmd/node/option/conf.go @@ -186,7 +186,7 @@ func (a *Conf) AddFlags(fs *pflag.FlagSet) { fs.Int32Var(&a.ImageGCLowThresholdPercent, "image-gc-low-threshold", 75, "The percent of disk usage before which image garbage collection is never run. Lowest disk usage to garbage collect to. Values must be within the range [0, 100] and should not be larger than that of --image-gc-high-threshold.") fs.StringVar(&a.RbdNamespace, "rbd-ns", "rbd-system", "The namespace of rainbond applications.") fs.StringVar(&a.ImageRepositoryHost, "image-repo-host", "goodrain.me", "The host of image repository") - fs.StringVar(&a.HostsFile, "hostsfile", "/tmp/hosts", "/etc/hosts mapped path in the container. eg. /etc/hosts:/tmp/hosts. Do not set hostsfile to /etc/hosts") + fs.StringVar(&a.HostsFile, "hostsfile", "/newetc/hosts", "/etc/hosts mapped path in the container. eg. /etc/hosts:/tmp/hosts. Do not set hostsfile to /etc/hosts") } //SetLog 设置log From 2c480691873f535520f01ffadb63729092433180 Mon Sep 17 00:00:00 2001 From: barnettZQG Date: Sun, 9 Feb 2020 21:22:19 +0800 Subject: [PATCH 07/47] change grctl cluster command --- grctl/cmd/cluster.go | 23 +++++++---------------- 1 file changed, 7 insertions(+), 16 deletions(-) diff --git a/grctl/cmd/cluster.go b/grctl/cmd/cluster.go index 15538c324..590d3591b 100644 --- a/grctl/cmd/cluster.go +++ b/grctl/cmd/cluster.go @@ -21,6 +21,7 @@ package cmd import ( "fmt" "os" + "os/exec" "strconv" "strings" @@ -88,22 +89,8 @@ func getClusterInfo(c *cli.Context) error { }())+"%") fmt.Println(table) - //show services health status - allNodeHealth, err := clients.RegionClient.Nodes().GetAllNodeHealth() - handleErr(err) - serviceTable2 := termtables.CreateTable() - serviceTable2.AddHeaders("Service", "HealthyQuantity/Total", "Message") - serviceStatusInfo := allNodeHealth - status, message := clusterStatus(serviceStatusInfo["Role"], serviceStatusInfo["Ready"]) - serviceTable2.AddRow("\033[0;33;33mClusterStatus\033[0m", status, message) - for name, v := range serviceStatusInfo { - if name == "Role" { - continue - } - status, message := summaryResult(v) - serviceTable2.AddRow(name, status, message) - } - fmt.Println(serviceTable2.Render()) + //show component health status + printComponentStatus() //show node detail serviceTable := termtables.CreateTable() serviceTable.AddHeaders("Uid", "IP", "HostName", "NodeRole", "Status") @@ -232,3 +219,7 @@ func clusterStatus(roleList []map[string]string, ReadyList []map[string]string) } return clusterStatus, errMessage } + +func printComponentStatus() { + exec.Command("kubectl", "get", "pod", "-n", "rbd-system", "-o", "wide").Run() +} From 69c7975dd183e02c359425de89908ba04a0b0447 Mon Sep 17 00:00:00 2001 From: barnettZQG Date: Sun, 9 Feb 2020 21:43:51 +0800 Subject: [PATCH 08/47] fix concurrent write to websocket connection bug --- eventlog/exit/web/pusher.go | 36 +++++++++++++++++++++++++++++++----- 1 file changed, 31 insertions(+), 5 deletions(-) diff --git a/eventlog/exit/web/pusher.go b/eventlog/exit/web/pusher.go index 6468ef697..8576c1ed5 100644 --- a/eventlog/exit/web/pusher.go +++ b/eventlog/exit/web/pusher.go @@ -46,6 +46,11 @@ func (w *WebsocketMessage) Encode() []byte { return reb } +type sendMessage struct { + messageType int + data []byte +} + //PubContext websocket context type PubContext struct { ID string @@ -57,6 +62,7 @@ type PubContext struct { chans map[string]*Chan lock sync.Mutex close chan struct{} + sendQueue chan sendMessage } //Chan handle @@ -82,6 +88,7 @@ func NewPubContext(upgrader websocket.Upgrader, httpRequest: httpRequest, server: s, chans: make(map[string]*Chan, 2), + sendQueue: make(chan sendMessage, 1024), close: make(chan struct{}), } } @@ -221,7 +228,7 @@ func (p *PubContext) readMessage(closed chan struct{}) { continue } if messageType == websocket.PingMessage { - p.conn.WriteMessage(websocket.PongMessage, []byte{}) + p.SendWebsocketMessage(websocket.PongMessage) continue } if messageType == websocket.BinaryMessage { @@ -230,16 +237,34 @@ func (p *PubContext) readMessage(closed chan struct{}) { } } +func (p *PubContext) send() { + for { + select { + case m, ok := <-p.sendQueue: + if !ok { + return + } + p.conn.SetWriteDeadline(time.Now().Add(10 * time.Second)) + if err := p.conn.WriteMessage(m.messageType, m.data); err != nil { + p.server.log.Debugf("write websocket message failure %s", err.Error()) + } + case <-p.close: + p.server.log.Debugf("pub context send chan closed") + return + } + } +} + //SendMessage send websocket message func (p *PubContext) SendMessage(message WebsocketMessage) error { - p.conn.SetWriteDeadline(time.Now().Add(10 * time.Second)) - return p.conn.WriteMessage(websocket.TextMessage, message.Encode()) + p.sendQueue <- sendMessage{messageType: websocket.TextMessage, data: message.Encode()} + return nil } //SendWebsocketMessage send websocket message func (p *PubContext) SendWebsocketMessage(message int) error { - p.conn.SetWriteDeadline(time.Now().Add(10 * time.Second)) - return p.conn.WriteMessage(message, []byte{}) + p.sendQueue <- sendMessage{messageType: message, data: []byte{}} + return nil } func (p *PubContext) sendPing(closed chan struct{}) { @@ -263,6 +288,7 @@ func (p *PubContext) Start() { p.server.log.Error("Create web socket conn error.", err.Error()) return } + go p.send() pingclosed := make(chan struct{}) readclosed := make(chan struct{}) go p.sendPing(pingclosed) From 984b5902ed458260dc4ce0ad94d3be174967854d Mon Sep 17 00:00:00 2001 From: barnettZQG Date: Tue, 11 Feb 2020 12:43:10 +0800 Subject: [PATCH 09/47] support push image to ali hub --- release.sh | 13 ++++++++++--- worker/appm/conversion/version.go | 4 ++++ 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/release.sh b/release.sh index 1696c3b06..6d8f583df 100755 --- a/release.sh +++ b/release.sh @@ -9,6 +9,7 @@ if [ $BUILD_IMAGE_BASE_NAME ]; then IMAGE_BASE_NAME=${BUILD_IMAGE_BASE_NAME} fi + GO_VERSION=1.11-stretch GATEWAY_GO_VERSION=1.11-alpine3.8 @@ -82,10 +83,16 @@ build::image() { fi echo "---> build image:$1" sed "s/__RELEASE_DESC__/${release_desc}/" Dockerfile > Dockerfile.release - docker build -t ${IMAGE_BASE_NAME}/rbd-$1:${VERSION} -f Dockerfile.release . + docker build -t "${IMAGE_BASE_NAME}/rbd-$1:${VERSION}" -f Dockerfile.release . if [ "$2" = "push" ];then - docker login -u $DOCKER_USERNAME -p $DOCKER_PASSWORD - docker push ${IMAGE_BASE_NAME}/rbd-$1:${VERSION} + docker login -u "$DOCKER_USERNAME" -p "$DOCKER_PASSWORD" + docker push "${IMAGE_BASE_NAME}/rbd-$1:${VERSION}" + if [ ${DOMESTIC_BASE_NAME} ]; + then + docker tag "${IMAGE_BASE_NAME}/rbd-$1:${VERSION}" "${DOMESTIC_BASE_NAME}/rbd-$1:${VERSION}" + docker login -u "$DOMESTIC_DOCKER_USERNAME" -p "$DOMESTIC_DOCKER_PASSWORD" + docker push "${DOMESTIC_BASE_NAME}/rbd-$1:${VERSION}" + fi fi rm -f ./Dockerfile.release rm -f ./${BASE_NAME}-$1 diff --git a/worker/appm/conversion/version.go b/worker/appm/conversion/version.go index 3875c50e0..ea79351e2 100644 --- a/worker/appm/conversion/version.go +++ b/worker/appm/conversion/version.go @@ -99,6 +99,10 @@ func TenantServiceVersion(as *v1.AppService, dbmanager db.Manager) error { }(), }, } + var terminationGracePeriodSeconds int64 = 10 + if as.GetDeployment() != nil { + podtmpSpec.Spec.TerminationGracePeriodSeconds = &terminationGracePeriodSeconds + } //set to deployment or statefulset as.SetPodTemplate(podtmpSpec) return nil From dcf445bfe5443aea357834fbedede39d97cc6483 Mon Sep 17 00:00:00 2001 From: barnettZQG Date: Tue, 11 Feb 2020 13:07:26 +0800 Subject: [PATCH 10/47] change docker login --- release.sh | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/release.sh b/release.sh index 6d8f583df..d9401f22d 100755 --- a/release.sh +++ b/release.sh @@ -89,9 +89,9 @@ build::image() { docker push "${IMAGE_BASE_NAME}/rbd-$1:${VERSION}" if [ ${DOMESTIC_BASE_NAME} ]; then - docker tag "${IMAGE_BASE_NAME}/rbd-$1:${VERSION}" "${DOMESTIC_BASE_NAME}/rbd-$1:${VERSION}" - docker login -u "$DOMESTIC_DOCKER_USERNAME" -p "$DOMESTIC_DOCKER_PASSWORD" - docker push "${DOMESTIC_BASE_NAME}/rbd-$1:${VERSION}" + docker tag "${IMAGE_BASE_NAME}/rbd-$1:${VERSION}" "${DOMESTIC_BASE_NAME}/${DOMESTIC_NAMESPACE}/rbd-$1:${VERSION}" + docker login -u "$DOMESTIC_DOCKER_USERNAME" -p "$DOMESTIC_DOCKER_PASSWORD" ${DOMESTIC_BASE_NAME} + docker push "${DOMESTIC_BASE_NAME}/${DOMESTIC_NAMESPACE}/rbd-$1:${VERSION}" fi fi rm -f ./Dockerfile.release From 61cd17125103937a06a3aa0d0adb63ba24fe40f1 Mon Sep 17 00:00:00 2001 From: glyasai Date: Tue, 11 Feb 2020 14:43:59 +0800 Subject: [PATCH 11/47] make kube-conf detault to "" --- cmd/gateway/option/option.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmd/gateway/option/option.go b/cmd/gateway/option/option.go index 32e2358a7..dc5744199 100644 --- a/cmd/gateway/option/option.go +++ b/cmd/gateway/option/option.go @@ -99,7 +99,7 @@ type ListenPorts struct { // AddFlags adds flags func (g *GWServer) AddFlags(fs *pflag.FlagSet) { fs.StringVar(&g.LogLevel, "log-level", "debug", "the gateway log level") - fs.StringVar(&g.K8SConfPath, "kube-conf", "/opt/rainbond/etc/kubernetes/kubecfg/admin.kubeconfig", "absolute path to the kubeconfig file") + fs.StringVar(&g.K8SConfPath, "kube-conf", "", "absolute path to the kubeconfig file") fs.IntVar(&g.ListenPorts.Status, "status-port", 18080, `Port to use for the lua HTTP endpoint configuration.`) fs.IntVar(&g.ListenPorts.Stream, "stream-port", 18081, `Port to use for the lua TCP/UDP endpoint configuration.`) fs.IntVar(&g.ListenPorts.Health, "healthz-port", 10254, `Port to use for the healthz endpoint.`) From 72f4dfd16c553cbecaf6bc2b229eeeb8657e7764 Mon Sep 17 00:00:00 2001 From: barnettZQG Date: Tue, 11 Feb 2020 16:40:32 +0800 Subject: [PATCH 12/47] fix "too many Host headers" bug --- gateway/annotations/proxy/proxy.go | 3 ++- gateway/controller/config/config.go | 14 +++++++++++++- hack/contrib/docker/gateway/nginxtmp/servers.tmpl | 12 +----------- 3 files changed, 16 insertions(+), 13 deletions(-) diff --git a/gateway/annotations/proxy/proxy.go b/gateway/annotations/proxy/proxy.go index 80745a309..1c10b3f7e 100644 --- a/gateway/annotations/proxy/proxy.go +++ b/gateway/annotations/proxy/proxy.go @@ -253,7 +253,8 @@ func (a proxy) Parse(ing *extensions.Ingress) (interface{}, error) { config.ProxyBuffering = defBackend.ProxyBuffering } - config.SetHeaders = make(map[string]string) + config.SetHeaders = defBackend.ProxySetHeaders + //default header for k, v := range defBackend.ProxySetHeaders { config.SetHeaders[k] = v } diff --git a/gateway/controller/config/config.go b/gateway/controller/config/config.go index cf99eb4d9..69b4776d0 100644 --- a/gateway/controller/config/config.go +++ b/gateway/controller/config/config.go @@ -102,7 +102,19 @@ func NewDefault() Configuration { LimitRate: 0, LimitRateAfter: 0, ProxyBuffering: "off", - ProxySetHeaders: map[string]string{}, + //defaut set header + ProxySetHeaders: map[string]string{ + "Host": "$best_http_host", + "X-Real-IP": "$remote_addr", + "X-Forwarded-For": "$remote_addr", + "X-Forwarded-Host": "$best_http_host", + "X-Forwarded-Port": "$pass_port", + "X-Forwarded-Proto": "$pass_access_scheme", + "X-Scheme": "$pass_access_scheme", + // mitigate HTTPoxy Vulnerability + // https://www.nginx.com/blog/mitigating-the-httpoxy-vulnerability-with-nginx/ + "Proxy": "\"\"", + }, }, } return cfg diff --git a/hack/contrib/docker/gateway/nginxtmp/servers.tmpl b/hack/contrib/docker/gateway/nginxtmp/servers.tmpl index 70f7c13b7..c5002b947 100644 --- a/hack/contrib/docker/gateway/nginxtmp/servers.tmpl +++ b/hack/contrib/docker/gateway/nginxtmp/servers.tmpl @@ -51,17 +51,7 @@ server { set $pass_access_scheme $scheme; set $best_http_host $http_host; set $pass_port $server_port; - # default proxy_set_header - proxy_set_header Host $best_http_host; - proxy_set_header X-Real-IP $remote_addr; - proxy_set_header X-Forwarded-For $remote_addr; - proxy_set_header X-Forwarded-Host $best_http_host; - proxy_set_header X-Forwarded-Port $pass_port; - proxy_set_header X-Forwarded-Proto $pass_access_scheme; - proxy_set_header X-Scheme $pass_access_scheme; - # mitigate HTTPoxy Vulnerability - # https://www.nginx.com/blog/mitigating-the-httpoxy-vulnerability-with-nginx/ - proxy_set_header Proxy ""; + # custom proxy_set_header {{ range $k, $v := $loc.Proxy.SetHeaders }} proxy_set_header {{$k}} {{$v}}; From ee9d8e089234d9f45157e709b3339970a89b3586 Mon Sep 17 00:00:00 2001 From: barnettZQG Date: Tue, 11 Feb 2020 19:33:28 +0800 Subject: [PATCH 13/47] fix windowx build bug --- node/nodem/controller/controller_service_windows.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/node/nodem/controller/controller_service_windows.go b/node/nodem/controller/controller_service_windows.go index 577100fd7..d9e74d2d8 100644 --- a/node/nodem/controller/controller_service_windows.go +++ b/node/nodem/controller/controller_service_windows.go @@ -90,7 +90,7 @@ func (w *windowsServiceController) StopList(list []*service.Service) error { func (w *windowsServiceController) RestartService(s *service.Service) error { if err := windows.RestartService(s.Name); err != nil { if strings.Contains(err.Error(), "does not exist") { - if err := w.WriteConfig(s); err != nil { + if _, err := w.WriteConfig(s); err != nil { return fmt.Errorf("ReWrite service config failure %s", err.Error()) } } From fd53fa657402a6a3ea68c7df79816ecbd5faf8b2 Mon Sep 17 00:00:00 2001 From: barnettZQG Date: Tue, 11 Feb 2020 20:44:49 +0800 Subject: [PATCH 14/47] change grctl name --- hack/contrib/docker/grctl/entrypoint.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hack/contrib/docker/grctl/entrypoint.sh b/hack/contrib/docker/grctl/entrypoint.sh index 578dc6cf5..a6cae88fa 100644 --- a/hack/contrib/docker/grctl/entrypoint.sh +++ b/hack/contrib/docker/grctl/entrypoint.sh @@ -4,7 +4,7 @@ if [ "$1" = "bash" ];then elif [ "$1" = "version" ];then echo "$RELEASE_DESC" elif [ "$1" = "install" ];then - cp -a /run/rainbond-grctl /rootfs/path + cp -a /run/rainbond-grctl /rootfs/path/grctl mkdir -p /rootfs/root/.rbd cp -a /etc/goodrain/region.goodrain.me/ssl/ca.pem /ssl/ca.pem cp -a /etc/goodrain/region.goodrain.me/ssl/client.pem /ssl/client.pem From 73bd7e6fb6d0b69f26f8ce8721c9d8642eb4bb37 Mon Sep 17 00:00:00 2001 From: glyasai Date: Wed, 12 Feb 2020 05:47:57 +0800 Subject: [PATCH 15/47] delete metrics-server installation --- cmd/worker/server/server.go | 12 +- worker/master/master.go | 30 +- worker/master/metricsserv/metric_serv_test.go | 97 ------ worker/master/metricsserv/metrics_serv.go | 280 ------------------ 4 files changed, 6 insertions(+), 413 deletions(-) delete mode 100644 worker/master/metricsserv/metric_serv_test.go delete mode 100644 worker/master/metricsserv/metrics_serv.go diff --git a/cmd/worker/server/server.go b/cmd/worker/server/server.go index e14cf500d..a0c91e9fe 100644 --- a/cmd/worker/server/server.go +++ b/cmd/worker/server/server.go @@ -25,9 +25,6 @@ import ( "github.com/Sirupsen/logrus" "github.com/eapache/channels" - "k8s.io/client-go/kubernetes" - kubeaggregatorclientset "k8s.io/kube-aggregator/pkg/client/clientset_generated/clientset" - "github.com/goodrain/rainbond/cmd/worker/option" "github.com/goodrain/rainbond/db" "github.com/goodrain/rainbond/db/config" @@ -42,6 +39,7 @@ import ( "github.com/goodrain/rainbond/worker/master" "github.com/goodrain/rainbond/worker/monitor" "github.com/goodrain/rainbond/worker/server" + "k8s.io/client-go/kubernetes" ) //Run start run @@ -85,12 +83,6 @@ func Run(s *option.Worker) error { } s.Config.KubeClient = clientset - kubeaggregatorclientset, err := kubeaggregatorclientset.NewForConfig(restConfig) - if err != nil { - logrus.Error("kube aggregator; read kube config file error.", err) - return err - } - //step 3: create resource store startCh := channels.NewRingChannel(1024) updateCh := channels.NewRingChannel(1024) @@ -111,7 +103,7 @@ func Run(s *option.Worker) error { defer controllerManager.Stop() //step 5 : start runtime master - masterCon, err := master.NewMasterController(s.Config, cachestore, kubeaggregatorclientset) + masterCon, err := master.NewMasterController(s.Config, cachestore) if err != nil { return err } diff --git a/worker/master/master.go b/worker/master/master.go index 1c85c6cd4..085ab5e46 100644 --- a/worker/master/master.go +++ b/worker/master/master.go @@ -24,18 +24,15 @@ import ( "strings" "time" + "github.com/Sirupsen/logrus" "github.com/prometheus/client_golang/prometheus" corev1 "k8s.io/api/core/v1" - kubeaggregatorclientset "k8s.io/kube-aggregator/pkg/client/clientset_generated/clientset" - "github.com/Sirupsen/logrus" "github.com/goodrain/rainbond/cmd/worker/option" "github.com/goodrain/rainbond/db" "github.com/goodrain/rainbond/db/model" - etcdutil "github.com/goodrain/rainbond/util/etcd" "github.com/goodrain/rainbond/util/leader" "github.com/goodrain/rainbond/worker/appm/store" - "github.com/goodrain/rainbond/worker/master/metricsserv" "github.com/goodrain/rainbond/worker/master/podevent" "github.com/goodrain/rainbond/worker/master/volumes/provider" "github.com/goodrain/rainbond/worker/master/volumes/provider/lib/controller" @@ -58,12 +55,10 @@ type Controller struct { stopCh chan struct{} podEventChs []chan *corev1.Pod podEvent *podevent.PodEvent - - metricsServerManager *metricsserv.MetricsServiceManager } //NewMasterController new master controller -func NewMasterController(conf option.Config, store store.Storer, kubeaggregatorclientset kubeaggregatorclientset.Interface) (*Controller, error) { +func NewMasterController(conf option.Config, store store.Storer) (*Controller, error) { ctx, cancel := context.WithCancel(context.Background()) // The controller needs to know what the server version is because out-of-tree // provisioners aren't officially supported until 1.5 @@ -87,19 +82,6 @@ func NewMasterController(conf option.Config, store store.Storer, kubeaggregatorc rainbondsslcProvisioner.Name(): rainbondsslcProvisioner, }, serverVersion.GitVersion) stopCh := make(chan struct{}) - etcdClientArgs := &etcdutil.ClientArgs{ - Endpoints: conf.EtcdEndPoints, - CaFile: conf.EtcdCaFile, - CertFile: conf.EtcdCertFile, - KeyFile: conf.EtcdKeyFile, - AutoSyncInterval: time.Second * 30, - DialTimeout: time.Second * 10, - } - clientv3, err := etcdutil.NewClient(ctx, etcdClientArgs) - if err != nil { - cancel() - return nil, err - } return &Controller{ conf: conf, @@ -119,9 +101,8 @@ func NewMasterController(conf option.Config, store store.Storer, kubeaggregatorc Name: "appfs", Help: "tenant service fs used.", }, []string{"tenant_id", "service_id", "volume_type"}), - diskCache: statistical.CreatDiskCache(ctx), - podEvent: podevent.New(conf.KubeClient, stopCh), - metricsServerManager: metricsserv.New(conf.KubeClient, kubeaggregatorclientset, clientv3), + diskCache: statistical.CreatDiskCache(ctx), + podEvent: podevent.New(conf.KubeClient, stopCh), }, nil } @@ -159,9 +140,6 @@ func (m *Controller) Start() error { if m.conf.LeaderElectionIdentity == "" { return fmt.Errorf("-leader-election-identity must not be empty") } - if err := m.metricsServerManager.Start(); err != nil { - return fmt.Errorf("start metrics-server manager: %v", err) - } // Name of config map with leader election lock lockName := "rainbond-appruntime-worker-leader" go leader.RunAsLeader(m.ctx, m.conf.KubeClient, m.conf.LeaderElectionNamespace, m.conf.LeaderElectionIdentity, lockName, start, func() {}) diff --git a/worker/master/metricsserv/metric_serv_test.go b/worker/master/metricsserv/metric_serv_test.go deleted file mode 100644 index 2e0aaacd1..000000000 --- a/worker/master/metricsserv/metric_serv_test.go +++ /dev/null @@ -1,97 +0,0 @@ -// RAINBOND, Application Management Platform -// Copyright (C) 2014-2017 Goodrain Co., Ltd. - -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. For any non-GPL usage of Rainbond, -// one or multiple Commercial Licenses authorized by Goodrain Co., Ltd. -// must be obtained first. - -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with this program. If not, see . - -package metricsserv - -import ( - "testing" - "time" - - "github.com/coreos/etcd/clientv3" - "k8s.io/client-go/kubernetes" - - "k8s.io/client-go/tools/clientcmd" - kubeaggregatorclientset "k8s.io/kube-aggregator/pkg/client/clientset_generated/clientset" -) - -func TestNewMetricsServerAPIServer(t *testing.T) { - c, err := clientcmd.BuildConfigFromFlags("", "/opt/rainbond/etc/kubernetes/kubecfg/admin.kubeconfig") - if err != nil { - t.Fatal(err) - } - - kubeaggregatorclientset, err := kubeaggregatorclientset.NewForConfig(c) - if err != nil { - t.Fatal(err) - } - - metricsServiceManager := New(nil, kubeaggregatorclientset, nil) - if err := metricsServiceManager.newMetricsServerAPIService(); err != nil { - t.Fatal(err) - } -} - -func TestStart(t *testing.T) { - clientv3, err := clientv3.New(clientv3.Config{ - Endpoints: []string{"http://127.0.0.1:2379"}, - AutoSyncInterval: time.Second * 30, - DialTimeout: time.Second * 10, - }) - if err != nil { - t.Fatal(err) - } - - c, err := clientcmd.BuildConfigFromFlags("", "../../../test/admin.kubeconfig") - if err != nil { - t.Fatal(err) - } - - clientset, err := kubernetes.NewForConfig(c) - if err != nil { - t.Fatal(err) - } - - kubeaggregatorclientset, err := kubeaggregatorclientset.NewForConfig(c) - if err != nil { - t.Fatal(err) - } - - metricsServiceManager := New(clientset, kubeaggregatorclientset, clientv3) - if err := metricsServiceManager.Start(); err != nil { - t.Fatal(err) - } - - select {} -} - -func TestListMetricsServiceEndpoints(t *testing.T) { - clientv3, err := clientv3.New(clientv3.Config{ - Endpoints: []string{"http://127.0.0.1:2379"}, - AutoSyncInterval: time.Second * 30, - DialTimeout: time.Second * 10, - }) - if err != nil { - t.Fatal(err) - } - metricsServiceManager := New(nil, nil, clientv3) - e, err := metricsServiceManager.listMetricsServiceEndpoints() - if err != nil { - t.Fatal(err) - } - t.Log(e) -} diff --git a/worker/master/metricsserv/metrics_serv.go b/worker/master/metricsserv/metrics_serv.go deleted file mode 100644 index 12474c99f..000000000 --- a/worker/master/metricsserv/metrics_serv.go +++ /dev/null @@ -1,280 +0,0 @@ -// RAINBOND, Application Management Platform -// Copyright (C) 2014-2017 Goodrain Co., Ltd. - -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. For any non-GPL usage of Rainbond, -// one or multiple Commercial Licenses authorized by Goodrain Co., Ltd. -// must be obtained first. - -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with this program. If not, see . - -package metricsserv - -import ( - "context" - "fmt" - "strconv" - "strings" - - "github.com/coreos/etcd/mvcc/mvccpb" - "github.com/pquerna/ffjson/ffjson" - "k8s.io/apimachinery/pkg/util/intstr" - - "github.com/Sirupsen/logrus" - "github.com/coreos/etcd/clientv3" - corev1 "k8s.io/api/core/v1" - k8sErrors "k8s.io/apimachinery/pkg/api/errors" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/client-go/kubernetes" - "k8s.io/kube-aggregator/pkg/apis/apiregistration/v1beta1" - kubeaggregatorclientset "k8s.io/kube-aggregator/pkg/client/clientset_generated/clientset" -) - -// MetricsServiceManager - -type MetricsServiceManager struct { - clientset kubernetes.Interface - apiregistrationClientset kubeaggregatorclientset.Interface - clientv3 *clientv3.Client - - stopCh chan struct{} -} - -type metricsServerEndpoint struct { - Address string - Port int -} - -//New new -func New(clientset kubernetes.Interface, apiregistrationClientset kubeaggregatorclientset.Interface, clientv3 *clientv3.Client) *MetricsServiceManager { - msm := &MetricsServiceManager{ - clientset: clientset, - apiregistrationClientset: apiregistrationClientset, - clientv3: clientv3, - } - - return msm -} - -//Start start -func (m *MetricsServiceManager) Start() error { - if err := m.newMetricsServerAPIService(); err != nil { - return err - } - - if err := m.newMetricsServerService(); err != nil { - return err - } - - if err := m.newMetricsServiceEndpoints(); err != nil { - return err - } - - return nil -} - -func (m *MetricsServiceManager) newMetricsServerAPIService() error { - apiService := &v1beta1.APIService{ - ObjectMeta: metav1.ObjectMeta{ - Name: "v1beta1.metrics.k8s.io", - }, - Spec: v1beta1.APIServiceSpec{ - Service: &v1beta1.ServiceReference{ - Name: "metrics-server", - Namespace: "kube-system", - }, - Group: "metrics.k8s.io", - Version: "v1beta1", - InsecureSkipTLSVerify: true, - GroupPriorityMinimum: 100, - VersionPriority: 30, - }, - } - - old, err := m.apiregistrationClientset.ApiregistrationV1beta1().APIServices().Get(apiService.GetName(), metav1.GetOptions{}) - if err != nil { - if k8sErrors.IsNotFound(err) { - logrus.Infof("api service(%s) not found, create one.", apiService.GetName()) - _, err = m.apiregistrationClientset.ApiregistrationV1beta1().APIServices().Create(apiService) - if err != nil { - return fmt.Errorf("create new api service: %v", err) - } - return nil - } - return fmt.Errorf("retrieve api service: %v", err) - } - - logrus.Infof("an old api service(%s) has been found, update it.", apiService.GetName()) - apiService.ResourceVersion = old.ResourceVersion - if _, err := m.apiregistrationClientset.ApiregistrationV1beta1().APIServices().Update(apiService); err != nil { - return fmt.Errorf("update api service: %v", err) - } - - return nil -} - -func (m *MetricsServiceManager) newMetricsServerService() error { - new := &corev1.Service{ - ObjectMeta: metav1.ObjectMeta{ - Name: "metrics-server", - Namespace: "kube-system", - Labels: map[string]string{ - "kubernetes.io/name": "Metrics-server", - "kubernetes.io/cluster-service": "true", - }, - }, - Spec: corev1.ServiceSpec{ - Ports: []corev1.ServicePort{ - { - Port: 443, - Protocol: corev1.ProtocolTCP, - TargetPort: intstr.FromString("main-port"), - }, - }, - }, - Status: corev1.ServiceStatus{}, - } - - old, err := m.clientset.CoreV1().Services(new.Namespace).Get(new.Name, metav1.GetOptions{}) - if err != nil { - if k8sErrors.IsNotFound(err) { - _, err = m.clientset.CoreV1().Services(new.Namespace).Create(new) - if err != nil { - return fmt.Errorf("create new service for : %v", err) - } - return nil - } - return fmt.Errorf("retrieve service for metrics-server: %v", err) - } - - new.ResourceVersion = old.ResourceVersion - new.Spec.ClusterIP = old.Spec.ClusterIP - _, err = m.clientset.CoreV1().Services(new.Namespace).Update(new) - if err != nil { - return fmt.Errorf("update service for metrics-server: %v", err) - } - - return nil -} - -func (m *MetricsServiceManager) newMetricsServiceEndpoints() error { - endpoints, err := m.listMetricsServiceEndpoints() - if err != nil { - return err - } - ep := m.metricsServerEndpoint2CoreV1Endpoints(endpoints) - m.ensureEndpoints(ep) - go m.watchMetricsServiceEndpoints() - return nil -} - -func (m *MetricsServiceManager) listMetricsServiceEndpoints() ([]metricsServerEndpoint, error) { - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() - resp, err := m.clientv3.Get(ctx, "/rainbond/endpoint/METRICS_SERVER_ENDPOINTS", clientv3.WithPrefix()) - if err != nil { - return nil, fmt.Errorf("list metrics-server endpoints: %v", err) - } - var endpoints []metricsServerEndpoint - for _, kv := range resp.Kvs { - eps := m.str2MetricsServerEndpoint(kv) - endpoints = append(endpoints, eps...) - } - return endpoints, nil -} - -func (m *MetricsServiceManager) watchMetricsServiceEndpoints() { - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() - watchCh := m.clientv3.Watch(ctx, "/rainbond/endpoint/METRICS_SERVER_ENDPOINTS", clientv3.WithPrefix()) - for { - select { - case resp := <-watchCh: - for _, event := range resp.Events { - eps := m.str2MetricsServerEndpoint(event.Kv) - ep := m.metricsServerEndpoint2CoreV1Endpoints(eps) - m.ensureEndpoints(ep) - } - } - } - -} - -func (m *MetricsServiceManager) str2MetricsServerEndpoint(kv *mvccpb.KeyValue) []metricsServerEndpoint { - var endpoints []metricsServerEndpoint - var eps []string - if err := ffjson.Unmarshal(kv.Value, &eps); err != nil { - logrus.Warningf("key: %s; value: %s; wrong metrics-server endpoints: %v", kv.Key, kv.Value, err) - return nil - } - - for _, ep := range eps { - ep = strings.Replace(ep, "http://", "", -1) - ep = strings.Replace(ep, "https://", "", -1) - epsli := strings.Split(ep, ":") - if len(epsli) != 2 { - logrus.Warningf("key: %s; value: %s; wrong metrics-server endpoints.", kv.Key, kv.Value) - continue - } - port, err := strconv.Atoi(epsli[1]) - if err != nil { - logrus.Warningf("key: %s; value: %s; wrong metrics-server endpoints: %v", kv.Key, kv.Value, err) - continue - } - endpoints = append(endpoints, metricsServerEndpoint{ - Address: epsli[0], - Port: port, - }) - } - return endpoints -} - -func (m *MetricsServiceManager) metricsServerEndpoint2CoreV1Endpoints(endpoints []metricsServerEndpoint) *corev1.Endpoints { - var subsets []corev1.EndpointSubset - for _, ep := range endpoints { - subset := corev1.EndpointSubset{ - Addresses: []corev1.EndpointAddress{{IP: ep.Address}}, - Ports: []corev1.EndpointPort{{Port: int32(ep.Port)}}, - } - subsets = append(subsets, subset) - } - return &corev1.Endpoints{ - ObjectMeta: metav1.ObjectMeta{ - Name: "metrics-server", - Namespace: "kube-system", - Labels: map[string]string{ - "kubernetes.io/name": "Metrics-server", - "kubernetes.io/cluster-service": "true", - }, - }, - Subsets: subsets, - } -} - -func (m *MetricsServiceManager) ensureEndpoints(ep *corev1.Endpoints) { - old, err := m.clientset.CoreV1().Endpoints(ep.Namespace).Get(ep.Name, metav1.GetOptions{}) - if err != nil { - if k8sErrors.IsNotFound(err) { - _, err = m.clientset.CoreV1().Endpoints(ep.Namespace).Create(ep) - if err != nil { - logrus.Warningf("create endpoints for metrics-server: %v", err) - } - return - } - logrus.Errorf("retrieve endpoints: %v", err) - return - } - - ep.ResourceVersion = old.ResourceVersion - _, err = m.clientset.CoreV1().Endpoints(ep.Namespace).Update(ep) - if err != nil { - logrus.Warningf("update endpoints for metrics-server: %v", err) - } -} From d0140e532326cae276009687f4fb4a64bb2630fc Mon Sep 17 00:00:00 2001 From: barnettZQG Date: Wed, 12 Feb 2020 11:36:42 +0800 Subject: [PATCH 16/47] change windows util --- cmd/windowsutil/windowsutil.go | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/cmd/windowsutil/windowsutil.go b/cmd/windowsutil/windowsutil.go index c55e3b939..70041f2d9 100644 --- a/cmd/windowsutil/windowsutil.go +++ b/cmd/windowsutil/windowsutil.go @@ -46,6 +46,7 @@ func main() { } ctx, cancel := context.WithCancel(context.Background()) shell := strings.Split(conf.RunShell, " ") + logrus.Infof("run shell: %s", shell) cmd := exec.CommandContext(ctx, shell[0], shell[1:]...) startFunc := func() error { cmd.Stdin = os.Stdin @@ -67,9 +68,10 @@ func main() { cancel() } }() + var s os.Signal = syscall.SIGTERM defer func() { if cmd.Process != nil { - if err := cmd.Process.Signal(syscall.SIGTERM); err != nil { + if err := cmd.Process.Signal(s); err != nil { logrus.Errorf("send SIGTERM signal to progress failure %s", err.Error()) } time.Sleep(time.Second * 2) @@ -79,7 +81,8 @@ func main() { term := make(chan os.Signal) signal.Notify(term, os.Interrupt, syscall.SIGTERM) select { - case <-term: + case ls := <-term: + s = ls logrus.Warn("Received SIGTERM, exiting gracefully...") case <-ctx.Done(): } From ae096b8e1a2017c40cfb15a2a90c9c2de26f62b7 Mon Sep 17 00:00:00 2001 From: barnettZQG Date: Wed, 12 Feb 2020 12:35:50 +0800 Subject: [PATCH 17/47] change go version to 1.13 --- release.sh | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/release.sh b/release.sh index d9401f22d..461b27e01 100755 --- a/release.sh +++ b/release.sh @@ -10,13 +10,13 @@ then IMAGE_BASE_NAME=${BUILD_IMAGE_BASE_NAME} fi -GO_VERSION=1.11-stretch -GATEWAY_GO_VERSION=1.11-alpine3.8 +GO_VERSION=1.13 +GATEWAY_GO_VERSION=1.13-alpine if [ -z "$VERSION" ];then if [ -z "$TRAVIS_TAG" ]; then if [ -z "$TRAVIS_BRANCH" ]; then - VERSION=V5.1-dev + VERSION=V5.2-dev else VERSION=$TRAVIS_BRANCH-dev fi @@ -59,26 +59,26 @@ build::binary() { build::image() { local REPO_PATH="$PWD" - pushd ./hack/contrib/docker/$1 + pushd "./hack/contrib/docker/$1" echo "---> build binary:$1" local DOCKER_PATH="./hack/contrib/docker/$1" if [ "$1" = "eventlog" ];then docker build -t goodraim.me/event-build:v1 build - docker run --rm -v ${REPO_PATH}:${WORK_DIR} -w ${WORK_DIR} goodraim.me/event-build:v1 go build -ldflags "-w -s -X github.com/goodrain/rainbond/cmd.version=${release_desc}" -o ${DOCKER_PATH}/${BASE_NAME}-$1 ./cmd/eventlog + docker run --rm -v "${REPO_PATH}":${WORK_DIR} -w ${WORK_DIR} goodraim.me/event-build:v1 go build -ldflags "-w -s -X github.com/goodrain/rainbond/cmd.version=${release_desc}" -o ${DOCKER_PATH}/${BASE_NAME}-$1 ./cmd/eventlog elif [ "$1" = "chaos" ];then - docker run --rm -v ${REPO_PATH}:${WORK_DIR} -w ${WORK_DIR} -it golang:${GO_VERSION} go build -ldflags "-w -s -X github.com/goodrain/rainbond/cmd.version=${release_desc}" -o ${DOCKER_PATH}/${BASE_NAME}-$1 ./cmd/builder + docker run --rm -v "${REPO_PATH}":${WORK_DIR} -w ${WORK_DIR} -it golang:${GO_VERSION} go build -ldflags "-w -s -X github.com/goodrain/rainbond/cmd.version=${release_desc}" -o ${DOCKER_PATH}/${BASE_NAME}-$1 ./cmd/builder elif [ "$1" = "monitor" ];then - docker run --rm -v ${REPO_PATH}:${WORK_DIR} -w ${WORK_DIR} -it golang:${GO_VERSION} go build -ldflags "-w -s -extldflags '-static' -X github.com/goodrain/rainbond/cmd.version=${release_desc}" -tags 'netgo static_build' -o ${DOCKER_PATH}/${BASE_NAME}-$1 ./cmd/$1 + docker run -e CGO_ENABLED=0 --rm -v "${REPO_PATH}":${WORK_DIR} -w ${WORK_DIR} -it golang:${GO_VERSION} go build -ldflags "-w -s -X github.com/goodrain/rainbond/cmd.version=${release_desc}" -o ${DOCKER_PATH}/${BASE_NAME}-$1 ./cmd/$1 elif [ "$1" = "gateway" ];then - docker run --rm -v ${REPO_PATH}:${WORK_DIR} -w ${WORK_DIR} -it golang:${GATEWAY_GO_VERSION} go build -ldflags "-w -s -X github.com/goodrain/rainbond/cmd.version=${release_desc}" -o ${DOCKER_PATH}/${BASE_NAME}-$1 ./cmd/$1 + docker run --rm -v "${REPO_PATH}":${WORK_DIR} -w ${WORK_DIR} -it golang:${GATEWAY_GO_VERSION} go build -ldflags "-w -s -X github.com/goodrain/rainbond/cmd.version=${release_desc}" -o ${DOCKER_PATH}/${BASE_NAME}-$1 ./cmd/$1 elif [ "$1" = "mesh-data-panel" ];then echo "mesh-data-panel not need build"; else if [ "${ENTERPRISE}" = "true" ];then echo "---> ENTERPRISE:${ENTERPRISE}" - docker run --rm -v ${REPO_PATH}:${WORK_DIR} -w ${WORK_DIR} -it golang:${GO_VERSION} go build -ldflags "-w -s -X github.com/goodrain/rainbond/cmd.version=${release_desc} -X github.com/goodrain/rainbond/util/license.enterprise=${ENTERPRISE}" -o ${DOCKER_PATH}/${BASE_NAME}-$1 ./cmd/$1 + docker run --rm -v "${REPO_PATH}":${WORK_DIR} -w ${WORK_DIR} -it golang:${GO_VERSION} go build -ldflags "-w -s -X github.com/goodrain/rainbond/cmd.version=${release_desc} -X github.com/goodrain/rainbond/util/license.enterprise=${ENTERPRISE}" -o ${DOCKER_PATH}/${BASE_NAME}-$1 ./cmd/$1 else - docker run --rm -v ${REPO_PATH}:${WORK_DIR} -w ${WORK_DIR} -it golang:${GO_VERSION} go build -ldflags "-w -s -X github.com/goodrain/rainbond/cmd.version=${release_desc}" -o ${DOCKER_PATH}/${BASE_NAME}-$1 ./cmd/$1 + docker run --rm -v "${REPO_PATH}":${WORK_DIR} -w ${WORK_DIR} -it golang:${GO_VERSION} go build -ldflags "-w -s -X github.com/goodrain/rainbond/cmd.version=${release_desc}" -o ${DOCKER_PATH}/${BASE_NAME}-$1 ./cmd/$1 fi fi echo "---> build image:$1" From 90c0ef7260092926b0370771c9fb33b5e2c17b3f Mon Sep 17 00:00:00 2001 From: barnettZQG Date: Wed, 12 Feb 2020 12:40:22 +0800 Subject: [PATCH 18/47] fix code error --- db/model/app.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/db/model/app.go b/db/model/app.go index ec79055bf..afe4ef52d 100644 --- a/db/model/app.go +++ b/db/model/app.go @@ -28,7 +28,7 @@ type AppBackup struct { SourceDir string `gorm:"column:source_dir;size:255" json:"source_dir"` SourceType string `gorm:"column:source_type;size:255;default:'local'" json:"source_type"` BackupMode string `gorm:"column:backup_mode;size:32" json:"backup_mode"` - BuckupSize int64 `gorm:"column:backup_size type:bigint" json:"backup_size"` + BuckupSize int64 `gorm:"column:backup_size;type:bigint" json:"backup_size"` Deleted bool `gorm:"column:deleted" json:"deleted"` } From 5595da8cbbadd9300da1a154d494b72802ddc278 Mon Sep 17 00:00:00 2001 From: barnettZQG Date: Wed, 12 Feb 2020 16:09:02 +0800 Subject: [PATCH 19/47] support region.goodrain.me domain --- hack/contrib/docker/grctl/entrypoint.sh | 6 +++--- node/initiate/host.go | 8 +++++--- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/hack/contrib/docker/grctl/entrypoint.sh b/hack/contrib/docker/grctl/entrypoint.sh index a6cae88fa..fd6f280ef 100644 --- a/hack/contrib/docker/grctl/entrypoint.sh +++ b/hack/contrib/docker/grctl/entrypoint.sh @@ -6,9 +6,9 @@ elif [ "$1" = "version" ];then elif [ "$1" = "install" ];then cp -a /run/rainbond-grctl /rootfs/path/grctl mkdir -p /rootfs/root/.rbd - cp -a /etc/goodrain/region.goodrain.me/ssl/ca.pem /ssl/ca.pem - cp -a /etc/goodrain/region.goodrain.me/ssl/client.pem /ssl/client.pem - cp -a /etc/goodrain/region.goodrain.me/ssl/client.key.pem /ssl/client.key.pem + cat /etc/goodrain/region.goodrain.me/ssl/ca.pem > /ssl/ca.pem + cat /etc/goodrain/region.goodrain.me/ssl/client.pem > /ssl/client.pem + cat /etc/goodrain/region.goodrain.me/ssl/client.key.pem > /ssl/client.key.pem cp -a /run/grctl.yaml /rootfs/root/.rbd/grctl.yaml exec /run/rainbond-grctl install else diff --git a/node/initiate/host.go b/node/initiate/host.go index c147f0117..2912febea 100644 --- a/node/initiate/host.go +++ b/node/initiate/host.go @@ -25,13 +25,14 @@ import ( "context" "errors" "fmt" - "github.com/Sirupsen/logrus" - "github.com/goodrain/rainbond/discover.v2" - "github.com/goodrain/rainbond/discover/config" "net" "os" "strings" + "github.com/Sirupsen/logrus" + discover "github.com/goodrain/rainbond/discover.v2" + "github.com/goodrain/rainbond/discover/config" + "github.com/goodrain/rainbond/cmd/node/option" ) @@ -101,6 +102,7 @@ func (h *hostCallback) UpdateEndpoints(endpoints ...*config.Endpoint) { lines := []string{ startOfSection, endpoints[0].URL + " " + h.cfg.ImageRepositoryHost, + endpoints[0].URL + " " + "region.goodrain.me", endOfSection, } h.hosts.AddLines(lines...) From 1367135d210ad417b3a58be16dfeb22596369d7e Mon Sep 17 00:00:00 2001 From: glyasai Date: Wed, 12 Feb 2020 16:50:27 +0800 Subject: [PATCH 20/47] build grctl enterprise --- release.sh | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/release.sh b/release.sh index f00cc8496..6bbd82db9 100755 --- a/release.sh +++ b/release.sh @@ -40,7 +40,7 @@ build::node() { echo "build node" docker run --rm -v `pwd`:${WORK_DIR} -w ${WORK_DIR} -it golang:${GO_VERSION} go build -ldflags "-w -s -X github.com/goodrain/rainbond/cmd.version=${release_desc} -X github.com/goodrain/rainbond/util/license.enterprise=${ENTERPRISE}" -o $releasedir/dist/usr/local/bin/node ./cmd/node ;; - grctl) + grctl) echo "build grctl" docker run --rm -v `pwd`:${WORK_DIR} -w ${WORK_DIR} -it golang:${GO_VERSION} go build -ldflags "-w -s -X github.com/goodrain/rainbond/cmd.version=${release_desc}" -o $releasedir/dist/usr/local/bin/grctl ./cmd/grctl ;; @@ -51,8 +51,13 @@ build::node() { *) echo "build node" docker run --rm -v `pwd`:${WORK_DIR} -w ${WORK_DIR} -it golang:${GO_VERSION} go build -ldflags "-w -s -X github.com/goodrain/rainbond/cmd.version=${release_desc} -X github.com/goodrain/rainbond/util/license.enterprise=${ENTERPRISE}" -o $releasedir/dist/usr/local/bin/node ./cmd/node - echo "build grctl" - docker run --rm -v `pwd`:${WORK_DIR} -w ${WORK_DIR} -it golang:${GO_VERSION} go build -ldflags "-w -s -X github.com/goodrain/rainbond/cmd.version=${release_desc}" -o $releasedir/dist/usr/local/bin/grctl ./cmd/grctl + if [ "${ENTERPRISE}" = "true" ];then + echo "build grctl enterprise" + docker run --rm -v `pwd`:${WORK_DIR} -w ${WORK_DIR} -it golang:${GO_VERSION} go build -ldflags "-w -s -X github.com/goodrain/rainbond/cmd.version=${release_desc} -X github.com/goodrain/rainbond/util/license.enterprise=${ENTERPRISE}" -o $releasedir/dist/usr/local/bin/grctl ./cmd/grctl + else + echo "build grctl" + docker run --rm -v `pwd`:${WORK_DIR} -w ${WORK_DIR} -it golang:${GO_VERSION} go build -ldflags "-w -s -X github.com/goodrain/rainbond/cmd.version=${release_desc}" -o $releasedir/dist/usr/local/bin/grctl ./cmd/grctl + fi echo "build certutil" docker run --rm -v `pwd`:${WORK_DIR} -w ${WORK_DIR} -it golang:${GO_VERSION} go build -ldflags "-w -s -X github.com/goodrain/rainbond/cmd.version=${release_desc}" -o $releasedir/dist/usr/local/bin/grcert ./cmd/certutil pushd $distdir @@ -64,7 +69,7 @@ COPY pkg.tgz / EOF docker build -t ${BASE_NAME}/cni:rbd_$VERSION . if [ "$1" = "push" ];then - docker push ${BASE_NAME}/cni:rbd_$VERSION + docker push ${BASE_NAME}/cni:rbd_$VERSION fi popd ;; From 64d9039d656e59756202800db3c45fb1f96734e2 Mon Sep 17 00:00:00 2001 From: barnettZQG Date: Thu, 13 Feb 2020 18:39:10 +0800 Subject: [PATCH 21/47] webcli does not rely on kubectl for container control --- cmd/webcli/option/option.go | 2 + cmd/webcli/server/server.go | 3 +- node/initiate/host.go | 2 +- .../docker/docker/pkg/term/ascii.go | 66 + .../docker/docker/pkg/term/ascii_test.go | 43 + .../docker/docker/pkg/term/tc_linux_cgo.go | 50 + .../docker/docker/pkg/term/tc_other.go | 20 + .../docker/docker/pkg/term/tc_solaris_cgo.go | 63 + .../github.com/docker/docker/pkg/term/term.go | 123 ++ .../docker/docker/pkg/term/term_solaris.go | 41 + .../docker/docker/pkg/term/term_unix.go | 29 + .../docker/docker/pkg/term/term_windows.go | 233 ++++ .../docker/docker/pkg/term/termios_darwin.go | 69 + .../docker/docker/pkg/term/termios_freebsd.go | 69 + .../docker/docker/pkg/term/termios_linux.go | 47 + .../docker/docker/pkg/term/termios_openbsd.go | 69 + .../docker/pkg/term/windows/ansi_reader.go | 263 ++++ .../docker/pkg/term/windows/ansi_writer.go | 64 + .../docker/docker/pkg/term/windows/console.go | 35 + .../docker/docker/pkg/term/windows/windows.go | 33 + .../docker/pkg/term/windows/windows_test.go | 3 + .../docker/spdystream/CONTRIBUTING.md | 13 + vendor/github.com/docker/spdystream/LICENSE | 191 +++ .../github.com/docker/spdystream/LICENSE.docs | 425 ++++++ .../github.com/docker/spdystream/MAINTAINERS | 28 + vendor/github.com/docker/spdystream/README.md | 77 ++ .../docker/spdystream/connection.go | 959 ++++++++++++++ .../github.com/docker/spdystream/handlers.go | 36 + .../github.com/docker/spdystream/priority.go | 98 ++ .../docker/spdystream/priority_test.go | 108 ++ .../docker/spdystream/spdy/dictionary.go | 187 +++ .../github.com/docker/spdystream/spdy/read.go | 348 +++++ .../docker/spdystream/spdy/spdy_test.go | 644 +++++++++ .../docker/spdystream/spdy/types.go | 275 ++++ .../docker/spdystream/spdy/write.go | 318 +++++ .../docker/spdystream/spdy_bench_test.go | 113 ++ .../github.com/docker/spdystream/spdy_test.go | 1172 +++++++++++++++++ vendor/github.com/docker/spdystream/stream.go | 327 +++++ vendor/github.com/docker/spdystream/utils.go | 16 + .../docker/spdystream/ws/connection.go | 81 ++ .../docker/spdystream/ws/ws_test.go | 183 +++ .../mitchellh/go-wordwrap/LICENSE.md | 21 + .../mitchellh/go-wordwrap/README.md | 39 + .../github.com/mitchellh/go-wordwrap/go.mod | 1 + .../mitchellh/go-wordwrap/wordwrap.go | 73 + .../mitchellh/go-wordwrap/wordwrap_test.go | 85 ++ vendor/k8s.io/client-go/go.sum | 2 + vendor/k8s.io/kube-aggregator/go.sum | 2 + .../kubernetes/pkg/util/interrupt/BUILD | 25 + .../pkg/util/interrupt/interrupt.go | 104 ++ webcli/app/app.go | 176 ++- webcli/app/app_test.go | 27 + webcli/app/client_context.go | 64 +- webcli/app/exec.go | 98 ++ webcli/app/out.go | 59 + webcli/app/tty_test.go | 65 + webcli/cmd/main.go | 60 + webcli/term/BUILD | 110 ++ webcli/term/resize.go | 132 ++ webcli/term/resizeevents.go | 61 + webcli/term/resizeevents_windows.go | 62 + webcli/term/term.go | 110 ++ webcli/term/term_writer.go | 124 ++ webcli/term/term_writer_test.go | 98 ++ 64 files changed, 8473 insertions(+), 51 deletions(-) create mode 100644 vendor/github.com/docker/docker/pkg/term/ascii.go create mode 100644 vendor/github.com/docker/docker/pkg/term/ascii_test.go create mode 100644 vendor/github.com/docker/docker/pkg/term/tc_linux_cgo.go create mode 100644 vendor/github.com/docker/docker/pkg/term/tc_other.go create mode 100644 vendor/github.com/docker/docker/pkg/term/tc_solaris_cgo.go create mode 100644 vendor/github.com/docker/docker/pkg/term/term.go create mode 100644 vendor/github.com/docker/docker/pkg/term/term_solaris.go create mode 100644 vendor/github.com/docker/docker/pkg/term/term_unix.go create mode 100644 vendor/github.com/docker/docker/pkg/term/term_windows.go create mode 100644 vendor/github.com/docker/docker/pkg/term/termios_darwin.go create mode 100644 vendor/github.com/docker/docker/pkg/term/termios_freebsd.go create mode 100644 vendor/github.com/docker/docker/pkg/term/termios_linux.go create mode 100644 vendor/github.com/docker/docker/pkg/term/termios_openbsd.go create mode 100644 vendor/github.com/docker/docker/pkg/term/windows/ansi_reader.go create mode 100644 vendor/github.com/docker/docker/pkg/term/windows/ansi_writer.go create mode 100644 vendor/github.com/docker/docker/pkg/term/windows/console.go create mode 100644 vendor/github.com/docker/docker/pkg/term/windows/windows.go create mode 100644 vendor/github.com/docker/docker/pkg/term/windows/windows_test.go create mode 100644 vendor/github.com/docker/spdystream/CONTRIBUTING.md create mode 100644 vendor/github.com/docker/spdystream/LICENSE create mode 100644 vendor/github.com/docker/spdystream/LICENSE.docs create mode 100644 vendor/github.com/docker/spdystream/MAINTAINERS create mode 100644 vendor/github.com/docker/spdystream/README.md create mode 100644 vendor/github.com/docker/spdystream/connection.go create mode 100644 vendor/github.com/docker/spdystream/handlers.go create mode 100644 vendor/github.com/docker/spdystream/priority.go create mode 100644 vendor/github.com/docker/spdystream/priority_test.go create mode 100644 vendor/github.com/docker/spdystream/spdy/dictionary.go create mode 100644 vendor/github.com/docker/spdystream/spdy/read.go create mode 100644 vendor/github.com/docker/spdystream/spdy/spdy_test.go create mode 100644 vendor/github.com/docker/spdystream/spdy/types.go create mode 100644 vendor/github.com/docker/spdystream/spdy/write.go create mode 100644 vendor/github.com/docker/spdystream/spdy_bench_test.go create mode 100644 vendor/github.com/docker/spdystream/spdy_test.go create mode 100644 vendor/github.com/docker/spdystream/stream.go create mode 100644 vendor/github.com/docker/spdystream/utils.go create mode 100644 vendor/github.com/docker/spdystream/ws/connection.go create mode 100644 vendor/github.com/docker/spdystream/ws/ws_test.go create mode 100644 vendor/github.com/mitchellh/go-wordwrap/LICENSE.md create mode 100644 vendor/github.com/mitchellh/go-wordwrap/README.md create mode 100644 vendor/github.com/mitchellh/go-wordwrap/go.mod create mode 100644 vendor/github.com/mitchellh/go-wordwrap/wordwrap.go create mode 100644 vendor/github.com/mitchellh/go-wordwrap/wordwrap_test.go create mode 100644 vendor/k8s.io/kubernetes/pkg/util/interrupt/BUILD create mode 100644 vendor/k8s.io/kubernetes/pkg/util/interrupt/interrupt.go create mode 100644 webcli/app/app_test.go create mode 100644 webcli/app/exec.go create mode 100644 webcli/app/out.go create mode 100644 webcli/app/tty_test.go create mode 100644 webcli/cmd/main.go create mode 100644 webcli/term/BUILD create mode 100644 webcli/term/resize.go create mode 100644 webcli/term/resizeevents.go create mode 100644 webcli/term/resizeevents_windows.go create mode 100644 webcli/term/term.go create mode 100644 webcli/term/term_writer.go create mode 100644 webcli/term/term_writer_test.go diff --git a/cmd/webcli/option/option.go b/cmd/webcli/option/option.go index 10c4bfe86..53359e145 100644 --- a/cmd/webcli/option/option.go +++ b/cmd/webcli/option/option.go @@ -37,6 +37,7 @@ type Config struct { Port int SessionKey string PrometheusMetricPath string + K8SConfPath string } //WebCliServer container webcli server @@ -57,6 +58,7 @@ func (a *WebCliServer) AddFlags(fs *pflag.FlagSet) { fs.StringVar(&a.Address, "address", "0.0.0.0", "server listen address") fs.StringVar(&a.HostIP, "hostIP", "", "Current node Intranet IP") fs.StringVar(&a.HostName, "hostName", "", "Current node host name") + fs.StringVar(&a.K8SConfPath, "kube-conf", "", "absolute path to the kubeconfig file") fs.IntVar(&a.Port, "port", 7171, "server listen port") fs.StringVar(&a.PrometheusMetricPath, "metric", "/metrics", "prometheus metrics path") } diff --git a/cmd/webcli/server/server.go b/cmd/webcli/server/server.go index 7d03b1176..9f2758d9b 100644 --- a/cmd/webcli/server/server.go +++ b/cmd/webcli/server/server.go @@ -39,7 +39,8 @@ func Run(s *option.WebCliServer) error { option.Address = s.Address option.Port = strconv.Itoa(s.Port) option.SessionKey = s.SessionKey - ap, err := app.New(nil, &option) + option.K8SConfPath = s.K8SConfPath + ap, err := app.New(&option) if err != nil { return err } diff --git a/node/initiate/host.go b/node/initiate/host.go index 2912febea..0364dcb2d 100644 --- a/node/initiate/host.go +++ b/node/initiate/host.go @@ -136,7 +136,7 @@ func NewHostsLine(raw string) HostsLine { if !output.IsComment() { rawIP := fields[0] if net.ParseIP(rawIP) == nil { - output.Err = errors.New(fmt.Sprintf("Bad hosts line: %q", raw)) + output.Err = fmt.Errorf("Bad hosts line: %q", raw) } output.IP = rawIP diff --git a/vendor/github.com/docker/docker/pkg/term/ascii.go b/vendor/github.com/docker/docker/pkg/term/ascii.go new file mode 100644 index 000000000..f5262bccf --- /dev/null +++ b/vendor/github.com/docker/docker/pkg/term/ascii.go @@ -0,0 +1,66 @@ +package term + +import ( + "fmt" + "strings" +) + +// ASCII list the possible supported ASCII key sequence +var ASCII = []string{ + "ctrl-@", + "ctrl-a", + "ctrl-b", + "ctrl-c", + "ctrl-d", + "ctrl-e", + "ctrl-f", + "ctrl-g", + "ctrl-h", + "ctrl-i", + "ctrl-j", + "ctrl-k", + "ctrl-l", + "ctrl-m", + "ctrl-n", + "ctrl-o", + "ctrl-p", + "ctrl-q", + "ctrl-r", + "ctrl-s", + "ctrl-t", + "ctrl-u", + "ctrl-v", + "ctrl-w", + "ctrl-x", + "ctrl-y", + "ctrl-z", + "ctrl-[", + "ctrl-\\", + "ctrl-]", + "ctrl-^", + "ctrl-_", +} + +// ToBytes converts a string representing a suite of key-sequence to the corresponding ASCII code. +func ToBytes(keys string) ([]byte, error) { + codes := []byte{} +next: + for _, key := range strings.Split(keys, ",") { + if len(key) != 1 { + for code, ctrl := range ASCII { + if ctrl == key { + codes = append(codes, byte(code)) + continue next + } + } + if key == "DEL" { + codes = append(codes, 127) + } else { + return nil, fmt.Errorf("Unknown character: '%s'", key) + } + } else { + codes = append(codes, byte(key[0])) + } + } + return codes, nil +} diff --git a/vendor/github.com/docker/docker/pkg/term/ascii_test.go b/vendor/github.com/docker/docker/pkg/term/ascii_test.go new file mode 100644 index 000000000..4a1e7f302 --- /dev/null +++ b/vendor/github.com/docker/docker/pkg/term/ascii_test.go @@ -0,0 +1,43 @@ +package term + +import "testing" + +func TestToBytes(t *testing.T) { + codes, err := ToBytes("ctrl-a,a") + if err != nil { + t.Fatal(err) + } + if len(codes) != 2 { + t.Fatalf("Expected 2 codes, got %d", len(codes)) + } + if codes[0] != 1 || codes[1] != 97 { + t.Fatalf("Expected '1' '97', got '%d' '%d'", codes[0], codes[1]) + } + + codes, err = ToBytes("shift-z") + if err == nil { + t.Fatalf("Expected error, got none") + } + + codes, err = ToBytes("ctrl-@,ctrl-[,~,ctrl-o") + if err != nil { + t.Fatal(err) + } + if len(codes) != 4 { + t.Fatalf("Expected 4 codes, got %d", len(codes)) + } + if codes[0] != 0 || codes[1] != 27 || codes[2] != 126 || codes[3] != 15 { + t.Fatalf("Expected '0' '27' '126', '15', got '%d' '%d' '%d' '%d'", codes[0], codes[1], codes[2], codes[3]) + } + + codes, err = ToBytes("DEL,+") + if err != nil { + t.Fatal(err) + } + if len(codes) != 2 { + t.Fatalf("Expected 2 codes, got %d", len(codes)) + } + if codes[0] != 127 || codes[1] != 43 { + t.Fatalf("Expected '127 '43'', got '%d' '%d'", codes[0], codes[1]) + } +} diff --git a/vendor/github.com/docker/docker/pkg/term/tc_linux_cgo.go b/vendor/github.com/docker/docker/pkg/term/tc_linux_cgo.go new file mode 100644 index 000000000..59dac5ba8 --- /dev/null +++ b/vendor/github.com/docker/docker/pkg/term/tc_linux_cgo.go @@ -0,0 +1,50 @@ +// +build linux,cgo + +package term + +import ( + "syscall" + "unsafe" +) + +// #include +import "C" + +// Termios is the Unix API for terminal I/O. +// It is passthrough for syscall.Termios in order to make it portable with +// other platforms where it is not available or handled differently. +type Termios syscall.Termios + +// MakeRaw put the terminal connected to the given file descriptor into raw +// mode and returns the previous state of the terminal so that it can be +// restored. +func MakeRaw(fd uintptr) (*State, error) { + var oldState State + if err := tcget(fd, &oldState.termios); err != 0 { + return nil, err + } + + newState := oldState.termios + + C.cfmakeraw((*C.struct_termios)(unsafe.Pointer(&newState))) + if err := tcset(fd, &newState); err != 0 { + return nil, err + } + return &oldState, nil +} + +func tcget(fd uintptr, p *Termios) syscall.Errno { + ret, err := C.tcgetattr(C.int(fd), (*C.struct_termios)(unsafe.Pointer(p))) + if ret != 0 { + return err.(syscall.Errno) + } + return 0 +} + +func tcset(fd uintptr, p *Termios) syscall.Errno { + ret, err := C.tcsetattr(C.int(fd), C.TCSANOW, (*C.struct_termios)(unsafe.Pointer(p))) + if ret != 0 { + return err.(syscall.Errno) + } + return 0 +} diff --git a/vendor/github.com/docker/docker/pkg/term/tc_other.go b/vendor/github.com/docker/docker/pkg/term/tc_other.go new file mode 100644 index 000000000..750d7c3f6 --- /dev/null +++ b/vendor/github.com/docker/docker/pkg/term/tc_other.go @@ -0,0 +1,20 @@ +// +build !windows +// +build !linux !cgo +// +build !solaris !cgo + +package term + +import ( + "syscall" + "unsafe" +) + +func tcget(fd uintptr, p *Termios) syscall.Errno { + _, _, err := syscall.Syscall(syscall.SYS_IOCTL, fd, uintptr(getTermios), uintptr(unsafe.Pointer(p))) + return err +} + +func tcset(fd uintptr, p *Termios) syscall.Errno { + _, _, err := syscall.Syscall(syscall.SYS_IOCTL, fd, setTermios, uintptr(unsafe.Pointer(p))) + return err +} diff --git a/vendor/github.com/docker/docker/pkg/term/tc_solaris_cgo.go b/vendor/github.com/docker/docker/pkg/term/tc_solaris_cgo.go new file mode 100644 index 000000000..c9139d0ca --- /dev/null +++ b/vendor/github.com/docker/docker/pkg/term/tc_solaris_cgo.go @@ -0,0 +1,63 @@ +// +build solaris,cgo + +package term + +import ( + "syscall" + "unsafe" +) + +// #include +import "C" + +// Termios is the Unix API for terminal I/O. +// It is passthrough for syscall.Termios in order to make it portable with +// other platforms where it is not available or handled differently. +type Termios syscall.Termios + +// MakeRaw put the terminal connected to the given file descriptor into raw +// mode and returns the previous state of the terminal so that it can be +// restored. +func MakeRaw(fd uintptr) (*State, error) { + var oldState State + if err := tcget(fd, &oldState.termios); err != 0 { + return nil, err + } + + newState := oldState.termios + + newState.Iflag &^= (syscall.IGNBRK | syscall.BRKINT | syscall.PARMRK | syscall.ISTRIP | syscall.INLCR | syscall.IGNCR | syscall.ICRNL | syscall.IXON | syscall.IXANY) + newState.Oflag &^= syscall.OPOST + newState.Lflag &^= (syscall.ECHO | syscall.ECHONL | syscall.ICANON | syscall.ISIG | syscall.IEXTEN) + newState.Cflag &^= (syscall.CSIZE | syscall.PARENB) + newState.Cflag |= syscall.CS8 + + /* + VMIN is the minimum number of characters that needs to be read in non-canonical mode for it to be returned + Since VMIN is overloaded with another element in canonical mode when we switch modes it defaults to 4. It + needs to be explicitly set to 1. + */ + newState.Cc[C.VMIN] = 1 + newState.Cc[C.VTIME] = 0 + + if err := tcset(fd, &newState); err != 0 { + return nil, err + } + return &oldState, nil +} + +func tcget(fd uintptr, p *Termios) syscall.Errno { + ret, err := C.tcgetattr(C.int(fd), (*C.struct_termios)(unsafe.Pointer(p))) + if ret != 0 { + return err.(syscall.Errno) + } + return 0 +} + +func tcset(fd uintptr, p *Termios) syscall.Errno { + ret, err := C.tcsetattr(C.int(fd), C.TCSANOW, (*C.struct_termios)(unsafe.Pointer(p))) + if ret != 0 { + return err.(syscall.Errno) + } + return 0 +} diff --git a/vendor/github.com/docker/docker/pkg/term/term.go b/vendor/github.com/docker/docker/pkg/term/term.go new file mode 100644 index 000000000..816f8d752 --- /dev/null +++ b/vendor/github.com/docker/docker/pkg/term/term.go @@ -0,0 +1,123 @@ +// +build !windows + +// Package term provides structures and helper functions to work with +// terminal (state, sizes). +package term + +import ( + "errors" + "fmt" + "io" + "os" + "os/signal" + "syscall" +) + +var ( + // ErrInvalidState is returned if the state of the terminal is invalid. + ErrInvalidState = errors.New("Invalid terminal state") +) + +// State represents the state of the terminal. +type State struct { + termios Termios +} + +// Winsize represents the size of the terminal window. +type Winsize struct { + Height uint16 + Width uint16 + x uint16 + y uint16 +} + +// StdStreams returns the standard streams (stdin, stdout, stderr). +func StdStreams() (stdIn io.ReadCloser, stdOut, stdErr io.Writer) { + return os.Stdin, os.Stdout, os.Stderr +} + +// GetFdInfo returns the file descriptor for an os.File and indicates whether the file represents a terminal. +func GetFdInfo(in interface{}) (uintptr, bool) { + var inFd uintptr + var isTerminalIn bool + if file, ok := in.(*os.File); ok { + inFd = file.Fd() + isTerminalIn = IsTerminal(inFd) + } + return inFd, isTerminalIn +} + +// IsTerminal returns true if the given file descriptor is a terminal. +func IsTerminal(fd uintptr) bool { + var termios Termios + return tcget(fd, &termios) == 0 +} + +// RestoreTerminal restores the terminal connected to the given file descriptor +// to a previous state. +func RestoreTerminal(fd uintptr, state *State) error { + if state == nil { + return ErrInvalidState + } + if err := tcset(fd, &state.termios); err != 0 { + return err + } + return nil +} + +// SaveState saves the state of the terminal connected to the given file descriptor. +func SaveState(fd uintptr) (*State, error) { + var oldState State + if err := tcget(fd, &oldState.termios); err != 0 { + return nil, err + } + + return &oldState, nil +} + +// DisableEcho applies the specified state to the terminal connected to the file +// descriptor, with echo disabled. +func DisableEcho(fd uintptr, state *State) error { + newState := state.termios + newState.Lflag &^= syscall.ECHO + + if err := tcset(fd, &newState); err != 0 { + return err + } + handleInterrupt(fd, state) + return nil +} + +// SetRawTerminal puts the terminal connected to the given file descriptor into +// raw mode and returns the previous state. On UNIX, this puts both the input +// and output into raw mode. On Windows, it only puts the input into raw mode. +func SetRawTerminal(fd uintptr) (*State, error) { + oldState, err := MakeRaw(fd) + if err != nil { + return nil, err + } + handleInterrupt(fd, oldState) + return oldState, err +} + +// SetRawTerminalOutput puts the output of terminal connected to the given file +// descriptor into raw mode. On UNIX, this does nothing and returns nil for the +// state. On Windows, it disables LF -> CRLF translation. +func SetRawTerminalOutput(fd uintptr) (*State, error) { + return nil, nil +} + +func handleInterrupt(fd uintptr, state *State) { + sigchan := make(chan os.Signal, 1) + signal.Notify(sigchan, os.Interrupt) + go func() { + for range sigchan { + // quit cleanly and the new terminal item is on a new line + fmt.Println() + signal.Stop(sigchan) + close(sigchan) + RestoreTerminal(fd, state) + os.Exit(1) + } + }() +} diff --git a/vendor/github.com/docker/docker/pkg/term/term_solaris.go b/vendor/github.com/docker/docker/pkg/term/term_solaris.go new file mode 100644 index 000000000..112debbec --- /dev/null +++ b/vendor/github.com/docker/docker/pkg/term/term_solaris.go @@ -0,0 +1,41 @@ +// +build solaris + +package term + +import ( + "syscall" + "unsafe" +) + +/* +#include +#include +#include + +// Small wrapper to get rid of variadic args of ioctl() +int my_ioctl(int fd, int cmd, struct winsize *ws) { + return ioctl(fd, cmd, ws); +} +*/ +import "C" + +// GetWinsize returns the window size based on the specified file descriptor. +func GetWinsize(fd uintptr) (*Winsize, error) { + ws := &Winsize{} + ret, err := C.my_ioctl(C.int(fd), C.int(syscall.TIOCGWINSZ), (*C.struct_winsize)(unsafe.Pointer(ws))) + // Skip retval = 0 + if ret == 0 { + return ws, nil + } + return ws, err +} + +// SetWinsize tries to set the specified window size for the specified file descriptor. +func SetWinsize(fd uintptr, ws *Winsize) error { + ret, err := C.my_ioctl(C.int(fd), C.int(syscall.TIOCSWINSZ), (*C.struct_winsize)(unsafe.Pointer(ws))) + // Skip retval = 0 + if ret == 0 { + return nil + } + return err +} diff --git a/vendor/github.com/docker/docker/pkg/term/term_unix.go b/vendor/github.com/docker/docker/pkg/term/term_unix.go new file mode 100644 index 000000000..ddf87a0e5 --- /dev/null +++ b/vendor/github.com/docker/docker/pkg/term/term_unix.go @@ -0,0 +1,29 @@ +// +build !solaris,!windows + +package term + +import ( + "syscall" + "unsafe" +) + +// GetWinsize returns the window size based on the specified file descriptor. +func GetWinsize(fd uintptr) (*Winsize, error) { + ws := &Winsize{} + _, _, err := syscall.Syscall(syscall.SYS_IOCTL, fd, uintptr(syscall.TIOCGWINSZ), uintptr(unsafe.Pointer(ws))) + // Skipp errno = 0 + if err == 0 { + return ws, nil + } + return ws, err +} + +// SetWinsize tries to set the specified window size for the specified file descriptor. +func SetWinsize(fd uintptr, ws *Winsize) error { + _, _, err := syscall.Syscall(syscall.SYS_IOCTL, fd, uintptr(syscall.TIOCSWINSZ), uintptr(unsafe.Pointer(ws))) + // Skipp errno = 0 + if err == 0 { + return nil + } + return err +} diff --git a/vendor/github.com/docker/docker/pkg/term/term_windows.go b/vendor/github.com/docker/docker/pkg/term/term_windows.go new file mode 100644 index 000000000..62e9a9e69 --- /dev/null +++ b/vendor/github.com/docker/docker/pkg/term/term_windows.go @@ -0,0 +1,233 @@ +// +build windows + +package term + +import ( + "io" + "os" + "os/signal" + "syscall" + + "github.com/Azure/go-ansiterm/winterm" + "github.com/docker/docker/pkg/term/windows" +) + +// State holds the console mode for the terminal. +type State struct { + mode uint32 +} + +// Winsize is used for window size. +type Winsize struct { + Height uint16 + Width uint16 +} + +const ( + // https://msdn.microsoft.com/en-us/library/windows/desktop/ms683167(v=vs.85).aspx + enableVirtualTerminalInput = 0x0200 + enableVirtualTerminalProcessing = 0x0004 + disableNewlineAutoReturn = 0x0008 +) + +// vtInputSupported is true if enableVirtualTerminalInput is supported by the console +var vtInputSupported bool + +// StdStreams returns the standard streams (stdin, stdout, stderr). +func StdStreams() (stdIn io.ReadCloser, stdOut, stdErr io.Writer) { + // Turn on VT handling on all std handles, if possible. This might + // fail, in which case we will fall back to terminal emulation. + var emulateStdin, emulateStdout, emulateStderr bool + fd := os.Stdin.Fd() + if mode, err := winterm.GetConsoleMode(fd); err == nil { + // Validate that enableVirtualTerminalInput is supported, but do not set it. + if err = winterm.SetConsoleMode(fd, mode|enableVirtualTerminalInput); err != nil { + emulateStdin = true + } else { + vtInputSupported = true + } + // Unconditionally set the console mode back even on failure because SetConsoleMode + // remembers invalid bits on input handles. + winterm.SetConsoleMode(fd, mode) + } + + fd = os.Stdout.Fd() + if mode, err := winterm.GetConsoleMode(fd); err == nil { + // Validate disableNewlineAutoReturn is supported, but do not set it. + if err = winterm.SetConsoleMode(fd, mode|enableVirtualTerminalProcessing|disableNewlineAutoReturn); err != nil { + emulateStdout = true + } else { + winterm.SetConsoleMode(fd, mode|enableVirtualTerminalProcessing) + } + } + + fd = os.Stderr.Fd() + if mode, err := winterm.GetConsoleMode(fd); err == nil { + // Validate disableNewlineAutoReturn is supported, but do not set it. + if err = winterm.SetConsoleMode(fd, mode|enableVirtualTerminalProcessing|disableNewlineAutoReturn); err != nil { + emulateStderr = true + } else { + winterm.SetConsoleMode(fd, mode|enableVirtualTerminalProcessing) + } + } + + if os.Getenv("ConEmuANSI") == "ON" || os.Getenv("ConsoleZVersion") != "" { + // The ConEmu and ConsoleZ terminals emulate ANSI on output streams well. + emulateStdin = true + emulateStdout = false + emulateStderr = false + } + + if emulateStdin { + stdIn = windows.NewAnsiReader(syscall.STD_INPUT_HANDLE) + } else { + stdIn = os.Stdin + } + + if emulateStdout { + stdOut = windows.NewAnsiWriter(syscall.STD_OUTPUT_HANDLE) + } else { + stdOut = os.Stdout + } + + if emulateStderr { + stdErr = windows.NewAnsiWriter(syscall.STD_ERROR_HANDLE) + } else { + stdErr = os.Stderr + } + + return +} + +// GetFdInfo returns the file descriptor for an os.File and indicates whether the file represents a terminal. +func GetFdInfo(in interface{}) (uintptr, bool) { + return windows.GetHandleInfo(in) +} + +// GetWinsize returns the window size based on the specified file descriptor. +func GetWinsize(fd uintptr) (*Winsize, error) { + info, err := winterm.GetConsoleScreenBufferInfo(fd) + if err != nil { + return nil, err + } + + winsize := &Winsize{ + Width: uint16(info.Window.Right - info.Window.Left + 1), + Height: uint16(info.Window.Bottom - info.Window.Top + 1), + } + + return winsize, nil +} + +// IsTerminal returns true if the given file descriptor is a terminal. +func IsTerminal(fd uintptr) bool { + return windows.IsConsole(fd) +} + +// RestoreTerminal restores the terminal connected to the given file descriptor +// to a previous state. +func RestoreTerminal(fd uintptr, state *State) error { + return winterm.SetConsoleMode(fd, state.mode) +} + +// SaveState saves the state of the terminal connected to the given file descriptor. +func SaveState(fd uintptr) (*State, error) { + mode, e := winterm.GetConsoleMode(fd) + if e != nil { + return nil, e + } + + return &State{mode: mode}, nil +} + +// DisableEcho disables echo for the terminal connected to the given file descriptor. +// -- See https://msdn.microsoft.com/en-us/library/windows/desktop/ms683462(v=vs.85).aspx +func DisableEcho(fd uintptr, state *State) error { + mode := state.mode + mode &^= winterm.ENABLE_ECHO_INPUT + mode |= winterm.ENABLE_PROCESSED_INPUT | winterm.ENABLE_LINE_INPUT + err := winterm.SetConsoleMode(fd, mode) + if err != nil { + return err + } + + // Register an interrupt handler to catch and restore prior state + restoreAtInterrupt(fd, state) + return nil +} + +// SetRawTerminal puts the terminal connected to the given file descriptor into +// raw mode and returns the previous state. On UNIX, this puts both the input +// and output into raw mode. On Windows, it only puts the input into raw mode. +func SetRawTerminal(fd uintptr) (*State, error) { + state, err := MakeRaw(fd) + if err != nil { + return nil, err + } + + // Register an interrupt handler to catch and restore prior state + restoreAtInterrupt(fd, state) + return state, err +} + +// SetRawTerminalOutput puts the output of terminal connected to the given file +// descriptor into raw mode. On UNIX, this does nothing and returns nil for the +// state. On Windows, it disables LF -> CRLF translation. +func SetRawTerminalOutput(fd uintptr) (*State, error) { + state, err := SaveState(fd) + if err != nil { + return nil, err + } + + // Ignore failures, since disableNewlineAutoReturn might not be supported on this + // version of Windows. + winterm.SetConsoleMode(fd, state.mode|disableNewlineAutoReturn) + return state, err +} + +// MakeRaw puts the terminal (Windows Console) connected to the given file descriptor into raw +// mode and returns the previous state of the terminal so that it can be restored. +func MakeRaw(fd uintptr) (*State, error) { + state, err := SaveState(fd) + if err != nil { + return nil, err + } + + mode := state.mode + + // See + // -- https://msdn.microsoft.com/en-us/library/windows/desktop/ms686033(v=vs.85).aspx + // -- https://msdn.microsoft.com/en-us/library/windows/desktop/ms683462(v=vs.85).aspx + + // Disable these modes + mode &^= winterm.ENABLE_ECHO_INPUT + mode &^= winterm.ENABLE_LINE_INPUT + mode &^= winterm.ENABLE_MOUSE_INPUT + mode &^= winterm.ENABLE_WINDOW_INPUT + mode &^= winterm.ENABLE_PROCESSED_INPUT + + // Enable these modes + mode |= winterm.ENABLE_EXTENDED_FLAGS + mode |= winterm.ENABLE_INSERT_MODE + mode |= winterm.ENABLE_QUICK_EDIT_MODE + if vtInputSupported { + mode |= enableVirtualTerminalInput + } + + err = winterm.SetConsoleMode(fd, mode) + if err != nil { + return nil, err + } + return state, nil +} + +func restoreAtInterrupt(fd uintptr, state *State) { + sigchan := make(chan os.Signal, 1) + signal.Notify(sigchan, os.Interrupt) + + go func() { + _ = <-sigchan + RestoreTerminal(fd, state) + os.Exit(0) + }() +} diff --git a/vendor/github.com/docker/docker/pkg/term/termios_darwin.go b/vendor/github.com/docker/docker/pkg/term/termios_darwin.go new file mode 100644 index 000000000..480db900a --- /dev/null +++ b/vendor/github.com/docker/docker/pkg/term/termios_darwin.go @@ -0,0 +1,69 @@ +package term + +import ( + "syscall" + "unsafe" +) + +const ( + getTermios = syscall.TIOCGETA + setTermios = syscall.TIOCSETA +) + +// Termios magic numbers, passthrough to the ones defined in syscall. +const ( + IGNBRK = syscall.IGNBRK + PARMRK = syscall.PARMRK + INLCR = syscall.INLCR + IGNCR = syscall.IGNCR + ECHONL = syscall.ECHONL + CSIZE = syscall.CSIZE + ICRNL = syscall.ICRNL + ISTRIP = syscall.ISTRIP + PARENB = syscall.PARENB + ECHO = syscall.ECHO + ICANON = syscall.ICANON + ISIG = syscall.ISIG + IXON = syscall.IXON + BRKINT = syscall.BRKINT + INPCK = syscall.INPCK + OPOST = syscall.OPOST + CS8 = syscall.CS8 + IEXTEN = syscall.IEXTEN +) + +// Termios is the Unix API for terminal I/O. +type Termios struct { + Iflag uint64 + Oflag uint64 + Cflag uint64 + Lflag uint64 + Cc [20]byte + Ispeed uint64 + Ospeed uint64 +} + +// MakeRaw put the terminal connected to the given file descriptor into raw +// mode and returns the previous state of the terminal so that it can be +// restored. +func MakeRaw(fd uintptr) (*State, error) { + var oldState State + if _, _, err := syscall.Syscall(syscall.SYS_IOCTL, fd, uintptr(getTermios), uintptr(unsafe.Pointer(&oldState.termios))); err != 0 { + return nil, err + } + + newState := oldState.termios + newState.Iflag &^= (IGNBRK | BRKINT | PARMRK | ISTRIP | INLCR | IGNCR | ICRNL | IXON) + newState.Oflag &^= OPOST + newState.Lflag &^= (ECHO | ECHONL | ICANON | ISIG | IEXTEN) + newState.Cflag &^= (CSIZE | PARENB) + newState.Cflag |= CS8 + newState.Cc[syscall.VMIN] = 1 + newState.Cc[syscall.VTIME] = 0 + + if _, _, err := syscall.Syscall(syscall.SYS_IOCTL, fd, uintptr(setTermios), uintptr(unsafe.Pointer(&newState))); err != 0 { + return nil, err + } + + return &oldState, nil +} diff --git a/vendor/github.com/docker/docker/pkg/term/termios_freebsd.go b/vendor/github.com/docker/docker/pkg/term/termios_freebsd.go new file mode 100644 index 000000000..ed843ad69 --- /dev/null +++ b/vendor/github.com/docker/docker/pkg/term/termios_freebsd.go @@ -0,0 +1,69 @@ +package term + +import ( + "syscall" + "unsafe" +) + +const ( + getTermios = syscall.TIOCGETA + setTermios = syscall.TIOCSETA +) + +// Termios magic numbers, passthrough to the ones defined in syscall. +const ( + IGNBRK = syscall.IGNBRK + PARMRK = syscall.PARMRK + INLCR = syscall.INLCR + IGNCR = syscall.IGNCR + ECHONL = syscall.ECHONL + CSIZE = syscall.CSIZE + ICRNL = syscall.ICRNL + ISTRIP = syscall.ISTRIP + PARENB = syscall.PARENB + ECHO = syscall.ECHO + ICANON = syscall.ICANON + ISIG = syscall.ISIG + IXON = syscall.IXON + BRKINT = syscall.BRKINT + INPCK = syscall.INPCK + OPOST = syscall.OPOST + CS8 = syscall.CS8 + IEXTEN = syscall.IEXTEN +) + +// Termios is the Unix API for terminal I/O. +type Termios struct { + Iflag uint32 + Oflag uint32 + Cflag uint32 + Lflag uint32 + Cc [20]byte + Ispeed uint32 + Ospeed uint32 +} + +// MakeRaw put the terminal connected to the given file descriptor into raw +// mode and returns the previous state of the terminal so that it can be +// restored. +func MakeRaw(fd uintptr) (*State, error) { + var oldState State + if _, _, err := syscall.Syscall(syscall.SYS_IOCTL, fd, uintptr(getTermios), uintptr(unsafe.Pointer(&oldState.termios))); err != 0 { + return nil, err + } + + newState := oldState.termios + newState.Iflag &^= (IGNBRK | BRKINT | PARMRK | ISTRIP | INLCR | IGNCR | ICRNL | IXON) + newState.Oflag &^= OPOST + newState.Lflag &^= (ECHO | ECHONL | ICANON | ISIG | IEXTEN) + newState.Cflag &^= (CSIZE | PARENB) + newState.Cflag |= CS8 + newState.Cc[syscall.VMIN] = 1 + newState.Cc[syscall.VTIME] = 0 + + if _, _, err := syscall.Syscall(syscall.SYS_IOCTL, fd, uintptr(setTermios), uintptr(unsafe.Pointer(&newState))); err != 0 { + return nil, err + } + + return &oldState, nil +} diff --git a/vendor/github.com/docker/docker/pkg/term/termios_linux.go b/vendor/github.com/docker/docker/pkg/term/termios_linux.go new file mode 100644 index 000000000..22921b6ae --- /dev/null +++ b/vendor/github.com/docker/docker/pkg/term/termios_linux.go @@ -0,0 +1,47 @@ +// +build !cgo + +package term + +import ( + "syscall" + "unsafe" +) + +const ( + getTermios = syscall.TCGETS + setTermios = syscall.TCSETS +) + +// Termios is the Unix API for terminal I/O. +type Termios struct { + Iflag uint32 + Oflag uint32 + Cflag uint32 + Lflag uint32 + Cc [20]byte + Ispeed uint32 + Ospeed uint32 +} + +// MakeRaw put the terminal connected to the given file descriptor into raw +// mode and returns the previous state of the terminal so that it can be +// restored. +func MakeRaw(fd uintptr) (*State, error) { + var oldState State + if _, _, err := syscall.Syscall(syscall.SYS_IOCTL, fd, getTermios, uintptr(unsafe.Pointer(&oldState.termios))); err != 0 { + return nil, err + } + + newState := oldState.termios + + newState.Iflag &^= (syscall.IGNBRK | syscall.BRKINT | syscall.PARMRK | syscall.ISTRIP | syscall.INLCR | syscall.IGNCR | syscall.ICRNL | syscall.IXON) + newState.Oflag &^= syscall.OPOST + newState.Lflag &^= (syscall.ECHO | syscall.ECHONL | syscall.ICANON | syscall.ISIG | syscall.IEXTEN) + newState.Cflag &^= (syscall.CSIZE | syscall.PARENB) + newState.Cflag |= syscall.CS8 + + if _, _, err := syscall.Syscall(syscall.SYS_IOCTL, fd, setTermios, uintptr(unsafe.Pointer(&newState))); err != 0 { + return nil, err + } + return &oldState, nil +} diff --git a/vendor/github.com/docker/docker/pkg/term/termios_openbsd.go b/vendor/github.com/docker/docker/pkg/term/termios_openbsd.go new file mode 100644 index 000000000..ed843ad69 --- /dev/null +++ b/vendor/github.com/docker/docker/pkg/term/termios_openbsd.go @@ -0,0 +1,69 @@ +package term + +import ( + "syscall" + "unsafe" +) + +const ( + getTermios = syscall.TIOCGETA + setTermios = syscall.TIOCSETA +) + +// Termios magic numbers, passthrough to the ones defined in syscall. +const ( + IGNBRK = syscall.IGNBRK + PARMRK = syscall.PARMRK + INLCR = syscall.INLCR + IGNCR = syscall.IGNCR + ECHONL = syscall.ECHONL + CSIZE = syscall.CSIZE + ICRNL = syscall.ICRNL + ISTRIP = syscall.ISTRIP + PARENB = syscall.PARENB + ECHO = syscall.ECHO + ICANON = syscall.ICANON + ISIG = syscall.ISIG + IXON = syscall.IXON + BRKINT = syscall.BRKINT + INPCK = syscall.INPCK + OPOST = syscall.OPOST + CS8 = syscall.CS8 + IEXTEN = syscall.IEXTEN +) + +// Termios is the Unix API for terminal I/O. +type Termios struct { + Iflag uint32 + Oflag uint32 + Cflag uint32 + Lflag uint32 + Cc [20]byte + Ispeed uint32 + Ospeed uint32 +} + +// MakeRaw put the terminal connected to the given file descriptor into raw +// mode and returns the previous state of the terminal so that it can be +// restored. +func MakeRaw(fd uintptr) (*State, error) { + var oldState State + if _, _, err := syscall.Syscall(syscall.SYS_IOCTL, fd, uintptr(getTermios), uintptr(unsafe.Pointer(&oldState.termios))); err != 0 { + return nil, err + } + + newState := oldState.termios + newState.Iflag &^= (IGNBRK | BRKINT | PARMRK | ISTRIP | INLCR | IGNCR | ICRNL | IXON) + newState.Oflag &^= OPOST + newState.Lflag &^= (ECHO | ECHONL | ICANON | ISIG | IEXTEN) + newState.Cflag &^= (CSIZE | PARENB) + newState.Cflag |= CS8 + newState.Cc[syscall.VMIN] = 1 + newState.Cc[syscall.VTIME] = 0 + + if _, _, err := syscall.Syscall(syscall.SYS_IOCTL, fd, uintptr(setTermios), uintptr(unsafe.Pointer(&newState))); err != 0 { + return nil, err + } + + return &oldState, nil +} diff --git a/vendor/github.com/docker/docker/pkg/term/windows/ansi_reader.go b/vendor/github.com/docker/docker/pkg/term/windows/ansi_reader.go new file mode 100644 index 000000000..cb0b88356 --- /dev/null +++ b/vendor/github.com/docker/docker/pkg/term/windows/ansi_reader.go @@ -0,0 +1,263 @@ +// +build windows + +package windows + +import ( + "bytes" + "errors" + "fmt" + "io" + "os" + "strings" + "unsafe" + + ansiterm "github.com/Azure/go-ansiterm" + "github.com/Azure/go-ansiterm/winterm" +) + +const ( + escapeSequence = ansiterm.KEY_ESC_CSI +) + +// ansiReader wraps a standard input file (e.g., os.Stdin) providing ANSI sequence translation. +type ansiReader struct { + file *os.File + fd uintptr + buffer []byte + cbBuffer int + command []byte +} + +// NewAnsiReader returns an io.ReadCloser that provides VT100 terminal emulation on top of a +// Windows console input handle. +func NewAnsiReader(nFile int) io.ReadCloser { + initLogger() + file, fd := winterm.GetStdFile(nFile) + return &ansiReader{ + file: file, + fd: fd, + command: make([]byte, 0, ansiterm.ANSI_MAX_CMD_LENGTH), + buffer: make([]byte, 0), + } +} + +// Close closes the wrapped file. +func (ar *ansiReader) Close() (err error) { + return ar.file.Close() +} + +// Fd returns the file descriptor of the wrapped file. +func (ar *ansiReader) Fd() uintptr { + return ar.fd +} + +// Read reads up to len(p) bytes of translated input events into p. +func (ar *ansiReader) Read(p []byte) (int, error) { + if len(p) == 0 { + return 0, nil + } + + // Previously read bytes exist, read as much as we can and return + if len(ar.buffer) > 0 { + logger.Debugf("Reading previously cached bytes") + + originalLength := len(ar.buffer) + copiedLength := copy(p, ar.buffer) + + if copiedLength == originalLength { + ar.buffer = make([]byte, 0, len(p)) + } else { + ar.buffer = ar.buffer[copiedLength:] + } + + logger.Debugf("Read from cache p[%d]: % x", copiedLength, p) + return copiedLength, nil + } + + // Read and translate key events + events, err := readInputEvents(ar.fd, len(p)) + if err != nil { + return 0, err + } else if len(events) == 0 { + logger.Debug("No input events detected") + return 0, nil + } + + keyBytes := translateKeyEvents(events, []byte(escapeSequence)) + + // Save excess bytes and right-size keyBytes + if len(keyBytes) > len(p) { + logger.Debugf("Received %d keyBytes, only room for %d bytes", len(keyBytes), len(p)) + ar.buffer = keyBytes[len(p):] + keyBytes = keyBytes[:len(p)] + } else if len(keyBytes) == 0 { + logger.Debug("No key bytes returned from the translator") + return 0, nil + } + + copiedLength := copy(p, keyBytes) + if copiedLength != len(keyBytes) { + return 0, errors.New("unexpected copy length encountered") + } + + logger.Debugf("Read p[%d]: % x", copiedLength, p) + logger.Debugf("Read keyBytes[%d]: % x", copiedLength, keyBytes) + return copiedLength, nil +} + +// readInputEvents polls until at least one event is available. +func readInputEvents(fd uintptr, maxBytes int) ([]winterm.INPUT_RECORD, error) { + // Determine the maximum number of records to retrieve + // -- Cast around the type system to obtain the size of a single INPUT_RECORD. + // unsafe.Sizeof requires an expression vs. a type-reference; the casting + // tricks the type system into believing it has such an expression. + recordSize := int(unsafe.Sizeof(*((*winterm.INPUT_RECORD)(unsafe.Pointer(&maxBytes))))) + countRecords := maxBytes / recordSize + if countRecords > ansiterm.MAX_INPUT_EVENTS { + countRecords = ansiterm.MAX_INPUT_EVENTS + } else if countRecords == 0 { + countRecords = 1 + } + logger.Debugf("[windows] readInputEvents: Reading %v records (buffer size %v, record size %v)", countRecords, maxBytes, recordSize) + + // Wait for and read input events + events := make([]winterm.INPUT_RECORD, countRecords) + nEvents := uint32(0) + eventsExist, err := winterm.WaitForSingleObject(fd, winterm.WAIT_INFINITE) + if err != nil { + return nil, err + } + + if eventsExist { + err = winterm.ReadConsoleInput(fd, events, &nEvents) + if err != nil { + return nil, err + } + } + + // Return a slice restricted to the number of returned records + logger.Debugf("[windows] readInputEvents: Read %v events", nEvents) + return events[:nEvents], nil +} + +// KeyEvent Translation Helpers + +var arrowKeyMapPrefix = map[uint16]string{ + winterm.VK_UP: "%s%sA", + winterm.VK_DOWN: "%s%sB", + winterm.VK_RIGHT: "%s%sC", + winterm.VK_LEFT: "%s%sD", +} + +var keyMapPrefix = map[uint16]string{ + winterm.VK_UP: "\x1B[%sA", + winterm.VK_DOWN: "\x1B[%sB", + winterm.VK_RIGHT: "\x1B[%sC", + winterm.VK_LEFT: "\x1B[%sD", + winterm.VK_HOME: "\x1B[1%s~", // showkey shows ^[[1 + winterm.VK_END: "\x1B[4%s~", // showkey shows ^[[4 + winterm.VK_INSERT: "\x1B[2%s~", + winterm.VK_DELETE: "\x1B[3%s~", + winterm.VK_PRIOR: "\x1B[5%s~", + winterm.VK_NEXT: "\x1B[6%s~", + winterm.VK_F1: "", + winterm.VK_F2: "", + winterm.VK_F3: "\x1B[13%s~", + winterm.VK_F4: "\x1B[14%s~", + winterm.VK_F5: "\x1B[15%s~", + winterm.VK_F6: "\x1B[17%s~", + winterm.VK_F7: "\x1B[18%s~", + winterm.VK_F8: "\x1B[19%s~", + winterm.VK_F9: "\x1B[20%s~", + winterm.VK_F10: "\x1B[21%s~", + winterm.VK_F11: "\x1B[23%s~", + winterm.VK_F12: "\x1B[24%s~", +} + +// translateKeyEvents converts the input events into the appropriate ANSI string. +func translateKeyEvents(events []winterm.INPUT_RECORD, escapeSequence []byte) []byte { + var buffer bytes.Buffer + for _, event := range events { + if event.EventType == winterm.KEY_EVENT && event.KeyEvent.KeyDown != 0 { + buffer.WriteString(keyToString(&event.KeyEvent, escapeSequence)) + } + } + + return buffer.Bytes() +} + +// keyToString maps the given input event record to the corresponding string. +func keyToString(keyEvent *winterm.KEY_EVENT_RECORD, escapeSequence []byte) string { + if keyEvent.UnicodeChar == 0 { + return formatVirtualKey(keyEvent.VirtualKeyCode, keyEvent.ControlKeyState, escapeSequence) + } + + _, alt, control := getControlKeys(keyEvent.ControlKeyState) + if control { + // TODO(azlinux): Implement following control sequences + // -D Signals the end of input from the keyboard; also exits current shell. + // -H Deletes the first character to the left of the cursor. Also called the ERASE key. + // -Q Restarts printing after it has been stopped with -s. + // -S Suspends printing on the screen (does not stop the program). + // -U Deletes all characters on the current line. Also called the KILL key. + // -E Quits current command and creates a core + + } + + // +Key generates ESC N Key + if !control && alt { + return ansiterm.KEY_ESC_N + strings.ToLower(string(keyEvent.UnicodeChar)) + } + + return string(keyEvent.UnicodeChar) +} + +// formatVirtualKey converts a virtual key (e.g., up arrow) into the appropriate ANSI string. +func formatVirtualKey(key uint16, controlState uint32, escapeSequence []byte) string { + shift, alt, control := getControlKeys(controlState) + modifier := getControlKeysModifier(shift, alt, control) + + if format, ok := arrowKeyMapPrefix[key]; ok { + return fmt.Sprintf(format, escapeSequence, modifier) + } + + if format, ok := keyMapPrefix[key]; ok { + return fmt.Sprintf(format, modifier) + } + + return "" +} + +// getControlKeys extracts the shift, alt, and ctrl key states. +func getControlKeys(controlState uint32) (shift, alt, control bool) { + shift = 0 != (controlState & winterm.SHIFT_PRESSED) + alt = 0 != (controlState & (winterm.LEFT_ALT_PRESSED | winterm.RIGHT_ALT_PRESSED)) + control = 0 != (controlState & (winterm.LEFT_CTRL_PRESSED | winterm.RIGHT_CTRL_PRESSED)) + return shift, alt, control +} + +// getControlKeysModifier returns the ANSI modifier for the given combination of control keys. +func getControlKeysModifier(shift, alt, control bool) string { + if shift && alt && control { + return ansiterm.KEY_CONTROL_PARAM_8 + } + if alt && control { + return ansiterm.KEY_CONTROL_PARAM_7 + } + if shift && control { + return ansiterm.KEY_CONTROL_PARAM_6 + } + if control { + return ansiterm.KEY_CONTROL_PARAM_5 + } + if shift && alt { + return ansiterm.KEY_CONTROL_PARAM_4 + } + if alt { + return ansiterm.KEY_CONTROL_PARAM_3 + } + if shift { + return ansiterm.KEY_CONTROL_PARAM_2 + } + return "" +} diff --git a/vendor/github.com/docker/docker/pkg/term/windows/ansi_writer.go b/vendor/github.com/docker/docker/pkg/term/windows/ansi_writer.go new file mode 100644 index 000000000..a3ce5697d --- /dev/null +++ b/vendor/github.com/docker/docker/pkg/term/windows/ansi_writer.go @@ -0,0 +1,64 @@ +// +build windows + +package windows + +import ( + "io" + "os" + + ansiterm "github.com/Azure/go-ansiterm" + "github.com/Azure/go-ansiterm/winterm" +) + +// ansiWriter wraps a standard output file (e.g., os.Stdout) providing ANSI sequence translation. +type ansiWriter struct { + file *os.File + fd uintptr + infoReset *winterm.CONSOLE_SCREEN_BUFFER_INFO + command []byte + escapeSequence []byte + inAnsiSequence bool + parser *ansiterm.AnsiParser +} + +// NewAnsiWriter returns an io.Writer that provides VT100 terminal emulation on top of a +// Windows console output handle. +func NewAnsiWriter(nFile int) io.Writer { + initLogger() + file, fd := winterm.GetStdFile(nFile) + info, err := winterm.GetConsoleScreenBufferInfo(fd) + if err != nil { + return nil + } + + parser := ansiterm.CreateParser("Ground", winterm.CreateWinEventHandler(fd, file)) + logger.Infof("newAnsiWriter: parser %p", parser) + + aw := &ansiWriter{ + file: file, + fd: fd, + infoReset: info, + command: make([]byte, 0, ansiterm.ANSI_MAX_CMD_LENGTH), + escapeSequence: []byte(ansiterm.KEY_ESC_CSI), + parser: parser, + } + + logger.Infof("newAnsiWriter: aw.parser %p", aw.parser) + logger.Infof("newAnsiWriter: %v", aw) + return aw +} + +func (aw *ansiWriter) Fd() uintptr { + return aw.fd +} + +// Write writes len(p) bytes from p to the underlying data stream. +func (aw *ansiWriter) Write(p []byte) (total int, err error) { + if len(p) == 0 { + return 0, nil + } + + logger.Infof("Write: % x", p) + logger.Infof("Write: %s", string(p)) + return aw.parser.Parse(p) +} diff --git a/vendor/github.com/docker/docker/pkg/term/windows/console.go b/vendor/github.com/docker/docker/pkg/term/windows/console.go new file mode 100644 index 000000000..ca5c3b2e5 --- /dev/null +++ b/vendor/github.com/docker/docker/pkg/term/windows/console.go @@ -0,0 +1,35 @@ +// +build windows + +package windows + +import ( + "os" + + "github.com/Azure/go-ansiterm/winterm" +) + +// GetHandleInfo returns file descriptor and bool indicating whether the file is a console. +func GetHandleInfo(in interface{}) (uintptr, bool) { + switch t := in.(type) { + case *ansiReader: + return t.Fd(), true + case *ansiWriter: + return t.Fd(), true + } + + var inFd uintptr + var isTerminal bool + + if file, ok := in.(*os.File); ok { + inFd = file.Fd() + isTerminal = IsConsole(inFd) + } + return inFd, isTerminal +} + +// IsConsole returns true if the given file descriptor is a Windows Console. +// The code assumes that GetConsoleMode will return an error for file descriptors that are not a console. +func IsConsole(fd uintptr) bool { + _, e := winterm.GetConsoleMode(fd) + return e == nil +} diff --git a/vendor/github.com/docker/docker/pkg/term/windows/windows.go b/vendor/github.com/docker/docker/pkg/term/windows/windows.go new file mode 100644 index 000000000..ce4cb5990 --- /dev/null +++ b/vendor/github.com/docker/docker/pkg/term/windows/windows.go @@ -0,0 +1,33 @@ +// These files implement ANSI-aware input and output streams for use by the Docker Windows client. +// When asked for the set of standard streams (e.g., stdin, stdout, stderr), the code will create +// and return pseudo-streams that convert ANSI sequences to / from Windows Console API calls. + +package windows + +import ( + "io/ioutil" + "os" + "sync" + + ansiterm "github.com/Azure/go-ansiterm" + "github.com/Sirupsen/logrus" +) + +var logger *logrus.Logger +var initOnce sync.Once + +func initLogger() { + initOnce.Do(func() { + logFile := ioutil.Discard + + if isDebugEnv := os.Getenv(ansiterm.LogEnv); isDebugEnv == "1" { + logFile, _ = os.Create("ansiReaderWriter.log") + } + + logger = &logrus.Logger{ + Out: logFile, + Formatter: new(logrus.TextFormatter), + Level: logrus.DebugLevel, + } + }) +} diff --git a/vendor/github.com/docker/docker/pkg/term/windows/windows_test.go b/vendor/github.com/docker/docker/pkg/term/windows/windows_test.go new file mode 100644 index 000000000..52aeab54e --- /dev/null +++ b/vendor/github.com/docker/docker/pkg/term/windows/windows_test.go @@ -0,0 +1,3 @@ +// This file is necessary to pass the Docker tests. + +package windows diff --git a/vendor/github.com/docker/spdystream/CONTRIBUTING.md b/vendor/github.com/docker/spdystream/CONTRIBUTING.md new file mode 100644 index 000000000..d4eddcc53 --- /dev/null +++ b/vendor/github.com/docker/spdystream/CONTRIBUTING.md @@ -0,0 +1,13 @@ +# Contributing to SpdyStream + +Want to hack on spdystream? Awesome! Here are instructions to get you +started. + +SpdyStream is a part of the [Docker](https://docker.io) project, and follows +the same rules and principles. If you're already familiar with the way +Docker does things, you'll feel right at home. + +Otherwise, go read +[Docker's contributions guidelines](https://github.com/dotcloud/docker/blob/master/CONTRIBUTING.md). + +Happy hacking! diff --git a/vendor/github.com/docker/spdystream/LICENSE b/vendor/github.com/docker/spdystream/LICENSE new file mode 100644 index 000000000..9e4bd4dbe --- /dev/null +++ b/vendor/github.com/docker/spdystream/LICENSE @@ -0,0 +1,191 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + Copyright 2014-2015 Docker, Inc. + + 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. diff --git a/vendor/github.com/docker/spdystream/LICENSE.docs b/vendor/github.com/docker/spdystream/LICENSE.docs new file mode 100644 index 000000000..e26cd4fc8 --- /dev/null +++ b/vendor/github.com/docker/spdystream/LICENSE.docs @@ -0,0 +1,425 @@ +Attribution-ShareAlike 4.0 International + +======================================================================= + +Creative Commons Corporation ("Creative Commons") is not a law firm and +does not provide legal services or legal advice. Distribution of +Creative Commons public licenses does not create a lawyer-client or +other relationship. Creative Commons makes its licenses and related +information available on an "as-is" basis. Creative Commons gives no +warranties regarding its licenses, any material licensed under their +terms and conditions, or any related information. Creative Commons +disclaims all liability for damages resulting from their use to the +fullest extent possible. + +Using Creative Commons Public Licenses + +Creative Commons public licenses provide a standard set of terms and +conditions that creators and other rights holders may use to share +original works of authorship and other material subject to copyright +and certain other rights specified in the public license below. The +following considerations are for informational purposes only, are not +exhaustive, and do not form part of our licenses. + + Considerations for licensors: Our public licenses are + intended for use by those authorized to give the public + permission to use material in ways otherwise restricted by + copyright and certain other rights. Our licenses are + irrevocable. Licensors should read and understand the terms + and conditions of the license they choose before applying it. + Licensors should also secure all rights necessary before + applying our licenses so that the public can reuse the + material as expected. Licensors should clearly mark any + material not subject to the license. This includes other CC- + licensed material, or material used under an exception or + limitation to copyright. More considerations for licensors: + wiki.creativecommons.org/Considerations_for_licensors + + Considerations for the public: By using one of our public + licenses, a licensor grants the public permission to use the + licensed material under specified terms and conditions. If + the licensor's permission is not necessary for any reason--for + example, because of any applicable exception or limitation to + copyright--then that use is not regulated by the license. Our + licenses grant only permissions under copyright and certain + other rights that a licensor has authority to grant. Use of + the licensed material may still be restricted for other + reasons, including because others have copyright or other + rights in the material. A licensor may make special requests, + such as asking that all changes be marked or described. + Although not required by our licenses, you are encouraged to + respect those requests where reasonable. More_considerations + for the public: + wiki.creativecommons.org/Considerations_for_licensees + +======================================================================= + +Creative Commons Attribution-ShareAlike 4.0 International Public +License + +By exercising the Licensed Rights (defined below), You accept and agree +to be bound by the terms and conditions of this Creative Commons +Attribution-ShareAlike 4.0 International Public License ("Public +License"). To the extent this Public License may be interpreted as a +contract, You are granted the Licensed Rights in consideration of Your +acceptance of these terms and conditions, and the Licensor grants You +such rights in consideration of benefits the Licensor receives from +making the Licensed Material available under these terms and +conditions. + + +Section 1 -- Definitions. + + a. Adapted Material means material subject to Copyright and Similar + Rights that is derived from or based upon the Licensed Material + and in which the Licensed Material is translated, altered, + arranged, transformed, or otherwise modified in a manner requiring + permission under the Copyright and Similar Rights held by the + Licensor. For purposes of this Public License, where the Licensed + Material is a musical work, performance, or sound recording, + Adapted Material is always produced where the Licensed Material is + synched in timed relation with a moving image. + + b. Adapter's License means the license You apply to Your Copyright + and Similar Rights in Your contributions to Adapted Material in + accordance with the terms and conditions of this Public License. + + c. BY-SA Compatible License means a license listed at + creativecommons.org/compatiblelicenses, approved by Creative + Commons as essentially the equivalent of this Public License. + + d. Copyright and Similar Rights means copyright and/or similar rights + closely related to copyright including, without limitation, + performance, broadcast, sound recording, and Sui Generis Database + Rights, without regard to how the rights are labeled or + categorized. For purposes of this Public License, the rights + specified in Section 2(b)(1)-(2) are not Copyright and Similar + Rights. + + e. Effective Technological Measures means those measures that, in the + absence of proper authority, may not be circumvented under laws + fulfilling obligations under Article 11 of the WIPO Copyright + Treaty adopted on December 20, 1996, and/or similar international + agreements. + + f. Exceptions and Limitations means fair use, fair dealing, and/or + any other exception or limitation to Copyright and Similar Rights + that applies to Your use of the Licensed Material. + + g. License Elements means the license attributes listed in the name + of a Creative Commons Public License. The License Elements of this + Public License are Attribution and ShareAlike. + + h. Licensed Material means the artistic or literary work, database, + or other material to which the Licensor applied this Public + License. + + i. Licensed Rights means the rights granted to You subject to the + terms and conditions of this Public License, which are limited to + all Copyright and Similar Rights that apply to Your use of the + Licensed Material and that the Licensor has authority to license. + + j. Licensor means the individual(s) or entity(ies) granting rights + under this Public License. + + k. Share means to provide material to the public by any means or + process that requires permission under the Licensed Rights, such + as reproduction, public display, public performance, distribution, + dissemination, communication, or importation, and to make material + available to the public including in ways that members of the + public may access the material from a place and at a time + individually chosen by them. + + l. Sui Generis Database Rights means rights other than copyright + resulting from Directive 96/9/EC of the European Parliament and of + the Council of 11 March 1996 on the legal protection of databases, + as amended and/or succeeded, as well as other essentially + equivalent rights anywhere in the world. + + m. You means the individual or entity exercising the Licensed Rights + under this Public License. Your has a corresponding meaning. + + +Section 2 -- Scope. + + a. License grant. + + 1. Subject to the terms and conditions of this Public License, + the Licensor hereby grants You a worldwide, royalty-free, + non-sublicensable, non-exclusive, irrevocable license to + exercise the Licensed Rights in the Licensed Material to: + + a. reproduce and Share the Licensed Material, in whole or + in part; and + + b. produce, reproduce, and Share Adapted Material. + + 2. Exceptions and Limitations. For the avoidance of doubt, where + Exceptions and Limitations apply to Your use, this Public + License does not apply, and You do not need to comply with + its terms and conditions. + + 3. Term. The term of this Public License is specified in Section + 6(a). + + 4. Media and formats; technical modifications allowed. The + Licensor authorizes You to exercise the Licensed Rights in + all media and formats whether now known or hereafter created, + and to make technical modifications necessary to do so. The + Licensor waives and/or agrees not to assert any right or + authority to forbid You from making technical modifications + necessary to exercise the Licensed Rights, including + technical modifications necessary to circumvent Effective + Technological Measures. For purposes of this Public License, + simply making modifications authorized by this Section 2(a) + (4) never produces Adapted Material. + + 5. Downstream recipients. + + a. Offer from the Licensor -- Licensed Material. Every + recipient of the Licensed Material automatically + receives an offer from the Licensor to exercise the + Licensed Rights under the terms and conditions of this + Public License. + + b. Additional offer from the Licensor -- Adapted Material. + Every recipient of Adapted Material from You + automatically receives an offer from the Licensor to + exercise the Licensed Rights in the Adapted Material + under the conditions of the Adapter's License You apply. + + c. No downstream restrictions. You may not offer or impose + any additional or different terms or conditions on, or + apply any Effective Technological Measures to, the + Licensed Material if doing so restricts exercise of the + Licensed Rights by any recipient of the Licensed + Material. + + 6. No endorsement. Nothing in this Public License constitutes or + may be construed as permission to assert or imply that You + are, or that Your use of the Licensed Material is, connected + with, or sponsored, endorsed, or granted official status by, + the Licensor or others designated to receive attribution as + provided in Section 3(a)(1)(A)(i). + + b. Other rights. + + 1. Moral rights, such as the right of integrity, are not + licensed under this Public License, nor are publicity, + privacy, and/or other similar personality rights; however, to + the extent possible, the Licensor waives and/or agrees not to + assert any such rights held by the Licensor to the limited + extent necessary to allow You to exercise the Licensed + Rights, but not otherwise. + + 2. Patent and trademark rights are not licensed under this + Public License. + + 3. To the extent possible, the Licensor waives any right to + collect royalties from You for the exercise of the Licensed + Rights, whether directly or through a collecting society + under any voluntary or waivable statutory or compulsory + licensing scheme. In all other cases the Licensor expressly + reserves any right to collect such royalties. + + +Section 3 -- License Conditions. + +Your exercise of the Licensed Rights is expressly made subject to the +following conditions. + + a. Attribution. + + 1. If You Share the Licensed Material (including in modified + form), You must: + + a. retain the following if it is supplied by the Licensor + with the Licensed Material: + + i. identification of the creator(s) of the Licensed + Material and any others designated to receive + attribution, in any reasonable manner requested by + the Licensor (including by pseudonym if + designated); + + ii. a copyright notice; + + iii. a notice that refers to this Public License; + + iv. a notice that refers to the disclaimer of + warranties; + + v. a URI or hyperlink to the Licensed Material to the + extent reasonably practicable; + + b. indicate if You modified the Licensed Material and + retain an indication of any previous modifications; and + + c. indicate the Licensed Material is licensed under this + Public License, and include the text of, or the URI or + hyperlink to, this Public License. + + 2. You may satisfy the conditions in Section 3(a)(1) in any + reasonable manner based on the medium, means, and context in + which You Share the Licensed Material. For example, it may be + reasonable to satisfy the conditions by providing a URI or + hyperlink to a resource that includes the required + information. + + 3. If requested by the Licensor, You must remove any of the + information required by Section 3(a)(1)(A) to the extent + reasonably practicable. + + b. ShareAlike. + + In addition to the conditions in Section 3(a), if You Share + Adapted Material You produce, the following conditions also apply. + + 1. The Adapter's License You apply must be a Creative Commons + license with the same License Elements, this version or + later, or a BY-SA Compatible License. + + 2. You must include the text of, or the URI or hyperlink to, the + Adapter's License You apply. You may satisfy this condition + in any reasonable manner based on the medium, means, and + context in which You Share Adapted Material. + + 3. You may not offer or impose any additional or different terms + or conditions on, or apply any Effective Technological + Measures to, Adapted Material that restrict exercise of the + rights granted under the Adapter's License You apply. + + +Section 4 -- Sui Generis Database Rights. + +Where the Licensed Rights include Sui Generis Database Rights that +apply to Your use of the Licensed Material: + + a. for the avoidance of doubt, Section 2(a)(1) grants You the right + to extract, reuse, reproduce, and Share all or a substantial + portion of the contents of the database; + + b. if You include all or a substantial portion of the database + contents in a database in which You have Sui Generis Database + Rights, then the database in which You have Sui Generis Database + Rights (but not its individual contents) is Adapted Material, + + including for purposes of Section 3(b); and + c. You must comply with the conditions in Section 3(a) if You Share + all or a substantial portion of the contents of the database. + +For the avoidance of doubt, this Section 4 supplements and does not +replace Your obligations under this Public License where the Licensed +Rights include other Copyright and Similar Rights. + + +Section 5 -- Disclaimer of Warranties and Limitation of Liability. + + a. UNLESS OTHERWISE SEPARATELY UNDERTAKEN BY THE LICENSOR, TO THE + EXTENT POSSIBLE, THE LICENSOR OFFERS THE LICENSED MATERIAL AS-IS + AND AS-AVAILABLE, AND MAKES NO REPRESENTATIONS OR WARRANTIES OF + ANY KIND CONCERNING THE LICENSED MATERIAL, WHETHER EXPRESS, + IMPLIED, STATUTORY, OR OTHER. THIS INCLUDES, WITHOUT LIMITATION, + WARRANTIES OF TITLE, MERCHANTABILITY, FITNESS FOR A PARTICULAR + PURPOSE, NON-INFRINGEMENT, ABSENCE OF LATENT OR OTHER DEFECTS, + ACCURACY, OR THE PRESENCE OR ABSENCE OF ERRORS, WHETHER OR NOT + KNOWN OR DISCOVERABLE. WHERE DISCLAIMERS OF WARRANTIES ARE NOT + ALLOWED IN FULL OR IN PART, THIS DISCLAIMER MAY NOT APPLY TO YOU. + + b. TO THE EXTENT POSSIBLE, IN NO EVENT WILL THE LICENSOR BE LIABLE + TO YOU ON ANY LEGAL THEORY (INCLUDING, WITHOUT LIMITATION, + NEGLIGENCE) OR OTHERWISE FOR ANY DIRECT, SPECIAL, INDIRECT, + INCIDENTAL, CONSEQUENTIAL, PUNITIVE, EXEMPLARY, OR OTHER LOSSES, + COSTS, EXPENSES, OR DAMAGES ARISING OUT OF THIS PUBLIC LICENSE OR + USE OF THE LICENSED MATERIAL, EVEN IF THE LICENSOR HAS BEEN + ADVISED OF THE POSSIBILITY OF SUCH LOSSES, COSTS, EXPENSES, OR + DAMAGES. WHERE A LIMITATION OF LIABILITY IS NOT ALLOWED IN FULL OR + IN PART, THIS LIMITATION MAY NOT APPLY TO YOU. + + c. The disclaimer of warranties and limitation of liability provided + above shall be interpreted in a manner that, to the extent + possible, most closely approximates an absolute disclaimer and + waiver of all liability. + + +Section 6 -- Term and Termination. + + a. This Public License applies for the term of the Copyright and + Similar Rights licensed here. However, if You fail to comply with + this Public License, then Your rights under this Public License + terminate automatically. + + b. Where Your right to use the Licensed Material has terminated under + Section 6(a), it reinstates: + + 1. automatically as of the date the violation is cured, provided + it is cured within 30 days of Your discovery of the + violation; or + + 2. upon express reinstatement by the Licensor. + + For the avoidance of doubt, this Section 6(b) does not affect any + right the Licensor may have to seek remedies for Your violations + of this Public License. + + c. For the avoidance of doubt, the Licensor may also offer the + Licensed Material under separate terms or conditions or stop + distributing the Licensed Material at any time; however, doing so + will not terminate this Public License. + + d. Sections 1, 5, 6, 7, and 8 survive termination of this Public + License. + + +Section 7 -- Other Terms and Conditions. + + a. The Licensor shall not be bound by any additional or different + terms or conditions communicated by You unless expressly agreed. + + b. Any arrangements, understandings, or agreements regarding the + Licensed Material not stated herein are separate from and + independent of the terms and conditions of this Public License. + + +Section 8 -- Interpretation. + + a. For the avoidance of doubt, this Public License does not, and + shall not be interpreted to, reduce, limit, restrict, or impose + conditions on any use of the Licensed Material that could lawfully + be made without permission under this Public License. + + b. To the extent possible, if any provision of this Public License is + deemed unenforceable, it shall be automatically reformed to the + minimum extent necessary to make it enforceable. If the provision + cannot be reformed, it shall be severed from this Public License + without affecting the enforceability of the remaining terms and + conditions. + + c. No term or condition of this Public License will be waived and no + failure to comply consented to unless expressly agreed to by the + Licensor. + + d. Nothing in this Public License constitutes or may be interpreted + as a limitation upon, or waiver of, any privileges and immunities + that apply to the Licensor or You, including from the legal + processes of any jurisdiction or authority. + + +======================================================================= + +Creative Commons is not a party to its public licenses. +Notwithstanding, Creative Commons may elect to apply one of its public +licenses to material it publishes and in those instances will be +considered the "Licensor." Except for the limited purpose of indicating +that material is shared under a Creative Commons public license or as +otherwise permitted by the Creative Commons policies published at +creativecommons.org/policies, Creative Commons does not authorize the +use of the trademark "Creative Commons" or any other trademark or logo +of Creative Commons without its prior written consent including, +without limitation, in connection with any unauthorized modifications +to any of its public licenses or any other arrangements, +understandings, or agreements concerning use of licensed material. For +the avoidance of doubt, this paragraph does not form part of the public +licenses. + +Creative Commons may be contacted at creativecommons.org. diff --git a/vendor/github.com/docker/spdystream/MAINTAINERS b/vendor/github.com/docker/spdystream/MAINTAINERS new file mode 100644 index 000000000..14e263325 --- /dev/null +++ b/vendor/github.com/docker/spdystream/MAINTAINERS @@ -0,0 +1,28 @@ +# Spdystream maintainers file +# +# This file describes who runs the docker/spdystream project and how. +# This is a living document - if you see something out of date or missing, speak up! +# +# It is structured to be consumable by both humans and programs. +# To extract its contents programmatically, use any TOML-compliant parser. +# +# This file is compiled into the MAINTAINERS file in docker/opensource. +# +[Org] + [Org."Core maintainers"] + people = [ + "dmcgowan", + ] + +[people] + +# A reference list of all people associated with the project. +# All other sections should refer to people by their canonical key +# in the people section. + + # ADD YOURSELF HERE IN ALPHABETICAL ORDER + + [people.dmcgowan] + Name = "Derek McGowan" + Email = "derek@docker.com" + GitHub = "dmcgowan" diff --git a/vendor/github.com/docker/spdystream/README.md b/vendor/github.com/docker/spdystream/README.md new file mode 100644 index 000000000..11cccd0a0 --- /dev/null +++ b/vendor/github.com/docker/spdystream/README.md @@ -0,0 +1,77 @@ +# SpdyStream + +A multiplexed stream library using spdy + +## Usage + +Client example (connecting to mirroring server without auth) + +```go +package main + +import ( + "fmt" + "github.com/docker/spdystream" + "net" + "net/http" +) + +func main() { + conn, err := net.Dial("tcp", "localhost:8080") + if err != nil { + panic(err) + } + spdyConn, err := spdystream.NewConnection(conn, false) + if err != nil { + panic(err) + } + go spdyConn.Serve(spdystream.NoOpStreamHandler) + stream, err := spdyConn.CreateStream(http.Header{}, nil, false) + if err != nil { + panic(err) + } + + stream.Wait() + + fmt.Fprint(stream, "Writing to stream") + + buf := make([]byte, 25) + stream.Read(buf) + fmt.Println(string(buf)) + + stream.Close() +} +``` + +Server example (mirroring server without auth) + +```go +package main + +import ( + "github.com/docker/spdystream" + "net" +) + +func main() { + listener, err := net.Listen("tcp", "localhost:8080") + if err != nil { + panic(err) + } + for { + conn, err := listener.Accept() + if err != nil { + panic(err) + } + spdyConn, err := spdystream.NewConnection(conn, true) + if err != nil { + panic(err) + } + go spdyConn.Serve(spdystream.MirrorStreamHandler) + } +} +``` + +## Copyright and license + +Copyright © 2014-2015 Docker, Inc. All rights reserved, except as follows. Code is released under the Apache 2.0 license. The README.md file, and files in the "docs" folder are licensed under the Creative Commons Attribution 4.0 International License under the terms and conditions set forth in the file "LICENSE.docs". You may obtain a duplicate copy of the same license, titled CC-BY-SA-4.0, at http://creativecommons.org/licenses/by/4.0/. diff --git a/vendor/github.com/docker/spdystream/connection.go b/vendor/github.com/docker/spdystream/connection.go new file mode 100644 index 000000000..2023ecf84 --- /dev/null +++ b/vendor/github.com/docker/spdystream/connection.go @@ -0,0 +1,959 @@ +package spdystream + +import ( + "errors" + "fmt" + "io" + "net" + "net/http" + "sync" + "time" + + "github.com/docker/spdystream/spdy" +) + +var ( + ErrInvalidStreamId = errors.New("Invalid stream id") + ErrTimeout = errors.New("Timeout occurred") + ErrReset = errors.New("Stream reset") + ErrWriteClosedStream = errors.New("Write on closed stream") +) + +const ( + FRAME_WORKERS = 5 + QUEUE_SIZE = 50 +) + +type StreamHandler func(stream *Stream) + +type AuthHandler func(header http.Header, slot uint8, parent uint32) bool + +type idleAwareFramer struct { + f *spdy.Framer + conn *Connection + writeLock sync.Mutex + resetChan chan struct{} + setTimeoutLock sync.Mutex + setTimeoutChan chan time.Duration + timeout time.Duration +} + +func newIdleAwareFramer(framer *spdy.Framer) *idleAwareFramer { + iaf := &idleAwareFramer{ + f: framer, + resetChan: make(chan struct{}, 2), + // setTimeoutChan needs to be buffered to avoid deadlocks when calling setIdleTimeout at about + // the same time the connection is being closed + setTimeoutChan: make(chan time.Duration, 1), + } + return iaf +} + +func (i *idleAwareFramer) monitor() { + var ( + timer *time.Timer + expired <-chan time.Time + resetChan = i.resetChan + setTimeoutChan = i.setTimeoutChan + ) +Loop: + for { + select { + case timeout := <-i.setTimeoutChan: + i.timeout = timeout + if timeout == 0 { + if timer != nil { + timer.Stop() + } + } else { + if timer == nil { + timer = time.NewTimer(timeout) + expired = timer.C + } else { + timer.Reset(timeout) + } + } + case <-resetChan: + if timer != nil && i.timeout > 0 { + timer.Reset(i.timeout) + } + case <-expired: + i.conn.streamCond.L.Lock() + streams := i.conn.streams + i.conn.streams = make(map[spdy.StreamId]*Stream) + i.conn.streamCond.Broadcast() + i.conn.streamCond.L.Unlock() + go func() { + for _, stream := range streams { + stream.resetStream() + } + i.conn.Close() + }() + case <-i.conn.closeChan: + if timer != nil { + timer.Stop() + } + + // Start a goroutine to drain resetChan. This is needed because we've seen + // some unit tests with large numbers of goroutines get into a situation + // where resetChan fills up, at least 1 call to Write() is still trying to + // send to resetChan, the connection gets closed, and this case statement + // attempts to grab the write lock that Write() already has, causing a + // deadlock. + // + // See https://github.com/docker/spdystream/issues/49 for more details. + go func() { + for _ = range resetChan { + } + }() + + go func() { + for _ = range setTimeoutChan { + } + }() + + i.writeLock.Lock() + close(resetChan) + i.resetChan = nil + i.writeLock.Unlock() + + i.setTimeoutLock.Lock() + close(i.setTimeoutChan) + i.setTimeoutChan = nil + i.setTimeoutLock.Unlock() + + break Loop + } + } + + // Drain resetChan + for _ = range resetChan { + } +} + +func (i *idleAwareFramer) WriteFrame(frame spdy.Frame) error { + i.writeLock.Lock() + defer i.writeLock.Unlock() + if i.resetChan == nil { + return io.EOF + } + err := i.f.WriteFrame(frame) + if err != nil { + return err + } + + i.resetChan <- struct{}{} + + return nil +} + +func (i *idleAwareFramer) ReadFrame() (spdy.Frame, error) { + frame, err := i.f.ReadFrame() + if err != nil { + return nil, err + } + + // resetChan should never be closed since it is only closed + // when the connection has closed its closeChan. This closure + // only occurs after all Reads have finished + // TODO (dmcgowan): refactor relationship into connection + i.resetChan <- struct{}{} + + return frame, nil +} + +func (i *idleAwareFramer) setIdleTimeout(timeout time.Duration) { + i.setTimeoutLock.Lock() + defer i.setTimeoutLock.Unlock() + + if i.setTimeoutChan == nil { + return + } + + i.setTimeoutChan <- timeout +} + +type Connection struct { + conn net.Conn + framer *idleAwareFramer + + closeChan chan bool + goneAway bool + lastStreamChan chan<- *Stream + goAwayTimeout time.Duration + closeTimeout time.Duration + + streamLock *sync.RWMutex + streamCond *sync.Cond + streams map[spdy.StreamId]*Stream + + nextIdLock sync.Mutex + receiveIdLock sync.Mutex + nextStreamId spdy.StreamId + receivedStreamId spdy.StreamId + + pingIdLock sync.Mutex + pingId uint32 + pingChans map[uint32]chan error + + shutdownLock sync.Mutex + shutdownChan chan error + hasShutdown bool + + // for testing https://github.com/docker/spdystream/pull/56 + dataFrameHandler func(*spdy.DataFrame) error +} + +// NewConnection creates a new spdy connection from an existing +// network connection. +func NewConnection(conn net.Conn, server bool) (*Connection, error) { + framer, framerErr := spdy.NewFramer(conn, conn) + if framerErr != nil { + return nil, framerErr + } + idleAwareFramer := newIdleAwareFramer(framer) + var sid spdy.StreamId + var rid spdy.StreamId + var pid uint32 + if server { + sid = 2 + rid = 1 + pid = 2 + } else { + sid = 1 + rid = 2 + pid = 1 + } + + streamLock := new(sync.RWMutex) + streamCond := sync.NewCond(streamLock) + + session := &Connection{ + conn: conn, + framer: idleAwareFramer, + + closeChan: make(chan bool), + goAwayTimeout: time.Duration(0), + closeTimeout: time.Duration(0), + + streamLock: streamLock, + streamCond: streamCond, + streams: make(map[spdy.StreamId]*Stream), + nextStreamId: sid, + receivedStreamId: rid, + + pingId: pid, + pingChans: make(map[uint32]chan error), + + shutdownChan: make(chan error), + } + session.dataFrameHandler = session.handleDataFrame + idleAwareFramer.conn = session + go idleAwareFramer.monitor() + + return session, nil +} + +// Ping sends a ping frame across the connection and +// returns the response time +func (s *Connection) Ping() (time.Duration, error) { + pid := s.pingId + s.pingIdLock.Lock() + if s.pingId > 0x7ffffffe { + s.pingId = s.pingId - 0x7ffffffe + } else { + s.pingId = s.pingId + 2 + } + s.pingIdLock.Unlock() + pingChan := make(chan error) + s.pingChans[pid] = pingChan + defer delete(s.pingChans, pid) + + frame := &spdy.PingFrame{Id: pid} + startTime := time.Now() + writeErr := s.framer.WriteFrame(frame) + if writeErr != nil { + return time.Duration(0), writeErr + } + select { + case <-s.closeChan: + return time.Duration(0), errors.New("connection closed") + case err, ok := <-pingChan: + if ok && err != nil { + return time.Duration(0), err + } + break + } + return time.Now().Sub(startTime), nil +} + +// Serve handles frames sent from the server, including reply frames +// which are needed to fully initiate connections. Both clients and servers +// should call Serve in a separate goroutine before creating streams. +func (s *Connection) Serve(newHandler StreamHandler) { + // use a WaitGroup to wait for all frames to be drained after receiving + // go-away. + var wg sync.WaitGroup + + // Parition queues to ensure stream frames are handled + // by the same worker, ensuring order is maintained + frameQueues := make([]*PriorityFrameQueue, FRAME_WORKERS) + for i := 0; i < FRAME_WORKERS; i++ { + frameQueues[i] = NewPriorityFrameQueue(QUEUE_SIZE) + + // Ensure frame queue is drained when connection is closed + go func(frameQueue *PriorityFrameQueue) { + <-s.closeChan + frameQueue.Drain() + }(frameQueues[i]) + + wg.Add(1) + go func(frameQueue *PriorityFrameQueue) { + // let the WaitGroup know this worker is done + defer wg.Done() + + s.frameHandler(frameQueue, newHandler) + }(frameQueues[i]) + } + + var ( + partitionRoundRobin int + goAwayFrame *spdy.GoAwayFrame + ) +Loop: + for { + readFrame, err := s.framer.ReadFrame() + if err != nil { + if err != io.EOF { + debugMessage("frame read error: %s", err) + } else { + debugMessage("(%p) EOF received", s) + } + break + } + var priority uint8 + var partition int + switch frame := readFrame.(type) { + case *spdy.SynStreamFrame: + if s.checkStreamFrame(frame) { + priority = frame.Priority + partition = int(frame.StreamId % FRAME_WORKERS) + debugMessage("(%p) Add stream frame: %d ", s, frame.StreamId) + s.addStreamFrame(frame) + } else { + debugMessage("(%p) Rejected stream frame: %d ", s, frame.StreamId) + continue + } + case *spdy.SynReplyFrame: + priority = s.getStreamPriority(frame.StreamId) + partition = int(frame.StreamId % FRAME_WORKERS) + case *spdy.DataFrame: + priority = s.getStreamPriority(frame.StreamId) + partition = int(frame.StreamId % FRAME_WORKERS) + case *spdy.RstStreamFrame: + priority = s.getStreamPriority(frame.StreamId) + partition = int(frame.StreamId % FRAME_WORKERS) + case *spdy.HeadersFrame: + priority = s.getStreamPriority(frame.StreamId) + partition = int(frame.StreamId % FRAME_WORKERS) + case *spdy.PingFrame: + priority = 0 + partition = partitionRoundRobin + partitionRoundRobin = (partitionRoundRobin + 1) % FRAME_WORKERS + case *spdy.GoAwayFrame: + // hold on to the go away frame and exit the loop + goAwayFrame = frame + break Loop + default: + priority = 7 + partition = partitionRoundRobin + partitionRoundRobin = (partitionRoundRobin + 1) % FRAME_WORKERS + } + frameQueues[partition].Push(readFrame, priority) + } + close(s.closeChan) + + // wait for all frame handler workers to indicate they've drained their queues + // before handling the go away frame + wg.Wait() + + if goAwayFrame != nil { + s.handleGoAwayFrame(goAwayFrame) + } + + // now it's safe to close remote channels and empty s.streams + s.streamCond.L.Lock() + // notify streams that they're now closed, which will + // unblock any stream Read() calls + for _, stream := range s.streams { + stream.closeRemoteChannels() + } + s.streams = make(map[spdy.StreamId]*Stream) + s.streamCond.Broadcast() + s.streamCond.L.Unlock() +} + +func (s *Connection) frameHandler(frameQueue *PriorityFrameQueue, newHandler StreamHandler) { + for { + popFrame := frameQueue.Pop() + if popFrame == nil { + return + } + + var frameErr error + switch frame := popFrame.(type) { + case *spdy.SynStreamFrame: + frameErr = s.handleStreamFrame(frame, newHandler) + case *spdy.SynReplyFrame: + frameErr = s.handleReplyFrame(frame) + case *spdy.DataFrame: + frameErr = s.dataFrameHandler(frame) + case *spdy.RstStreamFrame: + frameErr = s.handleResetFrame(frame) + case *spdy.HeadersFrame: + frameErr = s.handleHeaderFrame(frame) + case *spdy.PingFrame: + frameErr = s.handlePingFrame(frame) + case *spdy.GoAwayFrame: + frameErr = s.handleGoAwayFrame(frame) + default: + frameErr = fmt.Errorf("unhandled frame type: %T", frame) + } + + if frameErr != nil { + debugMessage("frame handling error: %s", frameErr) + } + } +} + +func (s *Connection) getStreamPriority(streamId spdy.StreamId) uint8 { + stream, streamOk := s.getStream(streamId) + if !streamOk { + return 7 + } + return stream.priority +} + +func (s *Connection) addStreamFrame(frame *spdy.SynStreamFrame) { + var parent *Stream + if frame.AssociatedToStreamId != spdy.StreamId(0) { + parent, _ = s.getStream(frame.AssociatedToStreamId) + } + + stream := &Stream{ + streamId: frame.StreamId, + parent: parent, + conn: s, + startChan: make(chan error), + headers: frame.Headers, + finished: (frame.CFHeader.Flags & spdy.ControlFlagUnidirectional) != 0x00, + replyCond: sync.NewCond(new(sync.Mutex)), + dataChan: make(chan []byte), + headerChan: make(chan http.Header), + closeChan: make(chan bool), + priority: frame.Priority, + } + if frame.CFHeader.Flags&spdy.ControlFlagFin != 0x00 { + stream.closeRemoteChannels() + } + + s.addStream(stream) +} + +// checkStreamFrame checks to see if a stream frame is allowed. +// If the stream is invalid, then a reset frame with protocol error +// will be returned. +func (s *Connection) checkStreamFrame(frame *spdy.SynStreamFrame) bool { + s.receiveIdLock.Lock() + defer s.receiveIdLock.Unlock() + if s.goneAway { + return false + } + validationErr := s.validateStreamId(frame.StreamId) + if validationErr != nil { + go func() { + resetErr := s.sendResetFrame(spdy.ProtocolError, frame.StreamId) + if resetErr != nil { + debugMessage("reset error: %s", resetErr) + } + }() + return false + } + return true +} + +func (s *Connection) handleStreamFrame(frame *spdy.SynStreamFrame, newHandler StreamHandler) error { + stream, ok := s.getStream(frame.StreamId) + if !ok { + return fmt.Errorf("Missing stream: %d", frame.StreamId) + } + + newHandler(stream) + + return nil +} + +func (s *Connection) handleReplyFrame(frame *spdy.SynReplyFrame) error { + debugMessage("(%p) Reply frame received for %d", s, frame.StreamId) + stream, streamOk := s.getStream(frame.StreamId) + if !streamOk { + debugMessage("Reply frame gone away for %d", frame.StreamId) + // Stream has already gone away + return nil + } + if stream.replied { + // Stream has already received reply + return nil + } + stream.replied = true + + // TODO Check for error + if (frame.CFHeader.Flags & spdy.ControlFlagFin) != 0x00 { + s.remoteStreamFinish(stream) + } + + close(stream.startChan) + + return nil +} + +func (s *Connection) handleResetFrame(frame *spdy.RstStreamFrame) error { + stream, streamOk := s.getStream(frame.StreamId) + if !streamOk { + // Stream has already been removed + return nil + } + s.removeStream(stream) + stream.closeRemoteChannels() + + if !stream.replied { + stream.replied = true + stream.startChan <- ErrReset + close(stream.startChan) + } + + stream.finishLock.Lock() + stream.finished = true + stream.finishLock.Unlock() + + return nil +} + +func (s *Connection) handleHeaderFrame(frame *spdy.HeadersFrame) error { + stream, streamOk := s.getStream(frame.StreamId) + if !streamOk { + // Stream has already gone away + return nil + } + if !stream.replied { + // No reply received...Protocol error? + return nil + } + + // TODO limit headers while not blocking (use buffered chan or goroutine?) + select { + case <-stream.closeChan: + return nil + case stream.headerChan <- frame.Headers: + } + + if (frame.CFHeader.Flags & spdy.ControlFlagFin) != 0x00 { + s.remoteStreamFinish(stream) + } + + return nil +} + +func (s *Connection) handleDataFrame(frame *spdy.DataFrame) error { + debugMessage("(%p) Data frame received for %d", s, frame.StreamId) + stream, streamOk := s.getStream(frame.StreamId) + if !streamOk { + debugMessage("(%p) Data frame gone away for %d", s, frame.StreamId) + // Stream has already gone away + return nil + } + if !stream.replied { + debugMessage("(%p) Data frame not replied %d", s, frame.StreamId) + // No reply received...Protocol error? + return nil + } + + debugMessage("(%p) (%d) Data frame handling", stream, stream.streamId) + if len(frame.Data) > 0 { + stream.dataLock.RLock() + select { + case <-stream.closeChan: + debugMessage("(%p) (%d) Data frame not sent (stream shut down)", stream, stream.streamId) + case stream.dataChan <- frame.Data: + debugMessage("(%p) (%d) Data frame sent", stream, stream.streamId) + } + stream.dataLock.RUnlock() + } + if (frame.Flags & spdy.DataFlagFin) != 0x00 { + s.remoteStreamFinish(stream) + } + return nil +} + +func (s *Connection) handlePingFrame(frame *spdy.PingFrame) error { + if s.pingId&0x01 != frame.Id&0x01 { + return s.framer.WriteFrame(frame) + } + pingChan, pingOk := s.pingChans[frame.Id] + if pingOk { + close(pingChan) + } + return nil +} + +func (s *Connection) handleGoAwayFrame(frame *spdy.GoAwayFrame) error { + debugMessage("(%p) Go away received", s) + s.receiveIdLock.Lock() + if s.goneAway { + s.receiveIdLock.Unlock() + return nil + } + s.goneAway = true + s.receiveIdLock.Unlock() + + if s.lastStreamChan != nil { + stream, _ := s.getStream(frame.LastGoodStreamId) + go func() { + s.lastStreamChan <- stream + }() + } + + // Do not block frame handler waiting for closure + go s.shutdown(s.goAwayTimeout) + + return nil +} + +func (s *Connection) remoteStreamFinish(stream *Stream) { + stream.closeRemoteChannels() + + stream.finishLock.Lock() + if stream.finished { + // Stream is fully closed, cleanup + s.removeStream(stream) + } + stream.finishLock.Unlock() +} + +// CreateStream creates a new spdy stream using the parameters for +// creating the stream frame. The stream frame will be sent upon +// calling this function, however this function does not wait for +// the reply frame. If waiting for the reply is desired, use +// the stream Wait or WaitTimeout function on the stream returned +// by this function. +func (s *Connection) CreateStream(headers http.Header, parent *Stream, fin bool) (*Stream, error) { + // MUST synchronize stream creation (all the way to writing the frame) + // as stream IDs **MUST** increase monotonically. + s.nextIdLock.Lock() + defer s.nextIdLock.Unlock() + + streamId := s.getNextStreamId() + if streamId == 0 { + return nil, fmt.Errorf("Unable to get new stream id") + } + + stream := &Stream{ + streamId: streamId, + parent: parent, + conn: s, + startChan: make(chan error), + headers: headers, + dataChan: make(chan []byte), + headerChan: make(chan http.Header), + closeChan: make(chan bool), + } + + debugMessage("(%p) (%p) Create stream", s, stream) + + s.addStream(stream) + + return stream, s.sendStream(stream, fin) +} + +func (s *Connection) shutdown(closeTimeout time.Duration) { + // TODO Ensure this isn't called multiple times + s.shutdownLock.Lock() + if s.hasShutdown { + s.shutdownLock.Unlock() + return + } + s.hasShutdown = true + s.shutdownLock.Unlock() + + var timeout <-chan time.Time + if closeTimeout > time.Duration(0) { + timeout = time.After(closeTimeout) + } + streamsClosed := make(chan bool) + + go func() { + s.streamCond.L.Lock() + for len(s.streams) > 0 { + debugMessage("Streams opened: %d, %#v", len(s.streams), s.streams) + s.streamCond.Wait() + } + s.streamCond.L.Unlock() + close(streamsClosed) + }() + + var err error + select { + case <-streamsClosed: + // No active streams, close should be safe + err = s.conn.Close() + case <-timeout: + // Force ungraceful close + err = s.conn.Close() + // Wait for cleanup to clear active streams + <-streamsClosed + } + + if err != nil { + duration := 10 * time.Minute + time.AfterFunc(duration, func() { + select { + case err, ok := <-s.shutdownChan: + if ok { + debugMessage("Unhandled close error after %s: %s", duration, err) + } + default: + } + }) + s.shutdownChan <- err + } + close(s.shutdownChan) + + return +} + +// Closes spdy connection by sending GoAway frame and initiating shutdown +func (s *Connection) Close() error { + s.receiveIdLock.Lock() + if s.goneAway { + s.receiveIdLock.Unlock() + return nil + } + s.goneAway = true + s.receiveIdLock.Unlock() + + var lastStreamId spdy.StreamId + if s.receivedStreamId > 2 { + lastStreamId = s.receivedStreamId - 2 + } + + goAwayFrame := &spdy.GoAwayFrame{ + LastGoodStreamId: lastStreamId, + Status: spdy.GoAwayOK, + } + + err := s.framer.WriteFrame(goAwayFrame) + if err != nil { + return err + } + + go s.shutdown(s.closeTimeout) + + return nil +} + +// CloseWait closes the connection and waits for shutdown +// to finish. Note the underlying network Connection +// is not closed until the end of shutdown. +func (s *Connection) CloseWait() error { + closeErr := s.Close() + if closeErr != nil { + return closeErr + } + shutdownErr, ok := <-s.shutdownChan + if ok { + return shutdownErr + } + return nil +} + +// Wait waits for the connection to finish shutdown or for +// the wait timeout duration to expire. This needs to be +// called either after Close has been called or the GOAWAYFRAME +// has been received. If the wait timeout is 0, this function +// will block until shutdown finishes. If wait is never called +// and a shutdown error occurs, that error will be logged as an +// unhandled error. +func (s *Connection) Wait(waitTimeout time.Duration) error { + var timeout <-chan time.Time + if waitTimeout > time.Duration(0) { + timeout = time.After(waitTimeout) + } + + select { + case err, ok := <-s.shutdownChan: + if ok { + return err + } + case <-timeout: + return ErrTimeout + } + return nil +} + +// NotifyClose registers a channel to be called when the remote +// peer inidicates connection closure. The last stream to be +// received by the remote will be sent on the channel. The notify +// timeout will determine the duration between go away received +// and the connection being closed. +func (s *Connection) NotifyClose(c chan<- *Stream, timeout time.Duration) { + s.goAwayTimeout = timeout + s.lastStreamChan = c +} + +// SetCloseTimeout sets the amount of time close will wait for +// streams to finish before terminating the underlying network +// connection. Setting the timeout to 0 will cause close to +// wait forever, which is the default. +func (s *Connection) SetCloseTimeout(timeout time.Duration) { + s.closeTimeout = timeout +} + +// SetIdleTimeout sets the amount of time the connection may sit idle before +// it is forcefully terminated. +func (s *Connection) SetIdleTimeout(timeout time.Duration) { + s.framer.setIdleTimeout(timeout) +} + +func (s *Connection) sendHeaders(headers http.Header, stream *Stream, fin bool) error { + var flags spdy.ControlFlags + if fin { + flags = spdy.ControlFlagFin + } + + headerFrame := &spdy.HeadersFrame{ + StreamId: stream.streamId, + Headers: headers, + CFHeader: spdy.ControlFrameHeader{Flags: flags}, + } + + return s.framer.WriteFrame(headerFrame) +} + +func (s *Connection) sendReply(headers http.Header, stream *Stream, fin bool) error { + var flags spdy.ControlFlags + if fin { + flags = spdy.ControlFlagFin + } + + replyFrame := &spdy.SynReplyFrame{ + StreamId: stream.streamId, + Headers: headers, + CFHeader: spdy.ControlFrameHeader{Flags: flags}, + } + + return s.framer.WriteFrame(replyFrame) +} + +func (s *Connection) sendResetFrame(status spdy.RstStreamStatus, streamId spdy.StreamId) error { + resetFrame := &spdy.RstStreamFrame{ + StreamId: streamId, + Status: status, + } + + return s.framer.WriteFrame(resetFrame) +} + +func (s *Connection) sendReset(status spdy.RstStreamStatus, stream *Stream) error { + return s.sendResetFrame(status, stream.streamId) +} + +func (s *Connection) sendStream(stream *Stream, fin bool) error { + var flags spdy.ControlFlags + if fin { + flags = spdy.ControlFlagFin + stream.finished = true + } + + var parentId spdy.StreamId + if stream.parent != nil { + parentId = stream.parent.streamId + } + + streamFrame := &spdy.SynStreamFrame{ + StreamId: spdy.StreamId(stream.streamId), + AssociatedToStreamId: spdy.StreamId(parentId), + Headers: stream.headers, + CFHeader: spdy.ControlFrameHeader{Flags: flags}, + } + + return s.framer.WriteFrame(streamFrame) +} + +// getNextStreamId returns the next sequential id +// every call should produce a unique value or an error +func (s *Connection) getNextStreamId() spdy.StreamId { + sid := s.nextStreamId + if sid > 0x7fffffff { + return 0 + } + s.nextStreamId = s.nextStreamId + 2 + return sid +} + +// PeekNextStreamId returns the next sequential id and keeps the next id untouched +func (s *Connection) PeekNextStreamId() spdy.StreamId { + sid := s.nextStreamId + return sid +} + +func (s *Connection) validateStreamId(rid spdy.StreamId) error { + if rid > 0x7fffffff || rid < s.receivedStreamId { + return ErrInvalidStreamId + } + s.receivedStreamId = rid + 2 + return nil +} + +func (s *Connection) addStream(stream *Stream) { + s.streamCond.L.Lock() + s.streams[stream.streamId] = stream + debugMessage("(%p) (%p) Stream added, broadcasting: %d", s, stream, stream.streamId) + s.streamCond.Broadcast() + s.streamCond.L.Unlock() +} + +func (s *Connection) removeStream(stream *Stream) { + s.streamCond.L.Lock() + delete(s.streams, stream.streamId) + debugMessage("(%p) (%p) Stream removed, broadcasting: %d", s, stream, stream.streamId) + s.streamCond.Broadcast() + s.streamCond.L.Unlock() +} + +func (s *Connection) getStream(streamId spdy.StreamId) (stream *Stream, ok bool) { + s.streamLock.RLock() + stream, ok = s.streams[streamId] + s.streamLock.RUnlock() + return +} + +// FindStream looks up the given stream id and either waits for the +// stream to be found or returns nil if the stream id is no longer +// valid. +func (s *Connection) FindStream(streamId uint32) *Stream { + var stream *Stream + var ok bool + s.streamCond.L.Lock() + stream, ok = s.streams[spdy.StreamId(streamId)] + debugMessage("(%p) Found stream %d? %t", s, spdy.StreamId(streamId), ok) + for !ok && streamId >= uint32(s.receivedStreamId) { + s.streamCond.Wait() + stream, ok = s.streams[spdy.StreamId(streamId)] + } + s.streamCond.L.Unlock() + return stream +} + +func (s *Connection) CloseChan() <-chan bool { + return s.closeChan +} diff --git a/vendor/github.com/docker/spdystream/handlers.go b/vendor/github.com/docker/spdystream/handlers.go new file mode 100644 index 000000000..d4ee7be81 --- /dev/null +++ b/vendor/github.com/docker/spdystream/handlers.go @@ -0,0 +1,36 @@ +package spdystream + +import ( + "io" + "net/http" +) + +// MirrorStreamHandler mirrors all streams. +func MirrorStreamHandler(stream *Stream) { + replyErr := stream.SendReply(http.Header{}, false) + if replyErr != nil { + return + } + + go func() { + io.Copy(stream, stream) + stream.Close() + }() + go func() { + for { + header, receiveErr := stream.ReceiveHeader() + if receiveErr != nil { + return + } + sendErr := stream.SendHeader(header, false) + if sendErr != nil { + return + } + } + }() +} + +// NoopStreamHandler does nothing when stream connects. +func NoOpStreamHandler(stream *Stream) { + stream.SendReply(http.Header{}, false) +} diff --git a/vendor/github.com/docker/spdystream/priority.go b/vendor/github.com/docker/spdystream/priority.go new file mode 100644 index 000000000..fc8582b5c --- /dev/null +++ b/vendor/github.com/docker/spdystream/priority.go @@ -0,0 +1,98 @@ +package spdystream + +import ( + "container/heap" + "sync" + + "github.com/docker/spdystream/spdy" +) + +type prioritizedFrame struct { + frame spdy.Frame + priority uint8 + insertId uint64 +} + +type frameQueue []*prioritizedFrame + +func (fq frameQueue) Len() int { + return len(fq) +} + +func (fq frameQueue) Less(i, j int) bool { + if fq[i].priority == fq[j].priority { + return fq[i].insertId < fq[j].insertId + } + return fq[i].priority < fq[j].priority +} + +func (fq frameQueue) Swap(i, j int) { + fq[i], fq[j] = fq[j], fq[i] +} + +func (fq *frameQueue) Push(x interface{}) { + *fq = append(*fq, x.(*prioritizedFrame)) +} + +func (fq *frameQueue) Pop() interface{} { + old := *fq + n := len(old) + *fq = old[0 : n-1] + return old[n-1] +} + +type PriorityFrameQueue struct { + queue *frameQueue + c *sync.Cond + size int + nextInsertId uint64 + drain bool +} + +func NewPriorityFrameQueue(size int) *PriorityFrameQueue { + queue := make(frameQueue, 0, size) + heap.Init(&queue) + + return &PriorityFrameQueue{ + queue: &queue, + size: size, + c: sync.NewCond(&sync.Mutex{}), + } +} + +func (q *PriorityFrameQueue) Push(frame spdy.Frame, priority uint8) { + q.c.L.Lock() + defer q.c.L.Unlock() + for q.queue.Len() >= q.size { + q.c.Wait() + } + pFrame := &prioritizedFrame{ + frame: frame, + priority: priority, + insertId: q.nextInsertId, + } + q.nextInsertId = q.nextInsertId + 1 + heap.Push(q.queue, pFrame) + q.c.Signal() +} + +func (q *PriorityFrameQueue) Pop() spdy.Frame { + q.c.L.Lock() + defer q.c.L.Unlock() + for q.queue.Len() == 0 { + if q.drain { + return nil + } + q.c.Wait() + } + frame := heap.Pop(q.queue).(*prioritizedFrame).frame + q.c.Signal() + return frame +} + +func (q *PriorityFrameQueue) Drain() { + q.c.L.Lock() + defer q.c.L.Unlock() + q.drain = true + q.c.Broadcast() +} diff --git a/vendor/github.com/docker/spdystream/priority_test.go b/vendor/github.com/docker/spdystream/priority_test.go new file mode 100644 index 000000000..0746be2a8 --- /dev/null +++ b/vendor/github.com/docker/spdystream/priority_test.go @@ -0,0 +1,108 @@ +package spdystream + +import ( + "sync" + "testing" + "time" + + "github.com/docker/spdystream/spdy" +) + +func TestPriorityQueueOrdering(t *testing.T) { + queue := NewPriorityFrameQueue(150) + data1 := &spdy.DataFrame{} + data2 := &spdy.DataFrame{} + data3 := &spdy.DataFrame{} + data4 := &spdy.DataFrame{} + queue.Push(data1, 2) + queue.Push(data2, 1) + queue.Push(data3, 1) + queue.Push(data4, 0) + + if queue.Pop() != data4 { + t.Fatalf("Wrong order, expected data4 first") + } + if queue.Pop() != data2 { + t.Fatalf("Wrong order, expected data2 second") + } + if queue.Pop() != data3 { + t.Fatalf("Wrong order, expected data3 third") + } + if queue.Pop() != data1 { + t.Fatalf("Wrong order, expected data1 fourth") + } + + // Insert 50 Medium priority frames + for i := spdy.StreamId(50); i < 100; i++ { + queue.Push(&spdy.DataFrame{StreamId: i}, 1) + } + // Insert 50 low priority frames + for i := spdy.StreamId(100); i < 150; i++ { + queue.Push(&spdy.DataFrame{StreamId: i}, 2) + } + // Insert 50 high priority frames + for i := spdy.StreamId(0); i < 50; i++ { + queue.Push(&spdy.DataFrame{StreamId: i}, 0) + } + + for i := spdy.StreamId(0); i < 150; i++ { + frame := queue.Pop() + if frame.(*spdy.DataFrame).StreamId != i { + t.Fatalf("Wrong frame\nActual: %d\nExpecting: %d", frame.(*spdy.DataFrame).StreamId, i) + } + } +} + +func TestPriorityQueueSync(t *testing.T) { + queue := NewPriorityFrameQueue(150) + var wg sync.WaitGroup + insertRange := func(start, stop spdy.StreamId, priority uint8) { + for i := start; i < stop; i++ { + queue.Push(&spdy.DataFrame{StreamId: i}, priority) + } + wg.Done() + } + wg.Add(3) + go insertRange(spdy.StreamId(100), spdy.StreamId(150), 2) + go insertRange(spdy.StreamId(0), spdy.StreamId(50), 0) + go insertRange(spdy.StreamId(50), spdy.StreamId(100), 1) + + wg.Wait() + for i := spdy.StreamId(0); i < 150; i++ { + frame := queue.Pop() + if frame.(*spdy.DataFrame).StreamId != i { + t.Fatalf("Wrong frame\nActual: %d\nExpecting: %d", frame.(*spdy.DataFrame).StreamId, i) + } + } +} + +func TestPriorityQueueBlocking(t *testing.T) { + queue := NewPriorityFrameQueue(15) + for i := 0; i < 15; i++ { + queue.Push(&spdy.DataFrame{}, 2) + } + doneChan := make(chan bool) + go func() { + queue.Push(&spdy.DataFrame{}, 2) + close(doneChan) + }() + select { + case <-doneChan: + t.Fatalf("Push succeeded, expected to block") + case <-time.After(time.Millisecond): + break + } + + queue.Pop() + + select { + case <-doneChan: + break + case <-time.After(time.Millisecond): + t.Fatalf("Push should have succeeded, but timeout reached") + } + + for i := 0; i < 15; i++ { + queue.Pop() + } +} diff --git a/vendor/github.com/docker/spdystream/spdy/dictionary.go b/vendor/github.com/docker/spdystream/spdy/dictionary.go new file mode 100644 index 000000000..5a5ff0e14 --- /dev/null +++ b/vendor/github.com/docker/spdystream/spdy/dictionary.go @@ -0,0 +1,187 @@ +// Copyright 2013 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package spdy + +// headerDictionary is the dictionary sent to the zlib compressor/decompressor. +var headerDictionary = []byte{ + 0x00, 0x00, 0x00, 0x07, 0x6f, 0x70, 0x74, 0x69, + 0x6f, 0x6e, 0x73, 0x00, 0x00, 0x00, 0x04, 0x68, + 0x65, 0x61, 0x64, 0x00, 0x00, 0x00, 0x04, 0x70, + 0x6f, 0x73, 0x74, 0x00, 0x00, 0x00, 0x03, 0x70, + 0x75, 0x74, 0x00, 0x00, 0x00, 0x06, 0x64, 0x65, + 0x6c, 0x65, 0x74, 0x65, 0x00, 0x00, 0x00, 0x05, + 0x74, 0x72, 0x61, 0x63, 0x65, 0x00, 0x00, 0x00, + 0x06, 0x61, 0x63, 0x63, 0x65, 0x70, 0x74, 0x00, + 0x00, 0x00, 0x0e, 0x61, 0x63, 0x63, 0x65, 0x70, + 0x74, 0x2d, 0x63, 0x68, 0x61, 0x72, 0x73, 0x65, + 0x74, 0x00, 0x00, 0x00, 0x0f, 0x61, 0x63, 0x63, + 0x65, 0x70, 0x74, 0x2d, 0x65, 0x6e, 0x63, 0x6f, + 0x64, 0x69, 0x6e, 0x67, 0x00, 0x00, 0x00, 0x0f, + 0x61, 0x63, 0x63, 0x65, 0x70, 0x74, 0x2d, 0x6c, + 0x61, 0x6e, 0x67, 0x75, 0x61, 0x67, 0x65, 0x00, + 0x00, 0x00, 0x0d, 0x61, 0x63, 0x63, 0x65, 0x70, + 0x74, 0x2d, 0x72, 0x61, 0x6e, 0x67, 0x65, 0x73, + 0x00, 0x00, 0x00, 0x03, 0x61, 0x67, 0x65, 0x00, + 0x00, 0x00, 0x05, 0x61, 0x6c, 0x6c, 0x6f, 0x77, + 0x00, 0x00, 0x00, 0x0d, 0x61, 0x75, 0x74, 0x68, + 0x6f, 0x72, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, + 0x6e, 0x00, 0x00, 0x00, 0x0d, 0x63, 0x61, 0x63, + 0x68, 0x65, 0x2d, 0x63, 0x6f, 0x6e, 0x74, 0x72, + 0x6f, 0x6c, 0x00, 0x00, 0x00, 0x0a, 0x63, 0x6f, + 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, + 0x00, 0x00, 0x00, 0x0c, 0x63, 0x6f, 0x6e, 0x74, + 0x65, 0x6e, 0x74, 0x2d, 0x62, 0x61, 0x73, 0x65, + 0x00, 0x00, 0x00, 0x10, 0x63, 0x6f, 0x6e, 0x74, + 0x65, 0x6e, 0x74, 0x2d, 0x65, 0x6e, 0x63, 0x6f, + 0x64, 0x69, 0x6e, 0x67, 0x00, 0x00, 0x00, 0x10, + 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x2d, + 0x6c, 0x61, 0x6e, 0x67, 0x75, 0x61, 0x67, 0x65, + 0x00, 0x00, 0x00, 0x0e, 0x63, 0x6f, 0x6e, 0x74, + 0x65, 0x6e, 0x74, 0x2d, 0x6c, 0x65, 0x6e, 0x67, + 0x74, 0x68, 0x00, 0x00, 0x00, 0x10, 0x63, 0x6f, + 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x2d, 0x6c, 0x6f, + 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x00, 0x00, + 0x00, 0x0b, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, + 0x74, 0x2d, 0x6d, 0x64, 0x35, 0x00, 0x00, 0x00, + 0x0d, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, + 0x2d, 0x72, 0x61, 0x6e, 0x67, 0x65, 0x00, 0x00, + 0x00, 0x0c, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, + 0x74, 0x2d, 0x74, 0x79, 0x70, 0x65, 0x00, 0x00, + 0x00, 0x04, 0x64, 0x61, 0x74, 0x65, 0x00, 0x00, + 0x00, 0x04, 0x65, 0x74, 0x61, 0x67, 0x00, 0x00, + 0x00, 0x06, 0x65, 0x78, 0x70, 0x65, 0x63, 0x74, + 0x00, 0x00, 0x00, 0x07, 0x65, 0x78, 0x70, 0x69, + 0x72, 0x65, 0x73, 0x00, 0x00, 0x00, 0x04, 0x66, + 0x72, 0x6f, 0x6d, 0x00, 0x00, 0x00, 0x04, 0x68, + 0x6f, 0x73, 0x74, 0x00, 0x00, 0x00, 0x08, 0x69, + 0x66, 0x2d, 0x6d, 0x61, 0x74, 0x63, 0x68, 0x00, + 0x00, 0x00, 0x11, 0x69, 0x66, 0x2d, 0x6d, 0x6f, + 0x64, 0x69, 0x66, 0x69, 0x65, 0x64, 0x2d, 0x73, + 0x69, 0x6e, 0x63, 0x65, 0x00, 0x00, 0x00, 0x0d, + 0x69, 0x66, 0x2d, 0x6e, 0x6f, 0x6e, 0x65, 0x2d, + 0x6d, 0x61, 0x74, 0x63, 0x68, 0x00, 0x00, 0x00, + 0x08, 0x69, 0x66, 0x2d, 0x72, 0x61, 0x6e, 0x67, + 0x65, 0x00, 0x00, 0x00, 0x13, 0x69, 0x66, 0x2d, + 0x75, 0x6e, 0x6d, 0x6f, 0x64, 0x69, 0x66, 0x69, + 0x65, 0x64, 0x2d, 0x73, 0x69, 0x6e, 0x63, 0x65, + 0x00, 0x00, 0x00, 0x0d, 0x6c, 0x61, 0x73, 0x74, + 0x2d, 0x6d, 0x6f, 0x64, 0x69, 0x66, 0x69, 0x65, + 0x64, 0x00, 0x00, 0x00, 0x08, 0x6c, 0x6f, 0x63, + 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x00, 0x00, 0x00, + 0x0c, 0x6d, 0x61, 0x78, 0x2d, 0x66, 0x6f, 0x72, + 0x77, 0x61, 0x72, 0x64, 0x73, 0x00, 0x00, 0x00, + 0x06, 0x70, 0x72, 0x61, 0x67, 0x6d, 0x61, 0x00, + 0x00, 0x00, 0x12, 0x70, 0x72, 0x6f, 0x78, 0x79, + 0x2d, 0x61, 0x75, 0x74, 0x68, 0x65, 0x6e, 0x74, + 0x69, 0x63, 0x61, 0x74, 0x65, 0x00, 0x00, 0x00, + 0x13, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x2d, 0x61, + 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x61, + 0x74, 0x69, 0x6f, 0x6e, 0x00, 0x00, 0x00, 0x05, + 0x72, 0x61, 0x6e, 0x67, 0x65, 0x00, 0x00, 0x00, + 0x07, 0x72, 0x65, 0x66, 0x65, 0x72, 0x65, 0x72, + 0x00, 0x00, 0x00, 0x0b, 0x72, 0x65, 0x74, 0x72, + 0x79, 0x2d, 0x61, 0x66, 0x74, 0x65, 0x72, 0x00, + 0x00, 0x00, 0x06, 0x73, 0x65, 0x72, 0x76, 0x65, + 0x72, 0x00, 0x00, 0x00, 0x02, 0x74, 0x65, 0x00, + 0x00, 0x00, 0x07, 0x74, 0x72, 0x61, 0x69, 0x6c, + 0x65, 0x72, 0x00, 0x00, 0x00, 0x11, 0x74, 0x72, + 0x61, 0x6e, 0x73, 0x66, 0x65, 0x72, 0x2d, 0x65, + 0x6e, 0x63, 0x6f, 0x64, 0x69, 0x6e, 0x67, 0x00, + 0x00, 0x00, 0x07, 0x75, 0x70, 0x67, 0x72, 0x61, + 0x64, 0x65, 0x00, 0x00, 0x00, 0x0a, 0x75, 0x73, + 0x65, 0x72, 0x2d, 0x61, 0x67, 0x65, 0x6e, 0x74, + 0x00, 0x00, 0x00, 0x04, 0x76, 0x61, 0x72, 0x79, + 0x00, 0x00, 0x00, 0x03, 0x76, 0x69, 0x61, 0x00, + 0x00, 0x00, 0x07, 0x77, 0x61, 0x72, 0x6e, 0x69, + 0x6e, 0x67, 0x00, 0x00, 0x00, 0x10, 0x77, 0x77, + 0x77, 0x2d, 0x61, 0x75, 0x74, 0x68, 0x65, 0x6e, + 0x74, 0x69, 0x63, 0x61, 0x74, 0x65, 0x00, 0x00, + 0x00, 0x06, 0x6d, 0x65, 0x74, 0x68, 0x6f, 0x64, + 0x00, 0x00, 0x00, 0x03, 0x67, 0x65, 0x74, 0x00, + 0x00, 0x00, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, + 0x73, 0x00, 0x00, 0x00, 0x06, 0x32, 0x30, 0x30, + 0x20, 0x4f, 0x4b, 0x00, 0x00, 0x00, 0x07, 0x76, + 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x00, 0x00, + 0x00, 0x08, 0x48, 0x54, 0x54, 0x50, 0x2f, 0x31, + 0x2e, 0x31, 0x00, 0x00, 0x00, 0x03, 0x75, 0x72, + 0x6c, 0x00, 0x00, 0x00, 0x06, 0x70, 0x75, 0x62, + 0x6c, 0x69, 0x63, 0x00, 0x00, 0x00, 0x0a, 0x73, + 0x65, 0x74, 0x2d, 0x63, 0x6f, 0x6f, 0x6b, 0x69, + 0x65, 0x00, 0x00, 0x00, 0x0a, 0x6b, 0x65, 0x65, + 0x70, 0x2d, 0x61, 0x6c, 0x69, 0x76, 0x65, 0x00, + 0x00, 0x00, 0x06, 0x6f, 0x72, 0x69, 0x67, 0x69, + 0x6e, 0x31, 0x30, 0x30, 0x31, 0x30, 0x31, 0x32, + 0x30, 0x31, 0x32, 0x30, 0x32, 0x32, 0x30, 0x35, + 0x32, 0x30, 0x36, 0x33, 0x30, 0x30, 0x33, 0x30, + 0x32, 0x33, 0x30, 0x33, 0x33, 0x30, 0x34, 0x33, + 0x30, 0x35, 0x33, 0x30, 0x36, 0x33, 0x30, 0x37, + 0x34, 0x30, 0x32, 0x34, 0x30, 0x35, 0x34, 0x30, + 0x36, 0x34, 0x30, 0x37, 0x34, 0x30, 0x38, 0x34, + 0x30, 0x39, 0x34, 0x31, 0x30, 0x34, 0x31, 0x31, + 0x34, 0x31, 0x32, 0x34, 0x31, 0x33, 0x34, 0x31, + 0x34, 0x34, 0x31, 0x35, 0x34, 0x31, 0x36, 0x34, + 0x31, 0x37, 0x35, 0x30, 0x32, 0x35, 0x30, 0x34, + 0x35, 0x30, 0x35, 0x32, 0x30, 0x33, 0x20, 0x4e, + 0x6f, 0x6e, 0x2d, 0x41, 0x75, 0x74, 0x68, 0x6f, + 0x72, 0x69, 0x74, 0x61, 0x74, 0x69, 0x76, 0x65, + 0x20, 0x49, 0x6e, 0x66, 0x6f, 0x72, 0x6d, 0x61, + 0x74, 0x69, 0x6f, 0x6e, 0x32, 0x30, 0x34, 0x20, + 0x4e, 0x6f, 0x20, 0x43, 0x6f, 0x6e, 0x74, 0x65, + 0x6e, 0x74, 0x33, 0x30, 0x31, 0x20, 0x4d, 0x6f, + 0x76, 0x65, 0x64, 0x20, 0x50, 0x65, 0x72, 0x6d, + 0x61, 0x6e, 0x65, 0x6e, 0x74, 0x6c, 0x79, 0x34, + 0x30, 0x30, 0x20, 0x42, 0x61, 0x64, 0x20, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x34, 0x30, + 0x31, 0x20, 0x55, 0x6e, 0x61, 0x75, 0x74, 0x68, + 0x6f, 0x72, 0x69, 0x7a, 0x65, 0x64, 0x34, 0x30, + 0x33, 0x20, 0x46, 0x6f, 0x72, 0x62, 0x69, 0x64, + 0x64, 0x65, 0x6e, 0x34, 0x30, 0x34, 0x20, 0x4e, + 0x6f, 0x74, 0x20, 0x46, 0x6f, 0x75, 0x6e, 0x64, + 0x35, 0x30, 0x30, 0x20, 0x49, 0x6e, 0x74, 0x65, + 0x72, 0x6e, 0x61, 0x6c, 0x20, 0x53, 0x65, 0x72, + 0x76, 0x65, 0x72, 0x20, 0x45, 0x72, 0x72, 0x6f, + 0x72, 0x35, 0x30, 0x31, 0x20, 0x4e, 0x6f, 0x74, + 0x20, 0x49, 0x6d, 0x70, 0x6c, 0x65, 0x6d, 0x65, + 0x6e, 0x74, 0x65, 0x64, 0x35, 0x30, 0x33, 0x20, + 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x20, + 0x55, 0x6e, 0x61, 0x76, 0x61, 0x69, 0x6c, 0x61, + 0x62, 0x6c, 0x65, 0x4a, 0x61, 0x6e, 0x20, 0x46, + 0x65, 0x62, 0x20, 0x4d, 0x61, 0x72, 0x20, 0x41, + 0x70, 0x72, 0x20, 0x4d, 0x61, 0x79, 0x20, 0x4a, + 0x75, 0x6e, 0x20, 0x4a, 0x75, 0x6c, 0x20, 0x41, + 0x75, 0x67, 0x20, 0x53, 0x65, 0x70, 0x74, 0x20, + 0x4f, 0x63, 0x74, 0x20, 0x4e, 0x6f, 0x76, 0x20, + 0x44, 0x65, 0x63, 0x20, 0x30, 0x30, 0x3a, 0x30, + 0x30, 0x3a, 0x30, 0x30, 0x20, 0x4d, 0x6f, 0x6e, + 0x2c, 0x20, 0x54, 0x75, 0x65, 0x2c, 0x20, 0x57, + 0x65, 0x64, 0x2c, 0x20, 0x54, 0x68, 0x75, 0x2c, + 0x20, 0x46, 0x72, 0x69, 0x2c, 0x20, 0x53, 0x61, + 0x74, 0x2c, 0x20, 0x53, 0x75, 0x6e, 0x2c, 0x20, + 0x47, 0x4d, 0x54, 0x63, 0x68, 0x75, 0x6e, 0x6b, + 0x65, 0x64, 0x2c, 0x74, 0x65, 0x78, 0x74, 0x2f, + 0x68, 0x74, 0x6d, 0x6c, 0x2c, 0x69, 0x6d, 0x61, + 0x67, 0x65, 0x2f, 0x70, 0x6e, 0x67, 0x2c, 0x69, + 0x6d, 0x61, 0x67, 0x65, 0x2f, 0x6a, 0x70, 0x67, + 0x2c, 0x69, 0x6d, 0x61, 0x67, 0x65, 0x2f, 0x67, + 0x69, 0x66, 0x2c, 0x61, 0x70, 0x70, 0x6c, 0x69, + 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2f, 0x78, + 0x6d, 0x6c, 0x2c, 0x61, 0x70, 0x70, 0x6c, 0x69, + 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2f, 0x78, + 0x68, 0x74, 0x6d, 0x6c, 0x2b, 0x78, 0x6d, 0x6c, + 0x2c, 0x74, 0x65, 0x78, 0x74, 0x2f, 0x70, 0x6c, + 0x61, 0x69, 0x6e, 0x2c, 0x74, 0x65, 0x78, 0x74, + 0x2f, 0x6a, 0x61, 0x76, 0x61, 0x73, 0x63, 0x72, + 0x69, 0x70, 0x74, 0x2c, 0x70, 0x75, 0x62, 0x6c, + 0x69, 0x63, 0x70, 0x72, 0x69, 0x76, 0x61, 0x74, + 0x65, 0x6d, 0x61, 0x78, 0x2d, 0x61, 0x67, 0x65, + 0x3d, 0x67, 0x7a, 0x69, 0x70, 0x2c, 0x64, 0x65, + 0x66, 0x6c, 0x61, 0x74, 0x65, 0x2c, 0x73, 0x64, + 0x63, 0x68, 0x63, 0x68, 0x61, 0x72, 0x73, 0x65, + 0x74, 0x3d, 0x75, 0x74, 0x66, 0x2d, 0x38, 0x63, + 0x68, 0x61, 0x72, 0x73, 0x65, 0x74, 0x3d, 0x69, + 0x73, 0x6f, 0x2d, 0x38, 0x38, 0x35, 0x39, 0x2d, + 0x31, 0x2c, 0x75, 0x74, 0x66, 0x2d, 0x2c, 0x2a, + 0x2c, 0x65, 0x6e, 0x71, 0x3d, 0x30, 0x2e, +} diff --git a/vendor/github.com/docker/spdystream/spdy/read.go b/vendor/github.com/docker/spdystream/spdy/read.go new file mode 100644 index 000000000..9359a9501 --- /dev/null +++ b/vendor/github.com/docker/spdystream/spdy/read.go @@ -0,0 +1,348 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package spdy + +import ( + "compress/zlib" + "encoding/binary" + "io" + "net/http" + "strings" +) + +func (frame *SynStreamFrame) read(h ControlFrameHeader, f *Framer) error { + return f.readSynStreamFrame(h, frame) +} + +func (frame *SynReplyFrame) read(h ControlFrameHeader, f *Framer) error { + return f.readSynReplyFrame(h, frame) +} + +func (frame *RstStreamFrame) read(h ControlFrameHeader, f *Framer) error { + frame.CFHeader = h + if err := binary.Read(f.r, binary.BigEndian, &frame.StreamId); err != nil { + return err + } + if err := binary.Read(f.r, binary.BigEndian, &frame.Status); err != nil { + return err + } + if frame.Status == 0 { + return &Error{InvalidControlFrame, frame.StreamId} + } + if frame.StreamId == 0 { + return &Error{ZeroStreamId, 0} + } + return nil +} + +func (frame *SettingsFrame) read(h ControlFrameHeader, f *Framer) error { + frame.CFHeader = h + var numSettings uint32 + if err := binary.Read(f.r, binary.BigEndian, &numSettings); err != nil { + return err + } + frame.FlagIdValues = make([]SettingsFlagIdValue, numSettings) + for i := uint32(0); i < numSettings; i++ { + if err := binary.Read(f.r, binary.BigEndian, &frame.FlagIdValues[i].Id); err != nil { + return err + } + frame.FlagIdValues[i].Flag = SettingsFlag((frame.FlagIdValues[i].Id & 0xff000000) >> 24) + frame.FlagIdValues[i].Id &= 0xffffff + if err := binary.Read(f.r, binary.BigEndian, &frame.FlagIdValues[i].Value); err != nil { + return err + } + } + return nil +} + +func (frame *PingFrame) read(h ControlFrameHeader, f *Framer) error { + frame.CFHeader = h + if err := binary.Read(f.r, binary.BigEndian, &frame.Id); err != nil { + return err + } + if frame.Id == 0 { + return &Error{ZeroStreamId, 0} + } + if frame.CFHeader.Flags != 0 { + return &Error{InvalidControlFrame, StreamId(frame.Id)} + } + return nil +} + +func (frame *GoAwayFrame) read(h ControlFrameHeader, f *Framer) error { + frame.CFHeader = h + if err := binary.Read(f.r, binary.BigEndian, &frame.LastGoodStreamId); err != nil { + return err + } + if frame.CFHeader.Flags != 0 { + return &Error{InvalidControlFrame, frame.LastGoodStreamId} + } + if frame.CFHeader.length != 8 { + return &Error{InvalidControlFrame, frame.LastGoodStreamId} + } + if err := binary.Read(f.r, binary.BigEndian, &frame.Status); err != nil { + return err + } + return nil +} + +func (frame *HeadersFrame) read(h ControlFrameHeader, f *Framer) error { + return f.readHeadersFrame(h, frame) +} + +func (frame *WindowUpdateFrame) read(h ControlFrameHeader, f *Framer) error { + frame.CFHeader = h + if err := binary.Read(f.r, binary.BigEndian, &frame.StreamId); err != nil { + return err + } + if frame.CFHeader.Flags != 0 { + return &Error{InvalidControlFrame, frame.StreamId} + } + if frame.CFHeader.length != 8 { + return &Error{InvalidControlFrame, frame.StreamId} + } + if err := binary.Read(f.r, binary.BigEndian, &frame.DeltaWindowSize); err != nil { + return err + } + return nil +} + +func newControlFrame(frameType ControlFrameType) (controlFrame, error) { + ctor, ok := cframeCtor[frameType] + if !ok { + return nil, &Error{Err: InvalidControlFrame} + } + return ctor(), nil +} + +var cframeCtor = map[ControlFrameType]func() controlFrame{ + TypeSynStream: func() controlFrame { return new(SynStreamFrame) }, + TypeSynReply: func() controlFrame { return new(SynReplyFrame) }, + TypeRstStream: func() controlFrame { return new(RstStreamFrame) }, + TypeSettings: func() controlFrame { return new(SettingsFrame) }, + TypePing: func() controlFrame { return new(PingFrame) }, + TypeGoAway: func() controlFrame { return new(GoAwayFrame) }, + TypeHeaders: func() controlFrame { return new(HeadersFrame) }, + TypeWindowUpdate: func() controlFrame { return new(WindowUpdateFrame) }, +} + +func (f *Framer) uncorkHeaderDecompressor(payloadSize int64) error { + if f.headerDecompressor != nil { + f.headerReader.N = payloadSize + return nil + } + f.headerReader = io.LimitedReader{R: f.r, N: payloadSize} + decompressor, err := zlib.NewReaderDict(&f.headerReader, []byte(headerDictionary)) + if err != nil { + return err + } + f.headerDecompressor = decompressor + return nil +} + +// ReadFrame reads SPDY encoded data and returns a decompressed Frame. +func (f *Framer) ReadFrame() (Frame, error) { + var firstWord uint32 + if err := binary.Read(f.r, binary.BigEndian, &firstWord); err != nil { + return nil, err + } + if firstWord&0x80000000 != 0 { + frameType := ControlFrameType(firstWord & 0xffff) + version := uint16(firstWord >> 16 & 0x7fff) + return f.parseControlFrame(version, frameType) + } + return f.parseDataFrame(StreamId(firstWord & 0x7fffffff)) +} + +func (f *Framer) parseControlFrame(version uint16, frameType ControlFrameType) (Frame, error) { + var length uint32 + if err := binary.Read(f.r, binary.BigEndian, &length); err != nil { + return nil, err + } + flags := ControlFlags((length & 0xff000000) >> 24) + length &= 0xffffff + header := ControlFrameHeader{version, frameType, flags, length} + cframe, err := newControlFrame(frameType) + if err != nil { + return nil, err + } + if err = cframe.read(header, f); err != nil { + return nil, err + } + return cframe, nil +} + +func parseHeaderValueBlock(r io.Reader, streamId StreamId) (http.Header, error) { + var numHeaders uint32 + if err := binary.Read(r, binary.BigEndian, &numHeaders); err != nil { + return nil, err + } + var e error + h := make(http.Header, int(numHeaders)) + for i := 0; i < int(numHeaders); i++ { + var length uint32 + if err := binary.Read(r, binary.BigEndian, &length); err != nil { + return nil, err + } + nameBytes := make([]byte, length) + if _, err := io.ReadFull(r, nameBytes); err != nil { + return nil, err + } + name := string(nameBytes) + if name != strings.ToLower(name) { + e = &Error{UnlowercasedHeaderName, streamId} + name = strings.ToLower(name) + } + if h[name] != nil { + e = &Error{DuplicateHeaders, streamId} + } + if err := binary.Read(r, binary.BigEndian, &length); err != nil { + return nil, err + } + value := make([]byte, length) + if _, err := io.ReadFull(r, value); err != nil { + return nil, err + } + valueList := strings.Split(string(value), headerValueSeparator) + for _, v := range valueList { + h.Add(name, v) + } + } + if e != nil { + return h, e + } + return h, nil +} + +func (f *Framer) readSynStreamFrame(h ControlFrameHeader, frame *SynStreamFrame) error { + frame.CFHeader = h + var err error + if err = binary.Read(f.r, binary.BigEndian, &frame.StreamId); err != nil { + return err + } + if err = binary.Read(f.r, binary.BigEndian, &frame.AssociatedToStreamId); err != nil { + return err + } + if err = binary.Read(f.r, binary.BigEndian, &frame.Priority); err != nil { + return err + } + frame.Priority >>= 5 + if err = binary.Read(f.r, binary.BigEndian, &frame.Slot); err != nil { + return err + } + reader := f.r + if !f.headerCompressionDisabled { + err := f.uncorkHeaderDecompressor(int64(h.length - 10)) + if err != nil { + return err + } + reader = f.headerDecompressor + } + frame.Headers, err = parseHeaderValueBlock(reader, frame.StreamId) + if !f.headerCompressionDisabled && (err == io.EOF && f.headerReader.N == 0 || f.headerReader.N != 0) { + err = &Error{WrongCompressedPayloadSize, 0} + } + if err != nil { + return err + } + for h := range frame.Headers { + if invalidReqHeaders[h] { + return &Error{InvalidHeaderPresent, frame.StreamId} + } + } + if frame.StreamId == 0 { + return &Error{ZeroStreamId, 0} + } + return nil +} + +func (f *Framer) readSynReplyFrame(h ControlFrameHeader, frame *SynReplyFrame) error { + frame.CFHeader = h + var err error + if err = binary.Read(f.r, binary.BigEndian, &frame.StreamId); err != nil { + return err + } + reader := f.r + if !f.headerCompressionDisabled { + err := f.uncorkHeaderDecompressor(int64(h.length - 4)) + if err != nil { + return err + } + reader = f.headerDecompressor + } + frame.Headers, err = parseHeaderValueBlock(reader, frame.StreamId) + if !f.headerCompressionDisabled && (err == io.EOF && f.headerReader.N == 0 || f.headerReader.N != 0) { + err = &Error{WrongCompressedPayloadSize, 0} + } + if err != nil { + return err + } + for h := range frame.Headers { + if invalidRespHeaders[h] { + return &Error{InvalidHeaderPresent, frame.StreamId} + } + } + if frame.StreamId == 0 { + return &Error{ZeroStreamId, 0} + } + return nil +} + +func (f *Framer) readHeadersFrame(h ControlFrameHeader, frame *HeadersFrame) error { + frame.CFHeader = h + var err error + if err = binary.Read(f.r, binary.BigEndian, &frame.StreamId); err != nil { + return err + } + reader := f.r + if !f.headerCompressionDisabled { + err := f.uncorkHeaderDecompressor(int64(h.length - 4)) + if err != nil { + return err + } + reader = f.headerDecompressor + } + frame.Headers, err = parseHeaderValueBlock(reader, frame.StreamId) + if !f.headerCompressionDisabled && (err == io.EOF && f.headerReader.N == 0 || f.headerReader.N != 0) { + err = &Error{WrongCompressedPayloadSize, 0} + } + if err != nil { + return err + } + var invalidHeaders map[string]bool + if frame.StreamId%2 == 0 { + invalidHeaders = invalidReqHeaders + } else { + invalidHeaders = invalidRespHeaders + } + for h := range frame.Headers { + if invalidHeaders[h] { + return &Error{InvalidHeaderPresent, frame.StreamId} + } + } + if frame.StreamId == 0 { + return &Error{ZeroStreamId, 0} + } + return nil +} + +func (f *Framer) parseDataFrame(streamId StreamId) (*DataFrame, error) { + var length uint32 + if err := binary.Read(f.r, binary.BigEndian, &length); err != nil { + return nil, err + } + var frame DataFrame + frame.StreamId = streamId + frame.Flags = DataFlags(length >> 24) + length &= 0xffffff + frame.Data = make([]byte, length) + if _, err := io.ReadFull(f.r, frame.Data); err != nil { + return nil, err + } + if frame.StreamId == 0 { + return nil, &Error{ZeroStreamId, 0} + } + return &frame, nil +} diff --git a/vendor/github.com/docker/spdystream/spdy/spdy_test.go b/vendor/github.com/docker/spdystream/spdy/spdy_test.go new file mode 100644 index 000000000..ce581f1d0 --- /dev/null +++ b/vendor/github.com/docker/spdystream/spdy/spdy_test.go @@ -0,0 +1,644 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package spdy + +import ( + "bytes" + "compress/zlib" + "encoding/base64" + "io" + "io/ioutil" + "net/http" + "reflect" + "testing" +) + +var HeadersFixture = http.Header{ + "Url": []string{"http://www.google.com/"}, + "Method": []string{"get"}, + "Version": []string{"http/1.1"}, +} + +func TestHeaderParsing(t *testing.T) { + var headerValueBlockBuf bytes.Buffer + writeHeaderValueBlock(&headerValueBlockBuf, HeadersFixture) + const bogusStreamId = 1 + newHeaders, err := parseHeaderValueBlock(&headerValueBlockBuf, bogusStreamId) + if err != nil { + t.Fatal("parseHeaderValueBlock:", err) + } + if !reflect.DeepEqual(HeadersFixture, newHeaders) { + t.Fatal("got: ", newHeaders, "\nwant: ", HeadersFixture) + } +} + +func TestCreateParseSynStreamFrameCompressionDisable(t *testing.T) { + buffer := new(bytes.Buffer) + // Fixture framer for no compression test. + framer := &Framer{ + headerCompressionDisabled: true, + w: buffer, + headerBuf: new(bytes.Buffer), + r: buffer, + } + synStreamFrame := SynStreamFrame{ + CFHeader: ControlFrameHeader{ + version: Version, + frameType: TypeSynStream, + }, + StreamId: 2, + Headers: HeadersFixture, + } + if err := framer.WriteFrame(&synStreamFrame); err != nil { + t.Fatal("WriteFrame without compression:", err) + } + frame, err := framer.ReadFrame() + if err != nil { + t.Fatal("ReadFrame without compression:", err) + } + parsedSynStreamFrame, ok := frame.(*SynStreamFrame) + if !ok { + t.Fatal("Parsed incorrect frame type:", frame) + } + if !reflect.DeepEqual(synStreamFrame, *parsedSynStreamFrame) { + t.Fatal("got: ", *parsedSynStreamFrame, "\nwant: ", synStreamFrame) + } +} + +func TestCreateParseSynStreamFrameCompressionEnable(t *testing.T) { + buffer := new(bytes.Buffer) + framer, err := NewFramer(buffer, buffer) + synStreamFrame := SynStreamFrame{ + CFHeader: ControlFrameHeader{ + version: Version, + frameType: TypeSynStream, + }, + StreamId: 2, + Headers: HeadersFixture, + } + if err != nil { + t.Fatal("Failed to create new framer:", err) + } + if err := framer.WriteFrame(&synStreamFrame); err != nil { + t.Fatal("WriteFrame with compression:", err) + } + frame, err := framer.ReadFrame() + if err != nil { + t.Fatal("ReadFrame with compression:", err) + } + parsedSynStreamFrame, ok := frame.(*SynStreamFrame) + if !ok { + t.Fatal("Parsed incorrect frame type:", frame) + } + if !reflect.DeepEqual(synStreamFrame, *parsedSynStreamFrame) { + t.Fatal("got: ", *parsedSynStreamFrame, "\nwant: ", synStreamFrame) + } +} + +func TestCreateParseSynReplyFrameCompressionDisable(t *testing.T) { + buffer := new(bytes.Buffer) + framer := &Framer{ + headerCompressionDisabled: true, + w: buffer, + headerBuf: new(bytes.Buffer), + r: buffer, + } + synReplyFrame := SynReplyFrame{ + CFHeader: ControlFrameHeader{ + version: Version, + frameType: TypeSynReply, + }, + StreamId: 2, + Headers: HeadersFixture, + } + if err := framer.WriteFrame(&synReplyFrame); err != nil { + t.Fatal("WriteFrame without compression:", err) + } + frame, err := framer.ReadFrame() + if err != nil { + t.Fatal("ReadFrame without compression:", err) + } + parsedSynReplyFrame, ok := frame.(*SynReplyFrame) + if !ok { + t.Fatal("Parsed incorrect frame type:", frame) + } + if !reflect.DeepEqual(synReplyFrame, *parsedSynReplyFrame) { + t.Fatal("got: ", *parsedSynReplyFrame, "\nwant: ", synReplyFrame) + } +} + +func TestCreateParseSynReplyFrameCompressionEnable(t *testing.T) { + buffer := new(bytes.Buffer) + framer, err := NewFramer(buffer, buffer) + synReplyFrame := SynReplyFrame{ + CFHeader: ControlFrameHeader{ + version: Version, + frameType: TypeSynReply, + }, + StreamId: 2, + Headers: HeadersFixture, + } + if err != nil { + t.Fatal("Failed to create new framer:", err) + } + if err := framer.WriteFrame(&synReplyFrame); err != nil { + t.Fatal("WriteFrame with compression:", err) + } + frame, err := framer.ReadFrame() + if err != nil { + t.Fatal("ReadFrame with compression:", err) + } + parsedSynReplyFrame, ok := frame.(*SynReplyFrame) + if !ok { + t.Fatal("Parsed incorrect frame type:", frame) + } + if !reflect.DeepEqual(synReplyFrame, *parsedSynReplyFrame) { + t.Fatal("got: ", *parsedSynReplyFrame, "\nwant: ", synReplyFrame) + } +} + +func TestCreateParseRstStream(t *testing.T) { + buffer := new(bytes.Buffer) + framer, err := NewFramer(buffer, buffer) + if err != nil { + t.Fatal("Failed to create new framer:", err) + } + rstStreamFrame := RstStreamFrame{ + CFHeader: ControlFrameHeader{ + version: Version, + frameType: TypeRstStream, + }, + StreamId: 1, + Status: InvalidStream, + } + if err := framer.WriteFrame(&rstStreamFrame); err != nil { + t.Fatal("WriteFrame:", err) + } + frame, err := framer.ReadFrame() + if err != nil { + t.Fatal("ReadFrame:", err) + } + parsedRstStreamFrame, ok := frame.(*RstStreamFrame) + if !ok { + t.Fatal("Parsed incorrect frame type:", frame) + } + if !reflect.DeepEqual(rstStreamFrame, *parsedRstStreamFrame) { + t.Fatal("got: ", *parsedRstStreamFrame, "\nwant: ", rstStreamFrame) + } +} + +func TestCreateParseSettings(t *testing.T) { + buffer := new(bytes.Buffer) + framer, err := NewFramer(buffer, buffer) + if err != nil { + t.Fatal("Failed to create new framer:", err) + } + settingsFrame := SettingsFrame{ + CFHeader: ControlFrameHeader{ + version: Version, + frameType: TypeSettings, + }, + FlagIdValues: []SettingsFlagIdValue{ + {FlagSettingsPersistValue, SettingsCurrentCwnd, 10}, + {FlagSettingsPersisted, SettingsUploadBandwidth, 1}, + }, + } + if err := framer.WriteFrame(&settingsFrame); err != nil { + t.Fatal("WriteFrame:", err) + } + frame, err := framer.ReadFrame() + if err != nil { + t.Fatal("ReadFrame:", err) + } + parsedSettingsFrame, ok := frame.(*SettingsFrame) + if !ok { + t.Fatal("Parsed incorrect frame type:", frame) + } + if !reflect.DeepEqual(settingsFrame, *parsedSettingsFrame) { + t.Fatal("got: ", *parsedSettingsFrame, "\nwant: ", settingsFrame) + } +} + +func TestCreateParsePing(t *testing.T) { + buffer := new(bytes.Buffer) + framer, err := NewFramer(buffer, buffer) + if err != nil { + t.Fatal("Failed to create new framer:", err) + } + pingFrame := PingFrame{ + CFHeader: ControlFrameHeader{ + version: Version, + frameType: TypePing, + }, + Id: 31337, + } + if err := framer.WriteFrame(&pingFrame); err != nil { + t.Fatal("WriteFrame:", err) + } + if pingFrame.CFHeader.Flags != 0 { + t.Fatal("Incorrect frame type:", pingFrame) + } + frame, err := framer.ReadFrame() + if err != nil { + t.Fatal("ReadFrame:", err) + } + parsedPingFrame, ok := frame.(*PingFrame) + if !ok { + t.Fatal("Parsed incorrect frame type:", frame) + } + if parsedPingFrame.CFHeader.Flags != 0 { + t.Fatal("Parsed incorrect frame type:", parsedPingFrame) + } + if !reflect.DeepEqual(pingFrame, *parsedPingFrame) { + t.Fatal("got: ", *parsedPingFrame, "\nwant: ", pingFrame) + } +} + +func TestCreateParseGoAway(t *testing.T) { + buffer := new(bytes.Buffer) + framer, err := NewFramer(buffer, buffer) + if err != nil { + t.Fatal("Failed to create new framer:", err) + } + goAwayFrame := GoAwayFrame{ + CFHeader: ControlFrameHeader{ + version: Version, + frameType: TypeGoAway, + }, + LastGoodStreamId: 31337, + Status: 1, + } + if err := framer.WriteFrame(&goAwayFrame); err != nil { + t.Fatal("WriteFrame:", err) + } + if goAwayFrame.CFHeader.Flags != 0 { + t.Fatal("Incorrect frame type:", goAwayFrame) + } + if goAwayFrame.CFHeader.length != 8 { + t.Fatal("Incorrect frame type:", goAwayFrame) + } + frame, err := framer.ReadFrame() + if err != nil { + t.Fatal("ReadFrame:", err) + } + parsedGoAwayFrame, ok := frame.(*GoAwayFrame) + if !ok { + t.Fatal("Parsed incorrect frame type:", frame) + } + if parsedGoAwayFrame.CFHeader.Flags != 0 { + t.Fatal("Incorrect frame type:", parsedGoAwayFrame) + } + if parsedGoAwayFrame.CFHeader.length != 8 { + t.Fatal("Incorrect frame type:", parsedGoAwayFrame) + } + if !reflect.DeepEqual(goAwayFrame, *parsedGoAwayFrame) { + t.Fatal("got: ", *parsedGoAwayFrame, "\nwant: ", goAwayFrame) + } +} + +func TestCreateParseHeadersFrame(t *testing.T) { + buffer := new(bytes.Buffer) + framer := &Framer{ + headerCompressionDisabled: true, + w: buffer, + headerBuf: new(bytes.Buffer), + r: buffer, + } + headersFrame := HeadersFrame{ + CFHeader: ControlFrameHeader{ + version: Version, + frameType: TypeHeaders, + }, + StreamId: 2, + } + headersFrame.Headers = HeadersFixture + if err := framer.WriteFrame(&headersFrame); err != nil { + t.Fatal("WriteFrame without compression:", err) + } + frame, err := framer.ReadFrame() + if err != nil { + t.Fatal("ReadFrame without compression:", err) + } + parsedHeadersFrame, ok := frame.(*HeadersFrame) + if !ok { + t.Fatal("Parsed incorrect frame type:", frame) + } + if !reflect.DeepEqual(headersFrame, *parsedHeadersFrame) { + t.Fatal("got: ", *parsedHeadersFrame, "\nwant: ", headersFrame) + } +} + +func TestCreateParseHeadersFrameCompressionEnable(t *testing.T) { + buffer := new(bytes.Buffer) + headersFrame := HeadersFrame{ + CFHeader: ControlFrameHeader{ + version: Version, + frameType: TypeHeaders, + }, + StreamId: 2, + } + headersFrame.Headers = HeadersFixture + + framer, err := NewFramer(buffer, buffer) + if err := framer.WriteFrame(&headersFrame); err != nil { + t.Fatal("WriteFrame with compression:", err) + } + frame, err := framer.ReadFrame() + if err != nil { + t.Fatal("ReadFrame with compression:", err) + } + parsedHeadersFrame, ok := frame.(*HeadersFrame) + if !ok { + t.Fatal("Parsed incorrect frame type:", frame) + } + if !reflect.DeepEqual(headersFrame, *parsedHeadersFrame) { + t.Fatal("got: ", *parsedHeadersFrame, "\nwant: ", headersFrame) + } +} + +func TestCreateParseWindowUpdateFrame(t *testing.T) { + buffer := new(bytes.Buffer) + framer, err := NewFramer(buffer, buffer) + if err != nil { + t.Fatal("Failed to create new framer:", err) + } + windowUpdateFrame := WindowUpdateFrame{ + CFHeader: ControlFrameHeader{ + version: Version, + frameType: TypeWindowUpdate, + }, + StreamId: 31337, + DeltaWindowSize: 1, + } + if err := framer.WriteFrame(&windowUpdateFrame); err != nil { + t.Fatal("WriteFrame:", err) + } + if windowUpdateFrame.CFHeader.Flags != 0 { + t.Fatal("Incorrect frame type:", windowUpdateFrame) + } + if windowUpdateFrame.CFHeader.length != 8 { + t.Fatal("Incorrect frame type:", windowUpdateFrame) + } + frame, err := framer.ReadFrame() + if err != nil { + t.Fatal("ReadFrame:", err) + } + parsedWindowUpdateFrame, ok := frame.(*WindowUpdateFrame) + if !ok { + t.Fatal("Parsed incorrect frame type:", frame) + } + if parsedWindowUpdateFrame.CFHeader.Flags != 0 { + t.Fatal("Incorrect frame type:", parsedWindowUpdateFrame) + } + if parsedWindowUpdateFrame.CFHeader.length != 8 { + t.Fatal("Incorrect frame type:", parsedWindowUpdateFrame) + } + if !reflect.DeepEqual(windowUpdateFrame, *parsedWindowUpdateFrame) { + t.Fatal("got: ", *parsedWindowUpdateFrame, "\nwant: ", windowUpdateFrame) + } +} + +func TestCreateParseDataFrame(t *testing.T) { + buffer := new(bytes.Buffer) + framer, err := NewFramer(buffer, buffer) + if err != nil { + t.Fatal("Failed to create new framer:", err) + } + dataFrame := DataFrame{ + StreamId: 1, + Data: []byte{'h', 'e', 'l', 'l', 'o'}, + } + if err := framer.WriteFrame(&dataFrame); err != nil { + t.Fatal("WriteFrame:", err) + } + frame, err := framer.ReadFrame() + if err != nil { + t.Fatal("ReadFrame:", err) + } + parsedDataFrame, ok := frame.(*DataFrame) + if !ok { + t.Fatal("Parsed incorrect frame type:", frame) + } + if !reflect.DeepEqual(dataFrame, *parsedDataFrame) { + t.Fatal("got: ", *parsedDataFrame, "\nwant: ", dataFrame) + } +} + +func TestCompressionContextAcrossFrames(t *testing.T) { + buffer := new(bytes.Buffer) + framer, err := NewFramer(buffer, buffer) + if err != nil { + t.Fatal("Failed to create new framer:", err) + } + headersFrame := HeadersFrame{ + CFHeader: ControlFrameHeader{ + version: Version, + frameType: TypeHeaders, + }, + StreamId: 2, + Headers: HeadersFixture, + } + if err := framer.WriteFrame(&headersFrame); err != nil { + t.Fatal("WriteFrame (HEADERS):", err) + } + synStreamFrame := SynStreamFrame{ + ControlFrameHeader{ + Version, + TypeSynStream, + 0, // Flags + 0, // length + }, + 2, // StreamId + 0, // AssociatedTOStreamID + 0, // Priority + 1, // Slot + nil, // Headers + } + synStreamFrame.Headers = HeadersFixture + + if err := framer.WriteFrame(&synStreamFrame); err != nil { + t.Fatal("WriteFrame (SYN_STREAM):", err) + } + frame, err := framer.ReadFrame() + if err != nil { + t.Fatal("ReadFrame (HEADERS):", err, buffer.Bytes()) + } + parsedHeadersFrame, ok := frame.(*HeadersFrame) + if !ok { + t.Fatalf("expected HeadersFrame; got %T %v", frame, frame) + } + if !reflect.DeepEqual(headersFrame, *parsedHeadersFrame) { + t.Fatal("got: ", *parsedHeadersFrame, "\nwant: ", headersFrame) + } + frame, err = framer.ReadFrame() + if err != nil { + t.Fatal("ReadFrame (SYN_STREAM):", err, buffer.Bytes()) + } + parsedSynStreamFrame, ok := frame.(*SynStreamFrame) + if !ok { + t.Fatalf("expected SynStreamFrame; got %T %v", frame, frame) + } + if !reflect.DeepEqual(synStreamFrame, *parsedSynStreamFrame) { + t.Fatal("got: ", *parsedSynStreamFrame, "\nwant: ", synStreamFrame) + } +} + +func TestMultipleSPDYFrames(t *testing.T) { + // Initialize the framers. + pr1, pw1 := io.Pipe() + pr2, pw2 := io.Pipe() + writer, err := NewFramer(pw1, pr2) + if err != nil { + t.Fatal("Failed to create writer:", err) + } + reader, err := NewFramer(pw2, pr1) + if err != nil { + t.Fatal("Failed to create reader:", err) + } + + // Set up the frames we're actually transferring. + headersFrame := HeadersFrame{ + CFHeader: ControlFrameHeader{ + version: Version, + frameType: TypeHeaders, + }, + StreamId: 2, + Headers: HeadersFixture, + } + synStreamFrame := SynStreamFrame{ + CFHeader: ControlFrameHeader{ + version: Version, + frameType: TypeSynStream, + }, + StreamId: 2, + Headers: HeadersFixture, + } + + // Start the goroutines to write the frames. + go func() { + if err := writer.WriteFrame(&headersFrame); err != nil { + t.Fatal("WriteFrame (HEADERS): ", err) + } + if err := writer.WriteFrame(&synStreamFrame); err != nil { + t.Fatal("WriteFrame (SYN_STREAM): ", err) + } + }() + + // Read the frames and verify they look as expected. + frame, err := reader.ReadFrame() + if err != nil { + t.Fatal("ReadFrame (HEADERS): ", err) + } + parsedHeadersFrame, ok := frame.(*HeadersFrame) + if !ok { + t.Fatal("Parsed incorrect frame type:", frame) + } + if !reflect.DeepEqual(headersFrame, *parsedHeadersFrame) { + t.Fatal("got: ", *parsedHeadersFrame, "\nwant: ", headersFrame) + } + frame, err = reader.ReadFrame() + if err != nil { + t.Fatal("ReadFrame (SYN_STREAM):", err) + } + parsedSynStreamFrame, ok := frame.(*SynStreamFrame) + if !ok { + t.Fatal("Parsed incorrect frame type.") + } + if !reflect.DeepEqual(synStreamFrame, *parsedSynStreamFrame) { + t.Fatal("got: ", *parsedSynStreamFrame, "\nwant: ", synStreamFrame) + } +} + +func TestReadMalformedZlibHeader(t *testing.T) { + // These were constructed by corrupting the first byte of the zlib + // header after writing. + malformedStructs := map[string]string{ + "SynStreamFrame": "gAIAAQAAABgAAAACAAAAAAAAF/nfolGyYmAAAAAA//8=", + "SynReplyFrame": "gAIAAgAAABQAAAACAAAX+d+iUbJiYAAAAAD//w==", + "HeadersFrame": "gAIACAAAABQAAAACAAAX+d+iUbJiYAAAAAD//w==", + } + for name, bad := range malformedStructs { + b, err := base64.StdEncoding.DecodeString(bad) + if err != nil { + t.Errorf("Unable to decode base64 encoded frame %s: %v", name, err) + } + buf := bytes.NewBuffer(b) + reader, err := NewFramer(buf, buf) + if err != nil { + t.Fatalf("NewFramer: %v", err) + } + _, err = reader.ReadFrame() + if err != zlib.ErrHeader { + t.Errorf("Frame %s, expected: %#v, actual: %#v", name, zlib.ErrHeader, err) + } + } +} + +// TODO: these tests are too weak for updating SPDY spec. Fix me. + +type zeroStream struct { + frame Frame + encoded string +} + +var streamIdZeroFrames = map[string]zeroStream{ + "SynStreamFrame": { + &SynStreamFrame{StreamId: 0}, + "gAIAAQAAABgAAAAAAAAAAAAAePnfolGyYmAAAAAA//8=", + }, + "SynReplyFrame": { + &SynReplyFrame{StreamId: 0}, + "gAIAAgAAABQAAAAAAAB4+d+iUbJiYAAAAAD//w==", + }, + "RstStreamFrame": { + &RstStreamFrame{StreamId: 0}, + "gAIAAwAAAAgAAAAAAAAAAA==", + }, + "HeadersFrame": { + &HeadersFrame{StreamId: 0}, + "gAIACAAAABQAAAAAAAB4+d+iUbJiYAAAAAD//w==", + }, + "DataFrame": { + &DataFrame{StreamId: 0}, + "AAAAAAAAAAA=", + }, + "PingFrame": { + &PingFrame{Id: 0}, + "gAIABgAAAAQAAAAA", + }, +} + +func TestNoZeroStreamId(t *testing.T) { + t.Log("skipping") // TODO: update to work with SPDY3 + return + + for name, f := range streamIdZeroFrames { + b, err := base64.StdEncoding.DecodeString(f.encoded) + if err != nil { + t.Errorf("Unable to decode base64 encoded frame %s: %v", f, err) + continue + } + framer, err := NewFramer(ioutil.Discard, bytes.NewReader(b)) + if err != nil { + t.Fatalf("NewFramer: %v", err) + } + err = framer.WriteFrame(f.frame) + checkZeroStreamId(t, name, "WriteFrame", err) + + _, err = framer.ReadFrame() + checkZeroStreamId(t, name, "ReadFrame", err) + } +} + +func checkZeroStreamId(t *testing.T, frame string, method string, err error) { + if err == nil { + t.Errorf("%s ZeroStreamId, no error on %s", method, frame) + return + } + eerr, ok := err.(*Error) + if !ok || eerr.Err != ZeroStreamId { + t.Errorf("%s ZeroStreamId, incorrect error %#v, frame %s", method, eerr, frame) + } +} diff --git a/vendor/github.com/docker/spdystream/spdy/types.go b/vendor/github.com/docker/spdystream/spdy/types.go new file mode 100644 index 000000000..7b6ee9c6f --- /dev/null +++ b/vendor/github.com/docker/spdystream/spdy/types.go @@ -0,0 +1,275 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package spdy implements the SPDY protocol (currently SPDY/3), described in +// http://www.chromium.org/spdy/spdy-protocol/spdy-protocol-draft3. +package spdy + +import ( + "bytes" + "compress/zlib" + "io" + "net/http" +) + +// Version is the protocol version number that this package implements. +const Version = 3 + +// ControlFrameType stores the type field in a control frame header. +type ControlFrameType uint16 + +const ( + TypeSynStream ControlFrameType = 0x0001 + TypeSynReply = 0x0002 + TypeRstStream = 0x0003 + TypeSettings = 0x0004 + TypePing = 0x0006 + TypeGoAway = 0x0007 + TypeHeaders = 0x0008 + TypeWindowUpdate = 0x0009 +) + +// ControlFlags are the flags that can be set on a control frame. +type ControlFlags uint8 + +const ( + ControlFlagFin ControlFlags = 0x01 + ControlFlagUnidirectional = 0x02 + ControlFlagSettingsClearSettings = 0x01 +) + +// DataFlags are the flags that can be set on a data frame. +type DataFlags uint8 + +const ( + DataFlagFin DataFlags = 0x01 +) + +// MaxDataLength is the maximum number of bytes that can be stored in one frame. +const MaxDataLength = 1<<24 - 1 + +// headerValueSepator separates multiple header values. +const headerValueSeparator = "\x00" + +// Frame is a single SPDY frame in its unpacked in-memory representation. Use +// Framer to read and write it. +type Frame interface { + write(f *Framer) error +} + +// ControlFrameHeader contains all the fields in a control frame header, +// in its unpacked in-memory representation. +type ControlFrameHeader struct { + // Note, high bit is the "Control" bit. + version uint16 // spdy version number + frameType ControlFrameType + Flags ControlFlags + length uint32 // length of data field +} + +type controlFrame interface { + Frame + read(h ControlFrameHeader, f *Framer) error +} + +// StreamId represents a 31-bit value identifying the stream. +type StreamId uint32 + +// SynStreamFrame is the unpacked, in-memory representation of a SYN_STREAM +// frame. +type SynStreamFrame struct { + CFHeader ControlFrameHeader + StreamId StreamId + AssociatedToStreamId StreamId // stream id for a stream which this stream is associated to + Priority uint8 // priority of this frame (3-bit) + Slot uint8 // index in the server's credential vector of the client certificate + Headers http.Header +} + +// SynReplyFrame is the unpacked, in-memory representation of a SYN_REPLY frame. +type SynReplyFrame struct { + CFHeader ControlFrameHeader + StreamId StreamId + Headers http.Header +} + +// RstStreamStatus represents the status that led to a RST_STREAM. +type RstStreamStatus uint32 + +const ( + ProtocolError RstStreamStatus = iota + 1 + InvalidStream + RefusedStream + UnsupportedVersion + Cancel + InternalError + FlowControlError + StreamInUse + StreamAlreadyClosed + InvalidCredentials + FrameTooLarge +) + +// RstStreamFrame is the unpacked, in-memory representation of a RST_STREAM +// frame. +type RstStreamFrame struct { + CFHeader ControlFrameHeader + StreamId StreamId + Status RstStreamStatus +} + +// SettingsFlag represents a flag in a SETTINGS frame. +type SettingsFlag uint8 + +const ( + FlagSettingsPersistValue SettingsFlag = 0x1 + FlagSettingsPersisted = 0x2 +) + +// SettingsFlag represents the id of an id/value pair in a SETTINGS frame. +type SettingsId uint32 + +const ( + SettingsUploadBandwidth SettingsId = iota + 1 + SettingsDownloadBandwidth + SettingsRoundTripTime + SettingsMaxConcurrentStreams + SettingsCurrentCwnd + SettingsDownloadRetransRate + SettingsInitialWindowSize + SettingsClientCretificateVectorSize +) + +// SettingsFlagIdValue is the unpacked, in-memory representation of the +// combined flag/id/value for a setting in a SETTINGS frame. +type SettingsFlagIdValue struct { + Flag SettingsFlag + Id SettingsId + Value uint32 +} + +// SettingsFrame is the unpacked, in-memory representation of a SPDY +// SETTINGS frame. +type SettingsFrame struct { + CFHeader ControlFrameHeader + FlagIdValues []SettingsFlagIdValue +} + +// PingFrame is the unpacked, in-memory representation of a PING frame. +type PingFrame struct { + CFHeader ControlFrameHeader + Id uint32 // unique id for this ping, from server is even, from client is odd. +} + +// GoAwayStatus represents the status in a GoAwayFrame. +type GoAwayStatus uint32 + +const ( + GoAwayOK GoAwayStatus = iota + GoAwayProtocolError + GoAwayInternalError +) + +// GoAwayFrame is the unpacked, in-memory representation of a GOAWAY frame. +type GoAwayFrame struct { + CFHeader ControlFrameHeader + LastGoodStreamId StreamId // last stream id which was accepted by sender + Status GoAwayStatus +} + +// HeadersFrame is the unpacked, in-memory representation of a HEADERS frame. +type HeadersFrame struct { + CFHeader ControlFrameHeader + StreamId StreamId + Headers http.Header +} + +// WindowUpdateFrame is the unpacked, in-memory representation of a +// WINDOW_UPDATE frame. +type WindowUpdateFrame struct { + CFHeader ControlFrameHeader + StreamId StreamId + DeltaWindowSize uint32 // additional number of bytes to existing window size +} + +// TODO: Implement credential frame and related methods. + +// DataFrame is the unpacked, in-memory representation of a DATA frame. +type DataFrame struct { + // Note, high bit is the "Control" bit. Should be 0 for data frames. + StreamId StreamId + Flags DataFlags + Data []byte // payload data of this frame +} + +// A SPDY specific error. +type ErrorCode string + +const ( + UnlowercasedHeaderName ErrorCode = "header was not lowercased" + DuplicateHeaders = "multiple headers with same name" + WrongCompressedPayloadSize = "compressed payload size was incorrect" + UnknownFrameType = "unknown frame type" + InvalidControlFrame = "invalid control frame" + InvalidDataFrame = "invalid data frame" + InvalidHeaderPresent = "frame contained invalid header" + ZeroStreamId = "stream id zero is disallowed" +) + +// Error contains both the type of error and additional values. StreamId is 0 +// if Error is not associated with a stream. +type Error struct { + Err ErrorCode + StreamId StreamId +} + +func (e *Error) Error() string { + return string(e.Err) +} + +var invalidReqHeaders = map[string]bool{ + "Connection": true, + "Host": true, + "Keep-Alive": true, + "Proxy-Connection": true, + "Transfer-Encoding": true, +} + +var invalidRespHeaders = map[string]bool{ + "Connection": true, + "Keep-Alive": true, + "Proxy-Connection": true, + "Transfer-Encoding": true, +} + +// Framer handles serializing/deserializing SPDY frames, including compressing/ +// decompressing payloads. +type Framer struct { + headerCompressionDisabled bool + w io.Writer + headerBuf *bytes.Buffer + headerCompressor *zlib.Writer + r io.Reader + headerReader io.LimitedReader + headerDecompressor io.ReadCloser +} + +// NewFramer allocates a new Framer for a given SPDY connection, represented by +// a io.Writer and io.Reader. Note that Framer will read and write individual fields +// from/to the Reader and Writer, so the caller should pass in an appropriately +// buffered implementation to optimize performance. +func NewFramer(w io.Writer, r io.Reader) (*Framer, error) { + compressBuf := new(bytes.Buffer) + compressor, err := zlib.NewWriterLevelDict(compressBuf, zlib.BestCompression, []byte(headerDictionary)) + if err != nil { + return nil, err + } + framer := &Framer{ + w: w, + headerBuf: compressBuf, + headerCompressor: compressor, + r: r, + } + return framer, nil +} diff --git a/vendor/github.com/docker/spdystream/spdy/write.go b/vendor/github.com/docker/spdystream/spdy/write.go new file mode 100644 index 000000000..b212f66a2 --- /dev/null +++ b/vendor/github.com/docker/spdystream/spdy/write.go @@ -0,0 +1,318 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package spdy + +import ( + "encoding/binary" + "io" + "net/http" + "strings" +) + +func (frame *SynStreamFrame) write(f *Framer) error { + return f.writeSynStreamFrame(frame) +} + +func (frame *SynReplyFrame) write(f *Framer) error { + return f.writeSynReplyFrame(frame) +} + +func (frame *RstStreamFrame) write(f *Framer) (err error) { + if frame.StreamId == 0 { + return &Error{ZeroStreamId, 0} + } + frame.CFHeader.version = Version + frame.CFHeader.frameType = TypeRstStream + frame.CFHeader.Flags = 0 + frame.CFHeader.length = 8 + + // Serialize frame to Writer. + if err = writeControlFrameHeader(f.w, frame.CFHeader); err != nil { + return + } + if err = binary.Write(f.w, binary.BigEndian, frame.StreamId); err != nil { + return + } + if frame.Status == 0 { + return &Error{InvalidControlFrame, frame.StreamId} + } + if err = binary.Write(f.w, binary.BigEndian, frame.Status); err != nil { + return + } + return +} + +func (frame *SettingsFrame) write(f *Framer) (err error) { + frame.CFHeader.version = Version + frame.CFHeader.frameType = TypeSettings + frame.CFHeader.length = uint32(len(frame.FlagIdValues)*8 + 4) + + // Serialize frame to Writer. + if err = writeControlFrameHeader(f.w, frame.CFHeader); err != nil { + return + } + if err = binary.Write(f.w, binary.BigEndian, uint32(len(frame.FlagIdValues))); err != nil { + return + } + for _, flagIdValue := range frame.FlagIdValues { + flagId := uint32(flagIdValue.Flag)<<24 | uint32(flagIdValue.Id) + if err = binary.Write(f.w, binary.BigEndian, flagId); err != nil { + return + } + if err = binary.Write(f.w, binary.BigEndian, flagIdValue.Value); err != nil { + return + } + } + return +} + +func (frame *PingFrame) write(f *Framer) (err error) { + if frame.Id == 0 { + return &Error{ZeroStreamId, 0} + } + frame.CFHeader.version = Version + frame.CFHeader.frameType = TypePing + frame.CFHeader.Flags = 0 + frame.CFHeader.length = 4 + + // Serialize frame to Writer. + if err = writeControlFrameHeader(f.w, frame.CFHeader); err != nil { + return + } + if err = binary.Write(f.w, binary.BigEndian, frame.Id); err != nil { + return + } + return +} + +func (frame *GoAwayFrame) write(f *Framer) (err error) { + frame.CFHeader.version = Version + frame.CFHeader.frameType = TypeGoAway + frame.CFHeader.Flags = 0 + frame.CFHeader.length = 8 + + // Serialize frame to Writer. + if err = writeControlFrameHeader(f.w, frame.CFHeader); err != nil { + return + } + if err = binary.Write(f.w, binary.BigEndian, frame.LastGoodStreamId); err != nil { + return + } + if err = binary.Write(f.w, binary.BigEndian, frame.Status); err != nil { + return + } + return nil +} + +func (frame *HeadersFrame) write(f *Framer) error { + return f.writeHeadersFrame(frame) +} + +func (frame *WindowUpdateFrame) write(f *Framer) (err error) { + frame.CFHeader.version = Version + frame.CFHeader.frameType = TypeWindowUpdate + frame.CFHeader.Flags = 0 + frame.CFHeader.length = 8 + + // Serialize frame to Writer. + if err = writeControlFrameHeader(f.w, frame.CFHeader); err != nil { + return + } + if err = binary.Write(f.w, binary.BigEndian, frame.StreamId); err != nil { + return + } + if err = binary.Write(f.w, binary.BigEndian, frame.DeltaWindowSize); err != nil { + return + } + return nil +} + +func (frame *DataFrame) write(f *Framer) error { + return f.writeDataFrame(frame) +} + +// WriteFrame writes a frame. +func (f *Framer) WriteFrame(frame Frame) error { + return frame.write(f) +} + +func writeControlFrameHeader(w io.Writer, h ControlFrameHeader) error { + if err := binary.Write(w, binary.BigEndian, 0x8000|h.version); err != nil { + return err + } + if err := binary.Write(w, binary.BigEndian, h.frameType); err != nil { + return err + } + flagsAndLength := uint32(h.Flags)<<24 | h.length + if err := binary.Write(w, binary.BigEndian, flagsAndLength); err != nil { + return err + } + return nil +} + +func writeHeaderValueBlock(w io.Writer, h http.Header) (n int, err error) { + n = 0 + if err = binary.Write(w, binary.BigEndian, uint32(len(h))); err != nil { + return + } + n += 2 + for name, values := range h { + if err = binary.Write(w, binary.BigEndian, uint32(len(name))); err != nil { + return + } + n += 2 + name = strings.ToLower(name) + if _, err = io.WriteString(w, name); err != nil { + return + } + n += len(name) + v := strings.Join(values, headerValueSeparator) + if err = binary.Write(w, binary.BigEndian, uint32(len(v))); err != nil { + return + } + n += 2 + if _, err = io.WriteString(w, v); err != nil { + return + } + n += len(v) + } + return +} + +func (f *Framer) writeSynStreamFrame(frame *SynStreamFrame) (err error) { + if frame.StreamId == 0 { + return &Error{ZeroStreamId, 0} + } + // Marshal the headers. + var writer io.Writer = f.headerBuf + if !f.headerCompressionDisabled { + writer = f.headerCompressor + } + if _, err = writeHeaderValueBlock(writer, frame.Headers); err != nil { + return + } + if !f.headerCompressionDisabled { + f.headerCompressor.Flush() + } + + // Set ControlFrameHeader. + frame.CFHeader.version = Version + frame.CFHeader.frameType = TypeSynStream + frame.CFHeader.length = uint32(len(f.headerBuf.Bytes()) + 10) + + // Serialize frame to Writer. + if err = writeControlFrameHeader(f.w, frame.CFHeader); err != nil { + return err + } + if err = binary.Write(f.w, binary.BigEndian, frame.StreamId); err != nil { + return err + } + if err = binary.Write(f.w, binary.BigEndian, frame.AssociatedToStreamId); err != nil { + return err + } + if err = binary.Write(f.w, binary.BigEndian, frame.Priority<<5); err != nil { + return err + } + if err = binary.Write(f.w, binary.BigEndian, frame.Slot); err != nil { + return err + } + if _, err = f.w.Write(f.headerBuf.Bytes()); err != nil { + return err + } + f.headerBuf.Reset() + return nil +} + +func (f *Framer) writeSynReplyFrame(frame *SynReplyFrame) (err error) { + if frame.StreamId == 0 { + return &Error{ZeroStreamId, 0} + } + // Marshal the headers. + var writer io.Writer = f.headerBuf + if !f.headerCompressionDisabled { + writer = f.headerCompressor + } + if _, err = writeHeaderValueBlock(writer, frame.Headers); err != nil { + return + } + if !f.headerCompressionDisabled { + f.headerCompressor.Flush() + } + + // Set ControlFrameHeader. + frame.CFHeader.version = Version + frame.CFHeader.frameType = TypeSynReply + frame.CFHeader.length = uint32(len(f.headerBuf.Bytes()) + 4) + + // Serialize frame to Writer. + if err = writeControlFrameHeader(f.w, frame.CFHeader); err != nil { + return + } + if err = binary.Write(f.w, binary.BigEndian, frame.StreamId); err != nil { + return + } + if _, err = f.w.Write(f.headerBuf.Bytes()); err != nil { + return + } + f.headerBuf.Reset() + return +} + +func (f *Framer) writeHeadersFrame(frame *HeadersFrame) (err error) { + if frame.StreamId == 0 { + return &Error{ZeroStreamId, 0} + } + // Marshal the headers. + var writer io.Writer = f.headerBuf + if !f.headerCompressionDisabled { + writer = f.headerCompressor + } + if _, err = writeHeaderValueBlock(writer, frame.Headers); err != nil { + return + } + if !f.headerCompressionDisabled { + f.headerCompressor.Flush() + } + + // Set ControlFrameHeader. + frame.CFHeader.version = Version + frame.CFHeader.frameType = TypeHeaders + frame.CFHeader.length = uint32(len(f.headerBuf.Bytes()) + 4) + + // Serialize frame to Writer. + if err = writeControlFrameHeader(f.w, frame.CFHeader); err != nil { + return + } + if err = binary.Write(f.w, binary.BigEndian, frame.StreamId); err != nil { + return + } + if _, err = f.w.Write(f.headerBuf.Bytes()); err != nil { + return + } + f.headerBuf.Reset() + return +} + +func (f *Framer) writeDataFrame(frame *DataFrame) (err error) { + if frame.StreamId == 0 { + return &Error{ZeroStreamId, 0} + } + if frame.StreamId&0x80000000 != 0 || len(frame.Data) > MaxDataLength { + return &Error{InvalidDataFrame, frame.StreamId} + } + + // Serialize frame to Writer. + if err = binary.Write(f.w, binary.BigEndian, frame.StreamId); err != nil { + return + } + flagsAndLength := uint32(frame.Flags)<<24 | uint32(len(frame.Data)) + if err = binary.Write(f.w, binary.BigEndian, flagsAndLength); err != nil { + return + } + if _, err = f.w.Write(frame.Data); err != nil { + return + } + return nil +} diff --git a/vendor/github.com/docker/spdystream/spdy_bench_test.go b/vendor/github.com/docker/spdystream/spdy_bench_test.go new file mode 100644 index 000000000..041a377fa --- /dev/null +++ b/vendor/github.com/docker/spdystream/spdy_bench_test.go @@ -0,0 +1,113 @@ +package spdystream + +import ( + "fmt" + "io" + "net" + "net/http" + "sync" + "testing" +) + +func configureServer() (io.Closer, string, *sync.WaitGroup) { + authenticated = true + wg := &sync.WaitGroup{} + server, listen, serverErr := runServer(wg) + + if serverErr != nil { + panic(serverErr) + } + + return server, listen, wg +} + +func BenchmarkDial10000(b *testing.B) { + server, addr, wg := configureServer() + + defer func() { + server.Close() + wg.Wait() + }() + + for i := 0; i < b.N; i++ { + conn, dialErr := net.Dial("tcp", addr) + if dialErr != nil { + panic(fmt.Sprintf("Error dialing server: %s", dialErr)) + } + conn.Close() + } +} + +func BenchmarkDialWithSPDYStream10000(b *testing.B) { + server, addr, wg := configureServer() + + defer func() { + server.Close() + wg.Wait() + }() + + for i := 0; i < b.N; i++ { + conn, dialErr := net.Dial("tcp", addr) + if dialErr != nil { + b.Fatalf("Error dialing server: %s", dialErr) + } + + spdyConn, spdyErr := NewConnection(conn, false) + if spdyErr != nil { + b.Fatalf("Error creating spdy connection: %s", spdyErr) + } + go spdyConn.Serve(NoOpStreamHandler) + + closeErr := spdyConn.Close() + if closeErr != nil { + b.Fatalf("Error closing connection: %s", closeErr) + } + } +} + +func benchmarkStreamWithDataAndSize(size uint64, b *testing.B) { + server, addr, wg := configureServer() + + defer func() { + server.Close() + wg.Wait() + }() + + for i := 0; i < b.N; i++ { + conn, dialErr := net.Dial("tcp", addr) + if dialErr != nil { + b.Fatalf("Error dialing server: %s", dialErr) + } + + spdyConn, spdyErr := NewConnection(conn, false) + if spdyErr != nil { + b.Fatalf("Error creating spdy connection: %s", spdyErr) + } + + go spdyConn.Serve(MirrorStreamHandler) + + stream, err := spdyConn.CreateStream(http.Header{}, nil, false) + + writer := make([]byte, size) + + stream.Write(writer) + + if err != nil { + panic(err) + } + + reader := make([]byte, size) + stream.Read(reader) + + stream.Close() + + closeErr := spdyConn.Close() + if closeErr != nil { + b.Fatalf("Error closing connection: %s", closeErr) + } + } +} + +func BenchmarkStreamWith1Byte10000(b *testing.B) { benchmarkStreamWithDataAndSize(1, b) } +func BenchmarkStreamWith1KiloByte10000(b *testing.B) { benchmarkStreamWithDataAndSize(1024, b) } +func BenchmarkStreamWith1Megabyte10000(b *testing.B) { benchmarkStreamWithDataAndSize(1024*1024, b) } diff --git a/vendor/github.com/docker/spdystream/spdy_test.go b/vendor/github.com/docker/spdystream/spdy_test.go new file mode 100644 index 000000000..e85b465ab --- /dev/null +++ b/vendor/github.com/docker/spdystream/spdy_test.go @@ -0,0 +1,1172 @@ +package spdystream + +import ( + "bufio" + "bytes" + "fmt" + "io" + "io/ioutil" + "net" + "net/http" + "net/http/httptest" + "sync" + "testing" + "time" + + "github.com/docker/spdystream/spdy" +) + +func TestSpdyStreams(t *testing.T) { + var wg sync.WaitGroup + server, listen, serverErr := runServer(&wg) + if serverErr != nil { + t.Fatalf("Error initializing server: %s", serverErr) + } + + conn, dialErr := net.Dial("tcp", listen) + if dialErr != nil { + t.Fatalf("Error dialing server: %s", dialErr) + } + + spdyConn, spdyErr := NewConnection(conn, false) + if spdyErr != nil { + t.Fatalf("Error creating spdy connection: %s", spdyErr) + } + go spdyConn.Serve(NoOpStreamHandler) + + authenticated = true + stream, streamErr := spdyConn.CreateStream(http.Header{}, nil, false) + if streamErr != nil { + t.Fatalf("Error creating stream: %s", streamErr) + } + + waitErr := stream.Wait() + if waitErr != nil { + t.Fatalf("Error waiting for stream: %s", waitErr) + } + + message := []byte("hello") + writeErr := stream.WriteData(message, false) + if writeErr != nil { + t.Fatalf("Error writing data") + } + + buf := make([]byte, 10) + n, readErr := stream.Read(buf) + if readErr != nil { + t.Fatalf("Error reading data from stream: %s", readErr) + } + if n != 5 { + t.Fatalf("Unexpected number of bytes read:\nActual: %d\nExpected: 5", n) + } + if bytes.Compare(buf[:n], message) != 0 { + t.Fatalf("Did not receive expected message:\nActual: %s\nExpectd: %s", buf, message) + } + + headers := http.Header{ + "TestKey": []string{"TestVal"}, + } + sendErr := stream.SendHeader(headers, false) + if sendErr != nil { + t.Fatalf("Error sending headers: %s", sendErr) + } + receiveHeaders, receiveErr := stream.ReceiveHeader() + if receiveErr != nil { + t.Fatalf("Error receiving headers: %s", receiveErr) + } + if len(receiveHeaders) != 1 { + t.Fatalf("Unexpected number of headers:\nActual: %d\nExpecting:%d", len(receiveHeaders), 1) + } + testVal := receiveHeaders.Get("TestKey") + if testVal != "TestVal" { + t.Fatalf("Wrong test value:\nActual: %q\nExpecting: %q", testVal, "TestVal") + } + + writeErr = stream.WriteData(message, true) + if writeErr != nil { + t.Fatalf("Error writing data") + } + + smallBuf := make([]byte, 3) + n, readErr = stream.Read(smallBuf) + if readErr != nil { + t.Fatalf("Error reading data from stream: %s", readErr) + } + if n != 3 { + t.Fatalf("Unexpected number of bytes read:\nActual: %d\nExpected: 3", n) + } + if bytes.Compare(smallBuf[:n], []byte("hel")) != 0 { + t.Fatalf("Did not receive expected message:\nActual: %s\nExpectd: %s", smallBuf[:n], message) + } + n, readErr = stream.Read(smallBuf) + if readErr != nil { + t.Fatalf("Error reading data from stream: %s", readErr) + } + if n != 2 { + t.Fatalf("Unexpected number of bytes read:\nActual: %d\nExpected: 2", n) + } + if bytes.Compare(smallBuf[:n], []byte("lo")) != 0 { + t.Fatalf("Did not receive expected message:\nActual: %s\nExpected: lo", smallBuf[:n]) + } + + n, readErr = stream.Read(buf) + if readErr != io.EOF { + t.Fatalf("Expected EOF reading from finished stream, read %d bytes", n) + } + + // Closing again should return error since stream is already closed + streamCloseErr := stream.Close() + if streamCloseErr == nil { + t.Fatalf("No error closing finished stream") + } + if streamCloseErr != ErrWriteClosedStream { + t.Fatalf("Unexpected error closing stream: %s", streamCloseErr) + } + + streamResetErr := stream.Reset() + if streamResetErr != nil { + t.Fatalf("Error reseting stream: %s", streamResetErr) + } + + authenticated = false + badStream, badStreamErr := spdyConn.CreateStream(http.Header{}, nil, false) + if badStreamErr != nil { + t.Fatalf("Error creating stream: %s", badStreamErr) + } + + waitErr = badStream.Wait() + if waitErr == nil { + t.Fatalf("Did not receive error creating stream") + } + if waitErr != ErrReset { + t.Fatalf("Unexpected error creating stream: %s", waitErr) + } + streamCloseErr = badStream.Close() + if streamCloseErr == nil { + t.Fatalf("No error closing bad stream") + } + + spdyCloseErr := spdyConn.Close() + if spdyCloseErr != nil { + t.Fatalf("Error closing spdy connection: %s", spdyCloseErr) + } + + closeErr := server.Close() + if closeErr != nil { + t.Fatalf("Error shutting down server: %s", closeErr) + } + wg.Wait() +} + +func TestPing(t *testing.T) { + var wg sync.WaitGroup + server, listen, serverErr := runServer(&wg) + if serverErr != nil { + t.Fatalf("Error initializing server: %s", serverErr) + } + + conn, dialErr := net.Dial("tcp", listen) + if dialErr != nil { + t.Fatalf("Error dialing server: %s", dialErr) + } + + spdyConn, spdyErr := NewConnection(conn, false) + if spdyErr != nil { + t.Fatalf("Error creating spdy connection: %s", spdyErr) + } + go spdyConn.Serve(NoOpStreamHandler) + + pingTime, pingErr := spdyConn.Ping() + if pingErr != nil { + t.Fatalf("Error pinging server: %s", pingErr) + } + if pingTime == time.Duration(0) { + t.Fatalf("Expecting non-zero ping time") + } + + closeErr := server.Close() + if closeErr != nil { + t.Fatalf("Error shutting down server: %s", closeErr) + } + wg.Wait() +} + +func TestHalfClose(t *testing.T) { + var wg sync.WaitGroup + server, listen, serverErr := runServer(&wg) + if serverErr != nil { + t.Fatalf("Error initializing server: %s", serverErr) + } + + conn, dialErr := net.Dial("tcp", listen) + if dialErr != nil { + t.Fatalf("Error dialing server: %s", dialErr) + } + + spdyConn, spdyErr := NewConnection(conn, false) + if spdyErr != nil { + t.Fatalf("Error creating spdy connection: %s", spdyErr) + } + go spdyConn.Serve(NoOpStreamHandler) + + authenticated = true + stream, streamErr := spdyConn.CreateStream(http.Header{}, nil, false) + if streamErr != nil { + t.Fatalf("Error creating stream: %s", streamErr) + } + + waitErr := stream.Wait() + if waitErr != nil { + t.Fatalf("Error waiting for stream: %s", waitErr) + } + + message := []byte("hello and will read after close") + writeErr := stream.WriteData(message, false) + if writeErr != nil { + t.Fatalf("Error writing data") + } + + streamCloseErr := stream.Close() + if streamCloseErr != nil { + t.Fatalf("Error closing stream: %s", streamCloseErr) + } + + buf := make([]byte, 40) + n, readErr := stream.Read(buf) + if readErr != nil { + t.Fatalf("Error reading data from stream: %s", readErr) + } + if n != 31 { + t.Fatalf("Unexpected number of bytes read:\nActual: %d\nExpected: 5", n) + } + if bytes.Compare(buf[:n], message) != 0 { + t.Fatalf("Did not receive expected message:\nActual: %s\nExpectd: %s", buf, message) + } + + spdyCloseErr := spdyConn.Close() + if spdyCloseErr != nil { + t.Fatalf("Error closing spdy connection: %s", spdyCloseErr) + } + + closeErr := server.Close() + if closeErr != nil { + t.Fatalf("Error shutting down server: %s", closeErr) + } + wg.Wait() +} + +func TestUnexpectedRemoteConnectionClosed(t *testing.T) { + tt := []struct { + closeReceiver bool + closeSender bool + }{ + {closeReceiver: true, closeSender: false}, + {closeReceiver: false, closeSender: true}, + {closeReceiver: false, closeSender: false}, + } + for tix, tc := range tt { + listener, listenErr := net.Listen("tcp", "localhost:0") + if listenErr != nil { + t.Fatalf("Error listening: %v", listenErr) + } + + var serverConn net.Conn + var connErr error + go func() { + serverConn, connErr = listener.Accept() + if connErr != nil { + t.Fatalf("Error accepting: %v", connErr) + } + + serverSpdyConn, _ := NewConnection(serverConn, true) + go serverSpdyConn.Serve(func(stream *Stream) { + stream.SendReply(http.Header{}, tc.closeSender) + }) + }() + + conn, dialErr := net.Dial("tcp", listener.Addr().String()) + if dialErr != nil { + t.Fatalf("Error dialing server: %s", dialErr) + } + + spdyConn, spdyErr := NewConnection(conn, false) + if spdyErr != nil { + t.Fatalf("Error creating spdy connection: %s", spdyErr) + } + go spdyConn.Serve(NoOpStreamHandler) + + authenticated = true + stream, streamErr := spdyConn.CreateStream(http.Header{}, nil, false) + if streamErr != nil { + t.Fatalf("Error creating stream: %s", streamErr) + } + + waitErr := stream.Wait() + if waitErr != nil { + t.Fatalf("Error waiting for stream: %s", waitErr) + } + + if tc.closeReceiver { + // make stream half closed, receive only + stream.Close() + } + + streamch := make(chan error, 1) + go func() { + b := make([]byte, 1) + _, err := stream.Read(b) + streamch <- err + }() + + closeErr := serverConn.Close() + if closeErr != nil { + t.Fatalf("Error shutting down server: %s", closeErr) + } + + select { + case e := <-streamch: + if e == nil || e != io.EOF { + t.Fatalf("(%d) Expected to get an EOF stream error", tix) + } + } + + closeErr = conn.Close() + if closeErr != nil { + t.Fatalf("Error closing client connection: %s", closeErr) + } + + listenErr = listener.Close() + if listenErr != nil { + t.Fatalf("Error closing listener: %s", listenErr) + } + } +} + +func TestCloseNotification(t *testing.T) { + listener, listenErr := net.Listen("tcp", "localhost:0") + if listenErr != nil { + t.Fatalf("Error listening: %v", listenErr) + } + listen := listener.Addr().String() + + serverConnChan := make(chan net.Conn) + go func() { + serverConn, err := listener.Accept() + if err != nil { + t.Fatalf("Error accepting: %v", err) + } + + serverSpdyConn, err := NewConnection(serverConn, true) + if err != nil { + t.Fatalf("Error creating server connection: %v", err) + } + go serverSpdyConn.Serve(NoOpStreamHandler) + <-serverSpdyConn.CloseChan() + serverConnChan <- serverConn + }() + + conn, dialErr := net.Dial("tcp", listen) + if dialErr != nil { + t.Fatalf("Error dialing server: %s", dialErr) + } + + spdyConn, spdyErr := NewConnection(conn, false) + if spdyErr != nil { + t.Fatalf("Error creating spdy connection: %s", spdyErr) + } + go spdyConn.Serve(NoOpStreamHandler) + + // close client conn + err := conn.Close() + if err != nil { + t.Fatalf("Error closing client connection: %v", err) + } + + var serverConn net.Conn + select { + case serverConn = <-serverConnChan: + } + + err = serverConn.Close() + if err != nil { + t.Fatalf("Error closing serverConn: %v", err) + } + + listenErr = listener.Close() + if listenErr != nil { + t.Fatalf("Error closing listener: %s", listenErr) + } +} + +func TestIdleShutdownRace(t *testing.T) { + var wg sync.WaitGroup + server, listen, serverErr := runServer(&wg) + if serverErr != nil { + t.Fatalf("Error initializing server: %s", serverErr) + } + + conn, dialErr := net.Dial("tcp", listen) + if dialErr != nil { + t.Fatalf("Error dialing server: %s", dialErr) + } + + spdyConn, spdyErr := NewConnection(conn, false) + if spdyErr != nil { + t.Fatalf("Error creating spdy connection: %s", spdyErr) + } + go spdyConn.Serve(NoOpStreamHandler) + + authenticated = true + stream, err := spdyConn.CreateStream(http.Header{}, nil, false) + if err != nil { + t.Fatalf("Error creating stream: %v", err) + } + + spdyConn.SetIdleTimeout(5 * time.Millisecond) + go func() { + time.Sleep(5 * time.Millisecond) + stream.Reset() + }() + + select { + case <-spdyConn.CloseChan(): + case <-time.After(20 * time.Millisecond): + t.Fatal("Timed out waiting for idle connection closure") + } + + closeErr := server.Close() + if closeErr != nil { + t.Fatalf("Error shutting down server: %s", closeErr) + } + wg.Wait() +} + +func TestIdleNoTimeoutSet(t *testing.T) { + var wg sync.WaitGroup + server, listen, serverErr := runServer(&wg) + if serverErr != nil { + t.Fatalf("Error initializing server: %s", serverErr) + } + + conn, dialErr := net.Dial("tcp", listen) + if dialErr != nil { + t.Fatalf("Error dialing server: %s", dialErr) + } + + spdyConn, spdyErr := NewConnection(conn, false) + if spdyErr != nil { + t.Fatalf("Error creating spdy connection: %s", spdyErr) + } + go spdyConn.Serve(NoOpStreamHandler) + + select { + case <-spdyConn.CloseChan(): + t.Fatal("Unexpected connection closure") + case <-time.After(10 * time.Millisecond): + } + + closeErr := server.Close() + if closeErr != nil { + t.Fatalf("Error shutting down server: %s", closeErr) + } + wg.Wait() +} + +func TestIdleClearTimeout(t *testing.T) { + var wg sync.WaitGroup + server, listen, serverErr := runServer(&wg) + if serverErr != nil { + t.Fatalf("Error initializing server: %s", serverErr) + } + + conn, dialErr := net.Dial("tcp", listen) + if dialErr != nil { + t.Fatalf("Error dialing server: %s", dialErr) + } + + spdyConn, spdyErr := NewConnection(conn, false) + if spdyErr != nil { + t.Fatalf("Error creating spdy connection: %s", spdyErr) + } + go spdyConn.Serve(NoOpStreamHandler) + + spdyConn.SetIdleTimeout(10 * time.Millisecond) + spdyConn.SetIdleTimeout(0) + select { + case <-spdyConn.CloseChan(): + t.Fatal("Unexpected connection closure") + case <-time.After(20 * time.Millisecond): + } + + closeErr := server.Close() + if closeErr != nil { + t.Fatalf("Error shutting down server: %s", closeErr) + } + wg.Wait() +} + +func TestIdleNoData(t *testing.T) { + var wg sync.WaitGroup + server, listen, serverErr := runServer(&wg) + if serverErr != nil { + t.Fatalf("Error initializing server: %s", serverErr) + } + + conn, dialErr := net.Dial("tcp", listen) + if dialErr != nil { + t.Fatalf("Error dialing server: %s", dialErr) + } + + spdyConn, spdyErr := NewConnection(conn, false) + if spdyErr != nil { + t.Fatalf("Error creating spdy connection: %s", spdyErr) + } + go spdyConn.Serve(NoOpStreamHandler) + + spdyConn.SetIdleTimeout(10 * time.Millisecond) + <-spdyConn.CloseChan() + + closeErr := server.Close() + if closeErr != nil { + t.Fatalf("Error shutting down server: %s", closeErr) + } + wg.Wait() +} + +func TestIdleWithData(t *testing.T) { + var wg sync.WaitGroup + server, listen, serverErr := runServer(&wg) + if serverErr != nil { + t.Fatalf("Error initializing server: %s", serverErr) + } + + conn, dialErr := net.Dial("tcp", listen) + if dialErr != nil { + t.Fatalf("Error dialing server: %s", dialErr) + } + + spdyConn, spdyErr := NewConnection(conn, false) + if spdyErr != nil { + t.Fatalf("Error creating spdy connection: %s", spdyErr) + } + go spdyConn.Serve(NoOpStreamHandler) + + spdyConn.SetIdleTimeout(25 * time.Millisecond) + + authenticated = true + stream, err := spdyConn.CreateStream(http.Header{}, nil, false) + if err != nil { + t.Fatalf("Error creating stream: %v", err) + } + + writeCh := make(chan struct{}) + + go func() { + b := []byte{1, 2, 3, 4, 5} + for i := 0; i < 10; i++ { + _, err = stream.Write(b) + if err != nil { + t.Fatalf("Error writing to stream: %v", err) + } + time.Sleep(10 * time.Millisecond) + } + close(writeCh) + }() + + writesFinished := false + +Loop: + for { + select { + case <-writeCh: + writesFinished = true + case <-spdyConn.CloseChan(): + if !writesFinished { + t.Fatal("Connection closed before all writes finished") + } + break Loop + } + } + + closeErr := server.Close() + if closeErr != nil { + t.Fatalf("Error shutting down server: %s", closeErr) + } + wg.Wait() +} + +func TestIdleRace(t *testing.T) { + var wg sync.WaitGroup + server, listen, serverErr := runServer(&wg) + if serverErr != nil { + t.Fatalf("Error initializing server: %s", serverErr) + } + + conn, dialErr := net.Dial("tcp", listen) + if dialErr != nil { + t.Fatalf("Error dialing server: %s", dialErr) + } + + spdyConn, spdyErr := NewConnection(conn, false) + if spdyErr != nil { + t.Fatalf("Error creating spdy connection: %s", spdyErr) + } + go spdyConn.Serve(NoOpStreamHandler) + + spdyConn.SetIdleTimeout(10 * time.Millisecond) + + authenticated = true + + for i := 0; i < 10; i++ { + _, err := spdyConn.CreateStream(http.Header{}, nil, false) + if err != nil { + t.Fatalf("Error creating stream: %v", err) + } + } + + <-spdyConn.CloseChan() + + closeErr := server.Close() + if closeErr != nil { + t.Fatalf("Error shutting down server: %s", closeErr) + } + wg.Wait() +} + +func TestHalfClosedIdleTimeout(t *testing.T) { + listener, listenErr := net.Listen("tcp", "localhost:0") + if listenErr != nil { + t.Fatalf("Error listening: %v", listenErr) + } + listen := listener.Addr().String() + + go func() { + serverConn, err := listener.Accept() + if err != nil { + t.Fatalf("Error accepting: %v", err) + } + + serverSpdyConn, err := NewConnection(serverConn, true) + if err != nil { + t.Fatalf("Error creating server connection: %v", err) + } + go serverSpdyConn.Serve(func(s *Stream) { + s.SendReply(http.Header{}, true) + }) + serverSpdyConn.SetIdleTimeout(10 * time.Millisecond) + }() + + conn, dialErr := net.Dial("tcp", listen) + if dialErr != nil { + t.Fatalf("Error dialing server: %s", dialErr) + } + + spdyConn, spdyErr := NewConnection(conn, false) + if spdyErr != nil { + t.Fatalf("Error creating spdy connection: %s", spdyErr) + } + go spdyConn.Serve(NoOpStreamHandler) + + stream, err := spdyConn.CreateStream(http.Header{}, nil, false) + if err != nil { + t.Fatalf("Error creating stream: %v", err) + } + + time.Sleep(20 * time.Millisecond) + + stream.Reset() + + err = spdyConn.Close() + if err != nil { + t.Fatalf("Error closing client spdy conn: %v", err) + } +} + +func TestStreamReset(t *testing.T) { + var wg sync.WaitGroup + server, listen, serverErr := runServer(&wg) + if serverErr != nil { + t.Fatalf("Error initializing server: %s", serverErr) + } + + conn, dialErr := net.Dial("tcp", listen) + if dialErr != nil { + t.Fatalf("Error dialing server: %s", dialErr) + } + + spdyConn, spdyErr := NewConnection(conn, false) + if spdyErr != nil { + t.Fatalf("Error creating spdy connection: %s", spdyErr) + } + go spdyConn.Serve(NoOpStreamHandler) + + authenticated = true + stream, streamErr := spdyConn.CreateStream(http.Header{}, nil, false) + if streamErr != nil { + t.Fatalf("Error creating stream: %s", streamErr) + } + + buf := []byte("dskjahfkdusahfkdsahfkdsafdkas") + for i := 0; i < 10; i++ { + if _, err := stream.Write(buf); err != nil { + t.Fatalf("Error writing to stream: %s", err) + } + } + for i := 0; i < 10; i++ { + if _, err := stream.Read(buf); err != nil { + t.Fatalf("Error reading from stream: %s", err) + } + } + + // fmt.Printf("Resetting...\n") + if err := stream.Reset(); err != nil { + t.Fatalf("Error reseting stream: %s", err) + } + + closeErr := server.Close() + if closeErr != nil { + t.Fatalf("Error shutting down server: %s", closeErr) + } + wg.Wait() +} + +func TestStreamResetWithDataRemaining(t *testing.T) { + var wg sync.WaitGroup + server, listen, serverErr := runServer(&wg) + if serverErr != nil { + t.Fatalf("Error initializing server: %s", serverErr) + } + + conn, dialErr := net.Dial("tcp", listen) + if dialErr != nil { + t.Fatalf("Error dialing server: %s", dialErr) + } + + spdyConn, spdyErr := NewConnection(conn, false) + if spdyErr != nil { + t.Fatalf("Error creating spdy connection: %s", spdyErr) + } + go spdyConn.Serve(NoOpStreamHandler) + + authenticated = true + stream, streamErr := spdyConn.CreateStream(http.Header{}, nil, false) + if streamErr != nil { + t.Fatalf("Error creating stream: %s", streamErr) + } + + buf := []byte("dskjahfkdusahfkdsahfkdsafdkas") + for i := 0; i < 10; i++ { + if _, err := stream.Write(buf); err != nil { + t.Fatalf("Error writing to stream: %s", err) + } + } + + // read a bit to make sure a goroutine gets to <-dataChan + if _, err := stream.Read(buf); err != nil { + t.Fatalf("Error reading from stream: %s", err) + } + + // fmt.Printf("Resetting...\n") + if err := stream.Reset(); err != nil { + t.Fatalf("Error reseting stream: %s", err) + } + + closeErr := server.Close() + if closeErr != nil { + t.Fatalf("Error shutting down server: %s", closeErr) + } + wg.Wait() +} + +type roundTripper struct { + conn net.Conn +} + +func (s *roundTripper) RoundTrip(req *http.Request) (*http.Response, error) { + r := *req + req = &r + + conn, err := net.Dial("tcp", req.URL.Host) + if err != nil { + return nil, err + } + + err = req.Write(conn) + if err != nil { + return nil, err + } + + resp, err := http.ReadResponse(bufio.NewReader(conn), req) + if err != nil { + return nil, err + } + + s.conn = conn + + return resp, nil +} + +// see https://github.com/GoogleCloudPlatform/kubernetes/issues/4882 +func TestFramingAfterRemoteConnectionClosed(t *testing.T) { + server := httptest.NewUnstartedServer(http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { + streamCh := make(chan *Stream) + + w.WriteHeader(http.StatusSwitchingProtocols) + + netconn, _, _ := w.(http.Hijacker).Hijack() + conn, _ := NewConnection(netconn, true) + go conn.Serve(func(s *Stream) { + s.SendReply(http.Header{}, false) + streamCh <- s + }) + + stream := <-streamCh + io.Copy(stream, stream) + + closeChan := make(chan struct{}) + go func() { + stream.Reset() + conn.Close() + close(closeChan) + }() + + <-closeChan + })) + + server.Start() + defer server.Close() + + req, err := http.NewRequest("GET", server.URL, nil) + if err != nil { + t.Fatalf("Error creating request: %s", err) + } + + rt := &roundTripper{} + client := &http.Client{Transport: rt} + + _, err = client.Do(req) + if err != nil { + t.Fatalf("unexpected error from client.Do: %s", err) + } + + conn, err := NewConnection(rt.conn, false) + go conn.Serve(NoOpStreamHandler) + + stream, err := conn.CreateStream(http.Header{}, nil, false) + if err != nil { + t.Fatalf("error creating client stream: %s", err) + } + + n, err := stream.Write([]byte("hello")) + if err != nil { + t.Fatalf("error writing to stream: %s", err) + } + if n != 5 { + t.Fatalf("Expected to write 5 bytes, but actually wrote %d", n) + } + + b := make([]byte, 5) + n, err = stream.Read(b) + if err != nil { + t.Fatalf("error reading from stream: %s", err) + } + if n != 5 { + t.Fatalf("Expected to read 5 bytes, but actually read %d", n) + } + if e, a := "hello", string(b[0:n]); e != a { + t.Fatalf("expected '%s', got '%s'", e, a) + } + + stream.Reset() + conn.Close() +} + +func TestGoAwayRace(t *testing.T) { + var done sync.WaitGroup + listener, err := net.Listen("tcp", "localhost:0") + if err != nil { + t.Fatalf("Error listening: %v", err) + } + listen := listener.Addr().String() + + processDataFrame := make(chan struct{}) + serverClosed := make(chan struct{}) + + done.Add(1) + go func() { + defer done.Done() + serverConn, err := listener.Accept() + if err != nil { + t.Fatalf("Error accepting: %v", err) + } + + serverSpdyConn, err := NewConnection(serverConn, true) + if err != nil { + t.Fatalf("Error creating server connection: %v", err) + } + go func() { + <-serverSpdyConn.CloseChan() + close(serverClosed) + }() + + // force the data frame handler to sleep before delivering the frame + serverSpdyConn.dataFrameHandler = func(frame *spdy.DataFrame) error { + <-processDataFrame + return serverSpdyConn.handleDataFrame(frame) + } + + streamCh := make(chan *Stream) + go serverSpdyConn.Serve(func(s *Stream) { + s.SendReply(http.Header{}, false) + streamCh <- s + }) + + stream, ok := <-streamCh + if !ok { + t.Fatalf("didn't get a stream") + } + stream.Close() + data, err := ioutil.ReadAll(stream) + if err != nil { + t.Error(err) + } + if e, a := "hello1hello2hello3hello4hello5", string(data); e != a { + t.Errorf("Expected %q, got %q", e, a) + } + }() + + dialConn, err := net.Dial("tcp", listen) + if err != nil { + t.Fatalf("Error dialing server: %s", err) + } + conn, err := NewConnection(dialConn, false) + if err != nil { + t.Fatalf("Error creating client connectin: %v", err) + } + go conn.Serve(NoOpStreamHandler) + + stream, err := conn.CreateStream(http.Header{}, nil, false) + if err != nil { + t.Fatalf("error creating client stream: %s", err) + } + if err := stream.Wait(); err != nil { + t.Fatalf("error waiting for stream creation: %v", err) + } + + fmt.Fprint(stream, "hello1") + fmt.Fprint(stream, "hello2") + fmt.Fprint(stream, "hello3") + fmt.Fprint(stream, "hello4") + fmt.Fprint(stream, "hello5") + + stream.Close() + conn.Close() + + // wait for the server to get the go away frame + <-serverClosed + + // allow the data frames to be delivered to the server's stream + close(processDataFrame) + + done.Wait() +} + +func TestSetIdleTimeoutAfterRemoteConnectionClosed(t *testing.T) { + listener, err := net.Listen("tcp", "localhost:0") + if err != nil { + t.Fatalf("Error listening: %v", err) + } + listen := listener.Addr().String() + + serverConns := make(chan *Connection, 1) + go func() { + conn, connErr := listener.Accept() + if connErr != nil { + t.Fatal(connErr) + } + serverSpdyConn, err := NewConnection(conn, true) + if err != nil { + t.Fatalf("Error creating server connection: %v", err) + } + go serverSpdyConn.Serve(NoOpStreamHandler) + serverConns <- serverSpdyConn + }() + + conn, dialErr := net.Dial("tcp", listen) + if dialErr != nil { + t.Fatalf("Error dialing server: %s", dialErr) + } + + spdyConn, spdyErr := NewConnection(conn, false) + if spdyErr != nil { + t.Fatalf("Error creating spdy connection: %s", spdyErr) + } + go spdyConn.Serve(NoOpStreamHandler) + + if err := spdyConn.Close(); err != nil { + t.Fatal(err) + } + + serverConn := <-serverConns + defer serverConn.Close() + <-serverConn.closeChan + + serverConn.SetIdleTimeout(10 * time.Second) +} + +func TestClientConnectionStopsServingAfterGoAway(t *testing.T) { + listener, err := net.Listen("tcp", "localhost:0") + if err != nil { + t.Fatalf("Error listening: %v", err) + } + listen := listener.Addr().String() + + serverConns := make(chan *Connection, 1) + go func() { + conn, connErr := listener.Accept() + if connErr != nil { + t.Fatal(connErr) + } + serverSpdyConn, err := NewConnection(conn, true) + if err != nil { + t.Fatalf("Error creating server connection: %v", err) + } + go serverSpdyConn.Serve(NoOpStreamHandler) + serverConns <- serverSpdyConn + }() + + conn, dialErr := net.Dial("tcp", listen) + if dialErr != nil { + t.Fatalf("Error dialing server: %s", dialErr) + } + + spdyConn, spdyErr := NewConnection(conn, false) + if spdyErr != nil { + t.Fatalf("Error creating spdy connection: %s", spdyErr) + } + go spdyConn.Serve(NoOpStreamHandler) + + stream, err := spdyConn.CreateStream(http.Header{}, nil, false) + if err != nil { + t.Fatalf("Error creating stream: %v", err) + } + if err := stream.WaitTimeout(30 * time.Second); err != nil { + t.Fatalf("Timed out waiting for stream: %v", err) + } + + readChan := make(chan struct{}) + go func() { + _, err := ioutil.ReadAll(stream) + if err != nil { + t.Fatalf("Error reading stream: %v", err) + } + close(readChan) + }() + + serverConn := <-serverConns + serverConn.Close() + + // make sure the client conn breaks out of the main loop in Serve() + <-spdyConn.closeChan + // make sure the remote channels are closed and the stream read is unblocked + <-readChan +} + +func TestStreamReadUnblocksAfterCloseThenReset(t *testing.T) { + listener, err := net.Listen("tcp", "localhost:0") + if err != nil { + t.Fatalf("Error listening: %v", err) + } + listen := listener.Addr().String() + + serverConns := make(chan *Connection, 1) + go func() { + conn, connErr := listener.Accept() + if connErr != nil { + t.Fatal(connErr) + } + serverSpdyConn, err := NewConnection(conn, true) + if err != nil { + t.Fatalf("Error creating server connection: %v", err) + } + go serverSpdyConn.Serve(NoOpStreamHandler) + serverConns <- serverSpdyConn + }() + + conn, dialErr := net.Dial("tcp", listen) + if dialErr != nil { + t.Fatalf("Error dialing server: %s", dialErr) + } + + spdyConn, spdyErr := NewConnection(conn, false) + if spdyErr != nil { + t.Fatalf("Error creating spdy connection: %s", spdyErr) + } + go spdyConn.Serve(NoOpStreamHandler) + + stream, err := spdyConn.CreateStream(http.Header{}, nil, false) + if err != nil { + t.Fatalf("Error creating stream: %v", err) + } + if err := stream.WaitTimeout(30 * time.Second); err != nil { + t.Fatalf("Timed out waiting for stream: %v", err) + } + + readChan := make(chan struct{}) + go func() { + _, err := ioutil.ReadAll(stream) + if err != nil { + t.Fatalf("Error reading stream: %v", err) + } + close(readChan) + }() + + serverConn := <-serverConns + defer serverConn.Close() + + if err := stream.Close(); err != nil { + t.Fatal(err) + } + if err := stream.Reset(); err != nil { + t.Fatal(err) + } + + // make sure close followed by reset unblocks stream.Read() + select { + case <-readChan: + case <-time.After(10 * time.Second): + t.Fatal("Timed out waiting for stream read to unblock") + } +} + +var authenticated bool + +func authStreamHandler(stream *Stream) { + if !authenticated { + stream.Refuse() + return + } + MirrorStreamHandler(stream) +} + +func runServer(wg *sync.WaitGroup) (io.Closer, string, error) { + listener, listenErr := net.Listen("tcp", "localhost:0") + if listenErr != nil { + return nil, "", listenErr + } + wg.Add(1) + go func() { + for { + conn, connErr := listener.Accept() + if connErr != nil { + break + } + + spdyConn, _ := NewConnection(conn, true) + go spdyConn.Serve(authStreamHandler) + + } + wg.Done() + }() + return listener, listener.Addr().String(), nil +} diff --git a/vendor/github.com/docker/spdystream/stream.go b/vendor/github.com/docker/spdystream/stream.go new file mode 100644 index 000000000..f9e9ee267 --- /dev/null +++ b/vendor/github.com/docker/spdystream/stream.go @@ -0,0 +1,327 @@ +package spdystream + +import ( + "errors" + "fmt" + "io" + "net" + "net/http" + "sync" + "time" + + "github.com/docker/spdystream/spdy" +) + +var ( + ErrUnreadPartialData = errors.New("unread partial data") +) + +type Stream struct { + streamId spdy.StreamId + parent *Stream + conn *Connection + startChan chan error + + dataLock sync.RWMutex + dataChan chan []byte + unread []byte + + priority uint8 + headers http.Header + headerChan chan http.Header + finishLock sync.Mutex + finished bool + replyCond *sync.Cond + replied bool + closeLock sync.Mutex + closeChan chan bool +} + +// WriteData writes data to stream, sending a dataframe per call +func (s *Stream) WriteData(data []byte, fin bool) error { + s.waitWriteReply() + var flags spdy.DataFlags + + if fin { + flags = spdy.DataFlagFin + s.finishLock.Lock() + if s.finished { + s.finishLock.Unlock() + return ErrWriteClosedStream + } + s.finished = true + s.finishLock.Unlock() + } + + dataFrame := &spdy.DataFrame{ + StreamId: s.streamId, + Flags: flags, + Data: data, + } + + debugMessage("(%p) (%d) Writing data frame", s, s.streamId) + return s.conn.framer.WriteFrame(dataFrame) +} + +// Write writes bytes to a stream, calling write data for each call. +func (s *Stream) Write(data []byte) (n int, err error) { + err = s.WriteData(data, false) + if err == nil { + n = len(data) + } + return +} + +// Read reads bytes from a stream, a single read will never get more +// than what is sent on a single data frame, but a multiple calls to +// read may get data from the same data frame. +func (s *Stream) Read(p []byte) (n int, err error) { + if s.unread == nil { + select { + case <-s.closeChan: + return 0, io.EOF + case read, ok := <-s.dataChan: + if !ok { + return 0, io.EOF + } + s.unread = read + } + } + n = copy(p, s.unread) + if n < len(s.unread) { + s.unread = s.unread[n:] + } else { + s.unread = nil + } + return +} + +// ReadData reads an entire data frame and returns the byte array +// from the data frame. If there is unread data from the result +// of a Read call, this function will return an ErrUnreadPartialData. +func (s *Stream) ReadData() ([]byte, error) { + debugMessage("(%p) Reading data from %d", s, s.streamId) + if s.unread != nil { + return nil, ErrUnreadPartialData + } + select { + case <-s.closeChan: + return nil, io.EOF + case read, ok := <-s.dataChan: + if !ok { + return nil, io.EOF + } + return read, nil + } +} + +func (s *Stream) waitWriteReply() { + if s.replyCond != nil { + s.replyCond.L.Lock() + for !s.replied { + s.replyCond.Wait() + } + s.replyCond.L.Unlock() + } +} + +// Wait waits for the stream to receive a reply. +func (s *Stream) Wait() error { + return s.WaitTimeout(time.Duration(0)) +} + +// WaitTimeout waits for the stream to receive a reply or for timeout. +// When the timeout is reached, ErrTimeout will be returned. +func (s *Stream) WaitTimeout(timeout time.Duration) error { + var timeoutChan <-chan time.Time + if timeout > time.Duration(0) { + timeoutChan = time.After(timeout) + } + + select { + case err := <-s.startChan: + if err != nil { + return err + } + break + case <-timeoutChan: + return ErrTimeout + } + return nil +} + +// Close closes the stream by sending an empty data frame with the +// finish flag set, indicating this side is finished with the stream. +func (s *Stream) Close() error { + select { + case <-s.closeChan: + // Stream is now fully closed + s.conn.removeStream(s) + default: + break + } + return s.WriteData([]byte{}, true) +} + +// Reset sends a reset frame, putting the stream into the fully closed state. +func (s *Stream) Reset() error { + s.conn.removeStream(s) + return s.resetStream() +} + +func (s *Stream) resetStream() error { + // Always call closeRemoteChannels, even if s.finished is already true. + // This makes it so that stream.Close() followed by stream.Reset() allows + // stream.Read() to unblock. + s.closeRemoteChannels() + + s.finishLock.Lock() + if s.finished { + s.finishLock.Unlock() + return nil + } + s.finished = true + s.finishLock.Unlock() + + resetFrame := &spdy.RstStreamFrame{ + StreamId: s.streamId, + Status: spdy.Cancel, + } + return s.conn.framer.WriteFrame(resetFrame) +} + +// CreateSubStream creates a stream using the current as the parent +func (s *Stream) CreateSubStream(headers http.Header, fin bool) (*Stream, error) { + return s.conn.CreateStream(headers, s, fin) +} + +// SetPriority sets the stream priority, does not affect the +// remote priority of this stream after Open has been called. +// Valid values are 0 through 7, 0 being the highest priority +// and 7 the lowest. +func (s *Stream) SetPriority(priority uint8) { + s.priority = priority +} + +// SendHeader sends a header frame across the stream +func (s *Stream) SendHeader(headers http.Header, fin bool) error { + return s.conn.sendHeaders(headers, s, fin) +} + +// SendReply sends a reply on a stream, only valid to be called once +// when handling a new stream +func (s *Stream) SendReply(headers http.Header, fin bool) error { + if s.replyCond == nil { + return errors.New("cannot reply on initiated stream") + } + s.replyCond.L.Lock() + defer s.replyCond.L.Unlock() + if s.replied { + return nil + } + + err := s.conn.sendReply(headers, s, fin) + if err != nil { + return err + } + + s.replied = true + s.replyCond.Broadcast() + return nil +} + +// Refuse sends a reset frame with the status refuse, only +// valid to be called once when handling a new stream. This +// may be used to indicate that a stream is not allowed +// when http status codes are not being used. +func (s *Stream) Refuse() error { + if s.replied { + return nil + } + s.replied = true + return s.conn.sendReset(spdy.RefusedStream, s) +} + +// Cancel sends a reset frame with the status canceled. This +// can be used at any time by the creator of the Stream to +// indicate the stream is no longer needed. +func (s *Stream) Cancel() error { + return s.conn.sendReset(spdy.Cancel, s) +} + +// ReceiveHeader receives a header sent on the other side +// of the stream. This function will block until a header +// is received or stream is closed. +func (s *Stream) ReceiveHeader() (http.Header, error) { + select { + case <-s.closeChan: + break + case header, ok := <-s.headerChan: + if !ok { + return nil, fmt.Errorf("header chan closed") + } + return header, nil + } + return nil, fmt.Errorf("stream closed") +} + +// Parent returns the parent stream +func (s *Stream) Parent() *Stream { + return s.parent +} + +// Headers returns the headers used to create the stream +func (s *Stream) Headers() http.Header { + return s.headers +} + +// String returns the string version of stream using the +// streamId to uniquely identify the stream +func (s *Stream) String() string { + return fmt.Sprintf("stream:%d", s.streamId) +} + +// Identifier returns a 32 bit identifier for the stream +func (s *Stream) Identifier() uint32 { + return uint32(s.streamId) +} + +// IsFinished returns whether the stream has finished +// sending data +func (s *Stream) IsFinished() bool { + return s.finished +} + +// Implement net.Conn interface + +func (s *Stream) LocalAddr() net.Addr { + return s.conn.conn.LocalAddr() +} + +func (s *Stream) RemoteAddr() net.Addr { + return s.conn.conn.RemoteAddr() +} + +// TODO set per stream values instead of connection-wide + +func (s *Stream) SetDeadline(t time.Time) error { + return s.conn.conn.SetDeadline(t) +} + +func (s *Stream) SetReadDeadline(t time.Time) error { + return s.conn.conn.SetReadDeadline(t) +} + +func (s *Stream) SetWriteDeadline(t time.Time) error { + return s.conn.conn.SetWriteDeadline(t) +} + +func (s *Stream) closeRemoteChannels() { + s.closeLock.Lock() + defer s.closeLock.Unlock() + select { + case <-s.closeChan: + default: + close(s.closeChan) + } +} diff --git a/vendor/github.com/docker/spdystream/utils.go b/vendor/github.com/docker/spdystream/utils.go new file mode 100644 index 000000000..1b2c199a4 --- /dev/null +++ b/vendor/github.com/docker/spdystream/utils.go @@ -0,0 +1,16 @@ +package spdystream + +import ( + "log" + "os" +) + +var ( + DEBUG = os.Getenv("DEBUG") +) + +func debugMessage(fmt string, args ...interface{}) { + if DEBUG != "" { + log.Printf(fmt, args...) + } +} diff --git a/vendor/github.com/docker/spdystream/ws/connection.go b/vendor/github.com/docker/spdystream/ws/connection.go new file mode 100644 index 000000000..06dc59afe --- /dev/null +++ b/vendor/github.com/docker/spdystream/ws/connection.go @@ -0,0 +1,81 @@ +package ws + +import ( + "fmt" + "io" + "time" + + "github.com/gorilla/websocket" +) + +// Wrap an HTTP2 connection over WebSockets and +// use the underlying WebSocket framing for proxy +// compatibility. +type Conn struct { + *websocket.Conn + reader io.Reader +} + +func NewConnection(w *websocket.Conn) *Conn { + return &Conn{Conn: w} +} + +func (c *Conn) Write(b []byte) (int, error) { + err := c.WriteMessage(websocket.BinaryMessage, b) + if err != nil { + return 0, err + } + return len(b), nil +} + +func (c *Conn) Read(b []byte) (int, error) { + if c.reader == nil { + if err := c.nextReader(); err != nil { + return 0, err + } + } + + for { + n, err := c.reader.Read(b) + if err != nil { + if err != io.EOF { + return n, err + } + + // get next reader if there is no data in the current one + if err := c.nextReader(); err != nil { + return 0, err + } + continue + } + return n, nil + } +} + +func (c *Conn) nextReader() error { + t, r, err := c.NextReader() + if err != nil { + return err + } + + if t != websocket.BinaryMessage { + return fmt.Errorf("ws: non-binary message in stream") + } + c.reader = r + return nil +} + +func (c *Conn) SetDeadline(t time.Time) error { + if err := c.Conn.SetReadDeadline(t); err != nil { + return err + } + if err := c.Conn.SetWriteDeadline(t); err != nil { + return err + } + return nil +} + +func (c *Conn) Close() error { + err := c.Conn.Close() + return err +} diff --git a/vendor/github.com/docker/spdystream/ws/ws_test.go b/vendor/github.com/docker/spdystream/ws/ws_test.go new file mode 100644 index 000000000..1abd03b28 --- /dev/null +++ b/vendor/github.com/docker/spdystream/ws/ws_test.go @@ -0,0 +1,183 @@ +package ws + +import ( + "bytes" + "io" + "log" + "net/http" + "net/http/httptest" + "strings" + "testing" + + "github.com/docker/spdystream" + "github.com/gorilla/websocket" +) + +var upgrader = websocket.Upgrader{ + ReadBufferSize: 1024, + WriteBufferSize: 1024, +} + +var serverSpdyConn *spdystream.Connection + +// Connect to the Websocket endpoint at ws://localhost +// using SPDY over Websockets framing. +func ExampleConn() { + wsconn, _, _ := websocket.DefaultDialer.Dial("ws://localhost/", http.Header{"Origin": {"http://localhost/"}}) + conn, _ := spdystream.NewConnection(NewConnection(wsconn), false) + go conn.Serve(spdystream.NoOpStreamHandler) + stream, _ := conn.CreateStream(http.Header{}, nil, false) + stream.Wait() +} + +func serveWs(w http.ResponseWriter, r *http.Request) { + if r.Method != "GET" { + http.Error(w, "Method not allowed", 405) + return + } + + ws, err := upgrader.Upgrade(w, r, nil) + if err != nil { + if _, ok := err.(websocket.HandshakeError); !ok { + log.Println(err) + } + return + } + + wrap := NewConnection(ws) + spdyConn, err := spdystream.NewConnection(wrap, true) + if err != nil { + log.Fatal(err) + return + } + serverSpdyConn = spdyConn + spdyConn.Serve(authStreamHandler) +} + +func TestSpdyStreamOverWs(t *testing.T) { + server := httptest.NewServer(http.HandlerFunc(serveWs)) + defer server.Close() + defer func() { + if serverSpdyConn != nil { + serverSpdyConn.Close() + } + }() + + wsconn, _, err := websocket.DefaultDialer.Dial(strings.Replace(server.URL, "http://", "ws://", 1), http.Header{"Origin": {server.URL}}) + if err != nil { + t.Fatal(err) + } + + wrap := NewConnection(wsconn) + spdyConn, err := spdystream.NewConnection(wrap, false) + if err != nil { + defer wsconn.Close() + t.Fatal(err) + } + defer spdyConn.Close() + authenticated = true + go spdyConn.Serve(spdystream.NoOpStreamHandler) + + stream, streamErr := spdyConn.CreateStream(http.Header{}, nil, false) + if streamErr != nil { + t.Fatalf("Error creating stream: %s", streamErr) + } + + waitErr := stream.Wait() + if waitErr != nil { + t.Fatalf("Error waiting for stream: %s", waitErr) + } + + message := []byte("hello") + writeErr := stream.WriteData(message, false) + if writeErr != nil { + t.Fatalf("Error writing data") + } + + buf := make([]byte, 10) + n, readErr := stream.Read(buf) + if readErr != nil { + t.Fatalf("Error reading data from stream: %s", readErr) + } + if n != 5 { + t.Fatalf("Unexpected number of bytes read:\nActual: %d\nExpected: 5", n) + } + if bytes.Compare(buf[:n], message) != 0 { + t.Fatalf("Did not receive expected message:\nActual: %s\nExpectd: %s", buf, message) + } + + writeErr = stream.WriteData(message, true) + if writeErr != nil { + t.Fatalf("Error writing data") + } + + smallBuf := make([]byte, 3) + n, readErr = stream.Read(smallBuf) + if readErr != nil { + t.Fatalf("Error reading data from stream: %s", readErr) + } + if n != 3 { + t.Fatalf("Unexpected number of bytes read:\nActual: %d\nExpected: 3", n) + } + if bytes.Compare(smallBuf[:n], []byte("hel")) != 0 { + t.Fatalf("Did not receive expected message:\nActual: %s\nExpectd: %s", smallBuf[:n], message) + } + n, readErr = stream.Read(smallBuf) + if readErr != nil { + t.Fatalf("Error reading data from stream: %s", readErr) + } + if n != 2 { + t.Fatalf("Unexpected number of bytes read:\nActual: %d\nExpected: 2", n) + } + if bytes.Compare(smallBuf[:n], []byte("lo")) != 0 { + t.Fatalf("Did not receive expected message:\nActual: %s\nExpected: lo", smallBuf[:n]) + } + + n, readErr = stream.Read(buf) + if readErr != io.EOF { + t.Fatalf("Expected EOF reading from finished stream, read %d bytes", n) + } + + // Closing again should return error since the stream is already closed + streamCloseErr := stream.Close() + if streamCloseErr == nil { + t.Fatalf("No error closing finished stream") + } + if streamCloseErr != spdystream.ErrWriteClosedStream { + t.Fatalf("Unexpected error closing stream: %s", streamCloseErr) + } + + streamResetErr := stream.Reset() + if streamResetErr != nil { + t.Fatalf("Error reseting stream: %s", streamResetErr) + } + + authenticated = false + badStream, badStreamErr := spdyConn.CreateStream(http.Header{}, nil, false) + if badStreamErr != nil { + t.Fatalf("Error creating stream: %s", badStreamErr) + } + + waitErr = badStream.Wait() + if waitErr == nil { + t.Fatalf("Did not receive error creating stream") + } + if waitErr != spdystream.ErrReset { + t.Fatalf("Unexpected error creating stream: %s", waitErr) + } + + spdyCloseErr := spdyConn.Close() + if spdyCloseErr != nil { + t.Fatalf("Error closing spdy connection: %s", spdyCloseErr) + } +} + +var authenticated bool + +func authStreamHandler(stream *spdystream.Stream) { + if !authenticated { + stream.Refuse() + return + } + spdystream.MirrorStreamHandler(stream) +} diff --git a/vendor/github.com/mitchellh/go-wordwrap/LICENSE.md b/vendor/github.com/mitchellh/go-wordwrap/LICENSE.md new file mode 100644 index 000000000..229851590 --- /dev/null +++ b/vendor/github.com/mitchellh/go-wordwrap/LICENSE.md @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2014 Mitchell Hashimoto + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/vendor/github.com/mitchellh/go-wordwrap/README.md b/vendor/github.com/mitchellh/go-wordwrap/README.md new file mode 100644 index 000000000..60ae31170 --- /dev/null +++ b/vendor/github.com/mitchellh/go-wordwrap/README.md @@ -0,0 +1,39 @@ +# go-wordwrap + +`go-wordwrap` (Golang package: `wordwrap`) is a package for Go that +automatically wraps words into multiple lines. The primary use case for this +is in formatting CLI output, but of course word wrapping is a generally useful +thing to do. + +## Installation and Usage + +Install using `go get github.com/mitchellh/go-wordwrap`. + +Full documentation is available at +http://godoc.org/github.com/mitchellh/go-wordwrap + +Below is an example of its usage ignoring errors: + +```go +wrapped := wordwrap.WrapString("foo bar baz", 3) +fmt.Println(wrapped) +``` + +Would output: + +``` +foo +bar +baz +``` + +## Word Wrap Algorithm + +This library doesn't use any clever algorithm for word wrapping. The wrapping +is actually very naive: whenever there is whitespace or an explicit linebreak. +The goal of this library is for word wrapping CLI output, so the input is +typically pretty well controlled human language. Because of this, the naive +approach typically works just fine. + +In the future, we'd like to make the algorithm more advanced. We would do +so without breaking the API. diff --git a/vendor/github.com/mitchellh/go-wordwrap/go.mod b/vendor/github.com/mitchellh/go-wordwrap/go.mod new file mode 100644 index 000000000..2ae411b20 --- /dev/null +++ b/vendor/github.com/mitchellh/go-wordwrap/go.mod @@ -0,0 +1 @@ +module github.com/mitchellh/go-wordwrap diff --git a/vendor/github.com/mitchellh/go-wordwrap/wordwrap.go b/vendor/github.com/mitchellh/go-wordwrap/wordwrap.go new file mode 100644 index 000000000..ac67205bc --- /dev/null +++ b/vendor/github.com/mitchellh/go-wordwrap/wordwrap.go @@ -0,0 +1,73 @@ +package wordwrap + +import ( + "bytes" + "unicode" +) + +// WrapString wraps the given string within lim width in characters. +// +// Wrapping is currently naive and only happens at white-space. A future +// version of the library will implement smarter wrapping. This means that +// pathological cases can dramatically reach past the limit, such as a very +// long word. +func WrapString(s string, lim uint) string { + // Initialize a buffer with a slightly larger size to account for breaks + init := make([]byte, 0, len(s)) + buf := bytes.NewBuffer(init) + + var current uint + var wordBuf, spaceBuf bytes.Buffer + + for _, char := range s { + if char == '\n' { + if wordBuf.Len() == 0 { + if current+uint(spaceBuf.Len()) > lim { + current = 0 + } else { + current += uint(spaceBuf.Len()) + spaceBuf.WriteTo(buf) + } + spaceBuf.Reset() + } else { + current += uint(spaceBuf.Len() + wordBuf.Len()) + spaceBuf.WriteTo(buf) + spaceBuf.Reset() + wordBuf.WriteTo(buf) + wordBuf.Reset() + } + buf.WriteRune(char) + current = 0 + } else if unicode.IsSpace(char) { + if spaceBuf.Len() == 0 || wordBuf.Len() > 0 { + current += uint(spaceBuf.Len() + wordBuf.Len()) + spaceBuf.WriteTo(buf) + spaceBuf.Reset() + wordBuf.WriteTo(buf) + wordBuf.Reset() + } + + spaceBuf.WriteRune(char) + } else { + + wordBuf.WriteRune(char) + + if current+uint(spaceBuf.Len()+wordBuf.Len()) > lim && uint(wordBuf.Len()) < lim { + buf.WriteRune('\n') + current = 0 + spaceBuf.Reset() + } + } + } + + if wordBuf.Len() == 0 { + if current+uint(spaceBuf.Len()) <= lim { + spaceBuf.WriteTo(buf) + } + } else { + spaceBuf.WriteTo(buf) + wordBuf.WriteTo(buf) + } + + return buf.String() +} diff --git a/vendor/github.com/mitchellh/go-wordwrap/wordwrap_test.go b/vendor/github.com/mitchellh/go-wordwrap/wordwrap_test.go new file mode 100644 index 000000000..bf6ae13da --- /dev/null +++ b/vendor/github.com/mitchellh/go-wordwrap/wordwrap_test.go @@ -0,0 +1,85 @@ +package wordwrap + +import ( + "testing" +) + +func TestWrapString(t *testing.T) { + cases := []struct { + Input, Output string + Lim uint + }{ + // A simple word passes through. + { + "foo", + "foo", + 4, + }, + // A single word that is too long passes through. + // We do not break words. + { + "foobarbaz", + "foobarbaz", + 4, + }, + // Lines are broken at whitespace. + { + "foo bar baz", + "foo\nbar\nbaz", + 4, + }, + // Lines are broken at whitespace, even if words + // are too long. We do not break words. + { + "foo bars bazzes", + "foo\nbars\nbazzes", + 4, + }, + // A word that would run beyond the width is wrapped. + { + "fo sop", + "fo\nsop", + 4, + }, + // Whitespace that trails a line and fits the width + // passes through, as does whitespace prefixing an + // explicit line break. A tab counts as one character. + { + "foo\nb\t r\n baz", + "foo\nb\t r\n baz", + 4, + }, + // Trailing whitespace is removed if it doesn't fit the width. + // Runs of whitespace on which a line is broken are removed. + { + "foo \nb ar ", + "foo\nb\nar", + 4, + }, + // An explicit line break at the end of the input is preserved. + { + "foo bar baz\n", + "foo\nbar\nbaz\n", + 4, + }, + // Explicit break are always preserved. + { + "\nfoo bar\n\n\nbaz\n", + "\nfoo\nbar\n\n\nbaz\n", + 4, + }, + // Complete example: + { + " This is a list: \n\n\t* foo\n\t* bar\n\n\n\t* baz \nBAM ", + " This\nis a\nlist: \n\n\t* foo\n\t* bar\n\n\n\t* baz\nBAM", + 6, + }, + } + + for i, tc := range cases { + actual := WrapString(tc.Input, tc.Lim) + if actual != tc.Output { + t.Fatalf("Case %d Input:\n\n`%s`\n\nActual Output:\n\n`%s`", i, tc.Input, actual) + } + } +} diff --git a/vendor/k8s.io/client-go/go.sum b/vendor/k8s.io/client-go/go.sum index 71a31a580..994786944 100644 --- a/vendor/k8s.io/client-go/go.sum +++ b/vendor/k8s.io/client-go/go.sum @@ -179,7 +179,9 @@ gopkg.in/yaml.v2 v2.2.4 h1:/eiJrUcujPVeJ3xlSWaiNi3uSVmDGBK1pDHUHAnao1I= gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +k8s.io/api v0.0.0-20191114100352-16d7abae0d2a h1:86XISgFlG7lPOWj6wYLxd+xqhhVt/WQjS4Tf39rP09s= k8s.io/api v0.0.0-20191114100352-16d7abae0d2a/go.mod h1:qetVJgs5i8jwdFIdoOZ70ks0ecgU+dYwqZ2uD1srwOU= +k8s.io/apimachinery v0.0.0-20191028221656-72ed19daf4bb h1:ZUNsbuPdXWrj0rZziRfCWcFg9ZP31OKkziqCbiphznI= k8s.io/apimachinery v0.0.0-20191028221656-72ed19daf4bb/go.mod h1:llRdnznGEAqC3DcNm6yEj472xaFVfLM7hnYofMb12tQ= k8s.io/gengo v0.0.0-20190128074634-0689ccc1d7d6/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0= k8s.io/klog v0.0.0-20181102134211-b9b56d5dfc92/go.mod h1:Gq+BEi5rUBO/HRz0bTSXDUcqjScdoY3a9IHpCEIOOfk= diff --git a/vendor/k8s.io/kube-aggregator/go.sum b/vendor/k8s.io/kube-aggregator/go.sum index a7644e0a6..2edbcad2f 100644 --- a/vendor/k8s.io/kube-aggregator/go.sum +++ b/vendor/k8s.io/kube-aggregator/go.sum @@ -312,8 +312,10 @@ honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWh honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= k8s.io/api v0.0.0-20191114100352-16d7abae0d2a/go.mod h1:qetVJgs5i8jwdFIdoOZ70ks0ecgU+dYwqZ2uD1srwOU= +k8s.io/apimachinery v0.0.0-20191028221656-72ed19daf4bb h1:ZUNsbuPdXWrj0rZziRfCWcFg9ZP31OKkziqCbiphznI= k8s.io/apimachinery v0.0.0-20191028221656-72ed19daf4bb/go.mod h1:llRdnznGEAqC3DcNm6yEj472xaFVfLM7hnYofMb12tQ= k8s.io/apiserver v0.0.0-20191114103151-9ca1dc586682/go.mod h1:Idob8Va6/sMX5SmwPLsU0pdvFlkwxuJ5x+fXMG8NbKE= +k8s.io/client-go v0.0.0-20191114101535-6c5935290e33 h1:07mhG/2oEoo3N+sHVOo0L9PJ/qvbk3N5n2dj8IWefnQ= k8s.io/client-go v0.0.0-20191114101535-6c5935290e33/go.mod h1:4L/zQOBkEf4pArQJ+CMk1/5xjA30B5oyWv+Bzb44DOw= k8s.io/code-generator v0.0.0-20191004115455-8e001e5d1894/go.mod h1:mJUgkl06XV4kstAnLHAIzJPVCOzVR+ZcfPIv4fUsFCY= k8s.io/component-base v0.0.0-20191114102325-35a9586014f7/go.mod h1:9rNMvrwbqPF4MxI+VQYETrWqMKxi8yAd8YZLdSJ9EDw= diff --git a/vendor/k8s.io/kubernetes/pkg/util/interrupt/BUILD b/vendor/k8s.io/kubernetes/pkg/util/interrupt/BUILD new file mode 100644 index 000000000..6e4484940 --- /dev/null +++ b/vendor/k8s.io/kubernetes/pkg/util/interrupt/BUILD @@ -0,0 +1,25 @@ +package(default_visibility = ["//visibility:public"]) + +load( + "@io_bazel_rules_go//go:def.bzl", + "go_library", +) + +go_library( + name = "go_default_library", + srcs = ["interrupt.go"], + importpath = "k8s.io/kubernetes/pkg/util/interrupt", +) + +filegroup( + name = "package-srcs", + srcs = glob(["**"]), + tags = ["automanaged"], + visibility = ["//visibility:private"], +) + +filegroup( + name = "all-srcs", + srcs = [":package-srcs"], + tags = ["automanaged"], +) diff --git a/vendor/k8s.io/kubernetes/pkg/util/interrupt/interrupt.go b/vendor/k8s.io/kubernetes/pkg/util/interrupt/interrupt.go new file mode 100644 index 000000000..0265b9fb1 --- /dev/null +++ b/vendor/k8s.io/kubernetes/pkg/util/interrupt/interrupt.go @@ -0,0 +1,104 @@ +/* +Copyright 2016 The Kubernetes Authors. + +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 interrupt + +import ( + "os" + "os/signal" + "sync" + "syscall" +) + +// terminationSignals are signals that cause the program to exit in the +// supported platforms (linux, darwin, windows). +var terminationSignals = []os.Signal{syscall.SIGHUP, syscall.SIGINT, syscall.SIGTERM, syscall.SIGQUIT} + +// Handler guarantees execution of notifications after a critical section (the function passed +// to a Run method), even in the presence of process termination. It guarantees exactly once +// invocation of the provided notify functions. +type Handler struct { + notify []func() + final func(os.Signal) + once sync.Once +} + +// Chain creates a new handler that invokes all notify functions when the critical section exits +// and then invokes the optional handler's notifications. This allows critical sections to be +// nested without losing exactly once invocations. Notify functions can invoke any cleanup needed +// but should not exit (which is the responsibility of the parent handler). +func Chain(handler *Handler, notify ...func()) *Handler { + if handler == nil { + return New(nil, notify...) + } + return New(handler.Signal, append(notify, handler.Close)...) +} + +// New creates a new handler that guarantees all notify functions are run after the critical +// section exits (or is interrupted by the OS), then invokes the final handler. If no final +// handler is specified, the default final is `os.Exit(1)`. A handler can only be used for +// one critical section. +func New(final func(os.Signal), notify ...func()) *Handler { + return &Handler{ + final: final, + notify: notify, + } +} + +// Close executes all the notification handlers if they have not yet been executed. +func (h *Handler) Close() { + h.once.Do(func() { + for _, fn := range h.notify { + fn() + } + }) +} + +// Signal is called when an os.Signal is received, and guarantees that all notifications +// are executed, then the final handler is executed. This function should only be called once +// per Handler instance. +func (h *Handler) Signal(s os.Signal) { + h.once.Do(func() { + for _, fn := range h.notify { + fn() + } + if h.final == nil { + os.Exit(1) + } + h.final(s) + }) +} + +// Run ensures that any notifications are invoked after the provided fn exits (even if the +// process is interrupted by an OS termination signal). Notifications are only invoked once +// per Handler instance, so calling Run more than once will not behave as the user expects. +func (h *Handler) Run(fn func() error) error { + ch := make(chan os.Signal, 1) + signal.Notify(ch, terminationSignals...) + defer func() { + signal.Stop(ch) + close(ch) + }() + go func() { + sig, ok := <-ch + if !ok { + return + } + h.Signal(sig) + }() + defer h.Close() + return fn() +} diff --git a/webcli/app/app.go b/webcli/app/app.go index d94579046..9e13982db 100644 --- a/webcli/app/app.go +++ b/webcli/app/app.go @@ -23,41 +23,58 @@ import ( "encoding/hex" "encoding/json" "errors" + "fmt" "net" "net/http" - "os/exec" "sync" "text/template" "github.com/Sirupsen/logrus" + "github.com/kr/pty" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/runtime/schema" httputil "github.com/goodrain/rainbond/util/http" + k8sutil "github.com/goodrain/rainbond/util/k8s" "github.com/gorilla/websocket" - "github.com/kr/pty" "github.com/prometheus/client_golang/prometheus" "github.com/prometheus/client_golang/prometheus/promhttp" "github.com/yudai/umutex" + api "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime/serializer" + "k8s.io/client-go/kubernetes" + "k8s.io/client-go/rest" + restclient "k8s.io/client-go/rest" ) +//ExecuteCommandTotal metric var ExecuteCommandTotal float64 = 0 + +//ExecuteCommandFailed metric var ExecuteCommandFailed float64 = 0 +//App - type App struct { - command []string options *Options upgrader *websocket.Upgrader titleTemplate *template.Template - onceMutex *umutex.UnblockingMutex + onceMutex *umutex.UnblockingMutex + restClient *restclient.RESTClient + coreClient *kubernetes.Clientset + config *restclient.Config } +//Options options type Options struct { - Address string `hcl:"address"` - Port string `hcl:"port"` - PermitWrite bool `hcl:"permit_write"` - IndexFile string `hcl:"index_file"` + Address string `hcl:"address"` + Port string `hcl:"port"` + PermitWrite bool `hcl:"permit_write"` + IndexFile string `hcl:"index_file"` + //titile format by golang templete TitleFormat string `hcl:"title_format"` EnableReconnect bool `hcl:"enable_reconnect"` ReconnectTime int `hcl:"reconnect_time"` @@ -66,10 +83,13 @@ type Options struct { Preferences HtermPrefernces `hcl:"preferences"` RawPreferences map[string]interface{} `hcl:"preferences"` SessionKey string `hcl:"session_key"` + K8SConfPath string } +//Version - var Version = "0.0.2" +//DefaultOptions - var DefaultOptions = Options{ Address: "", Port: "8080", @@ -83,6 +103,7 @@ var DefaultOptions = Options{ SessionKey: "_auth_user_id", } +//InitMessage - type InitMessage struct { TenantID string `json:"T_id"` ServiceID string `json:"S_id"` @@ -94,10 +115,10 @@ func checkSameOrigin(r *http.Request) bool { return true } -func New(command []string, options *Options) (*App, error) { +//New - +func New(options *Options) (*App, error) { titleTemplate, _ := template.New("title").Parse(options.TitleFormat) - return &App{ - command: command, + app := &App{ options: options, upgrader: &websocket.Upgrader{ ReadBufferSize: 1024, @@ -106,11 +127,17 @@ func New(command []string, options *Options) (*App, error) { }, titleTemplate: titleTemplate, onceMutex: umutex.New(), - }, nil + } + //create kube client and config + if err := app.createKubeClient(); err != nil { + return nil, err + } + return app, nil } //Run Run func (app *App) Run() error { + endpoint := net.JoinHostPort(app.options.Address, app.options.Port) wsHandler := http.HandlerFunc(app.handleWS) @@ -203,33 +230,134 @@ func (app *App) handleWS(w http.ResponseWriter, r *http.Request) { conn.Close() return } - - cmd := exec.Command("kubectl", "--namespace", init.TenantID, "exec", "-ti", init.PodName, "/bin/sh") - ExecuteCommandTotal++ - ptyIo, err := pty.Start(cmd) + pty, tty, err := pty.Open() if err != nil { - logrus.Printf("Failed to execute command:%s", err.Error()) + logrus.Errorf("open pty failure %s", err.Error()) + conn.WriteMessage(websocket.TextMessage, []byte("open tty failure!")) ExecuteCommandFailed++ return } - logrus.Printf("Command is running for client %s with PID %d ", r.RemoteAddr, cmd.Process.Pid) - - context := &clientContext{ + containerName, args, err := app.GetDefaultContainerName(init.TenantID, init.PodName) + if err != nil { + logrus.Errorf("get default container failure %s", err.Error()) + conn.WriteMessage(websocket.TextMessage, []byte("Get default container name failure!")) + ExecuteCommandFailed++ + return + } + request := app.NewRequest(init.PodName, init.TenantID, containerName, args) + context := &ClientContext{ app: app, request: r, connection: conn, - command: cmd, - pty: ptyIo, + pty: pty, writeMutex: &sync.Mutex{}, } - - context.goHandleClient() + out := CreateOut(tty) + t := out.SetTTY() + exec := NewExecContextByStd(context, out.Stdin, out.Stdout, out.Stderr, request, app.config) + context.exec = exec + stop := make(chan struct{}) + closed := func() { + tty.Close() + } + go func() { + fn := func() error { + if err := exec.Run(); err != nil { + logrus.Errorf("exec run failure %s", err.Error()) + conn.WriteMessage(websocket.TextMessage, []byte("Exec run failure!")) + ExecuteCommandFailed++ + return err + } + close(stop) + logrus.Debugf("%s/%s exec run complete", init.TenantID, init.PodName) + return nil + } + if err := t.Safe(fn); err != nil { + logrus.Errorf("tty run failure %s", err.Error()) + } + }() + context.goHandleClient(stop, closed) } +//Exit - func (app *App) Exit() (firstCall bool) { return true } +func (app *App) createKubeClient() error { + config, err := k8sutil.NewRestConfig(app.options.K8SConfPath) + if err != nil { + return err + } + config.UserAgent = "rainbond/webcli" + coreAPI, err := kubernetes.NewForConfig(config) + if err != nil { + return err + } + SetConfigDefaults(config) + app.config = config + restClient, err := restclient.RESTClientFor(config) + if err != nil { + return err + } + app.restClient = restClient + app.coreClient = coreAPI + return nil +} + +//SetConfigDefaults - +func SetConfigDefaults(config *rest.Config) error { + if config.APIPath == "" { + config.APIPath = "/api" + } + config.GroupVersion = &schema.GroupVersion{Group: "", Version: "v1"} + config.NegotiatedSerializer = serializer.NewCodecFactory(runtime.NewScheme()) + if config.UserAgent == "" { + config.UserAgent = rest.DefaultKubernetesUserAgent() + } + return nil +} + +//GetDefaultContainerName get default container name +func (app *App) GetDefaultContainerName(namespace, podname string) (string, []string, error) { + var args = []string{"/bin/sh"} + pod, err := app.coreClient.CoreV1().Pods(namespace).Get(podname, metav1.GetOptions{}) + if err != nil { + return "", args, err + } + + if pod.Status.Phase == api.PodSucceeded || pod.Status.Phase == api.PodFailed { + return "", args, fmt.Errorf("cannot exec into a container in a completed pod; current phase is %s", pod.Status.Phase) + } + if len(pod.Spec.Containers) > 0 { + for _, env := range pod.Spec.Containers[0].Env { + if env.Name == "ES_DEFAULT_EXEC_ARGS" { + args = []string{env.Name} + } + } + return pod.Spec.Containers[0].Name, args, nil + } + return "", args, fmt.Errorf("not have container in pod %s/%s", namespace, podname) +} + +//NewRequest new exec request +func (app *App) NewRequest(podName, namespace, containerName string, command []string) *restclient.Request { + // TODO: consider abstracting into a client invocation or client helper + req := app.restClient.Post(). + Resource("pods"). + Name(podName). + Namespace(namespace). + SubResource("exec"). + Param("container", containerName). + Param("stdin", "true"). + Param("stdout", "true"). + Param("tty", "true") + for _, c := range command { + req.Param("command", c) + } + return req +} + func wrapLogger(handler http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { rw := &responseWrapper{w, 200} diff --git a/webcli/app/app_test.go b/webcli/app/app_test.go new file mode 100644 index 000000000..42ad06234 --- /dev/null +++ b/webcli/app/app_test.go @@ -0,0 +1,27 @@ +// RAINBOND, Application Management Platform +// Copyright (C) 2014-2020 Goodrain Co., Ltd. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. For any non-GPL usage of Rainbond, +// one or multiple Commercial Licenses authorized by Goodrain Co., Ltd. +// must be obtained first. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +package app + +import ( + "testing" +) + +func TestSendCommand(t *testing.T) { + +} diff --git a/webcli/app/client_context.go b/webcli/app/client_context.go index c5669afeb..af96a588b 100644 --- a/webcli/app/client_context.go +++ b/webcli/app/client_context.go @@ -22,26 +22,30 @@ import ( "bytes" "encoding/base64" "encoding/json" - "log" "net/http" "os" "os/exec" "strings" "sync" "syscall" + "time" "unsafe" + "github.com/Sirupsen/logrus" "github.com/fatih/structs" "github.com/gorilla/websocket" + "k8s.io/client-go/tools/remotecommand" ) -type clientContext struct { +//ClientContext websocket context +type ClientContext struct { app *App request *http.Request connection *websocket.Conn command *exec.Cmd pty *os.File writeMutex *sync.Mutex + exec Exec } const ( @@ -70,7 +74,7 @@ type ContextVars struct { RemoteAddr string } -func (context *clientContext) goHandleClient() { +func (context *ClientContext) goHandleClient(stop chan struct{}, close func()) { exit := make(chan bool, 2) go func() { @@ -86,23 +90,26 @@ func (context *clientContext) goHandleClient() { }() go func() { - - <-exit + select { + case <-stop: + case <-exit: + } context.pty.Close() - + var once sync.Once // Even if the PTY has been closed, - // Read(0 in processSend() keeps blocking and the process doen't exit - context.command.Process.Signal(syscall.Signal(context.app.options.CloseSignal)) + for context.exec.WaitingStop() { + once.Do(close) + time.Sleep(time.Millisecond * 200) + } - context.command.Wait() context.connection.Close() - log.Printf("Connection closed: %s", context.request.RemoteAddr) + logrus.Info("Connection closed: %s", context.request.RemoteAddr) }() } -func (context *clientContext) processSend() { +func (context *ClientContext) processSend() { if err := context.sendInitialize(); err != nil { - log.Printf(err.Error()) + logrus.Errorf(err.Error()) return } @@ -111,28 +118,28 @@ func (context *clientContext) processSend() { for { size, err := context.pty.Read(buf) if err != nil { - log.Printf("Command exited for: %s", context.request.RemoteAddr) + logrus.Errorf("Command exited for: %s", context.request.RemoteAddr) return } safeMessage := base64.StdEncoding.EncodeToString([]byte(buf[:size])) if err = context.write(append([]byte{Output}, []byte(safeMessage)...)); err != nil { - log.Printf(err.Error()) + logrus.Errorf(err.Error()) return } } } -func (context *clientContext) write(data []byte) error { +func (context *ClientContext) write(data []byte) error { context.writeMutex.Lock() defer context.writeMutex.Unlock() return context.connection.WriteMessage(websocket.TextMessage, data) } -func (context *clientContext) sendInitialize() error { +func (context *ClientContext) sendInitialize() error { hostname, _ := os.Hostname() titleVars := ContextVars{ - Command: strings.Join(context.app.command, " "), - Pid: context.command.Process.Pid, + Command: "", //strings.Join(context.app.command, " "), + Pid: 0, //context.command.Process.Pid, Hostname: hostname, RemoteAddr: context.request.RemoteAddr, } @@ -171,15 +178,15 @@ func (context *clientContext) sendInitialize() error { return nil } -func (context *clientContext) processReceive() { +func (context *ClientContext) processReceive() { for { _, data, err := context.connection.ReadMessage() if err != nil { - log.Print(err.Error()) + logrus.Errorf(err.Error()) return } if len(data) == 0 { - log.Print("An error has occurred") + logrus.Errorf("An error has occurred") return } @@ -188,7 +195,6 @@ func (context *clientContext) processReceive() { if !context.app.options.PermitWrite { break } - _, err := context.pty.Write(data[1:]) if err != nil { return @@ -196,14 +202,14 @@ func (context *clientContext) processReceive() { case Ping: if err := context.write([]byte{Pong}); err != nil { - log.Print(err.Error()) + logrus.Errorf(err.Error()) return } case ResizeTerminal: var args argResizeTerminal err = json.Unmarshal(data[1:], &args) if err != nil { - log.Print("Malformed remote command") + logrus.Errorf("Malformed remote command") return } @@ -226,8 +232,16 @@ func (context *clientContext) processReceive() { ) default: - log.Print("Unknown message type") + logrus.Errorf("Unknown message type") return } } } + +//Next next +func (context *ClientContext) Next() *remotecommand.TerminalSize { + return &remotecommand.TerminalSize{ + Width: 1200, + Height: 600, + } +} diff --git a/webcli/app/exec.go b/webcli/app/exec.go new file mode 100644 index 000000000..5d9ed619b --- /dev/null +++ b/webcli/app/exec.go @@ -0,0 +1,98 @@ +// RAINBOND, Application Management Platform +// Copyright (C) 2014-2020 Goodrain Co., Ltd. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. For any non-GPL usage of Rainbond, +// one or multiple Commercial Licenses authorized by Goodrain Co., Ltd. +// must be obtained first. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +package app + +import ( + "fmt" + "io" + "os" + + restclient "k8s.io/client-go/rest" + "k8s.io/client-go/tools/remotecommand" +) + +//Exec exec interface +type Exec interface { + Run() error + WaitingStop() bool +} + +type execContext struct { + clientContext *ClientContext + tty, pty *os.File + Stdin io.Reader + Stdout io.Writer + Stderr io.Writer + kubeRequest *restclient.Request + config *restclient.Config + closed bool +} + +//NewExecContext new exec Context +func NewExecContext(clientContext *ClientContext, tty *os.File, kubeRequest *restclient.Request, config *restclient.Config) Exec { + return &execContext{ + clientContext: clientContext, + Stdin: tty, + Stdout: tty, + Stderr: tty, + kubeRequest: kubeRequest, + config: config, + } +} + +//NewExecContextByStd - +func NewExecContextByStd(clientContext *ClientContext, Stdin io.Reader, Stdout, Stderr io.Writer, kubeRequest *restclient.Request, config *restclient.Config) Exec { + return &execContext{ + clientContext: clientContext, + Stdin: Stdin, + Stdout: Stdout, + Stderr: Stderr, + kubeRequest: kubeRequest, + config: config, + } +} +func (e *execContext) WaitingStop() bool { + if e.closed { + return false + } + return true +} + +func (e *execContext) Close() { + e.tty.Close() +} + +func (e *execContext) Run() error { + defer e.Close() + defer func() { e.closed = true }() + exec, err := remotecommand.NewSPDYExecutor(e.config, "POST", e.kubeRequest.URL()) + if err != nil { + return fmt.Errorf("create executor failure %s", err.Error()) + } + if err := exec.Stream(remotecommand.StreamOptions{ + Stdin: e.Stdin, + Stdout: e.Stdout, + Stderr: e.Stderr, + Tty: false, + TerminalSizeQueue: e.clientContext, + }); err != nil { + return fmt.Errorf("executor stream failure %s", err.Error()) + } + return nil +} diff --git a/webcli/app/out.go b/webcli/app/out.go new file mode 100644 index 000000000..4c8bdd7dc --- /dev/null +++ b/webcli/app/out.go @@ -0,0 +1,59 @@ +// RAINBOND, Application Management Platform +// Copyright (C) 2014-2020 Goodrain Co., Ltd. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. For any non-GPL usage of Rainbond, +// one or multiple Commercial Licenses authorized by Goodrain Co., Ltd. +// must be obtained first. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +package app + +import ( + "io" + "os" + + "github.com/Sirupsen/logrus" + "github.com/goodrain/rainbond/webcli/term" +) + +//Out out +type Out struct { + Stdin io.Reader + Stdout io.Writer + Stderr io.Writer +} + +//CreateOut create out +func CreateOut(tty *os.File) *Out { + return &Out{ + Stdin: tty, + Stdout: tty, + Stderr: tty, + } +} + +//SetTTY set tty +func (o *Out) SetTTY() term.TTY { + t := term.TTY{ + Out: o.Stdout, + In: o.Stdin, + } + if !t.IsTerminalIn() { + logrus.Errorf("stdin is not tty") + return t + } + // if we get to here, the user wants to attach stdin, wants a TTY, and o.In is a terminal, so we + // can safely set t.Raw to true + t.Raw = true + return t +} diff --git a/webcli/app/tty_test.go b/webcli/app/tty_test.go new file mode 100644 index 000000000..39b7974da --- /dev/null +++ b/webcli/app/tty_test.go @@ -0,0 +1,65 @@ +// RAINBOND, Application Management Platform +// Copyright (C) 2014-2020 Goodrain Co., Ltd. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. For any non-GPL usage of Rainbond, +// one or multiple Commercial Licenses authorized by Goodrain Co., Ltd. +// must be obtained first. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +package app + +import ( + "fmt" + "log" + "strconv" + "testing" + "time" + + "github.com/kr/pty" +) + +func TestTTY(t *testing.T) { + pty, tty, err := pty.Open() + if err != nil { + t.Fatal(err) + } + go func() { + buf := make([]byte, 1024) + pty.WriteString("hello word") + var i int + for { + i++ + size, err := tty.Read(buf) + if err != nil { + log.Printf("Command exited for: %s", err.Error()) + return + } + pty.WriteString(string(buf[:size]) + strconv.Itoa(i)) + fmt.Println("tty write:", string(buf[:size])) + time.Sleep(time.Second * 1) + } + }() + // go func() { + // buf := make([]byte, 1024) + // for { + // size, err := tty.Read(buf) + // if err != nil { + // log.Printf("Command exited for: %s", err.Error()) + // return + // } + // pty.Write(buf[:size]) + // fmt.Println("pty write:", buf[:size]) + // } + // }() + time.Sleep(time.Minute * 1) +} diff --git a/webcli/cmd/main.go b/webcli/cmd/main.go new file mode 100644 index 000000000..ab271525a --- /dev/null +++ b/webcli/cmd/main.go @@ -0,0 +1,60 @@ +package main + +import ( + "os" + + "github.com/Sirupsen/logrus" + k8sutil "github.com/goodrain/rainbond/util/k8s" + "github.com/goodrain/rainbond/webcli/app" + restclient "k8s.io/client-go/rest" +) + +func main() { + option := app.DefaultOptions + option.K8SConfPath = "/tmp/config" + ap, err := app.New(&option) + if err != nil { + logrus.Error(err) + } + logrus.Info(ap.GetDefaultContainerName("rbd-system", "rainbond-operator-0")) + config, err := k8sutil.NewRestConfig(option.K8SConfPath) + if err != nil { + logrus.Error(err) + } + config.UserAgent = "rainbond/webcli" + app.SetConfigDefaults(config) + restClient, err := restclient.RESTClientFor(config) + if err != nil { + logrus.Error(err) + } + commands := []string{"sh"} + req := restClient.Post(). + Resource("pods"). + Name("rainbond-operator-0"). + Namespace("rbd-system"). + SubResource("exec"). + Param("container", "operator"). + Param("stdin", "true"). + Param("stdout", "true"). + Param("tty", "true") + for _, c := range commands { + req.Param("command", c) + } + out := &app.Out{ + Stdin: os.Stdin, + Stdout: os.Stdout, + Stderr: os.Stderr, + } + t := out.SetTTY() + fn := func() error { + exec := app.NewExecContextByStd(&app.ClientContext{}, out.Stdin, out.Stdout, out.Stderr, req, config) + if err := exec.Run(); err != nil { + logrus.Error(err) + return err + } + return nil + } + if err := t.Safe(fn); err != nil { + logrus.Error(err) + } +} diff --git a/webcli/term/BUILD b/webcli/term/BUILD new file mode 100644 index 000000000..3c0db1dc6 --- /dev/null +++ b/webcli/term/BUILD @@ -0,0 +1,110 @@ +package(default_visibility = ["//visibility:public"]) + +load( + "@io_bazel_rules_go//go:def.bzl", + "go_library", + "go_test", +) + +go_library( + name = "go_default_library", + srcs = [ + "resize.go", + "term.go", + "term_writer.go", + ] + select({ + "@io_bazel_rules_go//go/platform:android": [ + "resizeevents.go", + ], + "@io_bazel_rules_go//go/platform:darwin": [ + "resizeevents.go", + ], + "@io_bazel_rules_go//go/platform:dragonfly": [ + "resizeevents.go", + ], + "@io_bazel_rules_go//go/platform:freebsd": [ + "resizeevents.go", + ], + "@io_bazel_rules_go//go/platform:linux": [ + "resizeevents.go", + ], + "@io_bazel_rules_go//go/platform:nacl": [ + "resizeevents.go", + ], + "@io_bazel_rules_go//go/platform:netbsd": [ + "resizeevents.go", + ], + "@io_bazel_rules_go//go/platform:openbsd": [ + "resizeevents.go", + ], + "@io_bazel_rules_go//go/platform:plan9": [ + "resizeevents.go", + ], + "@io_bazel_rules_go//go/platform:solaris": [ + "resizeevents.go", + ], + "@io_bazel_rules_go//go/platform:windows": [ + "resizeevents_windows.go", + ], + "//conditions:default": [], + }), + importpath = "k8s.io/kubernetes/pkg/kubectl/util/term", + deps = [ + "//pkg/util/interrupt:go_default_library", + "//vendor/github.com/docker/docker/pkg/term:go_default_library", + "//vendor/github.com/mitchellh/go-wordwrap:go_default_library", + "//vendor/k8s.io/apimachinery/pkg/util/runtime:go_default_library", + "//vendor/k8s.io/client-go/tools/remotecommand:go_default_library", + ] + select({ + "@io_bazel_rules_go//go/platform:android": [ + "//vendor/golang.org/x/sys/unix:go_default_library", + ], + "@io_bazel_rules_go//go/platform:darwin": [ + "//vendor/golang.org/x/sys/unix:go_default_library", + ], + "@io_bazel_rules_go//go/platform:dragonfly": [ + "//vendor/golang.org/x/sys/unix:go_default_library", + ], + "@io_bazel_rules_go//go/platform:freebsd": [ + "//vendor/golang.org/x/sys/unix:go_default_library", + ], + "@io_bazel_rules_go//go/platform:linux": [ + "//vendor/golang.org/x/sys/unix:go_default_library", + ], + "@io_bazel_rules_go//go/platform:nacl": [ + "//vendor/golang.org/x/sys/unix:go_default_library", + ], + "@io_bazel_rules_go//go/platform:netbsd": [ + "//vendor/golang.org/x/sys/unix:go_default_library", + ], + "@io_bazel_rules_go//go/platform:openbsd": [ + "//vendor/golang.org/x/sys/unix:go_default_library", + ], + "@io_bazel_rules_go//go/platform:plan9": [ + "//vendor/golang.org/x/sys/unix:go_default_library", + ], + "@io_bazel_rules_go//go/platform:solaris": [ + "//vendor/golang.org/x/sys/unix:go_default_library", + ], + "//conditions:default": [], + }), +) + +go_test( + name = "go_default_test", + srcs = ["term_writer_test.go"], + embed = [":go_default_library"], +) + +filegroup( + name = "package-srcs", + srcs = glob(["**"]), + tags = ["automanaged"], + visibility = ["//visibility:private"], +) + +filegroup( + name = "all-srcs", + srcs = [":package-srcs"], + tags = ["automanaged"], +) diff --git a/webcli/term/resize.go b/webcli/term/resize.go new file mode 100644 index 000000000..7ca09a858 --- /dev/null +++ b/webcli/term/resize.go @@ -0,0 +1,132 @@ +/* +Copyright 2016 The Kubernetes Authors. + +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 term + +import ( + "fmt" + + "github.com/docker/docker/pkg/term" + "k8s.io/apimachinery/pkg/util/runtime" + "k8s.io/client-go/tools/remotecommand" +) + +// GetSize returns the current size of the user's terminal. If it isn't a terminal, +// nil is returned. +func (t TTY) GetSize() *remotecommand.TerminalSize { + outFd, isTerminal := term.GetFdInfo(t.Out) + if !isTerminal { + return nil + } + return GetSize(outFd) +} + +// GetSize returns the current size of the terminal associated with fd. +func GetSize(fd uintptr) *remotecommand.TerminalSize { + winsize, err := term.GetWinsize(fd) + if err != nil { + runtime.HandleError(fmt.Errorf("unable to get terminal size: %v", err)) + return nil + } + + return &remotecommand.TerminalSize{Width: winsize.Width, Height: winsize.Height} +} + +// MonitorSize monitors the terminal's size. It returns a TerminalSizeQueue primed with +// initialSizes, or nil if there's no TTY present. +func (t *TTY) MonitorSize(initialSizes ...*remotecommand.TerminalSize) remotecommand.TerminalSizeQueue { + outFd, isTerminal := term.GetFdInfo(t.Out) + if !isTerminal { + return nil + } + + t.sizeQueue = &sizeQueue{ + t: *t, + // make it buffered so we can send the initial terminal sizes without blocking, prior to starting + // the streaming below + resizeChan: make(chan remotecommand.TerminalSize, len(initialSizes)), + stopResizing: make(chan struct{}), + } + + t.sizeQueue.monitorSize(outFd, initialSizes...) + + return t.sizeQueue +} + +// sizeQueue implements remotecommand.TerminalSizeQueue +type sizeQueue struct { + t TTY + // resizeChan receives a Size each time the user's terminal is resized. + resizeChan chan remotecommand.TerminalSize + stopResizing chan struct{} +} + +// make sure sizeQueue implements the resize.TerminalSizeQueue interface +var _ remotecommand.TerminalSizeQueue = &sizeQueue{} + +// monitorSize primes resizeChan with initialSizes and then monitors for resize events. With each +// new event, it sends the current terminal size to resizeChan. +func (s *sizeQueue) monitorSize(outFd uintptr, initialSizes ...*remotecommand.TerminalSize) { + // send the initial sizes + for i := range initialSizes { + if initialSizes[i] != nil { + s.resizeChan <- *initialSizes[i] + } + } + + resizeEvents := make(chan remotecommand.TerminalSize, 1) + + monitorResizeEvents(outFd, resizeEvents, s.stopResizing) + + // listen for resize events in the background + go func() { + defer runtime.HandleCrash() + + for { + select { + case size, ok := <-resizeEvents: + if !ok { + return + } + + select { + // try to send the size to resizeChan, but don't block + case s.resizeChan <- size: + // send successful + default: + // unable to send / no-op + } + case <-s.stopResizing: + return + } + } + }() +} + +// Next returns the new terminal size after the terminal has been resized. It returns nil when +// monitoring has been stopped. +func (s *sizeQueue) Next() *remotecommand.TerminalSize { + size, ok := <-s.resizeChan + if !ok { + return nil + } + return &size +} + +// stop stops the background goroutine that is monitoring for terminal resizes. +func (s *sizeQueue) stop() { + close(s.stopResizing) +} diff --git a/webcli/term/resizeevents.go b/webcli/term/resizeevents.go new file mode 100644 index 000000000..e3476f978 --- /dev/null +++ b/webcli/term/resizeevents.go @@ -0,0 +1,61 @@ +// +build !windows + +/* +Copyright 2016 The Kubernetes Authors. + +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 term + +import ( + "os" + "os/signal" + + "golang.org/x/sys/unix" + "k8s.io/apimachinery/pkg/util/runtime" + "k8s.io/client-go/tools/remotecommand" +) + +// monitorResizeEvents spawns a goroutine that waits for SIGWINCH signals (these indicate the +// terminal has resized). After receiving a SIGWINCH, this gets the terminal size and tries to send +// it to the resizeEvents channel. The goroutine stops when the stop channel is closed. +func monitorResizeEvents(fd uintptr, resizeEvents chan<- remotecommand.TerminalSize, stop chan struct{}) { + go func() { + defer runtime.HandleCrash() + + winch := make(chan os.Signal, 1) + signal.Notify(winch, unix.SIGWINCH) + defer signal.Stop(winch) + + for { + select { + case <-winch: + size := GetSize(fd) + if size == nil { + return + } + + // try to send size + select { + case resizeEvents <- *size: + // success + default: + // not sent + } + case <-stop: + return + } + } + }() +} diff --git a/webcli/term/resizeevents_windows.go b/webcli/term/resizeevents_windows.go new file mode 100644 index 000000000..adccf8734 --- /dev/null +++ b/webcli/term/resizeevents_windows.go @@ -0,0 +1,62 @@ +/* +Copyright 2016 The Kubernetes Authors. + +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 term + +import ( + "time" + + "k8s.io/apimachinery/pkg/util/runtime" + "k8s.io/client-go/tools/remotecommand" +) + +// monitorResizeEvents spawns a goroutine that periodically gets the terminal size and tries to send +// it to the resizeEvents channel if the size has changed. The goroutine stops when the stop channel +// is closed. +func monitorResizeEvents(fd uintptr, resizeEvents chan<- remotecommand.TerminalSize, stop chan struct{}) { + go func() { + defer runtime.HandleCrash() + + size := GetSize(fd) + if size == nil { + return + } + lastSize := *size + + for { + // see if we need to stop running + select { + case <-stop: + return + default: + } + + size := GetSize(fd) + if size == nil { + return + } + + if size.Height != lastSize.Height || size.Width != lastSize.Width { + lastSize.Height = size.Height + lastSize.Width = size.Width + resizeEvents <- *size + } + + // sleep to avoid hot looping + time.Sleep(250 * time.Millisecond) + } + }() +} diff --git a/webcli/term/term.go b/webcli/term/term.go new file mode 100644 index 000000000..58baee831 --- /dev/null +++ b/webcli/term/term.go @@ -0,0 +1,110 @@ +/* +Copyright 2016 The Kubernetes Authors. + +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 term + +import ( + "io" + "os" + + "github.com/docker/docker/pkg/term" + + "k8s.io/kubernetes/pkg/util/interrupt" +) + +// SafeFunc is a function to be invoked by TTY. +type SafeFunc func() error + +// TTY helps invoke a function and preserve the state of the terminal, even if the process is +// terminated during execution. It also provides support for terminal resizing for remote command +// execution/attachment. +type TTY struct { + // In is a reader representing stdin. It is a required field. + In io.Reader + // Out is a writer representing stdout. It must be set to support terminal resizing. It is an + // optional field. + Out io.Writer + // Raw is true if the terminal should be set raw. + Raw bool + // TryDev indicates the TTY should try to open /dev/tty if the provided input + // is not a file descriptor. + TryDev bool + // Parent is an optional interrupt handler provided to this function - if provided + // it will be invoked after the terminal state is restored. If it is not provided, + // a signal received during the TTY will result in os.Exit(0) being invoked. + Parent *interrupt.Handler + + // sizeQueue is set after a call to MonitorSize() and is used to monitor SIGWINCH signals when the + // user's terminal resizes. + sizeQueue *sizeQueue +} + +// IsTerminalIn returns true if t.In is a terminal. Does not check /dev/tty +// even if TryDev is set. +func (t TTY) IsTerminalIn() bool { + return IsTerminal(t.In) +} + +// IsTerminalOut returns true if t.Out is a terminal. Does not check /dev/tty +// even if TryDev is set. +func (t TTY) IsTerminalOut() bool { + return IsTerminal(t.Out) +} + +// IsTerminal returns whether the passed object is a terminal or not +func IsTerminal(i interface{}) bool { + _, terminal := term.GetFdInfo(i) + return terminal +} + +// Safe invokes the provided function and will attempt to ensure that when the +// function returns (or a termination signal is sent) that the terminal state +// is reset to the condition it was in prior to the function being invoked. If +// t.Raw is true the terminal will be put into raw mode prior to calling the function. +// If the input file descriptor is not a TTY and TryDev is true, the /dev/tty file +// will be opened (if available). +func (t TTY) Safe(fn SafeFunc) error { + inFd, isTerminal := term.GetFdInfo(t.In) + + if !isTerminal && t.TryDev { + if f, err := os.Open("/dev/tty"); err == nil { + defer f.Close() + inFd = f.Fd() + isTerminal = term.IsTerminal(inFd) + } + } + if !isTerminal { + return fn() + } + + var state *term.State + var err error + if t.Raw { + state, err = term.MakeRaw(inFd) + } else { + state, err = term.SaveState(inFd) + } + if err != nil { + return err + } + return interrupt.Chain(t.Parent, func() { + if t.sizeQueue != nil { + t.sizeQueue.stop() + } + + term.RestoreTerminal(inFd, state) + }).Run(fn) +} diff --git a/webcli/term/term_writer.go b/webcli/term/term_writer.go new file mode 100644 index 000000000..2d72d1e45 --- /dev/null +++ b/webcli/term/term_writer.go @@ -0,0 +1,124 @@ +/* +Copyright 2016 The Kubernetes Authors. + +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 term + +import ( + "io" + "os" + + "github.com/docker/docker/pkg/term" + wordwrap "github.com/mitchellh/go-wordwrap" +) + +type wordWrapWriter struct { + limit uint + writer io.Writer +} + +// NewResponsiveWriter creates a Writer that detects the column width of the +// terminal we are in, and adjusts every line width to fit and use recommended +// terminal sizes for better readability. Does proper word wrapping automatically. +// if terminal width >= 120 columns use 120 columns +// if terminal width >= 100 columns use 100 columns +// if terminal width >= 80 columns use 80 columns +// In case we're not in a terminal or if it's smaller than 80 columns width, +// doesn't do any wrapping. +func NewResponsiveWriter(w io.Writer) io.Writer { + file, ok := w.(*os.File) + if !ok { + return w + } + fd := file.Fd() + if !term.IsTerminal(fd) { + return w + } + + terminalSize := GetSize(fd) + if terminalSize == nil { + return w + } + + var limit uint + switch { + case terminalSize.Width >= 120: + limit = 120 + case terminalSize.Width >= 100: + limit = 100 + case terminalSize.Width >= 80: + limit = 80 + } + + return NewWordWrapWriter(w, limit) +} + +// NewWordWrapWriter is a Writer that supports a limit of characters on every line +// and does auto word wrapping that respects that limit. +func NewWordWrapWriter(w io.Writer, limit uint) io.Writer { + return &wordWrapWriter{ + limit: limit, + writer: w, + } +} + +func (w wordWrapWriter) Write(p []byte) (nn int, err error) { + if w.limit == 0 { + return w.writer.Write(p) + } + original := string(p) + wrapped := wordwrap.WrapString(original, w.limit) + return w.writer.Write([]byte(wrapped)) +} + +// NewPunchCardWriter is a NewWordWrapWriter that limits the line width to 80 columns. +func NewPunchCardWriter(w io.Writer) io.Writer { + return NewWordWrapWriter(w, 80) +} + +type maxWidthWriter struct { + maxWidth uint + currentWidth uint + written uint + writer io.Writer +} + +// NewMaxWidthWriter is a Writer that supports a limit of characters on every +// line, but doesn't do any word wrapping automatically. +func NewMaxWidthWriter(w io.Writer, maxWidth uint) io.Writer { + return &maxWidthWriter{ + maxWidth: maxWidth, + writer: w, + } +} + +func (m maxWidthWriter) Write(p []byte) (nn int, err error) { + for _, b := range p { + if m.currentWidth == m.maxWidth { + m.writer.Write([]byte{'\n'}) + m.currentWidth = 0 + } + if b == '\n' { + m.currentWidth = 0 + } + _, err := m.writer.Write([]byte{b}) + if err != nil { + return int(m.written), err + } + m.written++ + m.currentWidth++ + } + return len(p), nil +} diff --git a/webcli/term/term_writer_test.go b/webcli/term/term_writer_test.go new file mode 100644 index 000000000..7d22d6c88 --- /dev/null +++ b/webcli/term/term_writer_test.go @@ -0,0 +1,98 @@ +/* +Copyright 2016 The Kubernetes Authors. + +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 term + +import ( + "bytes" + "strings" + "testing" +) + +const test = "Kube Kube Kube Kube Kube Kube Kube Kube Kube Kube Kube Kube Kube Kube Kube Kube Kube Kube Kube Kube Kube Kube Kube Kube Kube Kube Kube Kube Kube Kube Kube Kube Kube Kube Kube Kube Kube Kube Kube Kube Kube Kube Kube Kube Kube Kube Kube Kube Kube Kube Kube Kube Kube Kube Kube Kube Kube Kube Kube Kube Kube Kube Kube Kube Kube Kube Kube Kube" + +func TestWordWrapWriter(t *testing.T) { + testcases := map[string]struct { + input string + maxWidth uint + }{ + "max 10": {input: test, maxWidth: 10}, + "max 80": {input: test, maxWidth: 80}, + "max 120": {input: test, maxWidth: 120}, + "max 5000": {input: test, maxWidth: 5000}, + } + for k, tc := range testcases { + b := bytes.NewBufferString("") + w := NewWordWrapWriter(b, tc.maxWidth) + _, err := w.Write([]byte(tc.input)) + if err != nil { + t.Errorf("%s: Unexpected error: %v", k, err) + } + result := b.String() + if !strings.Contains(result, "Kube") { + t.Errorf("%s: Expected to contain \"Kube\"", k) + } + if len(result) < len(tc.input) { + t.Errorf("%s: Unexpectedly short string, got %d wanted at least %d chars: %q", k, len(result), len(tc.input), result) + } + for _, line := range strings.Split(result, "\n") { + if len(line) > int(tc.maxWidth) { + t.Errorf("%s: Every line must be at most %d chars long, got %d: %q", k, tc.maxWidth, len(line), line) + } + } + for _, word := range strings.Split(result, " ") { + if !strings.Contains(word, "Kube") { + t.Errorf("%s: Unexpected broken word: %q", k, word) + } + } + } +} + +func TestMaxWidthWriter(t *testing.T) { + testcases := map[string]struct { + input string + maxWidth uint + }{ + "max 10": {input: test, maxWidth: 10}, + "max 80": {input: test, maxWidth: 80}, + "max 120": {input: test, maxWidth: 120}, + "max 5000": {input: test, maxWidth: 5000}, + } + for k, tc := range testcases { + b := bytes.NewBufferString("") + w := NewMaxWidthWriter(b, tc.maxWidth) + _, err := w.Write([]byte(tc.input)) + if err != nil { + t.Errorf("%s: Unexpected error: %v", k, err) + } + result := b.String() + if !strings.Contains(result, "Kube") { + t.Errorf("%s: Expected to contain \"Kube\"", k) + } + if len(result) < len(tc.input) { + t.Errorf("%s: Unexpectedly short string, got %d wanted at least %d chars: %q", k, len(result), len(tc.input), result) + } + lines := strings.Split(result, "\n") + for i, line := range lines { + if len(line) > int(tc.maxWidth) { + t.Errorf("%s: Every line must be at most %d chars long, got %d: %q", k, tc.maxWidth, len(line), line) + } + if i < len(lines)-1 && len(line) != int(tc.maxWidth) { + t.Errorf("%s: Lines except the last one are expected to be exactly %d chars long, got %d: %q", k, tc.maxWidth, len(line), line) + } + } + } +} From ab08a1074f94ddec74a0fc19c430ad0ef98c2b7e Mon Sep 17 00:00:00 2001 From: glyasai Date: Thu, 13 Feb 2020 20:51:19 +0800 Subject: [PATCH 22/47] adapted to kubelet 1.16 --- api/handler/service.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/api/handler/service.go b/api/handler/service.go index 0f85036dd..861370d46 100644 --- a/api/handler/service.go +++ b/api/handler/service.go @@ -1762,7 +1762,7 @@ func (s *ServiceAction) GetPodContainerMemory(podNames []string) (map[string]map memoryUsageMap := make(map[string]map[string]string, 10) proxy := GetPrometheusProxy() queryName := strings.Join(podNames, "|") - query := fmt.Sprintf(`container_memory_rss{pod_name=~"%s"}`, queryName) + query := fmt.Sprintf(`container_memory_rss{pod=~"%s"}`, queryName) proQuery := strings.Replace(query, " ", "%20", -1) req, err := http.NewRequest("GET", fmt.Sprintf("http://127.0.0.1:9999/api/v1/query?query=%s", proQuery), nil) if err != nil { From b6143ff9755c4d332bbe080304c9ccdcb317bd8e Mon Sep 17 00:00:00 2001 From: GLYASAI Date: Thu, 13 Feb 2020 22:02:27 +0800 Subject: [PATCH 23/47] fix '/v2/cluster' 500 --- node/kubecache/kube_cache.go | 1 + 1 file changed, 1 insertion(+) diff --git a/node/kubecache/kube_cache.go b/node/kubecache/kube_cache.go index 4b7fcf9d9..42889c6a1 100644 --- a/node/kubecache/kube_cache.go +++ b/node/kubecache/kube_cache.go @@ -110,6 +110,7 @@ func NewKubeClient(cfg *conf.Conf, clientset kubernetes.Interface) (KubeClient, sharedInformers.Core().V1().Pods().Informer() sharedInformers.Start(stop) return &kubeClient{ + kubeclient: clientset, stop: stop, sharedInformers: sharedInformers, }, nil From cb3ee38c3f791bf6b43716ba2c9b46888d6e2b99 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=87=A1=E7=BE=8A=E7=BE=8A?= Date: Fri, 14 Feb 2020 08:25:42 +0800 Subject: [PATCH 24/47] remove job debug env --- builder/build/code_build.go | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/builder/build/code_build.go b/builder/build/code_build.go index 8cc3cf8ce..852039b71 100644 --- a/builder/build/code_build.go +++ b/builder/build/code_build.go @@ -272,7 +272,6 @@ func (s *slugBuild) runBuildJob(re *Request) error { corev1.EnvVar{Name: "SERVICE_ID", Value: re.ServiceID}, corev1.EnvVar{Name: "TENANT_ID", Value: re.TenantID}, corev1.EnvVar{Name: "LANGUAGE", Value: re.Lang.String()}, - corev1.EnvVar{Name: "DEBUG", Value: "true"}, } for k, v := range re.BuildEnvs { envs = append(envs, corev1.EnvVar{Name: k, Value: v}) @@ -318,7 +317,7 @@ func (s *slugBuild) runBuildJob(re *Request) error { logrus.Debugf("slug subpath is : %s", slugSubPath) appSubPath := strings.TrimPrefix(re.SourceDir, "/cache/") logrus.Debugf("app subpath is : %s", appSubPath) - cacheSubPath := strings.TrimPrefix((re.CacheDir), "/cache/") + cacheSubPath := strings.TrimPrefix(re.CacheDir, "/cache/") container.VolumeMounts = []corev1.VolumeMount{ corev1.VolumeMount{ Name: "app", @@ -470,7 +469,7 @@ func getJobPodLogs(ctx context.Context, podChan chan struct{}, clientset kuberne func delete(clientset kubernetes.Interface, namespace, job string) { logrus.Debugf("start delete job: %s", job) - listOptions := metav1.ListOptions{LabelSelector:fmt.Sprintf("job-name=%s", job)} + listOptions := metav1.ListOptions{LabelSelector: fmt.Sprintf("job-name=%s", job)} if err := clientset.CoreV1().Pods(namespace).DeleteCollection(&metav1.DeleteOptions{}, listOptions); err != nil { logrus.Errorf("delete job pod failed: %s", err.Error()) From 8058cee2c9b8e131cebcd1d898146c864059ba17 Mon Sep 17 00:00:00 2001 From: GLYASAI Date: Fri, 14 Feb 2020 11:31:22 +0800 Subject: [PATCH 25/47] fix 'runtime.Object is nil' --- discover.v2/k8s_discover.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/discover.v2/k8s_discover.go b/discover.v2/k8s_discover.go index a1bcc9000..ac8fcc424 100644 --- a/discover.v2/k8s_discover.go +++ b/discover.v2/k8s_discover.go @@ -84,6 +84,9 @@ func (k *k8sDiscover) discover(name string, callback CallbackUpdate) { case <-ctx.Done(): return case event := <-w.ResultChan(): + if event.Object == nil { + continue + } pod := event.Object.(*corev1.Pod) ep := endpointForPod(pod) switch event.Type { From 9e8dc20ace395a70f36a6ae233e69fd5623c1711 Mon Sep 17 00:00:00 2001 From: barnettZQG Date: Fri, 14 Feb 2020 16:26:42 +0800 Subject: [PATCH 26/47] grctl install support --- cmd/grctl/option/conf.go | 9 +- grctl/clients/kubernetes.go | 19 +- grctl/cmd/ansible.go | 5 +- grctl/cmd/cluster.go | 32 +- grctl/cmd/cmd.go | 6 +- grctl/cmd/install.go | 106 +- hack/contrib/docker/grctl/README.md | 10 + hack/contrib/docker/grctl/entrypoint.sh | 10 +- util/k8s/k8s.go | 73 + .../github.com/PuerkitoBio/purell/.gitignore | 5 + .../github.com/PuerkitoBio/purell/.travis.yml | 12 + vendor/github.com/PuerkitoBio/purell/LICENSE | 12 + .../github.com/PuerkitoBio/purell/README.md | 188 +++ .../github.com/PuerkitoBio/purell/purell.go | 379 ++++++ .../github.com/PuerkitoBio/urlesc/.travis.yml | 15 + vendor/github.com/PuerkitoBio/urlesc/LICENSE | 27 + .../github.com/PuerkitoBio/urlesc/README.md | 16 + .../github.com/PuerkitoBio/urlesc/urlesc.go | 180 +++ .../go-openapi/jsonpointer/.editorconfig | 26 + .../go-openapi/jsonpointer/.gitignore | 1 + .../go-openapi/jsonpointer/.travis.yml | 15 + .../go-openapi/jsonpointer/CODE_OF_CONDUCT.md | 74 ++ .../github.com/go-openapi/jsonpointer/LICENSE | 202 +++ .../go-openapi/jsonpointer/README.md | 15 + .../github.com/go-openapi/jsonpointer/go.mod | 6 + .../github.com/go-openapi/jsonpointer/go.sum | 22 + .../go-openapi/jsonpointer/pointer.go | 390 ++++++ .../go-openapi/jsonreference/.gitignore | 1 + .../go-openapi/jsonreference/.travis.yml | 15 + .../jsonreference/CODE_OF_CONDUCT.md | 74 ++ .../go-openapi/jsonreference/LICENSE | 202 +++ .../go-openapi/jsonreference/README.md | 15 + .../go-openapi/jsonreference/go.mod | 10 + .../go-openapi/jsonreference/go.sum | 36 + .../go-openapi/jsonreference/reference.go | 156 +++ .../github.com/go-openapi/spec/.editorconfig | 26 + vendor/github.com/go-openapi/spec/.gitignore | 2 + .../github.com/go-openapi/spec/.golangci.yml | 23 + vendor/github.com/go-openapi/spec/.travis.yml | 15 + .../go-openapi/spec/CODE_OF_CONDUCT.md | 74 ++ vendor/github.com/go-openapi/spec/LICENSE | 202 +++ vendor/github.com/go-openapi/spec/README.md | 10 + vendor/github.com/go-openapi/spec/bindata.go | 297 +++++ vendor/github.com/go-openapi/spec/cache.go | 60 + .../go-openapi/spec/contact_info.go | 24 + vendor/github.com/go-openapi/spec/debug.go | 47 + vendor/github.com/go-openapi/spec/expander.go | 650 +++++++++ .../go-openapi/spec/external_docs.go | 24 + vendor/github.com/go-openapi/spec/go.mod | 14 + vendor/github.com/go-openapi/spec/go.sum | 66 + vendor/github.com/go-openapi/spec/header.go | 197 +++ vendor/github.com/go-openapi/spec/info.go | 165 +++ vendor/github.com/go-openapi/spec/items.go | 244 ++++ vendor/github.com/go-openapi/spec/license.go | 23 + .../github.com/go-openapi/spec/normalizer.go | 152 +++ .../github.com/go-openapi/spec/operation.go | 398 ++++++ .../github.com/go-openapi/spec/parameter.go | 321 +++++ .../github.com/go-openapi/spec/path_item.go | 87 ++ vendor/github.com/go-openapi/spec/paths.go | 97 ++ vendor/github.com/go-openapi/spec/ref.go | 191 +++ vendor/github.com/go-openapi/spec/response.go | 131 ++ .../github.com/go-openapi/spec/responses.go | 127 ++ vendor/github.com/go-openapi/spec/schema.go | 596 +++++++++ .../go-openapi/spec/schema_loader.go | 275 ++++ .../go-openapi/spec/security_scheme.go | 140 ++ vendor/github.com/go-openapi/spec/spec.go | 86 ++ vendor/github.com/go-openapi/spec/swagger.go | 448 +++++++ vendor/github.com/go-openapi/spec/tag.go | 75 ++ vendor/github.com/go-openapi/spec/unused.go | 174 +++ .../github.com/go-openapi/spec/xml_object.go | 68 + .../github.com/go-openapi/swag/.editorconfig | 26 + vendor/github.com/go-openapi/swag/.gitignore | 4 + .../github.com/go-openapi/swag/.golangci.yml | 22 + vendor/github.com/go-openapi/swag/.travis.yml | 15 + .../go-openapi/swag/CODE_OF_CONDUCT.md | 74 ++ vendor/github.com/go-openapi/swag/LICENSE | 202 +++ vendor/github.com/go-openapi/swag/README.md | 22 + vendor/github.com/go-openapi/swag/convert.go | 208 +++ .../go-openapi/swag/convert_types.go | 595 +++++++++ vendor/github.com/go-openapi/swag/doc.go | 32 + vendor/github.com/go-openapi/swag/go.mod | 14 + vendor/github.com/go-openapi/swag/go.sum | 20 + vendor/github.com/go-openapi/swag/json.go | 312 +++++ vendor/github.com/go-openapi/swag/loading.go | 80 ++ .../github.com/go-openapi/swag/name_lexem.go | 87 ++ vendor/github.com/go-openapi/swag/net.go | 38 + vendor/github.com/go-openapi/swag/path.go | 59 + .../github.com/go-openapi/swag/post_go18.go | 23 + .../github.com/go-openapi/swag/post_go19.go | 67 + vendor/github.com/go-openapi/swag/pre_go18.go | 23 + vendor/github.com/go-openapi/swag/pre_go19.go | 69 + vendor/github.com/go-openapi/swag/split.go | 262 ++++ vendor/github.com/go-openapi/swag/util.go | 385 ++++++ vendor/github.com/go-openapi/swag/yaml.go | 227 ++++ .../pkg/apis/addtoscheme_rainbond_v1alpha1.go | 10 + .../rainbond-operator/pkg/apis/apis.go | 13 + .../pkg/apis/rainbond/group.go | 6 + .../pkg/apis/rainbond/v1alpha1/doc.go | 4 + .../v1alpha1/rainbondcluster_types.go | 223 ++++ .../v1alpha1/rainbondpackage_types.go | 107 ++ .../rainbond/v1alpha1/rbdcomponent_types.go | 139 ++ .../pkg/apis/rainbond/v1alpha1/register.go | 25 + .../v1alpha1/zz_generated.deepcopy.go | 580 ++++++++ .../rainbond/v1alpha1/zz_generated.openapi.go | 13 + vendor/github.com/mailru/easyjson/LICENSE | 7 + .../github.com/mailru/easyjson/buffer/pool.go | 270 ++++ .../mailru/easyjson/jlexer/bytestostr.go | 24 + .../easyjson/jlexer/bytestostr_nounsafe.go | 13 + .../mailru/easyjson/jlexer/error.go | 15 + .../mailru/easyjson/jlexer/lexer.go | 1182 +++++++++++++++++ .../mailru/easyjson/jwriter/writer.go | 390 ++++++ .../k8s.io/kube-openapi/pkg/common/common.go | 192 +++ vendor/k8s.io/kube-openapi/pkg/common/doc.go | 19 + .../controller-runtime/pkg/scheme/scheme.go | 94 ++ 114 files changed, 14050 insertions(+), 35 deletions(-) create mode 100644 hack/contrib/docker/grctl/README.md create mode 100644 vendor/github.com/PuerkitoBio/purell/.gitignore create mode 100644 vendor/github.com/PuerkitoBio/purell/.travis.yml create mode 100644 vendor/github.com/PuerkitoBio/purell/LICENSE create mode 100644 vendor/github.com/PuerkitoBio/purell/README.md create mode 100644 vendor/github.com/PuerkitoBio/purell/purell.go create mode 100644 vendor/github.com/PuerkitoBio/urlesc/.travis.yml create mode 100644 vendor/github.com/PuerkitoBio/urlesc/LICENSE create mode 100644 vendor/github.com/PuerkitoBio/urlesc/README.md create mode 100644 vendor/github.com/PuerkitoBio/urlesc/urlesc.go create mode 100644 vendor/github.com/go-openapi/jsonpointer/.editorconfig create mode 100644 vendor/github.com/go-openapi/jsonpointer/.gitignore create mode 100644 vendor/github.com/go-openapi/jsonpointer/.travis.yml create mode 100644 vendor/github.com/go-openapi/jsonpointer/CODE_OF_CONDUCT.md create mode 100644 vendor/github.com/go-openapi/jsonpointer/LICENSE create mode 100644 vendor/github.com/go-openapi/jsonpointer/README.md create mode 100644 vendor/github.com/go-openapi/jsonpointer/go.mod create mode 100644 vendor/github.com/go-openapi/jsonpointer/go.sum create mode 100644 vendor/github.com/go-openapi/jsonpointer/pointer.go create mode 100644 vendor/github.com/go-openapi/jsonreference/.gitignore create mode 100644 vendor/github.com/go-openapi/jsonreference/.travis.yml create mode 100644 vendor/github.com/go-openapi/jsonreference/CODE_OF_CONDUCT.md create mode 100644 vendor/github.com/go-openapi/jsonreference/LICENSE create mode 100644 vendor/github.com/go-openapi/jsonreference/README.md create mode 100644 vendor/github.com/go-openapi/jsonreference/go.mod create mode 100644 vendor/github.com/go-openapi/jsonreference/go.sum create mode 100644 vendor/github.com/go-openapi/jsonreference/reference.go create mode 100644 vendor/github.com/go-openapi/spec/.editorconfig create mode 100644 vendor/github.com/go-openapi/spec/.gitignore create mode 100644 vendor/github.com/go-openapi/spec/.golangci.yml create mode 100644 vendor/github.com/go-openapi/spec/.travis.yml create mode 100644 vendor/github.com/go-openapi/spec/CODE_OF_CONDUCT.md create mode 100644 vendor/github.com/go-openapi/spec/LICENSE create mode 100644 vendor/github.com/go-openapi/spec/README.md create mode 100644 vendor/github.com/go-openapi/spec/bindata.go create mode 100644 vendor/github.com/go-openapi/spec/cache.go create mode 100644 vendor/github.com/go-openapi/spec/contact_info.go create mode 100644 vendor/github.com/go-openapi/spec/debug.go create mode 100644 vendor/github.com/go-openapi/spec/expander.go create mode 100644 vendor/github.com/go-openapi/spec/external_docs.go create mode 100644 vendor/github.com/go-openapi/spec/go.mod create mode 100644 vendor/github.com/go-openapi/spec/go.sum create mode 100644 vendor/github.com/go-openapi/spec/header.go create mode 100644 vendor/github.com/go-openapi/spec/info.go create mode 100644 vendor/github.com/go-openapi/spec/items.go create mode 100644 vendor/github.com/go-openapi/spec/license.go create mode 100644 vendor/github.com/go-openapi/spec/normalizer.go create mode 100644 vendor/github.com/go-openapi/spec/operation.go create mode 100644 vendor/github.com/go-openapi/spec/parameter.go create mode 100644 vendor/github.com/go-openapi/spec/path_item.go create mode 100644 vendor/github.com/go-openapi/spec/paths.go create mode 100644 vendor/github.com/go-openapi/spec/ref.go create mode 100644 vendor/github.com/go-openapi/spec/response.go create mode 100644 vendor/github.com/go-openapi/spec/responses.go create mode 100644 vendor/github.com/go-openapi/spec/schema.go create mode 100644 vendor/github.com/go-openapi/spec/schema_loader.go create mode 100644 vendor/github.com/go-openapi/spec/security_scheme.go create mode 100644 vendor/github.com/go-openapi/spec/spec.go create mode 100644 vendor/github.com/go-openapi/spec/swagger.go create mode 100644 vendor/github.com/go-openapi/spec/tag.go create mode 100644 vendor/github.com/go-openapi/spec/unused.go create mode 100644 vendor/github.com/go-openapi/spec/xml_object.go create mode 100644 vendor/github.com/go-openapi/swag/.editorconfig create mode 100644 vendor/github.com/go-openapi/swag/.gitignore create mode 100644 vendor/github.com/go-openapi/swag/.golangci.yml create mode 100644 vendor/github.com/go-openapi/swag/.travis.yml create mode 100644 vendor/github.com/go-openapi/swag/CODE_OF_CONDUCT.md create mode 100644 vendor/github.com/go-openapi/swag/LICENSE create mode 100644 vendor/github.com/go-openapi/swag/README.md create mode 100644 vendor/github.com/go-openapi/swag/convert.go create mode 100644 vendor/github.com/go-openapi/swag/convert_types.go create mode 100644 vendor/github.com/go-openapi/swag/doc.go create mode 100644 vendor/github.com/go-openapi/swag/go.mod create mode 100644 vendor/github.com/go-openapi/swag/go.sum create mode 100644 vendor/github.com/go-openapi/swag/json.go create mode 100644 vendor/github.com/go-openapi/swag/loading.go create mode 100644 vendor/github.com/go-openapi/swag/name_lexem.go create mode 100644 vendor/github.com/go-openapi/swag/net.go create mode 100644 vendor/github.com/go-openapi/swag/path.go create mode 100644 vendor/github.com/go-openapi/swag/post_go18.go create mode 100644 vendor/github.com/go-openapi/swag/post_go19.go create mode 100644 vendor/github.com/go-openapi/swag/pre_go18.go create mode 100644 vendor/github.com/go-openapi/swag/pre_go19.go create mode 100644 vendor/github.com/go-openapi/swag/split.go create mode 100644 vendor/github.com/go-openapi/swag/util.go create mode 100644 vendor/github.com/go-openapi/swag/yaml.go create mode 100644 vendor/github.com/goodrain/rainbond-operator/pkg/apis/addtoscheme_rainbond_v1alpha1.go create mode 100644 vendor/github.com/goodrain/rainbond-operator/pkg/apis/apis.go create mode 100644 vendor/github.com/goodrain/rainbond-operator/pkg/apis/rainbond/group.go create mode 100644 vendor/github.com/goodrain/rainbond-operator/pkg/apis/rainbond/v1alpha1/doc.go create mode 100644 vendor/github.com/goodrain/rainbond-operator/pkg/apis/rainbond/v1alpha1/rainbondcluster_types.go create mode 100644 vendor/github.com/goodrain/rainbond-operator/pkg/apis/rainbond/v1alpha1/rainbondpackage_types.go create mode 100644 vendor/github.com/goodrain/rainbond-operator/pkg/apis/rainbond/v1alpha1/rbdcomponent_types.go create mode 100644 vendor/github.com/goodrain/rainbond-operator/pkg/apis/rainbond/v1alpha1/register.go create mode 100644 vendor/github.com/goodrain/rainbond-operator/pkg/apis/rainbond/v1alpha1/zz_generated.deepcopy.go create mode 100644 vendor/github.com/goodrain/rainbond-operator/pkg/apis/rainbond/v1alpha1/zz_generated.openapi.go create mode 100644 vendor/github.com/mailru/easyjson/LICENSE create mode 100644 vendor/github.com/mailru/easyjson/buffer/pool.go create mode 100644 vendor/github.com/mailru/easyjson/jlexer/bytestostr.go create mode 100644 vendor/github.com/mailru/easyjson/jlexer/bytestostr_nounsafe.go create mode 100644 vendor/github.com/mailru/easyjson/jlexer/error.go create mode 100644 vendor/github.com/mailru/easyjson/jlexer/lexer.go create mode 100644 vendor/github.com/mailru/easyjson/jwriter/writer.go create mode 100644 vendor/k8s.io/kube-openapi/pkg/common/common.go create mode 100644 vendor/k8s.io/kube-openapi/pkg/common/doc.go create mode 100644 vendor/sigs.k8s.io/controller-runtime/pkg/scheme/scheme.go diff --git a/cmd/grctl/option/conf.go b/cmd/grctl/option/conf.go index 3606f84c7..504901dca 100644 --- a/cmd/grctl/option/conf.go +++ b/cmd/grctl/option/conf.go @@ -35,7 +35,6 @@ var config Config //Config Config type Config struct { - RegionMysql RegionMysql `yaml:"region_db"` Kubernets Kubernets `yaml:"kube"` RegionAPI region.APIConf `yaml:"region_api"` DockerLogPath string `yaml:"docker_log_path"` @@ -51,7 +50,7 @@ type RegionMysql struct { //Kubernets Kubernets type Kubernets struct { - Master string `yaml:"master"` + KubeConf string `yaml:"kube-conf"` } //LoadConfig 加载配置 @@ -60,12 +59,6 @@ func LoadConfig(ctx *cli.Context) (Config, error) { RegionAPI: region.APIConf{ Endpoints: []string{"http://127.0.0.1:8888"}, }, - RegionMysql: RegionMysql{ - User: os.Getenv("MYSQL_USER"), - Pass: os.Getenv("MYSQL_PASS"), - URL: os.Getenv("MYSQL_URL"), - Database: os.Getenv("MYSQL_DB"), - }, } configfile := ctx.GlobalString("config") if configfile == "" { diff --git a/grctl/clients/kubernetes.go b/grctl/clients/kubernetes.go index f61028df6..e283aa7be 100644 --- a/grctl/clients/kubernetes.go +++ b/grctl/clients/kubernetes.go @@ -19,24 +19,33 @@ package clients import ( + "fmt" + "os" "path" + "github.com/Sirupsen/logrus" "github.com/goodrain/rainbond/builder/sources" k8sutil "github.com/goodrain/rainbond/util/k8s" - - "github.com/Sirupsen/logrus" "k8s.io/client-go/kubernetes" ) //K8SClient K8SClient var K8SClient kubernetes.Interface +//CRClient rainbond custom resource client +var CRClient k8sutil.RainbondCustomResourceClient + //InitClient init k8s client func InitClient(kubeconfig string) error { if kubeconfig == "" { homePath, _ := sources.Home() kubeconfig = path.Join(homePath, ".kube/config") } + _, err := os.Stat(kubeconfig) + if err != nil { + fmt.Printf("Please make sure the kube-config file(%s) exists\n", kubeconfig) + os.Exit(1) + } // use the current context in kubeconfig config, err := k8sutil.NewRestConfig(kubeconfig) if err != nil { @@ -50,5 +59,11 @@ func InitClient(kubeconfig string) error { logrus.Error("Create kubernetes client error.", err.Error()) return err } + + CRClient, err = k8sutil.NewRainbondCustomResourceClient(config) + if err != nil { + logrus.Error("Create kubernetes rest client error.", err.Error()) + return err + } return nil } diff --git a/grctl/cmd/ansible.go b/grctl/cmd/ansible.go index 2aa478dc0..a4e0561bd 100644 --- a/grctl/cmd/ansible.go +++ b/grctl/cmd/ansible.go @@ -29,8 +29,9 @@ import ( //NewCmdAnsible ansible config cmd func NewCmdAnsible() cli.Command { c := cli.Command{ - Name: "ansible", - Usage: "Manage the ansible environment", + Name: "ansible", + Usage: "Manage the ansible environment", + Hidden: true, Subcommands: []cli.Command{ cli.Command{ Name: "hosts", diff --git a/grctl/cmd/cluster.go b/grctl/cmd/cluster.go index 590d3591b..86d763fde 100644 --- a/grctl/cmd/cluster.go +++ b/grctl/cmd/cluster.go @@ -26,10 +26,12 @@ import ( "strings" "github.com/apcera/termtables" + "github.com/ghodss/yaml" "github.com/goodrain/rainbond/grctl/clients" "github.com/goodrain/rainbond/node/nodem/client" "github.com/gosuri/uitable" "github.com/urfave/cli" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) //NewCmdCluster cmd for cluster @@ -37,6 +39,16 @@ func NewCmdCluster() cli.Command { c := cli.Command{ Name: "cluster", Usage: "show curren cluster datacenter info", + Subcommands: []cli.Command{ + cli.Command{ + Name: "config", + Usage: "prints the current cluster configuration", + Action: func(c *cli.Context) error { + Common(c) + return printConfig(c) + }, + }, + }, Action: func(c *cli.Context) error { Common(c) return getClusterInfo(c) @@ -52,12 +64,10 @@ func getClusterInfo(c *cli.Context) error { if err.Code == 502 { fmt.Println("The current cluster node manager is not working properly.") fmt.Println("You can query the service log for troubleshooting.") - fmt.Println("Exec Command: journalctl -fu node") os.Exit(1) } fmt.Println("The current cluster api server is not working properly.") fmt.Println("You can query the service log for troubleshooting.") - fmt.Println("Exec Command: journalctl -fu rbd-api") os.Exit(1) } healthCPUFree := fmt.Sprintf("%.2f", float32(clusterInfo.HealthCapCPU)-clusterInfo.HealthReqCPU) @@ -221,5 +231,21 @@ func clusterStatus(roleList []map[string]string, ReadyList []map[string]string) } func printComponentStatus() { - exec.Command("kubectl", "get", "pod", "-n", "rbd-system", "-o", "wide").Run() + fmt.Println("----------------------------------------------------------------------------------") + fmt.Println() + cmd := exec.Command("kubectl", "get", "pod", "-n", "rbd-system", "-o", "wide") + cmd.Stdout = os.Stdout + cmd.Stderr = os.Stderr + cmd.Run() + fmt.Println() +} + +func printConfig(c *cli.Context) error { + config, err := clients.CRClient.GetRainbondCluster(metav1.GetOptions{}) + if err != nil { + showError(err.Error()) + } + out, _ := yaml.Marshal(config) + fmt.Println(string(out)) + return nil } diff --git a/grctl/cmd/cmd.go b/grctl/cmd/cmd.go index e6d7568f8..0d2ccc3ea 100644 --- a/grctl/cmd/cmd.go +++ b/grctl/cmd/cmd.go @@ -51,7 +51,11 @@ func Common(c *cli.Context) { if err != nil { logrus.Warn("Load config file error.", err.Error()) } - if err := clients.InitClient(c.GlobalString("kubeconfig")); err != nil { + kc := c.GlobalString("kubeconfig") + if kc != "" { + config.Kubernets.KubeConf = kc + } + if err := clients.InitClient(config.Kubernets.KubeConf); err != nil { logrus.Errorf("error config k8s,details %s", err.Error()) } //clients.SetInfo(config.RegionAPI.URL, config.RegionAPI.Token) diff --git a/grctl/cmd/install.go b/grctl/cmd/install.go index cfaa28e7a..e07419006 100644 --- a/grctl/cmd/install.go +++ b/grctl/cmd/install.go @@ -1,31 +1,115 @@ package cmd import ( + "fmt" + "io/ioutil" "os" - "os/signal" - "syscall" + "path" - "github.com/Sirupsen/logrus" + "github.com/goodrain/rainbond/api/region" + "github.com/goodrain/rainbond/builder/sources" + "github.com/goodrain/rainbond/cmd/grctl/option" + "github.com/goodrain/rainbond/grctl/clients" "github.com/urfave/cli" + yaml "gopkg.in/yaml.v2" + v1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) +var pemDirPath = "/opt/rainbond/etc/ssl/region/" +var clientPemPath = path.Join(pemDirPath, "client.pem") +var clientKeyPemPath = path.Join(pemDirPath, "client.key.pem") +var clientCAPemPath = path.Join(pemDirPath, "ca.pem") + //NewCmdInstall - func NewCmdInstall() cli.Command { c := cli.Command{ Name: "install", Hidden: true, - Usage: "grctl install", + Flags: []cli.Flag{ + cli.StringSliceFlag{ + Name: "gateway-ips", + Usage: "all gateway ip of this cluster, use it to access the region api", + EnvVar: "GatewayIP", + }, + }, + Usage: "grctl install", Action: func(c *cli.Context) error { - //step finally: listen Signal - term := make(chan os.Signal) - signal.Notify(term, os.Interrupt, syscall.SIGTERM) - select { - case s := <-term: - logrus.Infof("Received a Signal %s, exiting gracefully...", s.String()) + Common(c) + apiClientSecrit, err := clients.K8SClient.CoreV1().Secrets("rbd-system").Get("rbd-api-client-cert", metav1.GetOptions{}) + if err != nil { + showError(fmt.Sprintf("get region api tls secret failure %s", err.Error())) } - logrus.Info("See you next time!") + regionAPIIP := c.StringSlice("gateway-ip") + if len(regionAPIIP) == 0 { + cluster, err := clients.CRClient.GetRainbondCluster(metav1.GetOptions{}) + if err != nil { + showError(fmt.Sprintf("get rainbond cluster config failure %s", err.Error())) + } + gatewayIP := cluster.GatewayIngressIPs() + if len(gatewayIP) == 0 { + showError("gateway ip not found") + } + regionAPIIP = gatewayIP + } + if err := writeCertFile(apiClientSecrit); err != nil { + showError(fmt.Sprintf("write region api cert file failure %s", err.Error())) + } + if err := writeConfig(regionAPIIP); err != nil { + showError(fmt.Sprintf("write grctl config file failure %s", err.Error())) + } + fmt.Println("Install success!") return nil }, } return c } + +func writeCertFile(apiClientSecrit *v1.Secret) error { + if _, err := os.Stat(pemDirPath); err != nil { + os.MkdirAll(pemDirPath, os.ModeDir) + } + if err := ioutil.WriteFile(clientPemPath, apiClientSecrit.Data["client.pem"], 0411); err != nil && !os.IsExist(err) { + return err + } + if err := ioutil.WriteFile(clientKeyPemPath, apiClientSecrit.Data["client.key.pem"], 0411); err != nil && !os.IsExist(err) { + return err + } + if err := ioutil.WriteFile(clientCAPemPath, apiClientSecrit.Data["ca.pem"], 0411); err != nil && !os.IsExist(err) { + return err + } + return nil +} + +func writeConfig(ips []string) error { + var endpoints []string + for _, ip := range ips { + endpoints = append(endpoints, fmt.Sprintf("https://%s:8443", ip)) + } + var config = option.Config{ + RegionAPI: region.APIConf{ + Endpoints: endpoints, + Cacert: clientCAPemPath, + Cert: clientPemPath, + CertKey: clientKeyPemPath, + }, + } + home, _ := sources.Home() + configFilePath := path.Join(home, ".rbd", "grctl.yaml") + os.MkdirAll(path.Dir(configFilePath), os.ModeDir) + os.Remove(configFilePath) + configFile, err := os.OpenFile(configFilePath, os.O_CREATE|os.O_RDWR, 0411) + if err != nil { + return err + } + defer configFile.Close() + body, err := yaml.Marshal(&config) + if err != nil { + return err + } + _, err = configFile.Write(body) + if err != nil { + return err + } + return nil +} diff --git a/hack/contrib/docker/grctl/README.md b/hack/contrib/docker/grctl/README.md new file mode 100644 index 000000000..26605c722 --- /dev/null +++ b/hack/contrib/docker/grctl/README.md @@ -0,0 +1,10 @@ +## Install + +### necessary condition +`~/.kube/config` exist + +### install shell +```bash +docker run -it --rm -v /:/rootfs goodrain.me/rbd-grctl:V5.2-dev copy +grctl install +``` \ No newline at end of file diff --git a/hack/contrib/docker/grctl/entrypoint.sh b/hack/contrib/docker/grctl/entrypoint.sh index fd6f280ef..d8b9d95a3 100644 --- a/hack/contrib/docker/grctl/entrypoint.sh +++ b/hack/contrib/docker/grctl/entrypoint.sh @@ -3,14 +3,8 @@ if [ "$1" = "bash" ];then exec /bin/bash elif [ "$1" = "version" ];then echo "$RELEASE_DESC" -elif [ "$1" = "install" ];then - cp -a /run/rainbond-grctl /rootfs/path/grctl - mkdir -p /rootfs/root/.rbd - cat /etc/goodrain/region.goodrain.me/ssl/ca.pem > /ssl/ca.pem - cat /etc/goodrain/region.goodrain.me/ssl/client.pem > /ssl/client.pem - cat /etc/goodrain/region.goodrain.me/ssl/client.key.pem > /ssl/client.key.pem - cp -a /run/grctl.yaml /rootfs/root/.rbd/grctl.yaml - exec /run/rainbond-grctl install +elif [ "$1" = "copy" ];then + cp -a /run/rainbond-grctl /rootfs/usr/local/bin/ else exec /run/rainbond-grctl "$@" fi \ No newline at end of file diff --git a/util/k8s/k8s.go b/util/k8s/k8s.go index 784afc30f..203c2e464 100644 --- a/util/k8s/k8s.go +++ b/util/k8s/k8s.go @@ -6,9 +6,11 @@ import ( "time" "github.com/Sirupsen/logrus" + rainbondv1alpha1 "github.com/goodrain/rainbond-operator/pkg/apis/rainbond/v1alpha1" corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/types" + utilruntime "k8s.io/apimachinery/pkg/util/runtime" "k8s.io/client-go/informers" "k8s.io/client-go/kubernetes" "k8s.io/client-go/kubernetes/scheme" @@ -17,6 +19,10 @@ import ( "k8s.io/client-go/tools/reference" ) +func init() { + utilruntime.Must(rainbondv1alpha1.AddToScheme(scheme.Scheme)) +} + // NewClientset - func NewClientset(kubecfg string) (kubernetes.Interface, error) { c, err := clientcmd.BuildConfigFromFlags("", kubecfg) @@ -50,6 +56,11 @@ func NewRestConfig(kubecfg string) (restConfig *rest.Config, err error) { return clientcmd.BuildConfigFromFlags("", kubecfg) } +//NewRestClient new rest client +func NewRestClient(restConfig *rest.Config) (*rest.RESTClient, error) { + return rest.RESTClientFor(restConfig) +} + // InClusterConfig in cluster config func InClusterConfig() (*rest.Config, error) { // Work around https://github.com/kubernetes/kubernetes/issues/40973 @@ -105,3 +116,65 @@ func DefListEventsByPod(clientset kubernetes.Interface, pod *corev1.Pod) *corev1 events, _ := clientset.CoreV1().Events(pod.GetNamespace()).Search(scheme.Scheme, ref) return events } + +//ConfigureRainbondClient config rainbond custom resource +func ConfigureRainbondClient(config *rest.Config) { + groupversion := rainbondv1alpha1.SchemeGroupVersion + config.GroupVersion = &groupversion + config.APIPath = "/apis" + config.NegotiatedSerializer = scheme.Codecs.WithoutConversion() + if config.UserAgent == "" { + config.UserAgent = rest.DefaultKubernetesUserAgent() + } +} + +//RainbondCluster rainbond cluster +type RainbondCluster interface { + GetRainbondCluster(options metav1.GetOptions) (*rainbondv1alpha1.RainbondCluster, error) +} + +// RainbondCustomResourceClient client +type RainbondCustomResourceClient interface { + RESTClient() rest.Interface + RainbondCluster +} + +type rclient struct { + restClient *rest.RESTClient + namespace string +} + +func (r *rclient) GetRainbondCluster(options metav1.GetOptions) (*rainbondv1alpha1.RainbondCluster, error) { + var cluster rainbondv1alpha1.RainbondCluster + err := r.restClient.Get(). + Namespace(r.namespace). + Resource("rainbondclusters"). + Name("rainbondcluster"). + VersionedParams(&options, scheme.ParameterCodec). + Do().Into(&cluster) + if err != nil { + return nil, err + } + return &cluster, nil +} + +func (r *rclient) RESTClient() rest.Interface { + if r == nil { + return nil + } + return r.restClient +} + +//NewRainbondCustomResourceClient new rainbond cr client +func NewRainbondCustomResourceClient(config *rest.Config) (RainbondCustomResourceClient, error) { + copyConfig := *config + ConfigureRainbondClient(©Config) + restClient, err := NewRestClient(©Config) + if err != nil { + return nil, err + } + return &rclient{ + restClient: restClient, + namespace: "rbd-system", + }, nil +} diff --git a/vendor/github.com/PuerkitoBio/purell/.gitignore b/vendor/github.com/PuerkitoBio/purell/.gitignore new file mode 100644 index 000000000..748e4c807 --- /dev/null +++ b/vendor/github.com/PuerkitoBio/purell/.gitignore @@ -0,0 +1,5 @@ +*.sublime-* +.DS_Store +*.swp +*.swo +tags diff --git a/vendor/github.com/PuerkitoBio/purell/.travis.yml b/vendor/github.com/PuerkitoBio/purell/.travis.yml new file mode 100644 index 000000000..cf31e6af6 --- /dev/null +++ b/vendor/github.com/PuerkitoBio/purell/.travis.yml @@ -0,0 +1,12 @@ +language: go + +go: + - 1.4.x + - 1.5.x + - 1.6.x + - 1.7.x + - 1.8.x + - 1.9.x + - "1.10.x" + - "1.11.x" + - tip diff --git a/vendor/github.com/PuerkitoBio/purell/LICENSE b/vendor/github.com/PuerkitoBio/purell/LICENSE new file mode 100644 index 000000000..4b9986dea --- /dev/null +++ b/vendor/github.com/PuerkitoBio/purell/LICENSE @@ -0,0 +1,12 @@ +Copyright (c) 2012, Martin Angers +All rights reserved. + +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 the author 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 HOLDER 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. diff --git a/vendor/github.com/PuerkitoBio/purell/README.md b/vendor/github.com/PuerkitoBio/purell/README.md new file mode 100644 index 000000000..07de0c498 --- /dev/null +++ b/vendor/github.com/PuerkitoBio/purell/README.md @@ -0,0 +1,188 @@ +# Purell + +Purell is a tiny Go library to normalize URLs. It returns a pure URL. Pure-ell. Sanitizer and all. Yeah, I know... + +Based on the [wikipedia paper][wiki] and the [RFC 3986 document][rfc]. + +[![build status](https://travis-ci.org/PuerkitoBio/purell.svg?branch=master)](http://travis-ci.org/PuerkitoBio/purell) + +## Install + +`go get github.com/PuerkitoBio/purell` + +## Changelog + +* **v1.1.1** : Fix failing test due to Go1.12 changes (thanks to @ianlancetaylor). +* **2016-11-14 (v1.1.0)** : IDN: Conform to RFC 5895: Fold character width (thanks to @beeker1121). +* **2016-07-27 (v1.0.0)** : Normalize IDN to ASCII (thanks to @zenovich). +* **2015-02-08** : Add fix for relative paths issue ([PR #5][pr5]) and add fix for unnecessary encoding of reserved characters ([see issue #7][iss7]). +* **v0.2.0** : Add benchmarks, Attempt IDN support. +* **v0.1.0** : Initial release. + +## Examples + +From `example_test.go` (note that in your code, you would import "github.com/PuerkitoBio/purell", and would prefix references to its methods and constants with "purell."): + +```go +package purell + +import ( + "fmt" + "net/url" +) + +func ExampleNormalizeURLString() { + if normalized, err := NormalizeURLString("hTTp://someWEBsite.com:80/Amazing%3f/url/", + FlagLowercaseScheme|FlagLowercaseHost|FlagUppercaseEscapes); err != nil { + panic(err) + } else { + fmt.Print(normalized) + } + // Output: http://somewebsite.com:80/Amazing%3F/url/ +} + +func ExampleMustNormalizeURLString() { + normalized := MustNormalizeURLString("hTTpS://someWEBsite.com:443/Amazing%fa/url/", + FlagsUnsafeGreedy) + fmt.Print(normalized) + + // Output: http://somewebsite.com/Amazing%FA/url +} + +func ExampleNormalizeURL() { + if u, err := url.Parse("Http://SomeUrl.com:8080/a/b/.././c///g?c=3&a=1&b=9&c=0#target"); err != nil { + panic(err) + } else { + normalized := NormalizeURL(u, FlagsUsuallySafeGreedy|FlagRemoveDuplicateSlashes|FlagRemoveFragment) + fmt.Print(normalized) + } + + // Output: http://someurl.com:8080/a/c/g?c=3&a=1&b=9&c=0 +} +``` + +## API + +As seen in the examples above, purell offers three methods, `NormalizeURLString(string, NormalizationFlags) (string, error)`, `MustNormalizeURLString(string, NormalizationFlags) (string)` and `NormalizeURL(*url.URL, NormalizationFlags) (string)`. They all normalize the provided URL based on the specified flags. Here are the available flags: + +```go +const ( + // Safe normalizations + FlagLowercaseScheme NormalizationFlags = 1 << iota // HTTP://host -> http://host, applied by default in Go1.1 + FlagLowercaseHost // http://HOST -> http://host + FlagUppercaseEscapes // http://host/t%ef -> http://host/t%EF + FlagDecodeUnnecessaryEscapes // http://host/t%41 -> http://host/tA + FlagEncodeNecessaryEscapes // http://host/!"#$ -> http://host/%21%22#$ + FlagRemoveDefaultPort // http://host:80 -> http://host + FlagRemoveEmptyQuerySeparator // http://host/path? -> http://host/path + + // Usually safe normalizations + FlagRemoveTrailingSlash // http://host/path/ -> http://host/path + FlagAddTrailingSlash // http://host/path -> http://host/path/ (should choose only one of these add/remove trailing slash flags) + FlagRemoveDotSegments // http://host/path/./a/b/../c -> http://host/path/a/c + + // Unsafe normalizations + FlagRemoveDirectoryIndex // http://host/path/index.html -> http://host/path/ + FlagRemoveFragment // http://host/path#fragment -> http://host/path + FlagForceHTTP // https://host -> http://host + FlagRemoveDuplicateSlashes // http://host/path//a///b -> http://host/path/a/b + FlagRemoveWWW // http://www.host/ -> http://host/ + FlagAddWWW // http://host/ -> http://www.host/ (should choose only one of these add/remove WWW flags) + FlagSortQuery // http://host/path?c=3&b=2&a=1&b=1 -> http://host/path?a=1&b=1&b=2&c=3 + + // Normalizations not in the wikipedia article, required to cover tests cases + // submitted by jehiah + FlagDecodeDWORDHost // http://1113982867 -> http://66.102.7.147 + FlagDecodeOctalHost // http://0102.0146.07.0223 -> http://66.102.7.147 + FlagDecodeHexHost // http://0x42660793 -> http://66.102.7.147 + FlagRemoveUnnecessaryHostDots // http://.host../path -> http://host/path + FlagRemoveEmptyPortSeparator // http://host:/path -> http://host/path + + // Convenience set of safe normalizations + FlagsSafe NormalizationFlags = FlagLowercaseHost | FlagLowercaseScheme | FlagUppercaseEscapes | FlagDecodeUnnecessaryEscapes | FlagEncodeNecessaryEscapes | FlagRemoveDefaultPort | FlagRemoveEmptyQuerySeparator + + // For convenience sets, "greedy" uses the "remove trailing slash" and "remove www. prefix" flags, + // while "non-greedy" uses the "add (or keep) the trailing slash" and "add www. prefix". + + // Convenience set of usually safe normalizations (includes FlagsSafe) + FlagsUsuallySafeGreedy NormalizationFlags = FlagsSafe | FlagRemoveTrailingSlash | FlagRemoveDotSegments + FlagsUsuallySafeNonGreedy NormalizationFlags = FlagsSafe | FlagAddTrailingSlash | FlagRemoveDotSegments + + // Convenience set of unsafe normalizations (includes FlagsUsuallySafe) + FlagsUnsafeGreedy NormalizationFlags = FlagsUsuallySafeGreedy | FlagRemoveDirectoryIndex | FlagRemoveFragment | FlagForceHTTP | FlagRemoveDuplicateSlashes | FlagRemoveWWW | FlagSortQuery + FlagsUnsafeNonGreedy NormalizationFlags = FlagsUsuallySafeNonGreedy | FlagRemoveDirectoryIndex | FlagRemoveFragment | FlagForceHTTP | FlagRemoveDuplicateSlashes | FlagAddWWW | FlagSortQuery + + // Convenience set of all available flags + FlagsAllGreedy = FlagsUnsafeGreedy | FlagDecodeDWORDHost | FlagDecodeOctalHost | FlagDecodeHexHost | FlagRemoveUnnecessaryHostDots | FlagRemoveEmptyPortSeparator + FlagsAllNonGreedy = FlagsUnsafeNonGreedy | FlagDecodeDWORDHost | FlagDecodeOctalHost | FlagDecodeHexHost | FlagRemoveUnnecessaryHostDots | FlagRemoveEmptyPortSeparator +) +``` + +For convenience, the set of flags `FlagsSafe`, `FlagsUsuallySafe[Greedy|NonGreedy]`, `FlagsUnsafe[Greedy|NonGreedy]` and `FlagsAll[Greedy|NonGreedy]` are provided for the similarly grouped normalizations on [wikipedia's URL normalization page][wiki]. You can add (using the bitwise OR `|` operator) or remove (using the bitwise AND NOT `&^` operator) individual flags from the sets if required, to build your own custom set. + +The [full godoc reference is available on gopkgdoc][godoc]. + +Some things to note: + +* `FlagDecodeUnnecessaryEscapes`, `FlagEncodeNecessaryEscapes`, `FlagUppercaseEscapes` and `FlagRemoveEmptyQuerySeparator` are always implicitly set, because internally, the URL string is parsed as an URL object, which automatically decodes unnecessary escapes, uppercases and encodes necessary ones, and removes empty query separators (an unnecessary `?` at the end of the url). So this operation cannot **not** be done. For this reason, `FlagRemoveEmptyQuerySeparator` (as well as the other three) has been included in the `FlagsSafe` convenience set, instead of `FlagsUnsafe`, where Wikipedia puts it. + +* The `FlagDecodeUnnecessaryEscapes` decodes the following escapes (*from -> to*): + - %24 -> $ + - %26 -> & + - %2B-%3B -> +,-./0123456789:; + - %3D -> = + - %40-%5A -> @ABCDEFGHIJKLMNOPQRSTUVWXYZ + - %5F -> _ + - %61-%7A -> abcdefghijklmnopqrstuvwxyz + - %7E -> ~ + + +* When the `NormalizeURL` function is used (passing an URL object), this source URL object is modified (that is, after the call, the URL object will be modified to reflect the normalization). + +* The *replace IP with domain name* normalization (`http://208.77.188.166/ → http://www.example.com/`) is obviously not possible for a library without making some network requests. This is not implemented in purell. + +* The *remove unused query string parameters* and *remove default query parameters* are also not implemented, since this is a very case-specific normalization, and it is quite trivial to do with an URL object. + +### Safe vs Usually Safe vs Unsafe + +Purell allows you to control the level of risk you take while normalizing an URL. You can aggressively normalize, play it totally safe, or anything in between. + +Consider the following URL: + +`HTTPS://www.RooT.com/toto/t%45%1f///a/./b/../c/?z=3&w=2&a=4&w=1#invalid` + +Normalizing with the `FlagsSafe` gives: + +`https://www.root.com/toto/tE%1F///a/./b/../c/?z=3&w=2&a=4&w=1#invalid` + +With the `FlagsUsuallySafeGreedy`: + +`https://www.root.com/toto/tE%1F///a/c?z=3&w=2&a=4&w=1#invalid` + +And with `FlagsUnsafeGreedy`: + +`http://root.com/toto/tE%1F/a/c?a=4&w=1&w=2&z=3` + +## TODOs + +* Add a class/default instance to allow specifying custom directory index names? At the moment, removing directory index removes `(^|/)((?:default|index)\.\w{1,4})$`. + +## Thanks / Contributions + +@rogpeppe +@jehiah +@opennota +@pchristopher1275 +@zenovich +@beeker1121 + +## License + +The [BSD 3-Clause license][bsd]. + +[bsd]: http://opensource.org/licenses/BSD-3-Clause +[wiki]: http://en.wikipedia.org/wiki/URL_normalization +[rfc]: http://tools.ietf.org/html/rfc3986#section-6 +[godoc]: http://go.pkgdoc.org/github.com/PuerkitoBio/purell +[pr5]: https://github.com/PuerkitoBio/purell/pull/5 +[iss7]: https://github.com/PuerkitoBio/purell/issues/7 diff --git a/vendor/github.com/PuerkitoBio/purell/purell.go b/vendor/github.com/PuerkitoBio/purell/purell.go new file mode 100644 index 000000000..6d0fc190a --- /dev/null +++ b/vendor/github.com/PuerkitoBio/purell/purell.go @@ -0,0 +1,379 @@ +/* +Package purell offers URL normalization as described on the wikipedia page: +http://en.wikipedia.org/wiki/URL_normalization +*/ +package purell + +import ( + "bytes" + "fmt" + "net/url" + "regexp" + "sort" + "strconv" + "strings" + + "github.com/PuerkitoBio/urlesc" + "golang.org/x/net/idna" + "golang.org/x/text/unicode/norm" + "golang.org/x/text/width" +) + +// A set of normalization flags determines how a URL will +// be normalized. +type NormalizationFlags uint + +const ( + // Safe normalizations + FlagLowercaseScheme NormalizationFlags = 1 << iota // HTTP://host -> http://host, applied by default in Go1.1 + FlagLowercaseHost // http://HOST -> http://host + FlagUppercaseEscapes // http://host/t%ef -> http://host/t%EF + FlagDecodeUnnecessaryEscapes // http://host/t%41 -> http://host/tA + FlagEncodeNecessaryEscapes // http://host/!"#$ -> http://host/%21%22#$ + FlagRemoveDefaultPort // http://host:80 -> http://host + FlagRemoveEmptyQuerySeparator // http://host/path? -> http://host/path + + // Usually safe normalizations + FlagRemoveTrailingSlash // http://host/path/ -> http://host/path + FlagAddTrailingSlash // http://host/path -> http://host/path/ (should choose only one of these add/remove trailing slash flags) + FlagRemoveDotSegments // http://host/path/./a/b/../c -> http://host/path/a/c + + // Unsafe normalizations + FlagRemoveDirectoryIndex // http://host/path/index.html -> http://host/path/ + FlagRemoveFragment // http://host/path#fragment -> http://host/path + FlagForceHTTP // https://host -> http://host + FlagRemoveDuplicateSlashes // http://host/path//a///b -> http://host/path/a/b + FlagRemoveWWW // http://www.host/ -> http://host/ + FlagAddWWW // http://host/ -> http://www.host/ (should choose only one of these add/remove WWW flags) + FlagSortQuery // http://host/path?c=3&b=2&a=1&b=1 -> http://host/path?a=1&b=1&b=2&c=3 + + // Normalizations not in the wikipedia article, required to cover tests cases + // submitted by jehiah + FlagDecodeDWORDHost // http://1113982867 -> http://66.102.7.147 + FlagDecodeOctalHost // http://0102.0146.07.0223 -> http://66.102.7.147 + FlagDecodeHexHost // http://0x42660793 -> http://66.102.7.147 + FlagRemoveUnnecessaryHostDots // http://.host../path -> http://host/path + FlagRemoveEmptyPortSeparator // http://host:/path -> http://host/path + + // Convenience set of safe normalizations + FlagsSafe NormalizationFlags = FlagLowercaseHost | FlagLowercaseScheme | FlagUppercaseEscapes | FlagDecodeUnnecessaryEscapes | FlagEncodeNecessaryEscapes | FlagRemoveDefaultPort | FlagRemoveEmptyQuerySeparator + + // For convenience sets, "greedy" uses the "remove trailing slash" and "remove www. prefix" flags, + // while "non-greedy" uses the "add (or keep) the trailing slash" and "add www. prefix". + + // Convenience set of usually safe normalizations (includes FlagsSafe) + FlagsUsuallySafeGreedy NormalizationFlags = FlagsSafe | FlagRemoveTrailingSlash | FlagRemoveDotSegments + FlagsUsuallySafeNonGreedy NormalizationFlags = FlagsSafe | FlagAddTrailingSlash | FlagRemoveDotSegments + + // Convenience set of unsafe normalizations (includes FlagsUsuallySafe) + FlagsUnsafeGreedy NormalizationFlags = FlagsUsuallySafeGreedy | FlagRemoveDirectoryIndex | FlagRemoveFragment | FlagForceHTTP | FlagRemoveDuplicateSlashes | FlagRemoveWWW | FlagSortQuery + FlagsUnsafeNonGreedy NormalizationFlags = FlagsUsuallySafeNonGreedy | FlagRemoveDirectoryIndex | FlagRemoveFragment | FlagForceHTTP | FlagRemoveDuplicateSlashes | FlagAddWWW | FlagSortQuery + + // Convenience set of all available flags + FlagsAllGreedy = FlagsUnsafeGreedy | FlagDecodeDWORDHost | FlagDecodeOctalHost | FlagDecodeHexHost | FlagRemoveUnnecessaryHostDots | FlagRemoveEmptyPortSeparator + FlagsAllNonGreedy = FlagsUnsafeNonGreedy | FlagDecodeDWORDHost | FlagDecodeOctalHost | FlagDecodeHexHost | FlagRemoveUnnecessaryHostDots | FlagRemoveEmptyPortSeparator +) + +const ( + defaultHttpPort = ":80" + defaultHttpsPort = ":443" +) + +// Regular expressions used by the normalizations +var rxPort = regexp.MustCompile(`(:\d+)/?$`) +var rxDirIndex = regexp.MustCompile(`(^|/)((?:default|index)\.\w{1,4})$`) +var rxDupSlashes = regexp.MustCompile(`/{2,}`) +var rxDWORDHost = regexp.MustCompile(`^(\d+)((?:\.+)?(?:\:\d*)?)$`) +var rxOctalHost = regexp.MustCompile(`^(0\d*)\.(0\d*)\.(0\d*)\.(0\d*)((?:\.+)?(?:\:\d*)?)$`) +var rxHexHost = regexp.MustCompile(`^0x([0-9A-Fa-f]+)((?:\.+)?(?:\:\d*)?)$`) +var rxHostDots = regexp.MustCompile(`^(.+?)(:\d+)?$`) +var rxEmptyPort = regexp.MustCompile(`:+$`) + +// Map of flags to implementation function. +// FlagDecodeUnnecessaryEscapes has no action, since it is done automatically +// by parsing the string as an URL. Same for FlagUppercaseEscapes and FlagRemoveEmptyQuerySeparator. + +// Since maps have undefined traversing order, make a slice of ordered keys +var flagsOrder = []NormalizationFlags{ + FlagLowercaseScheme, + FlagLowercaseHost, + FlagRemoveDefaultPort, + FlagRemoveDirectoryIndex, + FlagRemoveDotSegments, + FlagRemoveFragment, + FlagForceHTTP, // Must be after remove default port (because https=443/http=80) + FlagRemoveDuplicateSlashes, + FlagRemoveWWW, + FlagAddWWW, + FlagSortQuery, + FlagDecodeDWORDHost, + FlagDecodeOctalHost, + FlagDecodeHexHost, + FlagRemoveUnnecessaryHostDots, + FlagRemoveEmptyPortSeparator, + FlagRemoveTrailingSlash, // These two (add/remove trailing slash) must be last + FlagAddTrailingSlash, +} + +// ... and then the map, where order is unimportant +var flags = map[NormalizationFlags]func(*url.URL){ + FlagLowercaseScheme: lowercaseScheme, + FlagLowercaseHost: lowercaseHost, + FlagRemoveDefaultPort: removeDefaultPort, + FlagRemoveDirectoryIndex: removeDirectoryIndex, + FlagRemoveDotSegments: removeDotSegments, + FlagRemoveFragment: removeFragment, + FlagForceHTTP: forceHTTP, + FlagRemoveDuplicateSlashes: removeDuplicateSlashes, + FlagRemoveWWW: removeWWW, + FlagAddWWW: addWWW, + FlagSortQuery: sortQuery, + FlagDecodeDWORDHost: decodeDWORDHost, + FlagDecodeOctalHost: decodeOctalHost, + FlagDecodeHexHost: decodeHexHost, + FlagRemoveUnnecessaryHostDots: removeUnncessaryHostDots, + FlagRemoveEmptyPortSeparator: removeEmptyPortSeparator, + FlagRemoveTrailingSlash: removeTrailingSlash, + FlagAddTrailingSlash: addTrailingSlash, +} + +// MustNormalizeURLString returns the normalized string, and panics if an error occurs. +// It takes an URL string as input, as well as the normalization flags. +func MustNormalizeURLString(u string, f NormalizationFlags) string { + result, e := NormalizeURLString(u, f) + if e != nil { + panic(e) + } + return result +} + +// NormalizeURLString returns the normalized string, or an error if it can't be parsed into an URL object. +// It takes an URL string as input, as well as the normalization flags. +func NormalizeURLString(u string, f NormalizationFlags) (string, error) { + parsed, err := url.Parse(u) + if err != nil { + return "", err + } + + if f&FlagLowercaseHost == FlagLowercaseHost { + parsed.Host = strings.ToLower(parsed.Host) + } + + // The idna package doesn't fully conform to RFC 5895 + // (https://tools.ietf.org/html/rfc5895), so we do it here. + // Taken from Go 1.8 cycle source, courtesy of bradfitz. + // TODO: Remove when (if?) idna package conforms to RFC 5895. + parsed.Host = width.Fold.String(parsed.Host) + parsed.Host = norm.NFC.String(parsed.Host) + if parsed.Host, err = idna.ToASCII(parsed.Host); err != nil { + return "", err + } + + return NormalizeURL(parsed, f), nil +} + +// NormalizeURL returns the normalized string. +// It takes a parsed URL object as input, as well as the normalization flags. +func NormalizeURL(u *url.URL, f NormalizationFlags) string { + for _, k := range flagsOrder { + if f&k == k { + flags[k](u) + } + } + return urlesc.Escape(u) +} + +func lowercaseScheme(u *url.URL) { + if len(u.Scheme) > 0 { + u.Scheme = strings.ToLower(u.Scheme) + } +} + +func lowercaseHost(u *url.URL) { + if len(u.Host) > 0 { + u.Host = strings.ToLower(u.Host) + } +} + +func removeDefaultPort(u *url.URL) { + if len(u.Host) > 0 { + scheme := strings.ToLower(u.Scheme) + u.Host = rxPort.ReplaceAllStringFunc(u.Host, func(val string) string { + if (scheme == "http" && val == defaultHttpPort) || (scheme == "https" && val == defaultHttpsPort) { + return "" + } + return val + }) + } +} + +func removeTrailingSlash(u *url.URL) { + if l := len(u.Path); l > 0 { + if strings.HasSuffix(u.Path, "/") { + u.Path = u.Path[:l-1] + } + } else if l = len(u.Host); l > 0 { + if strings.HasSuffix(u.Host, "/") { + u.Host = u.Host[:l-1] + } + } +} + +func addTrailingSlash(u *url.URL) { + if l := len(u.Path); l > 0 { + if !strings.HasSuffix(u.Path, "/") { + u.Path += "/" + } + } else if l = len(u.Host); l > 0 { + if !strings.HasSuffix(u.Host, "/") { + u.Host += "/" + } + } +} + +func removeDotSegments(u *url.URL) { + if len(u.Path) > 0 { + var dotFree []string + var lastIsDot bool + + sections := strings.Split(u.Path, "/") + for _, s := range sections { + if s == ".." { + if len(dotFree) > 0 { + dotFree = dotFree[:len(dotFree)-1] + } + } else if s != "." { + dotFree = append(dotFree, s) + } + lastIsDot = (s == "." || s == "..") + } + // Special case if host does not end with / and new path does not begin with / + u.Path = strings.Join(dotFree, "/") + if u.Host != "" && !strings.HasSuffix(u.Host, "/") && !strings.HasPrefix(u.Path, "/") { + u.Path = "/" + u.Path + } + // Special case if the last segment was a dot, make sure the path ends with a slash + if lastIsDot && !strings.HasSuffix(u.Path, "/") { + u.Path += "/" + } + } +} + +func removeDirectoryIndex(u *url.URL) { + if len(u.Path) > 0 { + u.Path = rxDirIndex.ReplaceAllString(u.Path, "$1") + } +} + +func removeFragment(u *url.URL) { + u.Fragment = "" +} + +func forceHTTP(u *url.URL) { + if strings.ToLower(u.Scheme) == "https" { + u.Scheme = "http" + } +} + +func removeDuplicateSlashes(u *url.URL) { + if len(u.Path) > 0 { + u.Path = rxDupSlashes.ReplaceAllString(u.Path, "/") + } +} + +func removeWWW(u *url.URL) { + if len(u.Host) > 0 && strings.HasPrefix(strings.ToLower(u.Host), "www.") { + u.Host = u.Host[4:] + } +} + +func addWWW(u *url.URL) { + if len(u.Host) > 0 && !strings.HasPrefix(strings.ToLower(u.Host), "www.") { + u.Host = "www." + u.Host + } +} + +func sortQuery(u *url.URL) { + q := u.Query() + + if len(q) > 0 { + arKeys := make([]string, len(q)) + i := 0 + for k := range q { + arKeys[i] = k + i++ + } + sort.Strings(arKeys) + buf := new(bytes.Buffer) + for _, k := range arKeys { + sort.Strings(q[k]) + for _, v := range q[k] { + if buf.Len() > 0 { + buf.WriteRune('&') + } + buf.WriteString(fmt.Sprintf("%s=%s", k, urlesc.QueryEscape(v))) + } + } + + // Rebuild the raw query string + u.RawQuery = buf.String() + } +} + +func decodeDWORDHost(u *url.URL) { + if len(u.Host) > 0 { + if matches := rxDWORDHost.FindStringSubmatch(u.Host); len(matches) > 2 { + var parts [4]int64 + + dword, _ := strconv.ParseInt(matches[1], 10, 0) + for i, shift := range []uint{24, 16, 8, 0} { + parts[i] = dword >> shift & 0xFF + } + u.Host = fmt.Sprintf("%d.%d.%d.%d%s", parts[0], parts[1], parts[2], parts[3], matches[2]) + } + } +} + +func decodeOctalHost(u *url.URL) { + if len(u.Host) > 0 { + if matches := rxOctalHost.FindStringSubmatch(u.Host); len(matches) > 5 { + var parts [4]int64 + + for i := 1; i <= 4; i++ { + parts[i-1], _ = strconv.ParseInt(matches[i], 8, 0) + } + u.Host = fmt.Sprintf("%d.%d.%d.%d%s", parts[0], parts[1], parts[2], parts[3], matches[5]) + } + } +} + +func decodeHexHost(u *url.URL) { + if len(u.Host) > 0 { + if matches := rxHexHost.FindStringSubmatch(u.Host); len(matches) > 2 { + // Conversion is safe because of regex validation + parsed, _ := strconv.ParseInt(matches[1], 16, 0) + // Set host as DWORD (base 10) encoded host + u.Host = fmt.Sprintf("%d%s", parsed, matches[2]) + // The rest is the same as decoding a DWORD host + decodeDWORDHost(u) + } + } +} + +func removeUnncessaryHostDots(u *url.URL) { + if len(u.Host) > 0 { + if matches := rxHostDots.FindStringSubmatch(u.Host); len(matches) > 1 { + // Trim the leading and trailing dots + u.Host = strings.Trim(matches[1], ".") + if len(matches) > 2 { + u.Host += matches[2] + } + } + } +} + +func removeEmptyPortSeparator(u *url.URL) { + if len(u.Host) > 0 { + u.Host = rxEmptyPort.ReplaceAllString(u.Host, "") + } +} diff --git a/vendor/github.com/PuerkitoBio/urlesc/.travis.yml b/vendor/github.com/PuerkitoBio/urlesc/.travis.yml new file mode 100644 index 000000000..ba6b225f9 --- /dev/null +++ b/vendor/github.com/PuerkitoBio/urlesc/.travis.yml @@ -0,0 +1,15 @@ +language: go + +go: + - 1.4.x + - 1.5.x + - 1.6.x + - 1.7.x + - 1.8.x + - tip + +install: + - go build . + +script: + - go test -v diff --git a/vendor/github.com/PuerkitoBio/urlesc/LICENSE b/vendor/github.com/PuerkitoBio/urlesc/LICENSE new file mode 100644 index 000000000..744875676 --- /dev/null +++ b/vendor/github.com/PuerkitoBio/urlesc/LICENSE @@ -0,0 +1,27 @@ +Copyright (c) 2012 The Go Authors. All rights reserved. + +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. diff --git a/vendor/github.com/PuerkitoBio/urlesc/README.md b/vendor/github.com/PuerkitoBio/urlesc/README.md new file mode 100644 index 000000000..57aff0a53 --- /dev/null +++ b/vendor/github.com/PuerkitoBio/urlesc/README.md @@ -0,0 +1,16 @@ +urlesc [![Build Status](https://travis-ci.org/PuerkitoBio/urlesc.svg?branch=master)](https://travis-ci.org/PuerkitoBio/urlesc) [![GoDoc](http://godoc.org/github.com/PuerkitoBio/urlesc?status.svg)](http://godoc.org/github.com/PuerkitoBio/urlesc) +====== + +Package urlesc implements query escaping as per RFC 3986. + +It contains some parts of the net/url package, modified so as to allow +some reserved characters incorrectly escaped by net/url (see [issue 5684](https://github.com/golang/go/issues/5684)). + +## Install + + go get github.com/PuerkitoBio/urlesc + +## License + +Go license (BSD-3-Clause) + diff --git a/vendor/github.com/PuerkitoBio/urlesc/urlesc.go b/vendor/github.com/PuerkitoBio/urlesc/urlesc.go new file mode 100644 index 000000000..1b8462459 --- /dev/null +++ b/vendor/github.com/PuerkitoBio/urlesc/urlesc.go @@ -0,0 +1,180 @@ +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package urlesc implements query escaping as per RFC 3986. +// It contains some parts of the net/url package, modified so as to allow +// some reserved characters incorrectly escaped by net/url. +// See https://github.com/golang/go/issues/5684 +package urlesc + +import ( + "bytes" + "net/url" + "strings" +) + +type encoding int + +const ( + encodePath encoding = 1 + iota + encodeUserPassword + encodeQueryComponent + encodeFragment +) + +// Return true if the specified character should be escaped when +// appearing in a URL string, according to RFC 3986. +func shouldEscape(c byte, mode encoding) bool { + // §2.3 Unreserved characters (alphanum) + if 'A' <= c && c <= 'Z' || 'a' <= c && c <= 'z' || '0' <= c && c <= '9' { + return false + } + + switch c { + case '-', '.', '_', '~': // §2.3 Unreserved characters (mark) + return false + + // §2.2 Reserved characters (reserved) + case ':', '/', '?', '#', '[', ']', '@', // gen-delims + '!', '$', '&', '\'', '(', ')', '*', '+', ',', ';', '=': // sub-delims + // Different sections of the URL allow a few of + // the reserved characters to appear unescaped. + switch mode { + case encodePath: // §3.3 + // The RFC allows sub-delims and : @. + // '/', '[' and ']' can be used to assign meaning to individual path + // segments. This package only manipulates the path as a whole, + // so we allow those as well. That leaves only ? and # to escape. + return c == '?' || c == '#' + + case encodeUserPassword: // §3.2.1 + // The RFC allows : and sub-delims in + // userinfo. The parsing of userinfo treats ':' as special so we must escape + // all the gen-delims. + return c == ':' || c == '/' || c == '?' || c == '#' || c == '[' || c == ']' || c == '@' + + case encodeQueryComponent: // §3.4 + // The RFC allows / and ?. + return c != '/' && c != '?' + + case encodeFragment: // §4.1 + // The RFC text is silent but the grammar allows + // everything, so escape nothing but # + return c == '#' + } + } + + // Everything else must be escaped. + return true +} + +// QueryEscape escapes the string so it can be safely placed +// inside a URL query. +func QueryEscape(s string) string { + return escape(s, encodeQueryComponent) +} + +func escape(s string, mode encoding) string { + spaceCount, hexCount := 0, 0 + for i := 0; i < len(s); i++ { + c := s[i] + if shouldEscape(c, mode) { + if c == ' ' && mode == encodeQueryComponent { + spaceCount++ + } else { + hexCount++ + } + } + } + + if spaceCount == 0 && hexCount == 0 { + return s + } + + t := make([]byte, len(s)+2*hexCount) + j := 0 + for i := 0; i < len(s); i++ { + switch c := s[i]; { + case c == ' ' && mode == encodeQueryComponent: + t[j] = '+' + j++ + case shouldEscape(c, mode): + t[j] = '%' + t[j+1] = "0123456789ABCDEF"[c>>4] + t[j+2] = "0123456789ABCDEF"[c&15] + j += 3 + default: + t[j] = s[i] + j++ + } + } + return string(t) +} + +var uiReplacer = strings.NewReplacer( + "%21", "!", + "%27", "'", + "%28", "(", + "%29", ")", + "%2A", "*", +) + +// unescapeUserinfo unescapes some characters that need not to be escaped as per RFC3986. +func unescapeUserinfo(s string) string { + return uiReplacer.Replace(s) +} + +// Escape reassembles the URL into a valid URL string. +// The general form of the result is one of: +// +// scheme:opaque +// scheme://userinfo@host/path?query#fragment +// +// If u.Opaque is non-empty, String uses the first form; +// otherwise it uses the second form. +// +// In the second form, the following rules apply: +// - if u.Scheme is empty, scheme: is omitted. +// - if u.User is nil, userinfo@ is omitted. +// - if u.Host is empty, host/ is omitted. +// - if u.Scheme and u.Host are empty and u.User is nil, +// the entire scheme://userinfo@host/ is omitted. +// - if u.Host is non-empty and u.Path begins with a /, +// the form host/path does not add its own /. +// - if u.RawQuery is empty, ?query is omitted. +// - if u.Fragment is empty, #fragment is omitted. +func Escape(u *url.URL) string { + var buf bytes.Buffer + if u.Scheme != "" { + buf.WriteString(u.Scheme) + buf.WriteByte(':') + } + if u.Opaque != "" { + buf.WriteString(u.Opaque) + } else { + if u.Scheme != "" || u.Host != "" || u.User != nil { + buf.WriteString("//") + if ui := u.User; ui != nil { + buf.WriteString(unescapeUserinfo(ui.String())) + buf.WriteByte('@') + } + if h := u.Host; h != "" { + buf.WriteString(h) + } + } + if u.Path != "" && u.Path[0] != '/' && u.Host != "" { + buf.WriteByte('/') + } + buf.WriteString(escape(u.Path, encodePath)) + } + if u.RawQuery != "" { + buf.WriteByte('?') + buf.WriteString(u.RawQuery) + } + if u.Fragment != "" { + buf.WriteByte('#') + buf.WriteString(escape(u.Fragment, encodeFragment)) + } + return buf.String() +} diff --git a/vendor/github.com/go-openapi/jsonpointer/.editorconfig b/vendor/github.com/go-openapi/jsonpointer/.editorconfig new file mode 100644 index 000000000..3152da69a --- /dev/null +++ b/vendor/github.com/go-openapi/jsonpointer/.editorconfig @@ -0,0 +1,26 @@ +# top-most EditorConfig file +root = true + +# Unix-style newlines with a newline ending every file +[*] +end_of_line = lf +insert_final_newline = true +indent_style = space +indent_size = 2 +trim_trailing_whitespace = true + +# Set default charset +[*.{js,py,go,scala,rb,java,html,css,less,sass,md}] +charset = utf-8 + +# Tab indentation (no size specified) +[*.go] +indent_style = tab + +[*.md] +trim_trailing_whitespace = false + +# Matches the exact files either package.json or .travis.yml +[{package.json,.travis.yml}] +indent_style = space +indent_size = 2 diff --git a/vendor/github.com/go-openapi/jsonpointer/.gitignore b/vendor/github.com/go-openapi/jsonpointer/.gitignore new file mode 100644 index 000000000..769c24400 --- /dev/null +++ b/vendor/github.com/go-openapi/jsonpointer/.gitignore @@ -0,0 +1 @@ +secrets.yml diff --git a/vendor/github.com/go-openapi/jsonpointer/.travis.yml b/vendor/github.com/go-openapi/jsonpointer/.travis.yml new file mode 100644 index 000000000..9aef9184e --- /dev/null +++ b/vendor/github.com/go-openapi/jsonpointer/.travis.yml @@ -0,0 +1,15 @@ +after_success: +- bash <(curl -s https://codecov.io/bash) +go: +- 1.11.x +- 1.12.x +install: +- GO111MODULE=off go get -u gotest.tools/gotestsum +env: +- GO111MODULE=on +language: go +notifications: + slack: + secure: a5VgoiwB1G/AZqzmephPZIhEB9avMlsWSlVnM1dSAtYAwdrQHGTQxAmpOxYIoSPDhWNN5bfZmjd29++UlTwLcHSR+e0kJhH6IfDlsHj/HplNCJ9tyI0zYc7XchtdKgeMxMzBKCzgwFXGSbQGydXTliDNBo0HOzmY3cou/daMFTP60K+offcjS+3LRAYb1EroSRXZqrk1nuF/xDL3792DZUdPMiFR/L/Df6y74D6/QP4sTkTDFQitz4Wy/7jbsfj8dG6qK2zivgV6/l+w4OVjFkxVpPXogDWY10vVXNVynqxfJ7to2d1I9lNCHE2ilBCkWMIPdyJF7hjF8pKW+82yP4EzRh0vu8Xn0HT5MZpQxdRY/YMxNrWaG7SxsoEaO4q5uhgdzAqLYY3TRa7MjIK+7Ur+aqOeTXn6OKwVi0CjvZ6mIU3WUKSwiwkFZMbjRAkSb5CYwMEfGFO/z964xz83qGt6WAtBXNotqCQpTIiKtDHQeLOMfksHImCg6JLhQcWBVxamVgu0G3Pdh8Y6DyPnxraXY95+QDavbjqv7TeYT9T/FNnrkXaTTK0s4iWE5H4ACU0Qvz0wUYgfQrZv0/Hp7V17+rabUwnzYySHCy9SWX/7OV9Cfh31iMp9ZIffr76xmmThtOEqs8TrTtU6BWI3rWwvA9cXQipZTVtL0oswrGw= +script: +- gotestsum -f short-verbose -- -race -coverprofile=coverage.txt -covermode=atomic ./... diff --git a/vendor/github.com/go-openapi/jsonpointer/CODE_OF_CONDUCT.md b/vendor/github.com/go-openapi/jsonpointer/CODE_OF_CONDUCT.md new file mode 100644 index 000000000..9322b065e --- /dev/null +++ b/vendor/github.com/go-openapi/jsonpointer/CODE_OF_CONDUCT.md @@ -0,0 +1,74 @@ +# Contributor Covenant Code of Conduct + +## Our Pledge + +In the interest of fostering an open and welcoming environment, we as +contributors and maintainers pledge to making participation in our project and +our community a harassment-free experience for everyone, regardless of age, body +size, disability, ethnicity, gender identity and expression, level of experience, +nationality, personal appearance, race, religion, or sexual identity and +orientation. + +## Our Standards + +Examples of behavior that contributes to creating a positive environment +include: + +* Using welcoming and inclusive language +* Being respectful of differing viewpoints and experiences +* Gracefully accepting constructive criticism +* Focusing on what is best for the community +* Showing empathy towards other community members + +Examples of unacceptable behavior by participants include: + +* The use of sexualized language or imagery and unwelcome sexual attention or +advances +* Trolling, insulting/derogatory comments, and personal or political attacks +* Public or private harassment +* Publishing others' private information, such as a physical or electronic + address, without explicit permission +* Other conduct which could reasonably be considered inappropriate in a + professional setting + +## Our Responsibilities + +Project maintainers are responsible for clarifying the standards of acceptable +behavior and are expected to take appropriate and fair corrective action in +response to any instances of unacceptable behavior. + +Project maintainers have the right and responsibility to remove, edit, or +reject comments, commits, code, wiki edits, issues, and other contributions +that are not aligned to this Code of Conduct, or to ban temporarily or +permanently any contributor for other behaviors that they deem inappropriate, +threatening, offensive, or harmful. + +## Scope + +This Code of Conduct applies both within project spaces and in public spaces +when an individual is representing the project or its community. Examples of +representing a project or community include using an official project e-mail +address, posting via an official social media account, or acting as an appointed +representative at an online or offline event. Representation of a project may be +further defined and clarified by project maintainers. + +## Enforcement + +Instances of abusive, harassing, or otherwise unacceptable behavior may be +reported by contacting the project team at ivan+abuse@flanders.co.nz. All +complaints will be reviewed and investigated and will result in a response that +is deemed necessary and appropriate to the circumstances. The project team is +obligated to maintain confidentiality with regard to the reporter of an incident. +Further details of specific enforcement policies may be posted separately. + +Project maintainers who do not follow or enforce the Code of Conduct in good +faith may face temporary or permanent repercussions as determined by other +members of the project's leadership. + +## Attribution + +This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, +available at [http://contributor-covenant.org/version/1/4][version] + +[homepage]: http://contributor-covenant.org +[version]: http://contributor-covenant.org/version/1/4/ diff --git a/vendor/github.com/go-openapi/jsonpointer/LICENSE b/vendor/github.com/go-openapi/jsonpointer/LICENSE new file mode 100644 index 000000000..d64569567 --- /dev/null +++ b/vendor/github.com/go-openapi/jsonpointer/LICENSE @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + 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. diff --git a/vendor/github.com/go-openapi/jsonpointer/README.md b/vendor/github.com/go-openapi/jsonpointer/README.md new file mode 100644 index 000000000..813788aff --- /dev/null +++ b/vendor/github.com/go-openapi/jsonpointer/README.md @@ -0,0 +1,15 @@ +# gojsonpointer [![Build Status](https://travis-ci.org/go-openapi/jsonpointer.svg?branch=master)](https://travis-ci.org/go-openapi/jsonpointer) [![codecov](https://codecov.io/gh/go-openapi/jsonpointer/branch/master/graph/badge.svg)](https://codecov.io/gh/go-openapi/jsonpointer) [![Slack Status](https://slackin.goswagger.io/badge.svg)](https://slackin.goswagger.io) + +[![license](http://img.shields.io/badge/license-Apache%20v2-orange.svg)](https://raw.githubusercontent.com/go-openapi/jsonpointer/master/LICENSE) [![GoDoc](https://godoc.org/github.com/go-openapi/jsonpointer?status.svg)](http://godoc.org/github.com/go-openapi/jsonpointer) +An implementation of JSON Pointer - Go language + +## Status +Completed YES + +Tested YES + +## References +http://tools.ietf.org/html/draft-ietf-appsawg-json-pointer-07 + +### Note +The 4.Evaluation part of the previous reference, starting with 'If the currently referenced value is a JSON array, the reference token MUST contain either...' is not implemented. diff --git a/vendor/github.com/go-openapi/jsonpointer/go.mod b/vendor/github.com/go-openapi/jsonpointer/go.mod new file mode 100644 index 000000000..422045df2 --- /dev/null +++ b/vendor/github.com/go-openapi/jsonpointer/go.mod @@ -0,0 +1,6 @@ +module github.com/go-openapi/jsonpointer + +require ( + github.com/go-openapi/swag v0.19.2 + github.com/stretchr/testify v1.3.0 +) diff --git a/vendor/github.com/go-openapi/jsonpointer/go.sum b/vendor/github.com/go-openapi/jsonpointer/go.sum new file mode 100644 index 000000000..f5e28beb4 --- /dev/null +++ b/vendor/github.com/go-openapi/jsonpointer/go.sum @@ -0,0 +1,22 @@ +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/go-openapi/swag v0.19.2 h1:jvO6bCMBEilGwMfHhrd61zIID4oIFdwb76V17SM88dE= +github.com/go-openapi/swag v0.19.2/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= +github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= +github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= +github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63 h1:nTT4s92Dgz2HlrB2NaMgvlfqHH39OgMhA7z3PK7PGD4= +github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY= +gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= diff --git a/vendor/github.com/go-openapi/jsonpointer/pointer.go b/vendor/github.com/go-openapi/jsonpointer/pointer.go new file mode 100644 index 000000000..fe2d6ee57 --- /dev/null +++ b/vendor/github.com/go-openapi/jsonpointer/pointer.go @@ -0,0 +1,390 @@ +// Copyright 2013 sigu-399 ( https://github.com/sigu-399 ) +// +// 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. + +// author sigu-399 +// author-github https://github.com/sigu-399 +// author-mail sigu.399@gmail.com +// +// repository-name jsonpointer +// repository-desc An implementation of JSON Pointer - Go language +// +// description Main and unique file. +// +// created 25-02-2013 + +package jsonpointer + +import ( + "errors" + "fmt" + "reflect" + "strconv" + "strings" + + "github.com/go-openapi/swag" +) + +const ( + emptyPointer = `` + pointerSeparator = `/` + + invalidStart = `JSON pointer must be empty or start with a "` + pointerSeparator +) + +var jsonPointableType = reflect.TypeOf(new(JSONPointable)).Elem() +var jsonSetableType = reflect.TypeOf(new(JSONSetable)).Elem() + +// JSONPointable is an interface for structs to implement when they need to customize the +// json pointer process +type JSONPointable interface { + JSONLookup(string) (interface{}, error) +} + +// JSONSetable is an interface for structs to implement when they need to customize the +// json pointer process +type JSONSetable interface { + JSONSet(string, interface{}) error +} + +// New creates a new json pointer for the given string +func New(jsonPointerString string) (Pointer, error) { + + var p Pointer + err := p.parse(jsonPointerString) + return p, err + +} + +// Pointer the json pointer reprsentation +type Pointer struct { + referenceTokens []string +} + +// "Constructor", parses the given string JSON pointer +func (p *Pointer) parse(jsonPointerString string) error { + + var err error + + if jsonPointerString != emptyPointer { + if !strings.HasPrefix(jsonPointerString, pointerSeparator) { + err = errors.New(invalidStart) + } else { + referenceTokens := strings.Split(jsonPointerString, pointerSeparator) + for _, referenceToken := range referenceTokens[1:] { + p.referenceTokens = append(p.referenceTokens, referenceToken) + } + } + } + + return err +} + +// Get uses the pointer to retrieve a value from a JSON document +func (p *Pointer) Get(document interface{}) (interface{}, reflect.Kind, error) { + return p.get(document, swag.DefaultJSONNameProvider) +} + +// Set uses the pointer to set a value from a JSON document +func (p *Pointer) Set(document interface{}, value interface{}) (interface{}, error) { + return document, p.set(document, value, swag.DefaultJSONNameProvider) +} + +// GetForToken gets a value for a json pointer token 1 level deep +func GetForToken(document interface{}, decodedToken string) (interface{}, reflect.Kind, error) { + return getSingleImpl(document, decodedToken, swag.DefaultJSONNameProvider) +} + +// SetForToken gets a value for a json pointer token 1 level deep +func SetForToken(document interface{}, decodedToken string, value interface{}) (interface{}, error) { + return document, setSingleImpl(document, value, decodedToken, swag.DefaultJSONNameProvider) +} + +func getSingleImpl(node interface{}, decodedToken string, nameProvider *swag.NameProvider) (interface{}, reflect.Kind, error) { + rValue := reflect.Indirect(reflect.ValueOf(node)) + kind := rValue.Kind() + + switch kind { + + case reflect.Struct: + if rValue.Type().Implements(jsonPointableType) { + r, err := node.(JSONPointable).JSONLookup(decodedToken) + if err != nil { + return nil, kind, err + } + return r, kind, nil + } + nm, ok := nameProvider.GetGoNameForType(rValue.Type(), decodedToken) + if !ok { + return nil, kind, fmt.Errorf("object has no field %q", decodedToken) + } + fld := rValue.FieldByName(nm) + return fld.Interface(), kind, nil + + case reflect.Map: + kv := reflect.ValueOf(decodedToken) + mv := rValue.MapIndex(kv) + + if mv.IsValid() && !swag.IsZero(mv) { + return mv.Interface(), kind, nil + } + return nil, kind, fmt.Errorf("object has no key %q", decodedToken) + + case reflect.Slice: + tokenIndex, err := strconv.Atoi(decodedToken) + if err != nil { + return nil, kind, err + } + sLength := rValue.Len() + if tokenIndex < 0 || tokenIndex >= sLength { + return nil, kind, fmt.Errorf("index out of bounds array[0,%d] index '%d'", sLength-1, tokenIndex) + } + + elem := rValue.Index(tokenIndex) + return elem.Interface(), kind, nil + + default: + return nil, kind, fmt.Errorf("invalid token reference %q", decodedToken) + } + +} + +func setSingleImpl(node, data interface{}, decodedToken string, nameProvider *swag.NameProvider) error { + rValue := reflect.Indirect(reflect.ValueOf(node)) + switch rValue.Kind() { + + case reflect.Struct: + if ns, ok := node.(JSONSetable); ok { // pointer impl + return ns.JSONSet(decodedToken, data) + } + + if rValue.Type().Implements(jsonSetableType) { + return node.(JSONSetable).JSONSet(decodedToken, data) + } + + nm, ok := nameProvider.GetGoNameForType(rValue.Type(), decodedToken) + if !ok { + return fmt.Errorf("object has no field %q", decodedToken) + } + fld := rValue.FieldByName(nm) + if fld.IsValid() { + fld.Set(reflect.ValueOf(data)) + } + return nil + + case reflect.Map: + kv := reflect.ValueOf(decodedToken) + rValue.SetMapIndex(kv, reflect.ValueOf(data)) + return nil + + case reflect.Slice: + tokenIndex, err := strconv.Atoi(decodedToken) + if err != nil { + return err + } + sLength := rValue.Len() + if tokenIndex < 0 || tokenIndex >= sLength { + return fmt.Errorf("index out of bounds array[0,%d] index '%d'", sLength, tokenIndex) + } + + elem := rValue.Index(tokenIndex) + if !elem.CanSet() { + return fmt.Errorf("can't set slice index %s to %v", decodedToken, data) + } + elem.Set(reflect.ValueOf(data)) + return nil + + default: + return fmt.Errorf("invalid token reference %q", decodedToken) + } + +} + +func (p *Pointer) get(node interface{}, nameProvider *swag.NameProvider) (interface{}, reflect.Kind, error) { + + if nameProvider == nil { + nameProvider = swag.DefaultJSONNameProvider + } + + kind := reflect.Invalid + + // Full document when empty + if len(p.referenceTokens) == 0 { + return node, kind, nil + } + + for _, token := range p.referenceTokens { + + decodedToken := Unescape(token) + + r, knd, err := getSingleImpl(node, decodedToken, nameProvider) + if err != nil { + return nil, knd, err + } + node, kind = r, knd + + } + + rValue := reflect.ValueOf(node) + kind = rValue.Kind() + + return node, kind, nil +} + +func (p *Pointer) set(node, data interface{}, nameProvider *swag.NameProvider) error { + knd := reflect.ValueOf(node).Kind() + + if knd != reflect.Ptr && knd != reflect.Struct && knd != reflect.Map && knd != reflect.Slice && knd != reflect.Array { + return fmt.Errorf("only structs, pointers, maps and slices are supported for setting values") + } + + if nameProvider == nil { + nameProvider = swag.DefaultJSONNameProvider + } + + // Full document when empty + if len(p.referenceTokens) == 0 { + return nil + } + + lastI := len(p.referenceTokens) - 1 + for i, token := range p.referenceTokens { + isLastToken := i == lastI + decodedToken := Unescape(token) + + if isLastToken { + + return setSingleImpl(node, data, decodedToken, nameProvider) + } + + rValue := reflect.Indirect(reflect.ValueOf(node)) + kind := rValue.Kind() + + switch kind { + + case reflect.Struct: + if rValue.Type().Implements(jsonPointableType) { + r, err := node.(JSONPointable).JSONLookup(decodedToken) + if err != nil { + return err + } + fld := reflect.ValueOf(r) + if fld.CanAddr() && fld.Kind() != reflect.Interface && fld.Kind() != reflect.Map && fld.Kind() != reflect.Slice && fld.Kind() != reflect.Ptr { + node = fld.Addr().Interface() + continue + } + node = r + continue + } + nm, ok := nameProvider.GetGoNameForType(rValue.Type(), decodedToken) + if !ok { + return fmt.Errorf("object has no field %q", decodedToken) + } + fld := rValue.FieldByName(nm) + if fld.CanAddr() && fld.Kind() != reflect.Interface && fld.Kind() != reflect.Map && fld.Kind() != reflect.Slice && fld.Kind() != reflect.Ptr { + node = fld.Addr().Interface() + continue + } + node = fld.Interface() + + case reflect.Map: + kv := reflect.ValueOf(decodedToken) + mv := rValue.MapIndex(kv) + + if !mv.IsValid() { + return fmt.Errorf("object has no key %q", decodedToken) + } + if mv.CanAddr() && mv.Kind() != reflect.Interface && mv.Kind() != reflect.Map && mv.Kind() != reflect.Slice && mv.Kind() != reflect.Ptr { + node = mv.Addr().Interface() + continue + } + node = mv.Interface() + + case reflect.Slice: + tokenIndex, err := strconv.Atoi(decodedToken) + if err != nil { + return err + } + sLength := rValue.Len() + if tokenIndex < 0 || tokenIndex >= sLength { + return fmt.Errorf("index out of bounds array[0,%d] index '%d'", sLength, tokenIndex) + } + + elem := rValue.Index(tokenIndex) + if elem.CanAddr() && elem.Kind() != reflect.Interface && elem.Kind() != reflect.Map && elem.Kind() != reflect.Slice && elem.Kind() != reflect.Ptr { + node = elem.Addr().Interface() + continue + } + node = elem.Interface() + + default: + return fmt.Errorf("invalid token reference %q", decodedToken) + } + + } + + return nil +} + +// DecodedTokens returns the decoded tokens +func (p *Pointer) DecodedTokens() []string { + result := make([]string, 0, len(p.referenceTokens)) + for _, t := range p.referenceTokens { + result = append(result, Unescape(t)) + } + return result +} + +// IsEmpty returns true if this is an empty json pointer +// this indicates that it points to the root document +func (p *Pointer) IsEmpty() bool { + return len(p.referenceTokens) == 0 +} + +// Pointer to string representation function +func (p *Pointer) String() string { + + if len(p.referenceTokens) == 0 { + return emptyPointer + } + + pointerString := pointerSeparator + strings.Join(p.referenceTokens, pointerSeparator) + + return pointerString +} + +// Specific JSON pointer encoding here +// ~0 => ~ +// ~1 => / +// ... and vice versa + +const ( + encRefTok0 = `~0` + encRefTok1 = `~1` + decRefTok0 = `~` + decRefTok1 = `/` +) + +// Unescape unescapes a json pointer reference token string to the original representation +func Unescape(token string) string { + step1 := strings.Replace(token, encRefTok1, decRefTok1, -1) + step2 := strings.Replace(step1, encRefTok0, decRefTok0, -1) + return step2 +} + +// Escape escapes a pointer reference token string +func Escape(token string) string { + step1 := strings.Replace(token, decRefTok0, encRefTok0, -1) + step2 := strings.Replace(step1, decRefTok1, encRefTok1, -1) + return step2 +} diff --git a/vendor/github.com/go-openapi/jsonreference/.gitignore b/vendor/github.com/go-openapi/jsonreference/.gitignore new file mode 100644 index 000000000..769c24400 --- /dev/null +++ b/vendor/github.com/go-openapi/jsonreference/.gitignore @@ -0,0 +1 @@ +secrets.yml diff --git a/vendor/github.com/go-openapi/jsonreference/.travis.yml b/vendor/github.com/go-openapi/jsonreference/.travis.yml new file mode 100644 index 000000000..40b90757d --- /dev/null +++ b/vendor/github.com/go-openapi/jsonreference/.travis.yml @@ -0,0 +1,15 @@ +after_success: +- bash <(curl -s https://codecov.io/bash) +go: +- 1.11.x +- 1.12.x +install: +- GO111MODULE=off go get -u gotest.tools/gotestsum +env: +- GO111MODULE=on +language: go +notifications: + slack: + secure: OpQG/36F7DSF00HLm9WZMhyqFCYYyYTsVDObW226cWiR8PWYiNfLZiSEvIzT1Gx4dDjhigKTIqcLhG34CkL5iNXDjm9Yyo2RYhQPlK8NErNqUEXuBqn4RqYHW48VGhEhOyDd4Ei0E2FN5ZbgpvHgtpkdZ6XDi64r3Ac89isP9aPHXQTuv2Jog6b4/OKKiUTftLcTIst0p4Cp3gqOJWf1wnoj+IadWiECNVQT6zb47IYjtyw6+uV8iUjTzdKcRB6Zc6b4Dq7JAg1Zd7Jfxkql3hlKp4PNlRf9Cy7y5iA3G7MLyg3FcPX5z2kmcyPt2jOTRMBWUJ5zIQpOxizAcN8WsT3WWBL5KbuYK6k0PzujrIDLqdxGpNmjkkMfDBT9cKmZpm2FdW+oZgPFJP+oKmAo4u4KJz/vjiPTXgQlN5bmrLuRMCp+AwC5wkIohTqWZVPE2TK6ZSnMYcg/W39s+RP/9mJoyryAvPSpBOLTI+biCgaUCTOAZxNTWpMFc3tPYntc41WWkdKcooZ9JA5DwfcaVFyTGQ3YXz+HvX6G1z/gW0Q/A4dBi9mj2iE1xm7tRTT+4VQ2AXFvSEI1HJpfPgYnwAtwOD1v3Qm2EUHk9sCdtEDR4wVGEPIVn44GnwFMnGKx9JWppMPYwFu3SVDdHt+E+LOlhZUply11Aa+IVrT2KUQ= +script: +- gotestsum -f short-verbose -- -race -coverprofile=coverage.txt -covermode=atomic ./... diff --git a/vendor/github.com/go-openapi/jsonreference/CODE_OF_CONDUCT.md b/vendor/github.com/go-openapi/jsonreference/CODE_OF_CONDUCT.md new file mode 100644 index 000000000..9322b065e --- /dev/null +++ b/vendor/github.com/go-openapi/jsonreference/CODE_OF_CONDUCT.md @@ -0,0 +1,74 @@ +# Contributor Covenant Code of Conduct + +## Our Pledge + +In the interest of fostering an open and welcoming environment, we as +contributors and maintainers pledge to making participation in our project and +our community a harassment-free experience for everyone, regardless of age, body +size, disability, ethnicity, gender identity and expression, level of experience, +nationality, personal appearance, race, religion, or sexual identity and +orientation. + +## Our Standards + +Examples of behavior that contributes to creating a positive environment +include: + +* Using welcoming and inclusive language +* Being respectful of differing viewpoints and experiences +* Gracefully accepting constructive criticism +* Focusing on what is best for the community +* Showing empathy towards other community members + +Examples of unacceptable behavior by participants include: + +* The use of sexualized language or imagery and unwelcome sexual attention or +advances +* Trolling, insulting/derogatory comments, and personal or political attacks +* Public or private harassment +* Publishing others' private information, such as a physical or electronic + address, without explicit permission +* Other conduct which could reasonably be considered inappropriate in a + professional setting + +## Our Responsibilities + +Project maintainers are responsible for clarifying the standards of acceptable +behavior and are expected to take appropriate and fair corrective action in +response to any instances of unacceptable behavior. + +Project maintainers have the right and responsibility to remove, edit, or +reject comments, commits, code, wiki edits, issues, and other contributions +that are not aligned to this Code of Conduct, or to ban temporarily or +permanently any contributor for other behaviors that they deem inappropriate, +threatening, offensive, or harmful. + +## Scope + +This Code of Conduct applies both within project spaces and in public spaces +when an individual is representing the project or its community. Examples of +representing a project or community include using an official project e-mail +address, posting via an official social media account, or acting as an appointed +representative at an online or offline event. Representation of a project may be +further defined and clarified by project maintainers. + +## Enforcement + +Instances of abusive, harassing, or otherwise unacceptable behavior may be +reported by contacting the project team at ivan+abuse@flanders.co.nz. All +complaints will be reviewed and investigated and will result in a response that +is deemed necessary and appropriate to the circumstances. The project team is +obligated to maintain confidentiality with regard to the reporter of an incident. +Further details of specific enforcement policies may be posted separately. + +Project maintainers who do not follow or enforce the Code of Conduct in good +faith may face temporary or permanent repercussions as determined by other +members of the project's leadership. + +## Attribution + +This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, +available at [http://contributor-covenant.org/version/1/4][version] + +[homepage]: http://contributor-covenant.org +[version]: http://contributor-covenant.org/version/1/4/ diff --git a/vendor/github.com/go-openapi/jsonreference/LICENSE b/vendor/github.com/go-openapi/jsonreference/LICENSE new file mode 100644 index 000000000..d64569567 --- /dev/null +++ b/vendor/github.com/go-openapi/jsonreference/LICENSE @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + 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. diff --git a/vendor/github.com/go-openapi/jsonreference/README.md b/vendor/github.com/go-openapi/jsonreference/README.md new file mode 100644 index 000000000..66345f4c6 --- /dev/null +++ b/vendor/github.com/go-openapi/jsonreference/README.md @@ -0,0 +1,15 @@ +# gojsonreference [![Build Status](https://travis-ci.org/go-openapi/jsonreference.svg?branch=master)](https://travis-ci.org/go-openapi/jsonreference) [![codecov](https://codecov.io/gh/go-openapi/jsonreference/branch/master/graph/badge.svg)](https://codecov.io/gh/go-openapi/jsonreference) [![Slack Status](https://slackin.goswagger.io/badge.svg)](https://slackin.goswagger.io) + +[![license](http://img.shields.io/badge/license-Apache%20v2-orange.svg)](https://raw.githubusercontent.com/go-openapi/jsonreference/master/LICENSE) [![GoDoc](https://godoc.org/github.com/go-openapi/jsonreference?status.svg)](http://godoc.org/github.com/go-openapi/jsonreference) +An implementation of JSON Reference - Go language + +## Status +Work in progress ( 90% done ) + +## Dependencies +https://github.com/go-openapi/jsonpointer + +## References +http://tools.ietf.org/html/draft-ietf-appsawg-json-pointer-07 + +http://tools.ietf.org/html/draft-pbryan-zyp-json-ref-03 diff --git a/vendor/github.com/go-openapi/jsonreference/go.mod b/vendor/github.com/go-openapi/jsonreference/go.mod new file mode 100644 index 000000000..35adddfe4 --- /dev/null +++ b/vendor/github.com/go-openapi/jsonreference/go.mod @@ -0,0 +1,10 @@ +module github.com/go-openapi/jsonreference + +require ( + github.com/PuerkitoBio/purell v1.1.1 + github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 // indirect + github.com/go-openapi/jsonpointer v0.19.2 + github.com/stretchr/testify v1.3.0 + golang.org/x/net v0.0.0-20190613194153-d28f0bde5980 // indirect + golang.org/x/text v0.3.2 // indirect +) diff --git a/vendor/github.com/go-openapi/jsonreference/go.sum b/vendor/github.com/go-openapi/jsonreference/go.sum new file mode 100644 index 000000000..f1a7a34e3 --- /dev/null +++ b/vendor/github.com/go-openapi/jsonreference/go.sum @@ -0,0 +1,36 @@ +github.com/PuerkitoBio/purell v1.1.1 h1:WEQqlqaGbrPkxLJWfBwQmfEAE1Z7ONdDLqrN38tNFfI= +github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= +github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 h1:d+Bc7a5rLufV/sSk/8dngufqelfh6jnri85riMAaF/M= +github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/go-openapi/jsonpointer v0.19.2 h1:A9+F4Dc/MCNB5jibxf6rRvOvR/iFgQdyNx9eIhnGqq0= +github.com/go-openapi/jsonpointer v0.19.2/go.mod h1:3akKfEdA7DF1sugOqz1dVQHBcuDBPKZGEoHC/NkiQRg= +github.com/go-openapi/swag v0.19.2 h1:jvO6bCMBEilGwMfHhrd61zIID4oIFdwb76V17SM88dE= +github.com/go-openapi/swag v0.19.2/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= +github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= +github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= +github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63 h1:nTT4s92Dgz2HlrB2NaMgvlfqHH39OgMhA7z3PK7PGD4= +github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/net v0.0.0-20190613194153-d28f0bde5980 h1:dfGZHvZk057jK2MCeWus/TowKpJ8y4AmooUzdBSR9GU= +golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs= +golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY= +gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= diff --git a/vendor/github.com/go-openapi/jsonreference/reference.go b/vendor/github.com/go-openapi/jsonreference/reference.go new file mode 100644 index 000000000..3bc0a6e26 --- /dev/null +++ b/vendor/github.com/go-openapi/jsonreference/reference.go @@ -0,0 +1,156 @@ +// Copyright 2013 sigu-399 ( https://github.com/sigu-399 ) +// +// 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. + +// author sigu-399 +// author-github https://github.com/sigu-399 +// author-mail sigu.399@gmail.com +// +// repository-name jsonreference +// repository-desc An implementation of JSON Reference - Go language +// +// description Main and unique file. +// +// created 26-02-2013 + +package jsonreference + +import ( + "errors" + "net/url" + "strings" + + "github.com/PuerkitoBio/purell" + "github.com/go-openapi/jsonpointer" +) + +const ( + fragmentRune = `#` +) + +// New creates a new reference for the given string +func New(jsonReferenceString string) (Ref, error) { + + var r Ref + err := r.parse(jsonReferenceString) + return r, err + +} + +// MustCreateRef parses the ref string and panics when it's invalid. +// Use the New method for a version that returns an error +func MustCreateRef(ref string) Ref { + r, err := New(ref) + if err != nil { + panic(err) + } + return r +} + +// Ref represents a json reference object +type Ref struct { + referenceURL *url.URL + referencePointer jsonpointer.Pointer + + HasFullURL bool + HasURLPathOnly bool + HasFragmentOnly bool + HasFileScheme bool + HasFullFilePath bool +} + +// GetURL gets the URL for this reference +func (r *Ref) GetURL() *url.URL { + return r.referenceURL +} + +// GetPointer gets the json pointer for this reference +func (r *Ref) GetPointer() *jsonpointer.Pointer { + return &r.referencePointer +} + +// String returns the best version of the url for this reference +func (r *Ref) String() string { + + if r.referenceURL != nil { + return r.referenceURL.String() + } + + if r.HasFragmentOnly { + return fragmentRune + r.referencePointer.String() + } + + return r.referencePointer.String() +} + +// IsRoot returns true if this reference is a root document +func (r *Ref) IsRoot() bool { + return r.referenceURL != nil && + !r.IsCanonical() && + !r.HasURLPathOnly && + r.referenceURL.Fragment == "" +} + +// IsCanonical returns true when this pointer starts with http(s):// or file:// +func (r *Ref) IsCanonical() bool { + return (r.HasFileScheme && r.HasFullFilePath) || (!r.HasFileScheme && r.HasFullURL) +} + +// "Constructor", parses the given string JSON reference +func (r *Ref) parse(jsonReferenceString string) error { + + parsed, err := url.Parse(jsonReferenceString) + if err != nil { + return err + } + + r.referenceURL, _ = url.Parse(purell.NormalizeURL(parsed, purell.FlagsSafe|purell.FlagRemoveDuplicateSlashes)) + refURL := r.referenceURL + + if refURL.Scheme != "" && refURL.Host != "" { + r.HasFullURL = true + } else { + if refURL.Path != "" { + r.HasURLPathOnly = true + } else if refURL.RawQuery == "" && refURL.Fragment != "" { + r.HasFragmentOnly = true + } + } + + r.HasFileScheme = refURL.Scheme == "file" + r.HasFullFilePath = strings.HasPrefix(refURL.Path, "/") + + // invalid json-pointer error means url has no json-pointer fragment. simply ignore error + r.referencePointer, _ = jsonpointer.New(refURL.Fragment) + + return nil +} + +// Inherits creates a new reference from a parent and a child +// If the child cannot inherit from the parent, an error is returned +func (r *Ref) Inherits(child Ref) (*Ref, error) { + childURL := child.GetURL() + parentURL := r.GetURL() + if childURL == nil { + return nil, errors.New("child url is nil") + } + if parentURL == nil { + return &child, nil + } + + ref, err := New(parentURL.ResolveReference(childURL).String()) + if err != nil { + return nil, err + } + return &ref, nil +} diff --git a/vendor/github.com/go-openapi/spec/.editorconfig b/vendor/github.com/go-openapi/spec/.editorconfig new file mode 100644 index 000000000..3152da69a --- /dev/null +++ b/vendor/github.com/go-openapi/spec/.editorconfig @@ -0,0 +1,26 @@ +# top-most EditorConfig file +root = true + +# Unix-style newlines with a newline ending every file +[*] +end_of_line = lf +insert_final_newline = true +indent_style = space +indent_size = 2 +trim_trailing_whitespace = true + +# Set default charset +[*.{js,py,go,scala,rb,java,html,css,less,sass,md}] +charset = utf-8 + +# Tab indentation (no size specified) +[*.go] +indent_style = tab + +[*.md] +trim_trailing_whitespace = false + +# Matches the exact files either package.json or .travis.yml +[{package.json,.travis.yml}] +indent_style = space +indent_size = 2 diff --git a/vendor/github.com/go-openapi/spec/.gitignore b/vendor/github.com/go-openapi/spec/.gitignore new file mode 100644 index 000000000..dd91ed6a0 --- /dev/null +++ b/vendor/github.com/go-openapi/spec/.gitignore @@ -0,0 +1,2 @@ +secrets.yml +coverage.out diff --git a/vendor/github.com/go-openapi/spec/.golangci.yml b/vendor/github.com/go-openapi/spec/.golangci.yml new file mode 100644 index 000000000..3e33f9f2e --- /dev/null +++ b/vendor/github.com/go-openapi/spec/.golangci.yml @@ -0,0 +1,23 @@ +linters-settings: + govet: + check-shadowing: true + golint: + min-confidence: 0 + gocyclo: + min-complexity: 45 + maligned: + suggest-new: true + dupl: + threshold: 200 + goconst: + min-len: 2 + min-occurrences: 2 + +linters: + enable-all: true + disable: + - maligned + - unparam + - lll + - gochecknoinits + - gochecknoglobals diff --git a/vendor/github.com/go-openapi/spec/.travis.yml b/vendor/github.com/go-openapi/spec/.travis.yml new file mode 100644 index 000000000..aa26d8763 --- /dev/null +++ b/vendor/github.com/go-openapi/spec/.travis.yml @@ -0,0 +1,15 @@ +after_success: +- bash <(curl -s https://codecov.io/bash) +go: +- 1.11.x +- 1.12.x +install: +- GO111MODULE=off go get -u gotest.tools/gotestsum +env: +- GO111MODULE=on +language: go +notifications: + slack: + secure: QUWvCkBBK09GF7YtEvHHVt70JOkdlNBG0nIKu/5qc4/nW5HP8I2w0SEf/XR2je0eED1Qe3L/AfMCWwrEj+IUZc3l4v+ju8X8R3Lomhme0Eb0jd1MTMCuPcBT47YCj0M7RON7vXtbFfm1hFJ/jLe5+9FXz0hpXsR24PJc5ZIi/ogNwkaPqG4BmndzecpSh0vc2FJPZUD9LT0I09REY/vXR0oQAalLkW0asGD5taHZTUZq/kBpsNxaAFrLM23i4mUcf33M5fjLpvx5LRICrX/57XpBrDh2TooBU6Qj3CgoY0uPRYUmSNxbVx1czNzl2JtEpb5yjoxfVPQeg0BvQM00G8LJINISR+ohrjhkZmAqchDupAX+yFrxTtORa78CtnIL6z/aTNlgwwVD8kvL/1pFA/JWYmKDmz93mV/+6wubGzNSQCstzjkFA4/iZEKewKUoRIAi/fxyscP6L/rCpmY/4llZZvrnyTqVbt6URWpopUpH4rwYqreXAtJxJsfBJIeSmUIiDIOMGkCTvyTEW3fWGmGoqWtSHLoaWDyAIGb7azb+KvfpWtEcoPFWfSWU+LGee0A/YsUhBl7ADB9A0CJEuR8q4BPpKpfLwPKSiKSAXL7zDkyjExyhtgqbSl2jS+rKIHOZNL8JkCcTP2MKMVd563C5rC5FMKqu3S9m2b6380E= +script: +- gotestsum -f short-verbose -- -race -coverprofile=coverage.txt -covermode=atomic ./... diff --git a/vendor/github.com/go-openapi/spec/CODE_OF_CONDUCT.md b/vendor/github.com/go-openapi/spec/CODE_OF_CONDUCT.md new file mode 100644 index 000000000..9322b065e --- /dev/null +++ b/vendor/github.com/go-openapi/spec/CODE_OF_CONDUCT.md @@ -0,0 +1,74 @@ +# Contributor Covenant Code of Conduct + +## Our Pledge + +In the interest of fostering an open and welcoming environment, we as +contributors and maintainers pledge to making participation in our project and +our community a harassment-free experience for everyone, regardless of age, body +size, disability, ethnicity, gender identity and expression, level of experience, +nationality, personal appearance, race, religion, or sexual identity and +orientation. + +## Our Standards + +Examples of behavior that contributes to creating a positive environment +include: + +* Using welcoming and inclusive language +* Being respectful of differing viewpoints and experiences +* Gracefully accepting constructive criticism +* Focusing on what is best for the community +* Showing empathy towards other community members + +Examples of unacceptable behavior by participants include: + +* The use of sexualized language or imagery and unwelcome sexual attention or +advances +* Trolling, insulting/derogatory comments, and personal or political attacks +* Public or private harassment +* Publishing others' private information, such as a physical or electronic + address, without explicit permission +* Other conduct which could reasonably be considered inappropriate in a + professional setting + +## Our Responsibilities + +Project maintainers are responsible for clarifying the standards of acceptable +behavior and are expected to take appropriate and fair corrective action in +response to any instances of unacceptable behavior. + +Project maintainers have the right and responsibility to remove, edit, or +reject comments, commits, code, wiki edits, issues, and other contributions +that are not aligned to this Code of Conduct, or to ban temporarily or +permanently any contributor for other behaviors that they deem inappropriate, +threatening, offensive, or harmful. + +## Scope + +This Code of Conduct applies both within project spaces and in public spaces +when an individual is representing the project or its community. Examples of +representing a project or community include using an official project e-mail +address, posting via an official social media account, or acting as an appointed +representative at an online or offline event. Representation of a project may be +further defined and clarified by project maintainers. + +## Enforcement + +Instances of abusive, harassing, or otherwise unacceptable behavior may be +reported by contacting the project team at ivan+abuse@flanders.co.nz. All +complaints will be reviewed and investigated and will result in a response that +is deemed necessary and appropriate to the circumstances. The project team is +obligated to maintain confidentiality with regard to the reporter of an incident. +Further details of specific enforcement policies may be posted separately. + +Project maintainers who do not follow or enforce the Code of Conduct in good +faith may face temporary or permanent repercussions as determined by other +members of the project's leadership. + +## Attribution + +This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, +available at [http://contributor-covenant.org/version/1/4][version] + +[homepage]: http://contributor-covenant.org +[version]: http://contributor-covenant.org/version/1/4/ diff --git a/vendor/github.com/go-openapi/spec/LICENSE b/vendor/github.com/go-openapi/spec/LICENSE new file mode 100644 index 000000000..d64569567 --- /dev/null +++ b/vendor/github.com/go-openapi/spec/LICENSE @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + 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. diff --git a/vendor/github.com/go-openapi/spec/README.md b/vendor/github.com/go-openapi/spec/README.md new file mode 100644 index 000000000..6354742cb --- /dev/null +++ b/vendor/github.com/go-openapi/spec/README.md @@ -0,0 +1,10 @@ +# OAI object model [![Build Status](https://travis-ci.org/go-openapi/spec.svg?branch=master)](https://travis-ci.org/go-openapi/spec) [![codecov](https://codecov.io/gh/go-openapi/spec/branch/master/graph/badge.svg)](https://codecov.io/gh/go-openapi/spec) [![Slack Status](https://slackin.goswagger.io/badge.svg)](https://slackin.goswagger.io) + +[![license](http://img.shields.io/badge/license-Apache%20v2-orange.svg)](https://raw.githubusercontent.com/go-openapi/spec/master/LICENSE) +[![GoDoc](https://godoc.org/github.com/go-openapi/spec?status.svg)](http://godoc.org/github.com/go-openapi/spec) +[![GolangCI](https://golangci.com/badges/github.com/go-openapi/spec.svg)](https://golangci.com) +[![Go Report Card](https://goreportcard.com/badge/github.com/go-openapi/spec)](https://goreportcard.com/report/github.com/go-openapi/spec) + +The object model for OpenAPI specification documents. + +Currently supports Swagger 2.0. diff --git a/vendor/github.com/go-openapi/spec/bindata.go b/vendor/github.com/go-openapi/spec/bindata.go new file mode 100644 index 000000000..d5ec7b900 --- /dev/null +++ b/vendor/github.com/go-openapi/spec/bindata.go @@ -0,0 +1,297 @@ +// Code generated by go-bindata. DO NOT EDIT. +// sources: +// schemas/jsonschema-draft-04.json (4.357kB) +// schemas/v2/schema.json (40.249kB) + +package spec + +import ( + "bytes" + "compress/gzip" + "crypto/sha256" + "fmt" + "io" + "io/ioutil" + "os" + "path/filepath" + "strings" + "time" +) + +func bindataRead(data []byte, name string) ([]byte, error) { + gz, err := gzip.NewReader(bytes.NewBuffer(data)) + if err != nil { + return nil, fmt.Errorf("Read %q: %v", name, err) + } + + var buf bytes.Buffer + _, err = io.Copy(&buf, gz) + clErr := gz.Close() + + if err != nil { + return nil, fmt.Errorf("Read %q: %v", name, err) + } + if clErr != nil { + return nil, err + } + + return buf.Bytes(), nil +} + +type asset struct { + bytes []byte + info os.FileInfo + digest [sha256.Size]byte +} + +type bindataFileInfo struct { + name string + size int64 + mode os.FileMode + modTime time.Time +} + +func (fi bindataFileInfo) Name() string { + return fi.name +} +func (fi bindataFileInfo) Size() int64 { + return fi.size +} +func (fi bindataFileInfo) Mode() os.FileMode { + return fi.mode +} +func (fi bindataFileInfo) ModTime() time.Time { + return fi.modTime +} +func (fi bindataFileInfo) IsDir() bool { + return false +} +func (fi bindataFileInfo) Sys() interface{} { + return nil +} + +var _jsonschemaDraft04JSON = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\xc4\x57\x3d\x6f\xdb\x3c\x10\xde\xf3\x2b\x08\x26\x63\xf2\x2a\x2f\xd0\xc9\x5b\xd1\x2e\x01\x5a\x34\x43\x37\x23\x03\x6d\x9d\x6c\x06\x14\xa9\x50\x54\x60\xc3\xd0\x7f\x2f\x28\x4a\x14\x29\x91\x92\x2d\xa7\x8d\x97\x28\xbc\xaf\xe7\x8e\xf7\xc5\xd3\x0d\x42\x08\x61\x9a\xe2\x15\xc2\x7b\xa5\x8a\x55\x92\xbc\x96\x82\x3f\x94\xdb\x3d\xe4\xe4\x3f\x21\x77\x49\x2a\x49\xa6\x1e\x1e\xbf\x24\xe6\xec\x16\xdf\x1b\xa1\x3b\xf3\xff\x02\xc9\x14\xca\xad\xa4\x85\xa2\x82\x6b\xe9\x6f\x42\x02\x32\x2c\x28\x07\x45\x5a\x15\x3d\x77\x46\x39\xd5\xcc\x25\x5e\x21\x83\xb8\x21\x18\xb6\xaf\x52\x92\xa3\x47\x68\x88\xea\x58\x80\x56\x4e\x1a\xf2\xbd\x4f\xcc\x29\x7f\x52\x90\x6b\x7d\xff\x0f\x48\xb4\x3d\x3f\x21\x7c\x27\x21\xd3\x2a\x6e\x31\xaa\x2d\x53\xdd\xf3\xe3\x42\x94\x54\xd1\x77\x78\xe2\x0a\x76\x20\xe3\x20\x68\xcb\x30\x86\x41\xf3\x2a\xc7\x2b\xf4\x78\x8e\xfe\xef\x90\x91\x8a\xa9\xc7\xb1\x1d\xc2\xd8\x2f\x0d\x75\xed\xc1\x4e\x9c\xc8\x25\x43\xac\xa8\xbe\xd7\xcc\xa9\xd1\xa9\x21\xa0\x1a\xbd\x04\x61\x94\x34\x2f\x18\xfc\x3e\x16\x50\x8e\x4d\x03\x6f\x1c\x58\xdb\x48\x23\xbc\x11\x82\x01\xe1\xfa\xd3\x3a\x8e\x30\xaf\x18\x33\x7f\xf3\x8d\x39\x11\x9b\x57\xd8\x2a\xfd\x55\x2a\x49\xf9\x0e\xc7\xec\x37\xd4\x25\xf7\xec\x5c\x66\xc7\xd7\x99\xaa\xcf\x4f\x89\x8a\xd3\xb7\x0a\x3a\xaa\x92\x15\xf4\x30\x6f\x1c\xb0\xd6\x46\xe7\x98\x39\x2d\xa4\x28\x40\x2a\x3a\x88\x9e\x29\xba\x88\x37\x2d\xca\x60\x38\xfa\xba\x5b\x20\xac\xa8\x62\xb0\x4c\xd4\xaf\xda\x45\x0a\xba\x5c\x3b\xb9\xc7\x79\xc5\x14\x2d\x18\x34\x19\x1c\x51\xdb\x25\x4d\xb4\x7e\x06\x14\x38\x6c\x59\x55\xd2\x77\xf8\x69\x59\xfc\x7b\x73\xed\x93\x43\xcb\x32\x6d\x3c\x28\xdc\x1b\x9a\xd3\x62\xab\xc2\x27\xf7\x41\xc9\x08\x2b\x23\x08\xad\x13\x57\x21\x9c\xd3\x72\x0d\x42\x72\xf8\x01\x7c\xa7\xf6\x83\xce\x39\xd7\x82\x3c\x1f\x2f\xd6\x60\x1b\xa2\xdf\x35\x89\x52\x20\xe7\x73\x74\xe0\x66\x26\x64\x4e\xb4\x97\x58\xc2\x0e\x0e\xe1\x60\x92\x34\x6d\xa0\x10\xd6\xb5\x83\x61\x27\xe6\x47\xd3\x89\xbd\x63\xfd\x3b\x8d\x03\x3d\x6c\x42\x2d\x5b\x70\xee\xe8\xdf\x4b\xf4\x66\x4e\xe1\x01\x45\x17\x80\x74\xad\x4f\xc3\xf3\xae\xc6\x1d\xc6\xd7\xc2\xce\xc9\xe1\x29\x30\x86\x2f\x4a\xa6\x4b\x15\x84\x73\xc9\x6f\xfd\x7f\xa5\x6e\x9e\xbd\xf1\xb0\xd4\xdd\x45\x5a\xc2\x3e\x4b\x78\xab\xa8\x84\x74\x4a\x91\x3b\x92\x23\x05\xf2\x1c\x1e\x7b\xf3\x09\xf8\xcf\xab\x24\xb6\x60\xa2\xe8\x4c\x9f\x75\x77\xaa\x8c\xe6\x01\x45\x36\x86\xcf\xc3\x63\x3a\xea\xd4\x8d\x7e\x06\xac\x14\x0a\xe0\x29\xf0\xed\x07\x22\x1a\x65\xda\x44\xae\xa2\x73\x1a\xe6\x90\x69\xa2\x8c\x46\xb2\x2f\xde\x49\x38\x08\xed\xfe\xfd\x41\xaf\x9f\xa9\x55\xd7\xdd\x22\x8d\xfa\x45\x63\xc5\x0f\x80\xf3\xb4\x08\xd6\x79\x30\x9e\x93\xee\x59\xa6\xd0\x4b\xee\x22\xe3\x33\xc1\x3a\x27\x68\x36\x78\x7e\x87\x0a\x06\xd5\x2e\x20\xd3\xaf\x15\xfb\xd8\x3b\x73\x14\xbb\x92\xed\x05\x5d\x2e\x29\x38\x2c\x94\xe4\x42\x45\x5e\xd3\xb5\x7d\xdf\x47\xca\x38\xb4\x5c\xaf\xfb\x7d\xdd\x6d\xf4\xa1\x2d\x77\xdd\x2f\xce\x6d\xc4\x7b\x8b\x4e\x67\xa9\x6f\xfe\x04\x00\x00\xff\xff\xb1\xd1\x27\x78\x05\x11\x00\x00") + +func jsonschemaDraft04JSONBytes() ([]byte, error) { + return bindataRead( + _jsonschemaDraft04JSON, + "jsonschema-draft-04.json", + ) +} + +func jsonschemaDraft04JSON() (*asset, error) { + bytes, err := jsonschemaDraft04JSONBytes() + if err != nil { + return nil, err + } + + info := bindataFileInfo{name: "jsonschema-draft-04.json", size: 4357, mode: os.FileMode(436), modTime: time.Unix(1540282154, 0)} + a := &asset{bytes: bytes, info: info, digest: [32]uint8{0xe1, 0x48, 0x9d, 0xb, 0x47, 0x55, 0xf0, 0x27, 0x93, 0x30, 0x25, 0x91, 0xd3, 0xfc, 0xb8, 0xf0, 0x7b, 0x68, 0x93, 0xa8, 0x2a, 0x94, 0xf2, 0x48, 0x95, 0xf8, 0xe4, 0xed, 0xf1, 0x1b, 0x82, 0xe2}} + return a, nil +} + +var _v2SchemaJSON = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\xec\x5d\x4f\x93\xdb\x36\xb2\xbf\xfb\x53\xa0\x14\x57\xd9\xae\xd8\x92\xe3\xf7\x2e\xcf\x97\xd4\xbc\xd8\x49\x66\x37\x5e\x4f\x79\x26\xbb\x87\x78\x5c\x05\x91\x2d\x09\x09\x09\x30\x00\x38\x33\x5a\xef\x7c\xf7\x2d\xf0\x9f\x08\x02\x20\x41\x8a\xd2\xc8\x0e\x0f\xa9\x78\x28\xa0\xd1\xdd\x68\x34\x7e\xdd\xf8\xf7\xf9\x11\x42\x33\x49\x64\x04\xb3\xd7\x68\x76\x86\xfe\x76\xf9\xfe\x1f\xe8\x32\xd8\x40\x8c\xd1\x8a\x71\x74\x79\x8b\xd7\x6b\xe0\xe8\xd5\xfc\x25\x3a\xbb\x38\x9f\xcf\x9e\xab\x0a\x24\x54\xa5\x37\x52\x26\xaf\x17\x0b\x91\x17\x99\x13\xb6\xb8\x79\xb5\x10\x59\xdd\xf9\xef\x82\xd1\x6f\xf2\xc2\x8f\xf3\x4f\xb5\x1a\xea\xc7\x17\x45\x41\xc6\xd7\x8b\x90\xe3\x95\x7c\xf1\xf2\x7f\x8b\xca\x45\x3d\xb9\x4d\x32\xa6\xd8\xf2\x77\x08\x64\xfe\x8d\xc3\x9f\x29\xe1\xa0\x9a\xff\xed\x11\x42\x08\xcd\x8a\xd6\xb3\x9f\x15\x67\x74\xc5\xca\x7f\x27\x58\x6e\xc4\xec\x11\x42\xd7\x59\x5d\x1c\x86\x44\x12\x46\x71\x74\xc1\x59\x02\x5c\x12\x10\xb3\xd7\x68\x85\x23\x01\x59\x81\x04\x4b\x09\x9c\x6a\xbf\x7e\xce\x49\x7d\xba\x7b\x51\xfd\xa1\x44\xe2\xb0\x52\xac\x7d\xb3\x08\x61\x45\x68\x46\x56\x2c\x6e\x80\x86\x8c\xbf\xbd\x93\x40\x05\x61\x74\x96\x95\xbe\x7f\x84\xd0\x7d\x4e\xde\x42\xb7\xe4\xbe\x46\xbb\x14\x5b\x48\x4e\xe8\xba\x90\x05\xa1\x19\xd0\x34\xae\xc4\xce\xbe\xbc\x9a\xbf\x9c\x15\x7f\x5d\x57\xc5\x42\x10\x01\x27\x89\xe2\x48\x51\xb9\xda\x40\xd5\x87\x37\xc0\x15\x5f\x88\xad\x90\xdc\x10\x81\x42\x16\xa4\x31\x50\x39\x2f\x38\xad\xab\xb0\x53\xd8\xac\x94\x56\x6f\xc3\x84\xf4\x11\xa4\x50\xb3\xfa\xe9\xd3\x6f\x9f\x3e\xdf\x2f\xd0\xeb\x8f\x1f\x3f\x7e\xbc\xfe\xf6\xe9\xf7\xaf\x5f\x7f\xfc\x18\x7e\xfb\xec\xfb\xc7\xb3\x36\x79\x54\x43\xe8\x29\xc5\x31\x20\xc6\x11\x49\x9e\xe5\x12\x41\x66\xa0\xe8\xed\x1d\x8e\x93\x08\x5e\xa3\x27\x3b\xc3\x7c\xa2\x73\xba\xc4\x02\x2e\xb0\xdc\xf4\xe5\x76\xd1\xca\x96\xa2\x8a\x94\xcd\x21\xc9\x6c\xec\x2c\x70\x42\x9e\x34\x74\x9d\x19\x7c\xcd\x20\x9c\xea\x2e\x0a\xfe\x42\x84\xd4\x29\x04\x8c\x8a\xb4\x41\xa2\xc1\xdc\x19\x8a\x88\x90\x4a\x49\xef\xce\xdf\xbd\x45\x4a\x52\x81\x70\x10\x40\x22\x21\x44\xcb\x6d\xc5\xec\x4e\x3c\x1c\x45\xef\x57\x9a\xb5\x7d\xae\xfe\xe5\xe4\x31\x86\x90\xe0\xab\x6d\x02\x3b\x2e\xcb\x11\x90\xd9\xa8\xc6\x77\xc2\x59\x98\x06\xfd\xf9\x2e\x78\x45\x01\xa6\xa8\xa0\x71\x5c\xbe\x33\xa7\xd2\xd9\x5f\x95\xef\xd9\xd5\xac\xfd\xdc\x5d\xbf\x5e\xb8\xd1\x3e\xc7\x31\x48\xe0\x5e\x4c\x14\x65\xdf\xb8\xa8\x71\x10\x09\xa3\xc2\xc7\x02\xcb\xa2\x4e\x5a\x02\x82\x94\x13\xb9\xf5\x30\xe6\xb2\xa4\xb5\xfe\x9b\x3e\x7a\xb2\x55\xd2\xa8\x4a\xbc\x16\xb6\x71\x8e\x39\xc7\xdb\x9d\xe1\x10\x09\x71\xbd\x9c\xb3\x41\x89\xd7\xa5\x89\xdc\x57\xb5\x53\x4a\xfe\x4c\xe1\xbc\xa0\x21\x79\x0a\x1a\x0f\x70\xa7\x5c\x08\x8e\xde\xb0\xc0\x43\x24\xad\x74\x63\x0e\xb1\xd9\x90\xe1\xb0\x2d\x13\xa7\x6d\x78\xfd\x04\x14\x38\x8e\x90\xaa\xce\x63\xac\x3e\x23\xbc\x64\xa9\xb4\xf8\x03\x63\xde\xcd\xbe\x16\x13\x4a\x55\xac\x82\x12\xc6\xac\xd4\x35\xf7\x22\xd4\x3a\xff\x22\x73\x0e\x6e\x51\xa0\x75\x1e\xae\x8f\xe8\x5d\xc7\x59\xe6\xe4\x9a\x18\x8d\xd6\x1c\x53\x84\x4d\xb7\x67\x28\x37\x09\x84\x69\x88\x12\x0e\x01\x11\x80\x32\xa2\xf5\xb9\xaa\xc6\xd9\x73\x53\xab\xfb\xb4\x2e\x20\xc6\x54\x92\xa0\x9a\xf3\x69\x1a\x2f\x81\x77\x37\xae\x53\x1a\xce\x40\xc4\xa8\x82\x1c\xb5\xef\xda\x24\x7d\xb9\x61\x69\x14\xa2\x25\xa0\x90\xac\x56\xc0\x81\x4a\xb4\xe2\x2c\xce\x4a\x64\x7a\x9a\x23\xf4\x13\x91\x3f\xa7\x4b\xf4\x63\x84\x6f\x18\x87\x10\xbd\xc3\xfc\x8f\x90\xdd\x52\x44\x04\xc2\x51\xc4\x6e\x21\x74\x48\x21\x81\xc7\xe2\xfd\xea\x12\xf8\x0d\x09\xf6\xe9\x47\x35\xaf\x67\xc4\x14\xf7\x22\x27\x97\xe1\xe2\x76\x2d\x06\x8c\x4a\x1c\x48\x3f\x73\x2d\x0b\x5b\x29\x45\x24\x00\x2a\x0c\x11\xec\x94\xca\xc2\xa6\xc1\x37\x21\x43\x83\x3b\x5f\x97\xf1\x43\x5e\x53\x73\x19\xa5\x36\xd8\x2d\x05\x2e\x34\x0b\xeb\x39\xfc\x1d\x63\x51\x01\xbd\x3d\xbb\x90\x84\x40\x25\x59\x6d\x09\x5d\xa3\x1c\x37\xe6\x5c\x16\x9a\x40\x09\x70\xc1\xe8\x82\xf1\x35\xa6\xe4\xdf\x99\x5c\x8e\x9e\x4d\x79\xb4\x27\x2f\xbf\x7e\xf8\x05\x25\x8c\x50\xa9\x98\x29\x90\x62\x60\xea\x75\xae\x13\xca\xbf\x2b\x1a\x29\x27\x76\xd6\x20\xc6\x64\x5f\xe6\x32\x1a\x08\x87\x21\x07\x21\xbc\xb4\xe4\xe0\x32\x67\xa6\xcd\xf3\x1e\xcd\xd9\x6b\xb6\x6f\x8e\x27\xa7\xed\xdb\xe7\xbc\xcc\x1a\x07\xce\x6f\x87\x33\xf0\xba\x51\x17\x22\x66\x78\x79\x8e\xce\xe5\x13\x81\x80\x06\x2c\xe5\x78\x0d\xa1\xb2\xb8\x54\xa8\x79\x09\xbd\xbf\x3c\x47\x01\x8b\x13\x2c\xc9\x32\xaa\xaa\x1d\xd5\xee\xab\x36\xbd\x6c\xfd\x54\x6c\xc8\x08\x01\x3c\xbd\xe7\x07\x88\xb0\x24\x37\x79\x90\x28\x4a\x1d\x10\x1a\x92\x1b\x12\xa6\x38\x42\x40\xc3\x4c\x43\x62\x8e\xae\x36\xb0\x45\x71\x2a\xa4\x9a\x23\x79\x59\xb1\xa8\xf2\xa4\x0c\x60\x9f\xcc\x8d\x40\xf5\x80\xca\xa8\x99\xc3\xa7\x85\x1f\x31\x25\xa9\x82\xc5\x6d\xbd\xd8\x36\x76\x7c\x02\x28\x97\xf6\x1d\x74\x3b\x11\x7e\x91\xae\x32\xf8\x6c\xf4\xe6\x7b\x9a\xa5\x1f\x62\xc6\x21\xcf\x9a\xe5\xed\x8b\x02\xf3\x2c\x33\x33\xdf\x00\xca\xc9\x09\xb4\x04\xf5\xa5\x08\xd7\xc3\x02\x18\x66\xf1\xab\x1e\x83\x37\x4c\xcd\x12\xc1\x1d\x50\xf6\xaa\xbd\xfe\xe2\x73\x48\x38\x08\xa0\x32\x9b\x18\x44\x86\x0b\x6a\xc1\xaa\x26\x96\x2d\x96\x3c\xa0\x54\x65\x73\x87\x15\xca\x15\xe5\xf5\x94\x46\x9f\x33\x1a\x0c\x9a\xb1\x5a\xd9\x6a\x95\xcd\xcb\x7e\xec\x9a\xc5\x94\x3b\x37\x26\x31\xd7\xfc\xe4\x1f\x13\x8c\x31\x75\x9c\xba\xf7\x87\x3c\xa1\xb7\x4f\x17\x1b\x09\x82\x98\xc4\x70\x95\xd3\xe8\x4c\x48\x5a\xa6\xd6\x2a\x3d\x56\x42\x80\x9f\xaf\xae\x2e\x50\x0c\x42\xe0\x35\x34\x3c\x8a\x62\x03\x37\xba\xb2\x27\x04\xda\x25\x8d\x06\xe2\xa0\x13\x8a\xf3\xf5\xec\x10\x72\x67\x88\x90\x3d\x4b\x64\xeb\xaa\xda\x8f\xf7\x5a\x75\x47\x9a\xa8\x51\x70\x26\xd2\x38\xc6\x7c\xbb\x57\xfc\xbd\xe4\x04\x56\xa8\xa0\x54\x9a\x45\xd5\xf7\x0f\x16\xfc\x57\x1c\x3c\xdf\x23\xba\x77\x38\xda\x16\x4b\x31\x53\x6a\x4d\x9a\x15\x63\xe7\xe1\x18\x69\x9f\x22\xe0\x24\xbb\x94\x4b\x97\xee\x2d\xf9\x70\x87\x72\x7b\xe6\xc4\x33\x2a\x66\x5e\x1c\x35\x72\xe3\x2d\xda\x73\xe4\xc7\x51\x6d\xa4\xa1\x2a\x4f\xde\x94\xcb\xb2\x3e\x31\x48\xae\x82\xce\xc9\xc8\x65\xcd\xc3\xb7\x34\xb6\x2b\xdf\x58\x65\x78\x6e\x73\xac\x5e\x24\x0d\x3f\xdc\x70\x23\xc6\xda\x52\x0b\x2d\x63\x7d\xa9\x49\x2d\x54\x48\x28\xc0\x12\x9c\xe3\x63\xc9\x58\x04\x98\x36\x07\xc8\x0a\xa7\x91\xd4\xf0\xbc\xc1\xa8\xb9\x70\xd0\xc6\xa9\xb6\x78\x80\x5a\xa3\xb4\x2c\xf4\x18\x0b\x8a\x9d\xd0\xb4\x55\x10\xee\x0d\xc5\xd6\xe0\x99\x93\xdc\xa1\x04\xbb\xf1\xa7\x23\xd1\xd1\x97\x8c\x87\x13\x0a\x21\x02\xe9\x99\x25\xed\x20\xc5\x92\x66\x3c\x32\x9c\xd6\x06\xb0\x31\x5c\x86\x29\x0a\xcb\x60\x33\x12\xa5\x91\xfc\x96\x75\xd0\x59\xd7\x13\xbd\xd3\x23\x79\xdd\x2a\x90\xa6\x38\x06\x91\x39\x7f\x20\x72\x03\x1c\x2d\x01\x61\xba\x45\x37\x38\x22\x61\x8e\x71\x85\xc4\x32\x15\x28\x60\x61\x16\xb8\x3d\x29\xdc\x4d\x3d\x2f\x12\x13\x7d\xc8\x7e\x37\xee\xa8\x7f\xfa\xdb\xcb\x17\xff\x77\xfd\xf9\x7f\xee\x9f\x3d\xfe\xcf\xa7\xa7\x45\xfb\xcf\x1e\xf7\xf3\xe0\xff\xc4\x51\x0a\x8e\x4c\xcb\x01\xdc\x0a\x65\xb2\x01\x83\xed\x3d\xe4\xa9\xa3\x4e\x2d\x59\xc5\xe8\x2f\x48\x7d\x5a\x6e\x37\xbf\x5c\x9f\x35\x13\x64\x14\xfa\xef\x0b\x68\xa6\x0d\xb4\x8e\xf1\xa8\xff\xbb\x60\xf4\x03\x64\xab\x5b\x81\x65\x51\xe6\xda\xca\xfa\xf0\xb0\xac\x3e\x9c\xca\x26\x0e\x1d\xdb\x57\x5b\xbb\xb4\x9a\xa6\xb6\x9b\x1a\x6b\xd1\x9a\x9e\x7e\x33\x9a\xec\x41\x69\x45\x22\xb8\xb4\x51\xeb\x04\x77\xca\x6f\x7b\x7b\xc8\xb2\xb0\x95\x92\x25\x5b\xd0\x42\xaa\x2a\xdd\x32\x78\x4f\x0c\xab\x68\x46\x6c\xea\x6d\xf4\x5c\x5e\xde\xc4\xac\xa5\xf9\xd1\x00\x9f\x7d\x98\x65\x24\xbd\xc7\x97\xd4\xb3\x3a\xa8\x2b\xa0\x34\x76\xf9\x65\x5f\x2d\x25\x95\x1b\xcf\xd6\xf4\x9b\x5f\x09\x95\xb0\x36\x3f\xdb\xd0\x39\x2a\x93\x1c\x9d\x03\xa2\x4a\xca\xf5\xf6\x10\xb6\x94\x89\x0b\x6a\x70\x12\x13\x49\x6e\x40\xe4\x29\x12\x2b\xbd\x80\x45\x11\x04\xaa\xc2\x8f\x56\x9e\x5c\x6b\xec\x8d\x5a\x0e\x14\x59\x06\x2b\x1e\x24\xcb\xc2\x56\x4a\x31\xbe\x23\x71\x1a\xfb\x51\x2a\x0b\x3b\x1c\x48\x10\xa5\x82\xdc\xc0\xbb\x3e\x24\x8d\x5a\x76\x2e\x09\xed\xc1\x65\x51\xb8\x83\xcb\x3e\x24\x8d\x5a\x2e\x5d\xfe\x02\x74\x2d\x3d\xf1\xef\xae\xb8\x4b\xe6\x5e\xd4\xaa\xe2\x2e\x5c\x5e\xec\x0e\xf5\x5b\x0c\xcb\x0a\xbb\xa4\x3c\xf7\x1f\x2a\x55\x69\x97\x8c\x7d\x68\x95\xa5\xad\xb4\xf4\x9c\xa5\x07\xb9\x7a\x05\xbb\xad\x50\x6f\xfb\xa0\x4e\x9b\x48\x23\x49\x92\x28\x87\x19\x3e\x32\xee\xca\x3b\x46\x7e\x7f\x18\x64\xcc\xcc\x0f\x34\xe9\x36\x8b\xb7\x6c\xa8\xa5\x5b\x54\x4c\x54\x5b\x15\x3a\xf1\x6c\x2d\xfe\x96\xc8\x0d\xba\x7b\x81\x88\xc8\x23\xab\xee\x7d\x3b\x92\xa7\x60\x29\xe3\xdc\xff\xb8\x64\xe1\xf6\xa2\x5a\x59\xdc\x6f\xeb\x45\x7d\x6a\xd1\x76\x1e\xea\xb8\xf1\xfa\x14\xd3\x36\x63\xe5\xd7\xf3\xe4\xbe\x25\xbd\x5e\x05\xeb\x73\x74\xb5\x21\x2a\x2e\x4e\xa3\x30\xdf\xbf\x43\x28\x2a\xd1\xa5\x2a\x9d\x8a\xfd\x76\xd8\x8d\xbc\x67\x65\xc7\xb8\x03\x45\xec\xa3\xb0\x37\x8a\x70\x4c\x68\x91\x51\x8e\x58\x80\xed\x4a\xf3\x81\x62\xca\x96\xbb\xf1\x52\xcd\x80\xfb\xe4\x4a\x5d\x6c\xdf\x6e\x20\x4b\x80\x30\x8e\x28\x93\xf9\xe9\x8d\x8a\x6d\xd5\x59\x65\x7b\xaa\x44\x9e\xc0\xc2\xd1\x7c\x40\x26\xd6\x1a\xce\xf9\xc5\x69\x7b\x6c\xec\xc8\x71\x7b\xe5\x21\x2e\xd3\xe5\x65\x93\x91\x53\x0b\x7b\x3a\xc7\xfa\x17\x6a\x01\xa7\x33\xd0\xf4\x40\x0f\x39\x87\xda\xe4\x54\x87\x3a\xd5\xe3\xc7\xa6\x8e\x20\xd4\x11\xb2\x4e\xb1\xe9\x14\x9b\x4e\xb1\xe9\x14\x9b\xfe\x15\x63\xd3\x47\xf5\xff\x97\x38\xe9\xcf\x14\xf8\x76\x82\x49\x13\x4c\xaa\x7d\xcd\x6c\x62\x42\x49\x87\x43\x49\x19\x33\x6f\xe3\x44\x6e\x9b\xab\x8a\x3e\x86\xaa\x99\x52\x1b\x5b\x59\x33\x02\x09\xa0\x21\xa1\x6b\x84\x6b\x66\xbb\xdc\x16\x0c\xd3\x68\xab\xec\x36\x4b\xd8\x60\x8a\x40\x31\x85\x6e\x14\x57\x13\xc2\xfb\x92\x10\xde\xbf\x88\xdc\xbc\x53\x5e\x7f\x82\x7a\x13\xd4\x9b\xa0\xde\x04\xf5\x90\x01\xf5\x94\xcb\x7b\x83\x25\x9e\xd0\xde\x84\xf6\x6a\x5f\x4b\xb3\x98\x00\xdf\x04\xf8\x6c\xbc\x7f\x19\x80\xaf\xf1\x71\x45\x22\x98\x40\xe0\x04\x02\x27\x10\xd8\x29\xf5\x04\x02\xff\x4a\x20\x30\xc1\x72\xf3\x65\x02\x40\xd7\xc1\xd1\xe2\x6b\xf1\xa9\x7b\xfb\xe4\x20\xc0\x68\x9d\xd4\xb4\xd3\x96\xb5\xa6\xd1\x41\x20\xe6\x89\xc3\x48\x65\x58\x13\x84\x9c\x56\x56\x3b\x0c\xe0\x6b\x83\x5c\x13\xd2\x9a\x90\xd6\x84\xb4\x26\xa4\x85\x0c\xa4\x45\x19\xfd\xff\x63\x6c\x52\xb5\x1f\x1e\x19\x74\x3a\xcd\xb9\x69\xce\xa6\x3a\x0f\x7a\x2d\x19\xc7\x81\x14\x5d\xcb\xd5\x03\xc9\x39\xd0\xb0\xd1\xb3\xcd\xfb\x7a\x2d\x5d\x3a\x48\xe1\xfa\x2e\xe6\x81\x42\x18\x86\xd6\xc1\xbe\xb1\x23\xd3\xf7\x34\xed\x19\x0a\x0b\xc4\x48\x44\xfd\x22\x50\xb6\x42\x58\xbb\xe5\x3d\xa7\x73\xd4\x8b\xc4\x8c\x70\x61\xec\x73\xee\xc3\x81\x8b\xf5\xe2\xd7\x52\x3e\xcf\xeb\xeb\x17\x3b\x71\x16\xda\x7d\xb8\xde\xf0\x7a\x8f\x06\x2d\xa7\x40\x7b\xc1\x9d\x41\x4d\xb6\x61\xa2\x4e\x9f\x3d\xa0\xc5\xae\xe3\x1c\x1d\x40\x6c\x48\x8b\x63\xa0\xb5\x01\xed\x8e\x02\xe9\x86\xc8\x3b\x06\xee\xdb\x4b\xde\xbd\xc0\xa1\x6f\xcb\xda\xfc\xc2\x44\x16\x87\x9c\x17\x31\xd3\x30\x20\x39\x42\xcb\x6f\xf2\xf1\xf4\x72\x10\xf8\x1c\xa0\xf3\xbd\x10\xea\x21\x35\x7d\xe8\x86\xdb\x15\xed\x81\x81\x07\x28\xbb\x13\x28\xc7\xf8\xce\x7d\x8d\xc2\x31\xb4\x7e\x94\xd6\xdb\x55\xef\x4a\xfb\xed\xc3\x40\x3e\xeb\x9f\xe9\x99\x0f\xdf\x08\x65\x88\x27\x73\x86\x31\x9d\x47\xdf\x55\x19\xba\x3d\xee\x15\x0a\xcd\x8c\xaa\x5e\xb9\xf6\x57\x33\x73\x5a\xa1\x89\x7b\x3b\xa0\xb2\xa4\xc2\xf6\xc1\x53\xb5\x00\xca\x23\xe5\xf4\x60\x6a\xb4\x2d\x74\xea\x4e\xed\x3b\xe3\x47\xfb\xed\x82\x3d\x19\xd4\x3b\x6b\xaf\xae\x2b\x2f\x57\xb3\x82\x68\xcb\xed\x88\x2e\xe1\x5c\xd7\x26\xfa\x0a\x65\xe7\xce\x11\x33\xb4\xdd\x66\xe3\x37\xf6\xfa\x70\xd6\x4f\xa1\x21\x51\xd8\x3c\x26\x14\x4b\xc6\x87\x44\x27\x1c\x70\xf8\x9e\x46\xce\xab\x21\x07\x5f\xc1\x76\x17\x1b\x77\xb4\xda\x75\xa0\x0a\x3a\x30\xe1\xf8\x97\x32\x16\x2b\x00\x75\x85\xee\x62\x46\xef\xd3\x85\xb5\x6b\x60\xbe\xf2\x30\x7a\x8c\x0b\x4b\xa6\xd0\xf9\x64\x42\xe7\x07\x41\x41\xe3\x2c\x5d\xf9\x6d\xe9\x39\x98\x3b\x3b\x5d\x67\xd4\x5c\xed\xf2\xf0\x48\x7b\xbd\x2d\x31\xdd\x3f\x34\xad\x44\x76\x51\x9a\x56\x22\xa7\x95\xc8\x69\x25\xf2\xe1\x56\x22\x1f\x00\x32\x6a\x73\x92\xed\xe1\xc6\x7d\x9f\x49\x2c\x69\x7e\xc8\x31\x4c\x0c\xb4\xf2\x54\x3b\x79\x3b\x9e\x4d\xb4\xd1\x18\x3e\x5f\x9a\x93\xa2\x11\xc3\xda\x27\x0b\xaf\x37\x2e\x5c\x37\xfb\xeb\x9a\xd6\xc3\xac\xc3\xcc\xf8\x1e\x5b\x9d\xac\x22\x64\xb7\xed\x26\xb8\xf3\xb9\x3c\xbb\x1f\xe2\xb0\x22\x77\x43\x6a\x62\x29\x39\x59\xa6\xe6\xe5\xcd\x7b\x83\xc0\x5b\x8e\x93\x64\xac\xeb\xca\x4f\x65\xac\x4a\xbc\x1e\xcd\x82\xfa\x3c\x70\x36\xb6\xb5\xed\x79\xef\xec\x68\x00\xff\x54\xfa\xb5\xe3\xf1\xdb\xe1\xbe\xce\x76\x17\xaf\x57\xb6\x6b\x89\x05\x09\xce\x52\xb9\x01\x2a\x49\xbe\xd9\xf4\xd2\xb8\x7a\xbf\x91\x02\xf3\x22\x8c\x13\xf2\x77\xd8\x8e\x43\x8b\xe1\x54\x6e\x5e\x9d\xc7\x49\x44\x02\x22\xc7\xa4\x79\x81\x85\xb8\x65\x3c\x1c\x93\xe6\x59\xa2\xf8\x1c\x51\x95\x05\xd9\x20\x00\x21\x7e\x60\x21\x58\xa9\x56\xff\xbe\xb6\x5a\x5e\x5b\x3f\x1f\xd6\xd3\x3c\xc4\x4d\xba\x99\xb4\x63\x6e\x7d\x3e\x3d\x57\xd2\x18\x5f\x47\xe8\xc3\x06\x8a\x68\x6c\x7f\x3b\x72\x0f\xe7\xe2\x77\x77\xf1\xd0\x99\xab\xdf\x2e\xfe\xd6\xbb\xcd\x1a\xb9\x90\xd1\xaf\xf2\x38\x3d\xdb\x74\xf8\xeb\xe3\xda\xe8\x2a\x62\xb7\xda\x1b\x07\xa9\xdc\x30\x5e\xbc\x68\xfb\x6b\x9f\x97\xf1\xc6\xb1\xd8\x5c\x29\x1e\x49\x30\xc5\xf7\xde\xad\x91\x42\xf9\xdd\xed\x89\x80\x25\xbe\x37\xd7\xe7\x32\x5c\xe6\x35\xac\xd4\x0c\x2d\xf7\x90\xc4\xe3\xf5\xe3\x2f\x7f\x54\x18\x88\xe3\x61\x47\x85\x64\x7f\xc0\xd7\x3f\x1a\x92\x42\xe9\xc7\x1e\x0d\x95\x76\xa7\x51\xa0\x8f\x02\x1b\x46\x9e\x06\x42\xd1\xf2\x01\x07\x02\xde\xe9\x7d\x1a\x0b\xa7\x32\x16\xcc\xc0\xee\xc4\x90\xd2\x5f\x6f\x98\x54\x5d\xf2\x95\xe1\xa7\x69\x10\x3a\x06\xe1\x65\xb3\x17\x47\x58\x78\xd0\x45\xd6\x5b\xd5\x5f\x25\x1d\x71\x49\xa6\x7a\x64\xda\xd0\x6f\xc7\x3a\x4c\xe3\x09\xc0\x6e\x96\x2c\xa7\xa7\x77\x34\x10\x05\x08\x21\x44\x92\x65\x77\xdf\x20\x5c\xbc\xe7\x97\x3f\xf4\x1a\x45\xd6\xe7\x27\x4a\xde\x74\x27\x66\x11\x7d\x70\xba\xd3\x78\xf9\x1e\x0d\xca\xc8\x39\xde\x7c\xb3\xa6\xe1\xbc\xd7\xc1\x6a\x6f\xb3\x0e\x52\xbe\xe4\x98\x8a\x15\x70\x94\x70\x26\x59\xc0\xa2\xf2\x1c\xfb\xd9\xc5\xf9\xbc\xd5\x92\x9c\xa3\xdf\xe6\x1e\xb3\x0d\x49\xba\x87\x50\x5f\x84\xfe\xe9\xd6\xf8\xbb\xe6\xf0\x7a\xeb\xa6\x65\x3b\x86\x8b\x79\x93\xf5\x59\x20\x6e\xb4\xa7\x44\xf4\x3f\xa5\xfe\x67\x42\x12\xdb\xd3\xe7\xbb\xa5\xa3\x8c\x5c\x2b\x97\xbb\xbb\x7f\x8e\xc5\x6e\xed\x43\x5c\xbf\x74\xc8\x8f\xff\xe6\xd6\xbe\x91\xb6\xf5\x95\xe4\xed\x93\xc4\xa8\x5b\xf9\x76\x4d\x35\xb7\xd8\x8c\xb6\x7d\xaf\x72\xe0\xb6\xbd\x01\x63\x9e\x76\xab\x1a\x32\x76\xe4\x8c\x76\xc2\xad\x6c\xa2\x65\xf7\xcf\xf8\xa7\xda\x2a\xb9\x8c\x3d\x3c\xa3\x9d\x64\x33\xe5\x1a\xb5\x2d\xfb\x86\xa2\x5a\x7f\x19\x5b\x7f\xc6\x3f\xd1\x53\xd3\xe2\x41\x5b\xd3\x4f\xf0\xec\xb0\x42\x73\x43\xd2\x68\x27\xd3\x6a\x6a\x34\xf6\x4e\x1e\x52\x8b\x87\x6c\xcc\xae\x44\xfb\x9e\xa7\x51\x4f\x9d\x55\x03\x81\x8e\x67\xfc\xb4\x69\xf0\x3a\x18\xf2\x40\xd0\xf6\xa8\x34\xe3\xc9\x98\xaf\xf6\xda\x24\xd3\xeb\x60\xb9\x0e\xd3\x1f\xa9\xff\xee\x1f\xfd\x37\x00\x00\xff\xff\x69\x5d\x0a\x6a\x39\x9d\x00\x00") + +func v2SchemaJSONBytes() ([]byte, error) { + return bindataRead( + _v2SchemaJSON, + "v2/schema.json", + ) +} + +func v2SchemaJSON() (*asset, error) { + bytes, err := v2SchemaJSONBytes() + if err != nil { + return nil, err + } + + info := bindataFileInfo{name: "v2/schema.json", size: 40249, mode: os.FileMode(436), modTime: time.Unix(1540282154, 0)} + a := &asset{bytes: bytes, info: info, digest: [32]uint8{0xcb, 0x25, 0x27, 0xe8, 0x46, 0xae, 0x22, 0xc4, 0xf4, 0x8b, 0x1, 0x32, 0x4d, 0x1f, 0xf8, 0xdf, 0x75, 0x15, 0xc8, 0x2d, 0xc7, 0xed, 0xe, 0x7e, 0x0, 0x75, 0xc0, 0xf9, 0xd2, 0x1f, 0x75, 0x57}} + return a, nil +} + +// Asset loads and returns the asset for the given name. +// It returns an error if the asset could not be found or +// could not be loaded. +func Asset(name string) ([]byte, error) { + canonicalName := strings.Replace(name, "\\", "/", -1) + if f, ok := _bindata[canonicalName]; ok { + a, err := f() + if err != nil { + return nil, fmt.Errorf("Asset %s can't read by error: %v", name, err) + } + return a.bytes, nil + } + return nil, fmt.Errorf("Asset %s not found", name) +} + +// AssetString returns the asset contents as a string (instead of a []byte). +func AssetString(name string) (string, error) { + data, err := Asset(name) + return string(data), err +} + +// MustAsset is like Asset but panics when Asset would return an error. +// It simplifies safe initialization of global variables. +func MustAsset(name string) []byte { + a, err := Asset(name) + if err != nil { + panic("asset: Asset(" + name + "): " + err.Error()) + } + + return a +} + +// MustAssetString is like AssetString but panics when Asset would return an +// error. It simplifies safe initialization of global variables. +func MustAssetString(name string) string { + return string(MustAsset(name)) +} + +// AssetInfo loads and returns the asset info for the given name. +// It returns an error if the asset could not be found or +// could not be loaded. +func AssetInfo(name string) (os.FileInfo, error) { + canonicalName := strings.Replace(name, "\\", "/", -1) + if f, ok := _bindata[canonicalName]; ok { + a, err := f() + if err != nil { + return nil, fmt.Errorf("AssetInfo %s can't read by error: %v", name, err) + } + return a.info, nil + } + return nil, fmt.Errorf("AssetInfo %s not found", name) +} + +// AssetDigest returns the digest of the file with the given name. It returns an +// error if the asset could not be found or the digest could not be loaded. +func AssetDigest(name string) ([sha256.Size]byte, error) { + canonicalName := strings.Replace(name, "\\", "/", -1) + if f, ok := _bindata[canonicalName]; ok { + a, err := f() + if err != nil { + return [sha256.Size]byte{}, fmt.Errorf("AssetDigest %s can't read by error: %v", name, err) + } + return a.digest, nil + } + return [sha256.Size]byte{}, fmt.Errorf("AssetDigest %s not found", name) +} + +// Digests returns a map of all known files and their checksums. +func Digests() (map[string][sha256.Size]byte, error) { + mp := make(map[string][sha256.Size]byte, len(_bindata)) + for name := range _bindata { + a, err := _bindata[name]() + if err != nil { + return nil, err + } + mp[name] = a.digest + } + return mp, nil +} + +// AssetNames returns the names of the assets. +func AssetNames() []string { + names := make([]string, 0, len(_bindata)) + for name := range _bindata { + names = append(names, name) + } + return names +} + +// _bindata is a table, holding each asset generator, mapped to its name. +var _bindata = map[string]func() (*asset, error){ + "jsonschema-draft-04.json": jsonschemaDraft04JSON, + + "v2/schema.json": v2SchemaJSON, +} + +// AssetDir returns the file names below a certain +// directory embedded in the file by go-bindata. +// For example if you run go-bindata on data/... and data contains the +// following hierarchy: +// data/ +// foo.txt +// img/ +// a.png +// b.png +// then AssetDir("data") would return []string{"foo.txt", "img"}, +// AssetDir("data/img") would return []string{"a.png", "b.png"}, +// AssetDir("foo.txt") and AssetDir("notexist") would return an error, and +// AssetDir("") will return []string{"data"}. +func AssetDir(name string) ([]string, error) { + node := _bintree + if len(name) != 0 { + canonicalName := strings.Replace(name, "\\", "/", -1) + pathList := strings.Split(canonicalName, "/") + for _, p := range pathList { + node = node.Children[p] + if node == nil { + return nil, fmt.Errorf("Asset %s not found", name) + } + } + } + if node.Func != nil { + return nil, fmt.Errorf("Asset %s not found", name) + } + rv := make([]string, 0, len(node.Children)) + for childName := range node.Children { + rv = append(rv, childName) + } + return rv, nil +} + +type bintree struct { + Func func() (*asset, error) + Children map[string]*bintree +} + +var _bintree = &bintree{nil, map[string]*bintree{ + "jsonschema-draft-04.json": &bintree{jsonschemaDraft04JSON, map[string]*bintree{}}, + "v2": &bintree{nil, map[string]*bintree{ + "schema.json": &bintree{v2SchemaJSON, map[string]*bintree{}}, + }}, +}} + +// RestoreAsset restores an asset under the given directory. +func RestoreAsset(dir, name string) error { + data, err := Asset(name) + if err != nil { + return err + } + info, err := AssetInfo(name) + if err != nil { + return err + } + err = os.MkdirAll(_filePath(dir, filepath.Dir(name)), os.FileMode(0755)) + if err != nil { + return err + } + err = ioutil.WriteFile(_filePath(dir, name), data, info.Mode()) + if err != nil { + return err + } + return os.Chtimes(_filePath(dir, name), info.ModTime(), info.ModTime()) +} + +// RestoreAssets restores an asset under the given directory recursively. +func RestoreAssets(dir, name string) error { + children, err := AssetDir(name) + // File + if err != nil { + return RestoreAsset(dir, name) + } + // Dir + for _, child := range children { + err = RestoreAssets(dir, filepath.Join(name, child)) + if err != nil { + return err + } + } + return nil +} + +func _filePath(dir, name string) string { + canonicalName := strings.Replace(name, "\\", "/", -1) + return filepath.Join(append([]string{dir}, strings.Split(canonicalName, "/")...)...) +} diff --git a/vendor/github.com/go-openapi/spec/cache.go b/vendor/github.com/go-openapi/spec/cache.go new file mode 100644 index 000000000..3fada0dae --- /dev/null +++ b/vendor/github.com/go-openapi/spec/cache.go @@ -0,0 +1,60 @@ +// Copyright 2015 go-swagger maintainers +// +// 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 spec + +import "sync" + +// ResolutionCache a cache for resolving urls +type ResolutionCache interface { + Get(string) (interface{}, bool) + Set(string, interface{}) +} + +type simpleCache struct { + lock sync.RWMutex + store map[string]interface{} +} + +// Get retrieves a cached URI +func (s *simpleCache) Get(uri string) (interface{}, bool) { + debugLog("getting %q from resolution cache", uri) + s.lock.RLock() + v, ok := s.store[uri] + debugLog("got %q from resolution cache: %t", uri, ok) + + s.lock.RUnlock() + return v, ok +} + +// Set caches a URI +func (s *simpleCache) Set(uri string, data interface{}) { + s.lock.Lock() + s.store[uri] = data + s.lock.Unlock() +} + +var resCache ResolutionCache + +func init() { + resCache = initResolutionCache() +} + +// initResolutionCache initializes the URI resolution cache +func initResolutionCache() ResolutionCache { + return &simpleCache{store: map[string]interface{}{ + "http://swagger.io/v2/schema.json": MustLoadSwagger20Schema(), + "http://json-schema.org/draft-04/schema": MustLoadJSONSchemaDraft04(), + }} +} diff --git a/vendor/github.com/go-openapi/spec/contact_info.go b/vendor/github.com/go-openapi/spec/contact_info.go new file mode 100644 index 000000000..f285970aa --- /dev/null +++ b/vendor/github.com/go-openapi/spec/contact_info.go @@ -0,0 +1,24 @@ +// Copyright 2015 go-swagger maintainers +// +// 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 spec + +// ContactInfo contact information for the exposed API. +// +// For more information: http://goo.gl/8us55a#contactObject +type ContactInfo struct { + Name string `json:"name,omitempty"` + URL string `json:"url,omitempty"` + Email string `json:"email,omitempty"` +} diff --git a/vendor/github.com/go-openapi/spec/debug.go b/vendor/github.com/go-openapi/spec/debug.go new file mode 100644 index 000000000..389c528ff --- /dev/null +++ b/vendor/github.com/go-openapi/spec/debug.go @@ -0,0 +1,47 @@ +// Copyright 2015 go-swagger maintainers +// +// 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 spec + +import ( + "fmt" + "log" + "os" + "path/filepath" + "runtime" +) + +var ( + // Debug is true when the SWAGGER_DEBUG env var is not empty. + // It enables a more verbose logging of this package. + Debug = os.Getenv("SWAGGER_DEBUG") != "" + // specLogger is a debug logger for this package + specLogger *log.Logger +) + +func init() { + debugOptions() +} + +func debugOptions() { + specLogger = log.New(os.Stdout, "spec:", log.LstdFlags) +} + +func debugLog(msg string, args ...interface{}) { + // A private, trivial trace logger, based on go-openapi/spec/expander.go:debugLog() + if Debug { + _, file1, pos1, _ := runtime.Caller(1) + specLogger.Printf("%s:%d: %s", filepath.Base(file1), pos1, fmt.Sprintf(msg, args...)) + } +} diff --git a/vendor/github.com/go-openapi/spec/expander.go b/vendor/github.com/go-openapi/spec/expander.go new file mode 100644 index 000000000..1e7fc8c49 --- /dev/null +++ b/vendor/github.com/go-openapi/spec/expander.go @@ -0,0 +1,650 @@ +// Copyright 2015 go-swagger maintainers +// +// 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 spec + +import ( + "encoding/json" + "fmt" + "strings" +) + +// ExpandOptions provides options for spec expand +type ExpandOptions struct { + RelativeBase string + SkipSchemas bool + ContinueOnError bool + AbsoluteCircularRef bool +} + +// ResolveRefWithBase resolves a reference against a context root with preservation of base path +func ResolveRefWithBase(root interface{}, ref *Ref, opts *ExpandOptions) (*Schema, error) { + resolver, err := defaultSchemaLoader(root, opts, nil, nil) + if err != nil { + return nil, err + } + specBasePath := "" + if opts != nil && opts.RelativeBase != "" { + specBasePath, _ = absPath(opts.RelativeBase) + } + + result := new(Schema) + if err := resolver.Resolve(ref, result, specBasePath); err != nil { + return nil, err + } + return result, nil +} + +// ResolveRef resolves a reference against a context root +// ref is guaranteed to be in root (no need to go to external files) +// ResolveRef is ONLY called from the code generation module +func ResolveRef(root interface{}, ref *Ref) (*Schema, error) { + res, _, err := ref.GetPointer().Get(root) + if err != nil { + panic(err) + } + switch sch := res.(type) { + case Schema: + return &sch, nil + case *Schema: + return sch, nil + case map[string]interface{}: + b, _ := json.Marshal(sch) + newSch := new(Schema) + _ = json.Unmarshal(b, newSch) + return newSch, nil + default: + return nil, fmt.Errorf("unknown type for the resolved reference") + } +} + +// ResolveParameter resolves a parameter reference against a context root +func ResolveParameter(root interface{}, ref Ref) (*Parameter, error) { + return ResolveParameterWithBase(root, ref, nil) +} + +// ResolveParameterWithBase resolves a parameter reference against a context root and base path +func ResolveParameterWithBase(root interface{}, ref Ref, opts *ExpandOptions) (*Parameter, error) { + resolver, err := defaultSchemaLoader(root, opts, nil, nil) + if err != nil { + return nil, err + } + + result := new(Parameter) + if err := resolver.Resolve(&ref, result, ""); err != nil { + return nil, err + } + return result, nil +} + +// ResolveResponse resolves response a reference against a context root +func ResolveResponse(root interface{}, ref Ref) (*Response, error) { + return ResolveResponseWithBase(root, ref, nil) +} + +// ResolveResponseWithBase resolves response a reference against a context root and base path +func ResolveResponseWithBase(root interface{}, ref Ref, opts *ExpandOptions) (*Response, error) { + resolver, err := defaultSchemaLoader(root, opts, nil, nil) + if err != nil { + return nil, err + } + + result := new(Response) + if err := resolver.Resolve(&ref, result, ""); err != nil { + return nil, err + } + return result, nil +} + +// ResolveItems resolves parameter items reference against a context root and base path. +// +// NOTE: stricly speaking, this construct is not supported by Swagger 2.0. +// Similarly, $ref are forbidden in response headers. +func ResolveItems(root interface{}, ref Ref, opts *ExpandOptions) (*Items, error) { + resolver, err := defaultSchemaLoader(root, opts, nil, nil) + if err != nil { + return nil, err + } + basePath := "" + if opts.RelativeBase != "" { + basePath = opts.RelativeBase + } + result := new(Items) + if err := resolver.Resolve(&ref, result, basePath); err != nil { + return nil, err + } + return result, nil +} + +// ResolvePathItem resolves response a path item against a context root and base path +func ResolvePathItem(root interface{}, ref Ref, opts *ExpandOptions) (*PathItem, error) { + resolver, err := defaultSchemaLoader(root, opts, nil, nil) + if err != nil { + return nil, err + } + basePath := "" + if opts.RelativeBase != "" { + basePath = opts.RelativeBase + } + result := new(PathItem) + if err := resolver.Resolve(&ref, result, basePath); err != nil { + return nil, err + } + return result, nil +} + +// ExpandSpec expands the references in a swagger spec +func ExpandSpec(spec *Swagger, options *ExpandOptions) error { + resolver, err := defaultSchemaLoader(spec, options, nil, nil) + // Just in case this ever returns an error. + if resolver.shouldStopOnError(err) { + return err + } + + // getting the base path of the spec to adjust all subsequent reference resolutions + specBasePath := "" + if options != nil && options.RelativeBase != "" { + specBasePath, _ = absPath(options.RelativeBase) + } + + if options == nil || !options.SkipSchemas { + for key, definition := range spec.Definitions { + var def *Schema + var err error + if def, err = expandSchema(definition, []string{fmt.Sprintf("#/definitions/%s", key)}, resolver, specBasePath); resolver.shouldStopOnError(err) { + return err + } + if def != nil { + spec.Definitions[key] = *def + } + } + } + + for key := range spec.Parameters { + parameter := spec.Parameters[key] + if err := expandParameterOrResponse(¶meter, resolver, specBasePath); resolver.shouldStopOnError(err) { + return err + } + spec.Parameters[key] = parameter + } + + for key := range spec.Responses { + response := spec.Responses[key] + if err := expandParameterOrResponse(&response, resolver, specBasePath); resolver.shouldStopOnError(err) { + return err + } + spec.Responses[key] = response + } + + if spec.Paths != nil { + for key := range spec.Paths.Paths { + path := spec.Paths.Paths[key] + if err := expandPathItem(&path, resolver, specBasePath); resolver.shouldStopOnError(err) { + return err + } + spec.Paths.Paths[key] = path + } + } + + return nil +} + +// baseForRoot loads in the cache the root document and produces a fake "root" base path entry +// for further $ref resolution +func baseForRoot(root interface{}, cache ResolutionCache) string { + // cache the root document to resolve $ref's + const rootBase = "root" + if root != nil { + base, _ := absPath(rootBase) + normalizedBase := normalizeAbsPath(base) + debugLog("setting root doc in cache at: %s", normalizedBase) + if cache == nil { + cache = resCache + } + cache.Set(normalizedBase, root) + return rootBase + } + return "" +} + +// ExpandSchema expands the refs in the schema object with reference to the root object +// go-openapi/validate uses this function +// notice that it is impossible to reference a json schema in a different file other than root +func ExpandSchema(schema *Schema, root interface{}, cache ResolutionCache) error { + opts := &ExpandOptions{ + // when a root is specified, cache the root as an in-memory document for $ref retrieval + RelativeBase: baseForRoot(root, cache), + SkipSchemas: false, + ContinueOnError: false, + // when no base path is specified, remaining $ref (circular) are rendered with an absolute path + AbsoluteCircularRef: true, + } + return ExpandSchemaWithBasePath(schema, cache, opts) +} + +// ExpandSchemaWithBasePath expands the refs in the schema object, base path configured through expand options +func ExpandSchemaWithBasePath(schema *Schema, cache ResolutionCache, opts *ExpandOptions) error { + if schema == nil { + return nil + } + + var basePath string + if opts.RelativeBase != "" { + basePath, _ = absPath(opts.RelativeBase) + } + + resolver, err := defaultSchemaLoader(nil, opts, cache, nil) + if err != nil { + return err + } + + refs := []string{""} + var s *Schema + if s, err = expandSchema(*schema, refs, resolver, basePath); err != nil { + return err + } + *schema = *s + return nil +} + +func expandItems(target Schema, parentRefs []string, resolver *schemaLoader, basePath string) (*Schema, error) { + if target.Items != nil { + if target.Items.Schema != nil { + t, err := expandSchema(*target.Items.Schema, parentRefs, resolver, basePath) + if err != nil { + return nil, err + } + *target.Items.Schema = *t + } + for i := range target.Items.Schemas { + t, err := expandSchema(target.Items.Schemas[i], parentRefs, resolver, basePath) + if err != nil { + return nil, err + } + target.Items.Schemas[i] = *t + } + } + return &target, nil +} + +func expandSchema(target Schema, parentRefs []string, resolver *schemaLoader, basePath string) (*Schema, error) { + if target.Ref.String() == "" && target.Ref.IsRoot() { + // normalizing is important + newRef := normalizeFileRef(&target.Ref, basePath) + target.Ref = *newRef + return &target, nil + + } + + // change the base path of resolution when an ID is encountered + // otherwise the basePath should inherit the parent's + // important: ID can be relative path + if target.ID != "" { + debugLog("schema has ID: %s", target.ID) + // handling the case when id is a folder + // remember that basePath has to be a file + refPath := target.ID + if strings.HasSuffix(target.ID, "/") { + // path.Clean here would not work correctly if basepath is http + refPath = fmt.Sprintf("%s%s", refPath, "placeholder.json") + } + basePath = normalizePaths(refPath, basePath) + } + + var t *Schema + // if Ref is found, everything else doesn't matter + // Ref also changes the resolution scope of children expandSchema + if target.Ref.String() != "" { + // here the resolution scope is changed because a $ref was encountered + normalizedRef := normalizeFileRef(&target.Ref, basePath) + normalizedBasePath := normalizedRef.RemoteURI() + + if resolver.isCircular(normalizedRef, basePath, parentRefs...) { + // this means there is a cycle in the recursion tree: return the Ref + // - circular refs cannot be expanded. We leave them as ref. + // - denormalization means that a new local file ref is set relative to the original basePath + debugLog("shortcut circular ref: basePath: %s, normalizedPath: %s, normalized ref: %s", + basePath, normalizedBasePath, normalizedRef.String()) + if !resolver.options.AbsoluteCircularRef { + target.Ref = *denormalizeFileRef(normalizedRef, normalizedBasePath, resolver.context.basePath) + } else { + target.Ref = *normalizedRef + } + return &target, nil + } + + debugLog("basePath: %s: calling Resolve with target: %#v", basePath, target) + if err := resolver.Resolve(&target.Ref, &t, basePath); resolver.shouldStopOnError(err) { + return nil, err + } + + if t != nil { + parentRefs = append(parentRefs, normalizedRef.String()) + var err error + transitiveResolver, err := resolver.transitiveResolver(basePath, target.Ref) + if transitiveResolver.shouldStopOnError(err) { + return nil, err + } + + basePath = resolver.updateBasePath(transitiveResolver, normalizedBasePath) + + return expandSchema(*t, parentRefs, transitiveResolver, basePath) + } + } + + t, err := expandItems(target, parentRefs, resolver, basePath) + if resolver.shouldStopOnError(err) { + return &target, err + } + if t != nil { + target = *t + } + + for i := range target.AllOf { + t, err := expandSchema(target.AllOf[i], parentRefs, resolver, basePath) + if resolver.shouldStopOnError(err) { + return &target, err + } + target.AllOf[i] = *t + } + for i := range target.AnyOf { + t, err := expandSchema(target.AnyOf[i], parentRefs, resolver, basePath) + if resolver.shouldStopOnError(err) { + return &target, err + } + target.AnyOf[i] = *t + } + for i := range target.OneOf { + t, err := expandSchema(target.OneOf[i], parentRefs, resolver, basePath) + if resolver.shouldStopOnError(err) { + return &target, err + } + if t != nil { + target.OneOf[i] = *t + } + } + if target.Not != nil { + t, err := expandSchema(*target.Not, parentRefs, resolver, basePath) + if resolver.shouldStopOnError(err) { + return &target, err + } + if t != nil { + *target.Not = *t + } + } + for k := range target.Properties { + t, err := expandSchema(target.Properties[k], parentRefs, resolver, basePath) + if resolver.shouldStopOnError(err) { + return &target, err + } + if t != nil { + target.Properties[k] = *t + } + } + if target.AdditionalProperties != nil && target.AdditionalProperties.Schema != nil { + t, err := expandSchema(*target.AdditionalProperties.Schema, parentRefs, resolver, basePath) + if resolver.shouldStopOnError(err) { + return &target, err + } + if t != nil { + *target.AdditionalProperties.Schema = *t + } + } + for k := range target.PatternProperties { + t, err := expandSchema(target.PatternProperties[k], parentRefs, resolver, basePath) + if resolver.shouldStopOnError(err) { + return &target, err + } + if t != nil { + target.PatternProperties[k] = *t + } + } + for k := range target.Dependencies { + if target.Dependencies[k].Schema != nil { + t, err := expandSchema(*target.Dependencies[k].Schema, parentRefs, resolver, basePath) + if resolver.shouldStopOnError(err) { + return &target, err + } + if t != nil { + *target.Dependencies[k].Schema = *t + } + } + } + if target.AdditionalItems != nil && target.AdditionalItems.Schema != nil { + t, err := expandSchema(*target.AdditionalItems.Schema, parentRefs, resolver, basePath) + if resolver.shouldStopOnError(err) { + return &target, err + } + if t != nil { + *target.AdditionalItems.Schema = *t + } + } + for k := range target.Definitions { + t, err := expandSchema(target.Definitions[k], parentRefs, resolver, basePath) + if resolver.shouldStopOnError(err) { + return &target, err + } + if t != nil { + target.Definitions[k] = *t + } + } + return &target, nil +} + +func expandPathItem(pathItem *PathItem, resolver *schemaLoader, basePath string) error { + if pathItem == nil { + return nil + } + + parentRefs := []string{} + if err := resolver.deref(pathItem, parentRefs, basePath); resolver.shouldStopOnError(err) { + return err + } + if pathItem.Ref.String() != "" { + var err error + resolver, err = resolver.transitiveResolver(basePath, pathItem.Ref) + if resolver.shouldStopOnError(err) { + return err + } + } + pathItem.Ref = Ref{} + + for idx := range pathItem.Parameters { + if err := expandParameterOrResponse(&(pathItem.Parameters[idx]), resolver, basePath); resolver.shouldStopOnError(err) { + return err + } + } + ops := []*Operation{ + pathItem.Get, + pathItem.Head, + pathItem.Options, + pathItem.Put, + pathItem.Post, + pathItem.Patch, + pathItem.Delete, + } + for _, op := range ops { + if err := expandOperation(op, resolver, basePath); resolver.shouldStopOnError(err) { + return err + } + } + return nil +} + +func expandOperation(op *Operation, resolver *schemaLoader, basePath string) error { + if op == nil { + return nil + } + + for i := range op.Parameters { + param := op.Parameters[i] + if err := expandParameterOrResponse(¶m, resolver, basePath); resolver.shouldStopOnError(err) { + return err + } + op.Parameters[i] = param + } + + if op.Responses != nil { + responses := op.Responses + if err := expandParameterOrResponse(responses.Default, resolver, basePath); resolver.shouldStopOnError(err) { + return err + } + for code := range responses.StatusCodeResponses { + response := responses.StatusCodeResponses[code] + if err := expandParameterOrResponse(&response, resolver, basePath); resolver.shouldStopOnError(err) { + return err + } + responses.StatusCodeResponses[code] = response + } + } + return nil +} + +// ExpandResponseWithRoot expands a response based on a root document, not a fetchable document +func ExpandResponseWithRoot(response *Response, root interface{}, cache ResolutionCache) error { + opts := &ExpandOptions{ + RelativeBase: baseForRoot(root, cache), + SkipSchemas: false, + ContinueOnError: false, + // when no base path is specified, remaining $ref (circular) are rendered with an absolute path + AbsoluteCircularRef: true, + } + resolver, err := defaultSchemaLoader(root, opts, nil, nil) + if err != nil { + return err + } + + return expandParameterOrResponse(response, resolver, opts.RelativeBase) +} + +// ExpandResponse expands a response based on a basepath +// This is the exported version of expandResponse +// all refs inside response will be resolved relative to basePath +func ExpandResponse(response *Response, basePath string) error { + var specBasePath string + if basePath != "" { + specBasePath, _ = absPath(basePath) + } + opts := &ExpandOptions{ + RelativeBase: specBasePath, + } + resolver, err := defaultSchemaLoader(nil, opts, nil, nil) + if err != nil { + return err + } + + return expandParameterOrResponse(response, resolver, opts.RelativeBase) +} + +// ExpandParameterWithRoot expands a parameter based on a root document, not a fetchable document +func ExpandParameterWithRoot(parameter *Parameter, root interface{}, cache ResolutionCache) error { + opts := &ExpandOptions{ + RelativeBase: baseForRoot(root, cache), + SkipSchemas: false, + ContinueOnError: false, + // when no base path is specified, remaining $ref (circular) are rendered with an absolute path + AbsoluteCircularRef: true, + } + resolver, err := defaultSchemaLoader(root, opts, nil, nil) + if err != nil { + return err + } + + return expandParameterOrResponse(parameter, resolver, opts.RelativeBase) +} + +// ExpandParameter expands a parameter based on a basepath. +// This is the exported version of expandParameter +// all refs inside parameter will be resolved relative to basePath +func ExpandParameter(parameter *Parameter, basePath string) error { + var specBasePath string + if basePath != "" { + specBasePath, _ = absPath(basePath) + } + opts := &ExpandOptions{ + RelativeBase: specBasePath, + } + resolver, err := defaultSchemaLoader(nil, opts, nil, nil) + if err != nil { + return err + } + + return expandParameterOrResponse(parameter, resolver, opts.RelativeBase) +} + +func getRefAndSchema(input interface{}) (*Ref, *Schema, error) { + var ref *Ref + var sch *Schema + switch refable := input.(type) { + case *Parameter: + if refable == nil { + return nil, nil, nil + } + ref = &refable.Ref + sch = refable.Schema + case *Response: + if refable == nil { + return nil, nil, nil + } + ref = &refable.Ref + sch = refable.Schema + default: + return nil, nil, fmt.Errorf("expand: unsupported type %T. Input should be of type *Parameter or *Response", input) + } + return ref, sch, nil +} + +func expandParameterOrResponse(input interface{}, resolver *schemaLoader, basePath string) error { + ref, _, err := getRefAndSchema(input) + if err != nil { + return err + } + if ref == nil { + return nil + } + parentRefs := []string{} + if err := resolver.deref(input, parentRefs, basePath); resolver.shouldStopOnError(err) { + return err + } + ref, sch, _ := getRefAndSchema(input) + if ref.String() != "" { + transitiveResolver, err := resolver.transitiveResolver(basePath, *ref) + if transitiveResolver.shouldStopOnError(err) { + return err + } + basePath = resolver.updateBasePath(transitiveResolver, basePath) + resolver = transitiveResolver + } + + if sch != nil && sch.Ref.String() != "" { + // schema expanded to a $ref in another root + var ern error + sch.Ref, ern = NewRef(normalizePaths(sch.Ref.String(), ref.RemoteURI())) + if ern != nil { + return ern + } + } + if ref != nil { + *ref = Ref{} + } + + if !resolver.options.SkipSchemas && sch != nil { + s, err := expandSchema(*sch, parentRefs, resolver, basePath) + if resolver.shouldStopOnError(err) { + return err + } + *sch = *s + } + return nil +} diff --git a/vendor/github.com/go-openapi/spec/external_docs.go b/vendor/github.com/go-openapi/spec/external_docs.go new file mode 100644 index 000000000..88add91b2 --- /dev/null +++ b/vendor/github.com/go-openapi/spec/external_docs.go @@ -0,0 +1,24 @@ +// Copyright 2015 go-swagger maintainers +// +// 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 spec + +// ExternalDocumentation allows referencing an external resource for +// extended documentation. +// +// For more information: http://goo.gl/8us55a#externalDocumentationObject +type ExternalDocumentation struct { + Description string `json:"description,omitempty"` + URL string `json:"url,omitempty"` +} diff --git a/vendor/github.com/go-openapi/spec/go.mod b/vendor/github.com/go-openapi/spec/go.mod new file mode 100644 index 000000000..42073be00 --- /dev/null +++ b/vendor/github.com/go-openapi/spec/go.mod @@ -0,0 +1,14 @@ +module github.com/go-openapi/spec + +require ( + github.com/go-openapi/jsonpointer v0.19.2 + github.com/go-openapi/jsonreference v0.19.2 + github.com/go-openapi/swag v0.19.2 + github.com/kr/pty v1.1.5 // indirect + github.com/stretchr/objx v0.2.0 // indirect + github.com/stretchr/testify v1.3.0 + golang.org/x/crypto v0.0.0-20190611184440-5c40567a22f8 // indirect + golang.org/x/sys v0.0.0-20190616124812-15dcb6c0061f // indirect + golang.org/x/tools v0.0.0-20190614205625-5aca471b1d59 // indirect + gopkg.in/yaml.v2 v2.2.2 +) diff --git a/vendor/github.com/go-openapi/spec/go.sum b/vendor/github.com/go-openapi/spec/go.sum new file mode 100644 index 000000000..73e97a2d7 --- /dev/null +++ b/vendor/github.com/go-openapi/spec/go.sum @@ -0,0 +1,66 @@ +github.com/PuerkitoBio/purell v1.1.0 h1:rmGxhojJlM0tuKtfdvliR84CFHljx9ag64t2xmVkjK4= +github.com/PuerkitoBio/purell v1.1.0/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= +github.com/PuerkitoBio/purell v1.1.1 h1:WEQqlqaGbrPkxLJWfBwQmfEAE1Z7ONdDLqrN38tNFfI= +github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= +github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 h1:d+Bc7a5rLufV/sSk/8dngufqelfh6jnri85riMAaF/M= +github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/go-openapi/jsonpointer v0.17.0 h1:nH6xp8XdXHx8dqveo0ZuJBluCO2qGrPbDNZ0dwoRHP0= +github.com/go-openapi/jsonpointer v0.17.0/go.mod h1:cOnomiV+CVVwFLk0A/MExoFMjwdsUdVpsRhURCKh+3M= +github.com/go-openapi/jsonpointer v0.19.0 h1:FTUMcX77w5rQkClIzDtTxvn6Bsa894CcrzNj2MMfeg8= +github.com/go-openapi/jsonpointer v0.19.0/go.mod h1:cOnomiV+CVVwFLk0A/MExoFMjwdsUdVpsRhURCKh+3M= +github.com/go-openapi/jsonpointer v0.19.2 h1:A9+F4Dc/MCNB5jibxf6rRvOvR/iFgQdyNx9eIhnGqq0= +github.com/go-openapi/jsonpointer v0.19.2/go.mod h1:3akKfEdA7DF1sugOqz1dVQHBcuDBPKZGEoHC/NkiQRg= +github.com/go-openapi/jsonreference v0.19.0 h1:BqWKpV1dFd+AuiKlgtddwVIFQsuMpxfBDBHGfM2yNpk= +github.com/go-openapi/jsonreference v0.19.0/go.mod h1:g4xxGn04lDIRh0GJb5QlpE3HfopLOL6uZrK/VgnsK9I= +github.com/go-openapi/jsonreference v0.19.2 h1:o20suLFB4Ri0tuzpWtyHlh7E7HnkqTNLq6aR6WVNS1w= +github.com/go-openapi/jsonreference v0.19.2/go.mod h1:jMjeRr2HHw6nAVajTXJ4eiUwohSTlpa0o73RUL1owJc= +github.com/go-openapi/swag v0.17.0 h1:iqrgMg7Q7SvtbWLlltPrkMs0UBJI6oTSs79JFRUi880= +github.com/go-openapi/swag v0.17.0/go.mod h1:AByQ+nYG6gQg71GINrmuDXCPWdL640yX49/kXLo40Tg= +github.com/go-openapi/swag v0.19.2 h1:jvO6bCMBEilGwMfHhrd61zIID4oIFdwb76V17SM88dE= +github.com/go-openapi/swag v0.19.2/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= +github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= +github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/pty v1.1.5/go.mod h1:9r2w37qlBe7rQ6e1fg1S/9xpWHSnaqNdHD3WcMdbPDA= +github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= +github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/mailru/easyjson v0.0.0-20180823135443-60711f1a8329 h1:2gxZ0XQIU/5z3Z3bUBu+FXuk2pFbkN6tcwi/pjyaDic= +github.com/mailru/easyjson v0.0.0-20180823135443-60711f1a8329/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= +github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63 h1:nTT4s92Dgz2HlrB2NaMgvlfqHH39OgMhA7z3PK7PGD4= +github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE= +github.com/stretchr/testify v1.2.2 h1:bSDNvY7ZPG5RlJ8otE/7V6gMiyenm9RtJ7IUVIAoJ1w= +github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= +github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20190611184440-5c40567a22f8/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/net v0.0.0-20181005035420-146acd28ed58 h1:otZG8yDCO4LVps5+9bxOeNiCvgmOyt96J3roHTYs7oE= +golang.org/x/net v0.0.0-20181005035420-146acd28ed58/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190613194153-d28f0bde5980 h1:dfGZHvZk057jK2MCeWus/TowKpJ8y4AmooUzdBSR9GU= +golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190616124812-15dcb6c0061f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs= +golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190614205625-5aca471b1d59/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY= +gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v2 v2.2.1 h1:mUhvW9EsL+naU5Q3cakzfE91YhliOondGd6ZrsDBHQE= +gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= diff --git a/vendor/github.com/go-openapi/spec/header.go b/vendor/github.com/go-openapi/spec/header.go new file mode 100644 index 000000000..39efe452b --- /dev/null +++ b/vendor/github.com/go-openapi/spec/header.go @@ -0,0 +1,197 @@ +// Copyright 2015 go-swagger maintainers +// +// 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 spec + +import ( + "encoding/json" + "strings" + + "github.com/go-openapi/jsonpointer" + "github.com/go-openapi/swag" +) + +const ( + jsonArray = "array" +) + +// HeaderProps describes a response header +type HeaderProps struct { + Description string `json:"description,omitempty"` +} + +// Header describes a header for a response of the API +// +// For more information: http://goo.gl/8us55a#headerObject +type Header struct { + CommonValidations + SimpleSchema + VendorExtensible + HeaderProps +} + +// ResponseHeader creates a new header instance for use in a response +func ResponseHeader() *Header { + return new(Header) +} + +// WithDescription sets the description on this response, allows for chaining +func (h *Header) WithDescription(description string) *Header { + h.Description = description + return h +} + +// Typed a fluent builder method for the type of parameter +func (h *Header) Typed(tpe, format string) *Header { + h.Type = tpe + h.Format = format + return h +} + +// CollectionOf a fluent builder method for an array item +func (h *Header) CollectionOf(items *Items, format string) *Header { + h.Type = jsonArray + h.Items = items + h.CollectionFormat = format + return h +} + +// WithDefault sets the default value on this item +func (h *Header) WithDefault(defaultValue interface{}) *Header { + h.Default = defaultValue + return h +} + +// WithMaxLength sets a max length value +func (h *Header) WithMaxLength(max int64) *Header { + h.MaxLength = &max + return h +} + +// WithMinLength sets a min length value +func (h *Header) WithMinLength(min int64) *Header { + h.MinLength = &min + return h +} + +// WithPattern sets a pattern value +func (h *Header) WithPattern(pattern string) *Header { + h.Pattern = pattern + return h +} + +// WithMultipleOf sets a multiple of value +func (h *Header) WithMultipleOf(number float64) *Header { + h.MultipleOf = &number + return h +} + +// WithMaximum sets a maximum number value +func (h *Header) WithMaximum(max float64, exclusive bool) *Header { + h.Maximum = &max + h.ExclusiveMaximum = exclusive + return h +} + +// WithMinimum sets a minimum number value +func (h *Header) WithMinimum(min float64, exclusive bool) *Header { + h.Minimum = &min + h.ExclusiveMinimum = exclusive + return h +} + +// WithEnum sets a the enum values (replace) +func (h *Header) WithEnum(values ...interface{}) *Header { + h.Enum = append([]interface{}{}, values...) + return h +} + +// WithMaxItems sets the max items +func (h *Header) WithMaxItems(size int64) *Header { + h.MaxItems = &size + return h +} + +// WithMinItems sets the min items +func (h *Header) WithMinItems(size int64) *Header { + h.MinItems = &size + return h +} + +// UniqueValues dictates that this array can only have unique items +func (h *Header) UniqueValues() *Header { + h.UniqueItems = true + return h +} + +// AllowDuplicates this array can have duplicates +func (h *Header) AllowDuplicates() *Header { + h.UniqueItems = false + return h +} + +// MarshalJSON marshal this to JSON +func (h Header) MarshalJSON() ([]byte, error) { + b1, err := json.Marshal(h.CommonValidations) + if err != nil { + return nil, err + } + b2, err := json.Marshal(h.SimpleSchema) + if err != nil { + return nil, err + } + b3, err := json.Marshal(h.HeaderProps) + if err != nil { + return nil, err + } + return swag.ConcatJSON(b1, b2, b3), nil +} + +// UnmarshalJSON unmarshals this header from JSON +func (h *Header) UnmarshalJSON(data []byte) error { + if err := json.Unmarshal(data, &h.CommonValidations); err != nil { + return err + } + if err := json.Unmarshal(data, &h.SimpleSchema); err != nil { + return err + } + if err := json.Unmarshal(data, &h.VendorExtensible); err != nil { + return err + } + return json.Unmarshal(data, &h.HeaderProps) +} + +// JSONLookup look up a value by the json property name +func (h Header) JSONLookup(token string) (interface{}, error) { + if ex, ok := h.Extensions[token]; ok { + return &ex, nil + } + + r, _, err := jsonpointer.GetForToken(h.CommonValidations, token) + if err != nil && !strings.HasPrefix(err.Error(), "object has no field") { + return nil, err + } + if r != nil { + return r, nil + } + r, _, err = jsonpointer.GetForToken(h.SimpleSchema, token) + if err != nil && !strings.HasPrefix(err.Error(), "object has no field") { + return nil, err + } + if r != nil { + return r, nil + } + r, _, err = jsonpointer.GetForToken(h.HeaderProps, token) + return r, err +} diff --git a/vendor/github.com/go-openapi/spec/info.go b/vendor/github.com/go-openapi/spec/info.go new file mode 100644 index 000000000..c458b49b2 --- /dev/null +++ b/vendor/github.com/go-openapi/spec/info.go @@ -0,0 +1,165 @@ +// Copyright 2015 go-swagger maintainers +// +// 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 spec + +import ( + "encoding/json" + "strings" + + "github.com/go-openapi/jsonpointer" + "github.com/go-openapi/swag" +) + +// Extensions vendor specific extensions +type Extensions map[string]interface{} + +// Add adds a value to these extensions +func (e Extensions) Add(key string, value interface{}) { + realKey := strings.ToLower(key) + e[realKey] = value +} + +// GetString gets a string value from the extensions +func (e Extensions) GetString(key string) (string, bool) { + if v, ok := e[strings.ToLower(key)]; ok { + str, ok := v.(string) + return str, ok + } + return "", false +} + +// GetBool gets a string value from the extensions +func (e Extensions) GetBool(key string) (bool, bool) { + if v, ok := e[strings.ToLower(key)]; ok { + str, ok := v.(bool) + return str, ok + } + return false, false +} + +// GetStringSlice gets a string value from the extensions +func (e Extensions) GetStringSlice(key string) ([]string, bool) { + if v, ok := e[strings.ToLower(key)]; ok { + arr, isSlice := v.([]interface{}) + if !isSlice { + return nil, false + } + var strs []string + for _, iface := range arr { + str, isString := iface.(string) + if !isString { + return nil, false + } + strs = append(strs, str) + } + return strs, ok + } + return nil, false +} + +// VendorExtensible composition block. +type VendorExtensible struct { + Extensions Extensions +} + +// AddExtension adds an extension to this extensible object +func (v *VendorExtensible) AddExtension(key string, value interface{}) { + if value == nil { + return + } + if v.Extensions == nil { + v.Extensions = make(map[string]interface{}) + } + v.Extensions.Add(key, value) +} + +// MarshalJSON marshals the extensions to json +func (v VendorExtensible) MarshalJSON() ([]byte, error) { + toser := make(map[string]interface{}) + for k, v := range v.Extensions { + lk := strings.ToLower(k) + if strings.HasPrefix(lk, "x-") { + toser[k] = v + } + } + return json.Marshal(toser) +} + +// UnmarshalJSON for this extensible object +func (v *VendorExtensible) UnmarshalJSON(data []byte) error { + var d map[string]interface{} + if err := json.Unmarshal(data, &d); err != nil { + return err + } + for k, vv := range d { + lk := strings.ToLower(k) + if strings.HasPrefix(lk, "x-") { + if v.Extensions == nil { + v.Extensions = map[string]interface{}{} + } + v.Extensions[k] = vv + } + } + return nil +} + +// InfoProps the properties for an info definition +type InfoProps struct { + Description string `json:"description,omitempty"` + Title string `json:"title,omitempty"` + TermsOfService string `json:"termsOfService,omitempty"` + Contact *ContactInfo `json:"contact,omitempty"` + License *License `json:"license,omitempty"` + Version string `json:"version,omitempty"` +} + +// Info object provides metadata about the API. +// The metadata can be used by the clients if needed, and can be presented in the Swagger-UI for convenience. +// +// For more information: http://goo.gl/8us55a#infoObject +type Info struct { + VendorExtensible + InfoProps +} + +// JSONLookup look up a value by the json property name +func (i Info) JSONLookup(token string) (interface{}, error) { + if ex, ok := i.Extensions[token]; ok { + return &ex, nil + } + r, _, err := jsonpointer.GetForToken(i.InfoProps, token) + return r, err +} + +// MarshalJSON marshal this to JSON +func (i Info) MarshalJSON() ([]byte, error) { + b1, err := json.Marshal(i.InfoProps) + if err != nil { + return nil, err + } + b2, err := json.Marshal(i.VendorExtensible) + if err != nil { + return nil, err + } + return swag.ConcatJSON(b1, b2), nil +} + +// UnmarshalJSON marshal this from JSON +func (i *Info) UnmarshalJSON(data []byte) error { + if err := json.Unmarshal(data, &i.InfoProps); err != nil { + return err + } + return json.Unmarshal(data, &i.VendorExtensible) +} diff --git a/vendor/github.com/go-openapi/spec/items.go b/vendor/github.com/go-openapi/spec/items.go new file mode 100644 index 000000000..365d16315 --- /dev/null +++ b/vendor/github.com/go-openapi/spec/items.go @@ -0,0 +1,244 @@ +// Copyright 2015 go-swagger maintainers +// +// 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 spec + +import ( + "encoding/json" + "strings" + + "github.com/go-openapi/jsonpointer" + "github.com/go-openapi/swag" +) + +const ( + jsonRef = "$ref" +) + +// SimpleSchema describe swagger simple schemas for parameters and headers +type SimpleSchema struct { + Type string `json:"type,omitempty"` + Nullable bool `json:"nullable,omitempty"` + Format string `json:"format,omitempty"` + Items *Items `json:"items,omitempty"` + CollectionFormat string `json:"collectionFormat,omitempty"` + Default interface{} `json:"default,omitempty"` + Example interface{} `json:"example,omitempty"` +} + +// TypeName return the type (or format) of a simple schema +func (s *SimpleSchema) TypeName() string { + if s.Format != "" { + return s.Format + } + return s.Type +} + +// ItemsTypeName yields the type of items in a simple schema array +func (s *SimpleSchema) ItemsTypeName() string { + if s.Items == nil { + return "" + } + return s.Items.TypeName() +} + +// CommonValidations describe common JSON-schema validations +type CommonValidations struct { + Maximum *float64 `json:"maximum,omitempty"` + ExclusiveMaximum bool `json:"exclusiveMaximum,omitempty"` + Minimum *float64 `json:"minimum,omitempty"` + ExclusiveMinimum bool `json:"exclusiveMinimum,omitempty"` + MaxLength *int64 `json:"maxLength,omitempty"` + MinLength *int64 `json:"minLength,omitempty"` + Pattern string `json:"pattern,omitempty"` + MaxItems *int64 `json:"maxItems,omitempty"` + MinItems *int64 `json:"minItems,omitempty"` + UniqueItems bool `json:"uniqueItems,omitempty"` + MultipleOf *float64 `json:"multipleOf,omitempty"` + Enum []interface{} `json:"enum,omitempty"` +} + +// Items a limited subset of JSON-Schema's items object. +// It is used by parameter definitions that are not located in "body". +// +// For more information: http://goo.gl/8us55a#items-object +type Items struct { + Refable + CommonValidations + SimpleSchema + VendorExtensible +} + +// NewItems creates a new instance of items +func NewItems() *Items { + return &Items{} +} + +// Typed a fluent builder method for the type of item +func (i *Items) Typed(tpe, format string) *Items { + i.Type = tpe + i.Format = format + return i +} + +// AsNullable flags this schema as nullable. +func (i *Items) AsNullable() *Items { + i.Nullable = true + return i +} + +// CollectionOf a fluent builder method for an array item +func (i *Items) CollectionOf(items *Items, format string) *Items { + i.Type = jsonArray + i.Items = items + i.CollectionFormat = format + return i +} + +// WithDefault sets the default value on this item +func (i *Items) WithDefault(defaultValue interface{}) *Items { + i.Default = defaultValue + return i +} + +// WithMaxLength sets a max length value +func (i *Items) WithMaxLength(max int64) *Items { + i.MaxLength = &max + return i +} + +// WithMinLength sets a min length value +func (i *Items) WithMinLength(min int64) *Items { + i.MinLength = &min + return i +} + +// WithPattern sets a pattern value +func (i *Items) WithPattern(pattern string) *Items { + i.Pattern = pattern + return i +} + +// WithMultipleOf sets a multiple of value +func (i *Items) WithMultipleOf(number float64) *Items { + i.MultipleOf = &number + return i +} + +// WithMaximum sets a maximum number value +func (i *Items) WithMaximum(max float64, exclusive bool) *Items { + i.Maximum = &max + i.ExclusiveMaximum = exclusive + return i +} + +// WithMinimum sets a minimum number value +func (i *Items) WithMinimum(min float64, exclusive bool) *Items { + i.Minimum = &min + i.ExclusiveMinimum = exclusive + return i +} + +// WithEnum sets a the enum values (replace) +func (i *Items) WithEnum(values ...interface{}) *Items { + i.Enum = append([]interface{}{}, values...) + return i +} + +// WithMaxItems sets the max items +func (i *Items) WithMaxItems(size int64) *Items { + i.MaxItems = &size + return i +} + +// WithMinItems sets the min items +func (i *Items) WithMinItems(size int64) *Items { + i.MinItems = &size + return i +} + +// UniqueValues dictates that this array can only have unique items +func (i *Items) UniqueValues() *Items { + i.UniqueItems = true + return i +} + +// AllowDuplicates this array can have duplicates +func (i *Items) AllowDuplicates() *Items { + i.UniqueItems = false + return i +} + +// UnmarshalJSON hydrates this items instance with the data from JSON +func (i *Items) UnmarshalJSON(data []byte) error { + var validations CommonValidations + if err := json.Unmarshal(data, &validations); err != nil { + return err + } + var ref Refable + if err := json.Unmarshal(data, &ref); err != nil { + return err + } + var simpleSchema SimpleSchema + if err := json.Unmarshal(data, &simpleSchema); err != nil { + return err + } + var vendorExtensible VendorExtensible + if err := json.Unmarshal(data, &vendorExtensible); err != nil { + return err + } + i.Refable = ref + i.CommonValidations = validations + i.SimpleSchema = simpleSchema + i.VendorExtensible = vendorExtensible + return nil +} + +// MarshalJSON converts this items object to JSON +func (i Items) MarshalJSON() ([]byte, error) { + b1, err := json.Marshal(i.CommonValidations) + if err != nil { + return nil, err + } + b2, err := json.Marshal(i.SimpleSchema) + if err != nil { + return nil, err + } + b3, err := json.Marshal(i.Refable) + if err != nil { + return nil, err + } + b4, err := json.Marshal(i.VendorExtensible) + if err != nil { + return nil, err + } + return swag.ConcatJSON(b4, b3, b1, b2), nil +} + +// JSONLookup look up a value by the json property name +func (i Items) JSONLookup(token string) (interface{}, error) { + if token == jsonRef { + return &i.Ref, nil + } + + r, _, err := jsonpointer.GetForToken(i.CommonValidations, token) + if err != nil && !strings.HasPrefix(err.Error(), "object has no field") { + return nil, err + } + if r != nil { + return r, nil + } + r, _, err = jsonpointer.GetForToken(i.SimpleSchema, token) + return r, err +} diff --git a/vendor/github.com/go-openapi/spec/license.go b/vendor/github.com/go-openapi/spec/license.go new file mode 100644 index 000000000..f20961b4f --- /dev/null +++ b/vendor/github.com/go-openapi/spec/license.go @@ -0,0 +1,23 @@ +// Copyright 2015 go-swagger maintainers +// +// 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 spec + +// License information for the exposed API. +// +// For more information: http://goo.gl/8us55a#licenseObject +type License struct { + Name string `json:"name,omitempty"` + URL string `json:"url,omitempty"` +} diff --git a/vendor/github.com/go-openapi/spec/normalizer.go b/vendor/github.com/go-openapi/spec/normalizer.go new file mode 100644 index 000000000..b8957e7c0 --- /dev/null +++ b/vendor/github.com/go-openapi/spec/normalizer.go @@ -0,0 +1,152 @@ +// Copyright 2015 go-swagger maintainers +// +// 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 spec + +import ( + "fmt" + "net/url" + "os" + "path" + "path/filepath" + "strings" +) + +// normalize absolute path for cache. +// on Windows, drive letters should be converted to lower as scheme in net/url.URL +func normalizeAbsPath(path string) string { + u, err := url.Parse(path) + if err != nil { + debugLog("normalize absolute path failed: %s", err) + return path + } + return u.String() +} + +// base or refPath could be a file path or a URL +// given a base absolute path and a ref path, return the absolute path of refPath +// 1) if refPath is absolute, return it +// 2) if refPath is relative, join it with basePath keeping the scheme, hosts, and ports if exists +// base could be a directory or a full file path +func normalizePaths(refPath, base string) string { + refURL, _ := url.Parse(refPath) + if path.IsAbs(refURL.Path) || filepath.IsAbs(refPath) { + // refPath is actually absolute + if refURL.Host != "" { + return refPath + } + parts := strings.Split(refPath, "#") + result := filepath.FromSlash(parts[0]) + if len(parts) == 2 { + result += "#" + parts[1] + } + return result + } + + // relative refPath + baseURL, _ := url.Parse(base) + if !strings.HasPrefix(refPath, "#") { + // combining paths + if baseURL.Host != "" { + baseURL.Path = path.Join(path.Dir(baseURL.Path), refURL.Path) + } else { // base is a file + newBase := fmt.Sprintf("%s#%s", filepath.Join(filepath.Dir(base), filepath.FromSlash(refURL.Path)), refURL.Fragment) + return newBase + } + + } + // copying fragment from ref to base + baseURL.Fragment = refURL.Fragment + return baseURL.String() +} + +// denormalizePaths returns to simplest notation on file $ref, +// i.e. strips the absolute path and sets a path relative to the base path. +// +// This is currently used when we rewrite ref after a circular ref has been detected +func denormalizeFileRef(ref *Ref, relativeBase, originalRelativeBase string) *Ref { + debugLog("denormalizeFileRef for: %s", ref.String()) + + if ref.String() == "" || ref.IsRoot() || ref.HasFragmentOnly { + return ref + } + // strip relativeBase from URI + relativeBaseURL, _ := url.Parse(relativeBase) + relativeBaseURL.Fragment = "" + + if relativeBaseURL.IsAbs() && strings.HasPrefix(ref.String(), relativeBase) { + // this should work for absolute URI (e.g. http://...): we have an exact match, just trim prefix + r, _ := NewRef(strings.TrimPrefix(ref.String(), relativeBase)) + return &r + } + + if relativeBaseURL.IsAbs() { + // other absolute URL get unchanged (i.e. with a non-empty scheme) + return ref + } + + // for relative file URIs: + originalRelativeBaseURL, _ := url.Parse(originalRelativeBase) + originalRelativeBaseURL.Fragment = "" + if strings.HasPrefix(ref.String(), originalRelativeBaseURL.String()) { + // the resulting ref is in the expanded spec: return a local ref + r, _ := NewRef(strings.TrimPrefix(ref.String(), originalRelativeBaseURL.String())) + return &r + } + + // check if we may set a relative path, considering the original base path for this spec. + // Example: + // spec is located at /mypath/spec.json + // my normalized ref points to: /mypath/item.json#/target + // expected result: item.json#/target + parts := strings.Split(ref.String(), "#") + relativePath, err := filepath.Rel(path.Dir(originalRelativeBaseURL.String()), parts[0]) + if err != nil { + // there is no common ancestor (e.g. different drives on windows) + // leaves the ref unchanged + return ref + } + if len(parts) == 2 { + relativePath += "#" + parts[1] + } + r, _ := NewRef(relativePath) + return &r +} + +// relativeBase could be an ABSOLUTE file path or an ABSOLUTE URL +func normalizeFileRef(ref *Ref, relativeBase string) *Ref { + // This is important for when the reference is pointing to the root schema + if ref.String() == "" { + r, _ := NewRef(relativeBase) + return &r + } + + debugLog("normalizing %s against %s", ref.String(), relativeBase) + + s := normalizePaths(ref.String(), relativeBase) + r, _ := NewRef(s) + return &r +} + +// absPath returns the absolute path of a file +func absPath(fname string) (string, error) { + if strings.HasPrefix(fname, "http") { + return fname, nil + } + if filepath.IsAbs(fname) { + return fname, nil + } + wd, err := os.Getwd() + return filepath.Join(wd, fname), err +} diff --git a/vendor/github.com/go-openapi/spec/operation.go b/vendor/github.com/go-openapi/spec/operation.go new file mode 100644 index 000000000..b1ebd5994 --- /dev/null +++ b/vendor/github.com/go-openapi/spec/operation.go @@ -0,0 +1,398 @@ +// Copyright 2015 go-swagger maintainers +// +// 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 spec + +import ( + "bytes" + "encoding/gob" + "encoding/json" + "sort" + + "github.com/go-openapi/jsonpointer" + "github.com/go-openapi/swag" +) + +func init() { + //gob.Register(map[string][]interface{}{}) + gob.Register(map[string]interface{}{}) + gob.Register([]interface{}{}) +} + +// OperationProps describes an operation +// +// NOTES: +// - schemes, when present must be from [http, https, ws, wss]: see validate +// - Security is handled as a special case: see MarshalJSON function +type OperationProps struct { + Description string `json:"description,omitempty"` + Consumes []string `json:"consumes,omitempty"` + Produces []string `json:"produces,omitempty"` + Schemes []string `json:"schemes,omitempty"` + Tags []string `json:"tags,omitempty"` + Summary string `json:"summary,omitempty"` + ExternalDocs *ExternalDocumentation `json:"externalDocs,omitempty"` + ID string `json:"operationId,omitempty"` + Deprecated bool `json:"deprecated,omitempty"` + Security []map[string][]string `json:"security,omitempty"` + Parameters []Parameter `json:"parameters,omitempty"` + Responses *Responses `json:"responses,omitempty"` +} + +// MarshalJSON takes care of serializing operation properties to JSON +// +// We use a custom marhaller here to handle a special cases related to +// the Security field. We need to preserve zero length slice +// while omitting the field when the value is nil/unset. +func (op OperationProps) MarshalJSON() ([]byte, error) { + type Alias OperationProps + if op.Security == nil { + return json.Marshal(&struct { + Security []map[string][]string `json:"security,omitempty"` + *Alias + }{ + Security: op.Security, + Alias: (*Alias)(&op), + }) + } + return json.Marshal(&struct { + Security []map[string][]string `json:"security"` + *Alias + }{ + Security: op.Security, + Alias: (*Alias)(&op), + }) +} + +// Operation describes a single API operation on a path. +// +// For more information: http://goo.gl/8us55a#operationObject +type Operation struct { + VendorExtensible + OperationProps +} + +// SuccessResponse gets a success response model +func (o *Operation) SuccessResponse() (*Response, int, bool) { + if o.Responses == nil { + return nil, 0, false + } + + responseCodes := make([]int, 0, len(o.Responses.StatusCodeResponses)) + for k := range o.Responses.StatusCodeResponses { + if k >= 200 && k < 300 { + responseCodes = append(responseCodes, k) + } + } + if len(responseCodes) > 0 { + sort.Ints(responseCodes) + v := o.Responses.StatusCodeResponses[responseCodes[0]] + return &v, responseCodes[0], true + } + + return o.Responses.Default, 0, false +} + +// JSONLookup look up a value by the json property name +func (o Operation) JSONLookup(token string) (interface{}, error) { + if ex, ok := o.Extensions[token]; ok { + return &ex, nil + } + r, _, err := jsonpointer.GetForToken(o.OperationProps, token) + return r, err +} + +// UnmarshalJSON hydrates this items instance with the data from JSON +func (o *Operation) UnmarshalJSON(data []byte) error { + if err := json.Unmarshal(data, &o.OperationProps); err != nil { + return err + } + return json.Unmarshal(data, &o.VendorExtensible) +} + +// MarshalJSON converts this items object to JSON +func (o Operation) MarshalJSON() ([]byte, error) { + b1, err := json.Marshal(o.OperationProps) + if err != nil { + return nil, err + } + b2, err := json.Marshal(o.VendorExtensible) + if err != nil { + return nil, err + } + concated := swag.ConcatJSON(b1, b2) + return concated, nil +} + +// NewOperation creates a new operation instance. +// It expects an ID as parameter but not passing an ID is also valid. +func NewOperation(id string) *Operation { + op := new(Operation) + op.ID = id + return op +} + +// WithID sets the ID property on this operation, allows for chaining. +func (o *Operation) WithID(id string) *Operation { + o.ID = id + return o +} + +// WithDescription sets the description on this operation, allows for chaining +func (o *Operation) WithDescription(description string) *Operation { + o.Description = description + return o +} + +// WithSummary sets the summary on this operation, allows for chaining +func (o *Operation) WithSummary(summary string) *Operation { + o.Summary = summary + return o +} + +// WithExternalDocs sets/removes the external docs for/from this operation. +// When you pass empty strings as params the external documents will be removed. +// When you pass non-empty string as one value then those values will be used on the external docs object. +// So when you pass a non-empty description, you should also pass the url and vice versa. +func (o *Operation) WithExternalDocs(description, url string) *Operation { + if description == "" && url == "" { + o.ExternalDocs = nil + return o + } + + if o.ExternalDocs == nil { + o.ExternalDocs = &ExternalDocumentation{} + } + o.ExternalDocs.Description = description + o.ExternalDocs.URL = url + return o +} + +// Deprecate marks the operation as deprecated +func (o *Operation) Deprecate() *Operation { + o.Deprecated = true + return o +} + +// Undeprecate marks the operation as not deprected +func (o *Operation) Undeprecate() *Operation { + o.Deprecated = false + return o +} + +// WithConsumes adds media types for incoming body values +func (o *Operation) WithConsumes(mediaTypes ...string) *Operation { + o.Consumes = append(o.Consumes, mediaTypes...) + return o +} + +// WithProduces adds media types for outgoing body values +func (o *Operation) WithProduces(mediaTypes ...string) *Operation { + o.Produces = append(o.Produces, mediaTypes...) + return o +} + +// WithTags adds tags for this operation +func (o *Operation) WithTags(tags ...string) *Operation { + o.Tags = append(o.Tags, tags...) + return o +} + +// AddParam adds a parameter to this operation, when a parameter for that location +// and with that name already exists it will be replaced +func (o *Operation) AddParam(param *Parameter) *Operation { + if param == nil { + return o + } + + for i, p := range o.Parameters { + if p.Name == param.Name && p.In == param.In { + params := append(o.Parameters[:i], *param) + params = append(params, o.Parameters[i+1:]...) + o.Parameters = params + return o + } + } + + o.Parameters = append(o.Parameters, *param) + return o +} + +// RemoveParam removes a parameter from the operation +func (o *Operation) RemoveParam(name, in string) *Operation { + for i, p := range o.Parameters { + if p.Name == name && p.In == in { + o.Parameters = append(o.Parameters[:i], o.Parameters[i+1:]...) + return o + } + } + return o +} + +// SecuredWith adds a security scope to this operation. +func (o *Operation) SecuredWith(name string, scopes ...string) *Operation { + o.Security = append(o.Security, map[string][]string{name: scopes}) + return o +} + +// WithDefaultResponse adds a default response to the operation. +// Passing a nil value will remove the response +func (o *Operation) WithDefaultResponse(response *Response) *Operation { + return o.RespondsWith(0, response) +} + +// RespondsWith adds a status code response to the operation. +// When the code is 0 the value of the response will be used as default response value. +// When the value of the response is nil it will be removed from the operation +func (o *Operation) RespondsWith(code int, response *Response) *Operation { + if o.Responses == nil { + o.Responses = new(Responses) + } + if code == 0 { + o.Responses.Default = response + return o + } + if response == nil { + delete(o.Responses.StatusCodeResponses, code) + return o + } + if o.Responses.StatusCodeResponses == nil { + o.Responses.StatusCodeResponses = make(map[int]Response) + } + o.Responses.StatusCodeResponses[code] = *response + return o +} + +type opsAlias OperationProps + +type gobAlias struct { + Security []map[string]struct { + List []string + Pad bool + } + Alias *opsAlias + SecurityIsEmpty bool +} + +// GobEncode provides a safe gob encoder for Operation, including empty security requirements +func (o Operation) GobEncode() ([]byte, error) { + raw := struct { + Ext VendorExtensible + Props OperationProps + }{ + Ext: o.VendorExtensible, + Props: o.OperationProps, + } + var b bytes.Buffer + err := gob.NewEncoder(&b).Encode(raw) + return b.Bytes(), err +} + +// GobDecode provides a safe gob decoder for Operation, including empty security requirements +func (o *Operation) GobDecode(b []byte) error { + var raw struct { + Ext VendorExtensible + Props OperationProps + } + + buf := bytes.NewBuffer(b) + err := gob.NewDecoder(buf).Decode(&raw) + if err != nil { + return err + } + o.VendorExtensible = raw.Ext + o.OperationProps = raw.Props + return nil +} + +// GobEncode provides a safe gob encoder for Operation, including empty security requirements +func (op OperationProps) GobEncode() ([]byte, error) { + raw := gobAlias{ + Alias: (*opsAlias)(&op), + } + + var b bytes.Buffer + if op.Security == nil { + // nil security requirement + err := gob.NewEncoder(&b).Encode(raw) + return b.Bytes(), err + } + + if len(op.Security) == 0 { + // empty, but non-nil security requirement + raw.SecurityIsEmpty = true + raw.Alias.Security = nil + err := gob.NewEncoder(&b).Encode(raw) + return b.Bytes(), err + } + + raw.Security = make([]map[string]struct { + List []string + Pad bool + }, 0, len(op.Security)) + for _, req := range op.Security { + v := make(map[string]struct { + List []string + Pad bool + }, len(req)) + for k, val := range req { + v[k] = struct { + List []string + Pad bool + }{ + List: val, + } + } + raw.Security = append(raw.Security, v) + } + + err := gob.NewEncoder(&b).Encode(raw) + return b.Bytes(), err +} + +// GobDecode provides a safe gob decoder for Operation, including empty security requirements +func (op *OperationProps) GobDecode(b []byte) error { + var raw gobAlias + + buf := bytes.NewBuffer(b) + err := gob.NewDecoder(buf).Decode(&raw) + if err != nil { + return err + } + if raw.Alias == nil { + return nil + } + + switch { + case raw.SecurityIsEmpty: + // empty, but non-nil security requirement + raw.Alias.Security = []map[string][]string{} + case len(raw.Alias.Security) == 0: + // nil security requirement + raw.Alias.Security = nil + default: + raw.Alias.Security = make([]map[string][]string, 0, len(raw.Security)) + for _, req := range raw.Security { + v := make(map[string][]string, len(req)) + for k, val := range req { + v[k] = make([]string, 0, len(val.List)) + v[k] = append(v[k], val.List...) + } + raw.Alias.Security = append(raw.Alias.Security, v) + } + } + + *op = *(*OperationProps)(raw.Alias) + return nil +} diff --git a/vendor/github.com/go-openapi/spec/parameter.go b/vendor/github.com/go-openapi/spec/parameter.go new file mode 100644 index 000000000..cecdff545 --- /dev/null +++ b/vendor/github.com/go-openapi/spec/parameter.go @@ -0,0 +1,321 @@ +// Copyright 2015 go-swagger maintainers +// +// 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 spec + +import ( + "encoding/json" + "strings" + + "github.com/go-openapi/jsonpointer" + "github.com/go-openapi/swag" +) + +// QueryParam creates a query parameter +func QueryParam(name string) *Parameter { + return &Parameter{ParamProps: ParamProps{Name: name, In: "query"}} +} + +// HeaderParam creates a header parameter, this is always required by default +func HeaderParam(name string) *Parameter { + return &Parameter{ParamProps: ParamProps{Name: name, In: "header", Required: true}} +} + +// PathParam creates a path parameter, this is always required +func PathParam(name string) *Parameter { + return &Parameter{ParamProps: ParamProps{Name: name, In: "path", Required: true}} +} + +// BodyParam creates a body parameter +func BodyParam(name string, schema *Schema) *Parameter { + return &Parameter{ParamProps: ParamProps{Name: name, In: "body", Schema: schema}, + SimpleSchema: SimpleSchema{Type: "object"}} +} + +// FormDataParam creates a body parameter +func FormDataParam(name string) *Parameter { + return &Parameter{ParamProps: ParamProps{Name: name, In: "formData"}} +} + +// FileParam creates a body parameter +func FileParam(name string) *Parameter { + return &Parameter{ParamProps: ParamProps{Name: name, In: "formData"}, + SimpleSchema: SimpleSchema{Type: "file"}} +} + +// SimpleArrayParam creates a param for a simple array (string, int, date etc) +func SimpleArrayParam(name, tpe, fmt string) *Parameter { + return &Parameter{ParamProps: ParamProps{Name: name}, + SimpleSchema: SimpleSchema{Type: jsonArray, CollectionFormat: "csv", + Items: &Items{SimpleSchema: SimpleSchema{Type: "string", Format: fmt}}}} +} + +// ParamRef creates a parameter that's a json reference +func ParamRef(uri string) *Parameter { + p := new(Parameter) + p.Ref = MustCreateRef(uri) + return p +} + +// ParamProps describes the specific attributes of an operation parameter +// +// NOTE: +// - Schema is defined when "in" == "body": see validate +// - AllowEmptyValue is allowed where "in" == "query" || "formData" +type ParamProps struct { + Description string `json:"description,omitempty"` + Name string `json:"name,omitempty"` + In string `json:"in,omitempty"` + Required bool `json:"required,omitempty"` + Schema *Schema `json:"schema,omitempty"` + AllowEmptyValue bool `json:"allowEmptyValue,omitempty"` +} + +// Parameter a unique parameter is defined by a combination of a [name](#parameterName) and [location](#parameterIn). +// +// There are five possible parameter types. +// * Path - Used together with [Path Templating](#pathTemplating), where the parameter value is actually part +// of the operation's URL. This does not include the host or base path of the API. For example, in `/items/{itemId}`, +// the path parameter is `itemId`. +// * Query - Parameters that are appended to the URL. For example, in `/items?id=###`, the query parameter is `id`. +// * Header - Custom headers that are expected as part of the request. +// * Body - The payload that's appended to the HTTP request. Since there can only be one payload, there can only be +// _one_ body parameter. The name of the body parameter has no effect on the parameter itself and is used for +// documentation purposes only. Since Form parameters are also in the payload, body and form parameters cannot exist +// together for the same operation. +// * Form - Used to describe the payload of an HTTP request when either `application/x-www-form-urlencoded` or +// `multipart/form-data` are used as the content type of the request (in Swagger's definition, +// the [`consumes`](#operationConsumes) property of an operation). This is the only parameter type that can be used +// to send files, thus supporting the `file` type. Since form parameters are sent in the payload, they cannot be +// declared together with a body parameter for the same operation. Form parameters have a different format based on +// the content-type used (for further details, consult http://www.w3.org/TR/html401/interact/forms.html#h-17.13.4). +// * `application/x-www-form-urlencoded` - Similar to the format of Query parameters but as a payload. +// For example, `foo=1&bar=swagger` - both `foo` and `bar` are form parameters. This is normally used for simple +// parameters that are being transferred. +// * `multipart/form-data` - each parameter takes a section in the payload with an internal header. +// For example, for the header `Content-Disposition: form-data; name="submit-name"` the name of the parameter is +// `submit-name`. This type of form parameters is more commonly used for file transfers. +// +// For more information: http://goo.gl/8us55a#parameterObject +type Parameter struct { + Refable + CommonValidations + SimpleSchema + VendorExtensible + ParamProps +} + +// JSONLookup look up a value by the json property name +func (p Parameter) JSONLookup(token string) (interface{}, error) { + if ex, ok := p.Extensions[token]; ok { + return &ex, nil + } + if token == jsonRef { + return &p.Ref, nil + } + + r, _, err := jsonpointer.GetForToken(p.CommonValidations, token) + if err != nil && !strings.HasPrefix(err.Error(), "object has no field") { + return nil, err + } + if r != nil { + return r, nil + } + r, _, err = jsonpointer.GetForToken(p.SimpleSchema, token) + if err != nil && !strings.HasPrefix(err.Error(), "object has no field") { + return nil, err + } + if r != nil { + return r, nil + } + r, _, err = jsonpointer.GetForToken(p.ParamProps, token) + return r, err +} + +// WithDescription a fluent builder method for the description of the parameter +func (p *Parameter) WithDescription(description string) *Parameter { + p.Description = description + return p +} + +// Named a fluent builder method to override the name of the parameter +func (p *Parameter) Named(name string) *Parameter { + p.Name = name + return p +} + +// WithLocation a fluent builder method to override the location of the parameter +func (p *Parameter) WithLocation(in string) *Parameter { + p.In = in + return p +} + +// Typed a fluent builder method for the type of the parameter value +func (p *Parameter) Typed(tpe, format string) *Parameter { + p.Type = tpe + p.Format = format + return p +} + +// CollectionOf a fluent builder method for an array parameter +func (p *Parameter) CollectionOf(items *Items, format string) *Parameter { + p.Type = jsonArray + p.Items = items + p.CollectionFormat = format + return p +} + +// WithDefault sets the default value on this parameter +func (p *Parameter) WithDefault(defaultValue interface{}) *Parameter { + p.AsOptional() // with default implies optional + p.Default = defaultValue + return p +} + +// AllowsEmptyValues flags this parameter as being ok with empty values +func (p *Parameter) AllowsEmptyValues() *Parameter { + p.AllowEmptyValue = true + return p +} + +// NoEmptyValues flags this parameter as not liking empty values +func (p *Parameter) NoEmptyValues() *Parameter { + p.AllowEmptyValue = false + return p +} + +// AsOptional flags this parameter as optional +func (p *Parameter) AsOptional() *Parameter { + p.Required = false + return p +} + +// AsRequired flags this parameter as required +func (p *Parameter) AsRequired() *Parameter { + if p.Default != nil { // with a default required makes no sense + return p + } + p.Required = true + return p +} + +// WithMaxLength sets a max length value +func (p *Parameter) WithMaxLength(max int64) *Parameter { + p.MaxLength = &max + return p +} + +// WithMinLength sets a min length value +func (p *Parameter) WithMinLength(min int64) *Parameter { + p.MinLength = &min + return p +} + +// WithPattern sets a pattern value +func (p *Parameter) WithPattern(pattern string) *Parameter { + p.Pattern = pattern + return p +} + +// WithMultipleOf sets a multiple of value +func (p *Parameter) WithMultipleOf(number float64) *Parameter { + p.MultipleOf = &number + return p +} + +// WithMaximum sets a maximum number value +func (p *Parameter) WithMaximum(max float64, exclusive bool) *Parameter { + p.Maximum = &max + p.ExclusiveMaximum = exclusive + return p +} + +// WithMinimum sets a minimum number value +func (p *Parameter) WithMinimum(min float64, exclusive bool) *Parameter { + p.Minimum = &min + p.ExclusiveMinimum = exclusive + return p +} + +// WithEnum sets a the enum values (replace) +func (p *Parameter) WithEnum(values ...interface{}) *Parameter { + p.Enum = append([]interface{}{}, values...) + return p +} + +// WithMaxItems sets the max items +func (p *Parameter) WithMaxItems(size int64) *Parameter { + p.MaxItems = &size + return p +} + +// WithMinItems sets the min items +func (p *Parameter) WithMinItems(size int64) *Parameter { + p.MinItems = &size + return p +} + +// UniqueValues dictates that this array can only have unique items +func (p *Parameter) UniqueValues() *Parameter { + p.UniqueItems = true + return p +} + +// AllowDuplicates this array can have duplicates +func (p *Parameter) AllowDuplicates() *Parameter { + p.UniqueItems = false + return p +} + +// UnmarshalJSON hydrates this items instance with the data from JSON +func (p *Parameter) UnmarshalJSON(data []byte) error { + if err := json.Unmarshal(data, &p.CommonValidations); err != nil { + return err + } + if err := json.Unmarshal(data, &p.Refable); err != nil { + return err + } + if err := json.Unmarshal(data, &p.SimpleSchema); err != nil { + return err + } + if err := json.Unmarshal(data, &p.VendorExtensible); err != nil { + return err + } + return json.Unmarshal(data, &p.ParamProps) +} + +// MarshalJSON converts this items object to JSON +func (p Parameter) MarshalJSON() ([]byte, error) { + b1, err := json.Marshal(p.CommonValidations) + if err != nil { + return nil, err + } + b2, err := json.Marshal(p.SimpleSchema) + if err != nil { + return nil, err + } + b3, err := json.Marshal(p.Refable) + if err != nil { + return nil, err + } + b4, err := json.Marshal(p.VendorExtensible) + if err != nil { + return nil, err + } + b5, err := json.Marshal(p.ParamProps) + if err != nil { + return nil, err + } + return swag.ConcatJSON(b3, b1, b2, b4, b5), nil +} diff --git a/vendor/github.com/go-openapi/spec/path_item.go b/vendor/github.com/go-openapi/spec/path_item.go new file mode 100644 index 000000000..68fc8e901 --- /dev/null +++ b/vendor/github.com/go-openapi/spec/path_item.go @@ -0,0 +1,87 @@ +// Copyright 2015 go-swagger maintainers +// +// 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 spec + +import ( + "encoding/json" + + "github.com/go-openapi/jsonpointer" + "github.com/go-openapi/swag" +) + +// PathItemProps the path item specific properties +type PathItemProps struct { + Get *Operation `json:"get,omitempty"` + Put *Operation `json:"put,omitempty"` + Post *Operation `json:"post,omitempty"` + Delete *Operation `json:"delete,omitempty"` + Options *Operation `json:"options,omitempty"` + Head *Operation `json:"head,omitempty"` + Patch *Operation `json:"patch,omitempty"` + Parameters []Parameter `json:"parameters,omitempty"` +} + +// PathItem describes the operations available on a single path. +// A Path Item may be empty, due to [ACL constraints](http://goo.gl/8us55a#securityFiltering). +// The path itself is still exposed to the documentation viewer but they will +// not know which operations and parameters are available. +// +// For more information: http://goo.gl/8us55a#pathItemObject +type PathItem struct { + Refable + VendorExtensible + PathItemProps +} + +// JSONLookup look up a value by the json property name +func (p PathItem) JSONLookup(token string) (interface{}, error) { + if ex, ok := p.Extensions[token]; ok { + return &ex, nil + } + if token == jsonRef { + return &p.Ref, nil + } + r, _, err := jsonpointer.GetForToken(p.PathItemProps, token) + return r, err +} + +// UnmarshalJSON hydrates this items instance with the data from JSON +func (p *PathItem) UnmarshalJSON(data []byte) error { + if err := json.Unmarshal(data, &p.Refable); err != nil { + return err + } + if err := json.Unmarshal(data, &p.VendorExtensible); err != nil { + return err + } + return json.Unmarshal(data, &p.PathItemProps) +} + +// MarshalJSON converts this items object to JSON +func (p PathItem) MarshalJSON() ([]byte, error) { + b3, err := json.Marshal(p.Refable) + if err != nil { + return nil, err + } + b4, err := json.Marshal(p.VendorExtensible) + if err != nil { + return nil, err + } + b5, err := json.Marshal(p.PathItemProps) + if err != nil { + return nil, err + } + concated := swag.ConcatJSON(b3, b4, b5) + return concated, nil +} diff --git a/vendor/github.com/go-openapi/spec/paths.go b/vendor/github.com/go-openapi/spec/paths.go new file mode 100644 index 000000000..9dc82a290 --- /dev/null +++ b/vendor/github.com/go-openapi/spec/paths.go @@ -0,0 +1,97 @@ +// Copyright 2015 go-swagger maintainers +// +// 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 spec + +import ( + "encoding/json" + "fmt" + "strings" + + "github.com/go-openapi/swag" +) + +// Paths holds the relative paths to the individual endpoints. +// The path is appended to the [`basePath`](http://goo.gl/8us55a#swaggerBasePath) in order +// to construct the full URL. +// The Paths may be empty, due to [ACL constraints](http://goo.gl/8us55a#securityFiltering). +// +// For more information: http://goo.gl/8us55a#pathsObject +type Paths struct { + VendorExtensible + Paths map[string]PathItem `json:"-"` // custom serializer to flatten this, each entry must start with "/" +} + +// JSONLookup look up a value by the json property name +func (p Paths) JSONLookup(token string) (interface{}, error) { + if pi, ok := p.Paths[token]; ok { + return &pi, nil + } + if ex, ok := p.Extensions[token]; ok { + return &ex, nil + } + return nil, fmt.Errorf("object has no field %q", token) +} + +// UnmarshalJSON hydrates this items instance with the data from JSON +func (p *Paths) UnmarshalJSON(data []byte) error { + var res map[string]json.RawMessage + if err := json.Unmarshal(data, &res); err != nil { + return err + } + for k, v := range res { + if strings.HasPrefix(strings.ToLower(k), "x-") { + if p.Extensions == nil { + p.Extensions = make(map[string]interface{}) + } + var d interface{} + if err := json.Unmarshal(v, &d); err != nil { + return err + } + p.Extensions[k] = d + } + if strings.HasPrefix(k, "/") { + if p.Paths == nil { + p.Paths = make(map[string]PathItem) + } + var pi PathItem + if err := json.Unmarshal(v, &pi); err != nil { + return err + } + p.Paths[k] = pi + } + } + return nil +} + +// MarshalJSON converts this items object to JSON +func (p Paths) MarshalJSON() ([]byte, error) { + b1, err := json.Marshal(p.VendorExtensible) + if err != nil { + return nil, err + } + + pths := make(map[string]PathItem) + for k, v := range p.Paths { + if strings.HasPrefix(k, "/") { + pths[k] = v + } + } + b2, err := json.Marshal(pths) + if err != nil { + return nil, err + } + concated := swag.ConcatJSON(b1, b2) + return concated, nil +} diff --git a/vendor/github.com/go-openapi/spec/ref.go b/vendor/github.com/go-openapi/spec/ref.go new file mode 100644 index 000000000..08ff869b2 --- /dev/null +++ b/vendor/github.com/go-openapi/spec/ref.go @@ -0,0 +1,191 @@ +// Copyright 2015 go-swagger maintainers +// +// 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 spec + +import ( + "bytes" + "encoding/gob" + "encoding/json" + "net/http" + "os" + "path/filepath" + + "github.com/go-openapi/jsonreference" +) + +// Refable is a struct for things that accept a $ref property +type Refable struct { + Ref Ref +} + +// MarshalJSON marshals the ref to json +func (r Refable) MarshalJSON() ([]byte, error) { + return r.Ref.MarshalJSON() +} + +// UnmarshalJSON unmarshalss the ref from json +func (r *Refable) UnmarshalJSON(d []byte) error { + return json.Unmarshal(d, &r.Ref) +} + +// Ref represents a json reference that is potentially resolved +type Ref struct { + jsonreference.Ref +} + +// RemoteURI gets the remote uri part of the ref +func (r *Ref) RemoteURI() string { + if r.String() == "" { + return r.String() + } + + u := *r.GetURL() + u.Fragment = "" + return u.String() +} + +// IsValidURI returns true when the url the ref points to can be found +func (r *Ref) IsValidURI(basepaths ...string) bool { + if r.String() == "" { + return true + } + + v := r.RemoteURI() + if v == "" { + return true + } + + if r.HasFullURL { + rr, err := http.Get(v) + if err != nil { + return false + } + + return rr.StatusCode/100 == 2 + } + + if !(r.HasFileScheme || r.HasFullFilePath || r.HasURLPathOnly) { + return false + } + + // check for local file + pth := v + if r.HasURLPathOnly { + base := "." + if len(basepaths) > 0 { + base = filepath.Dir(filepath.Join(basepaths...)) + } + p, e := filepath.Abs(filepath.ToSlash(filepath.Join(base, pth))) + if e != nil { + return false + } + pth = p + } + + fi, err := os.Stat(filepath.ToSlash(pth)) + if err != nil { + return false + } + + return !fi.IsDir() +} + +// Inherits creates a new reference from a parent and a child +// If the child cannot inherit from the parent, an error is returned +func (r *Ref) Inherits(child Ref) (*Ref, error) { + ref, err := r.Ref.Inherits(child.Ref) + if err != nil { + return nil, err + } + return &Ref{Ref: *ref}, nil +} + +// NewRef creates a new instance of a ref object +// returns an error when the reference uri is an invalid uri +func NewRef(refURI string) (Ref, error) { + ref, err := jsonreference.New(refURI) + if err != nil { + return Ref{}, err + } + return Ref{Ref: ref}, nil +} + +// MustCreateRef creates a ref object but panics when refURI is invalid. +// Use the NewRef method for a version that returns an error. +func MustCreateRef(refURI string) Ref { + return Ref{Ref: jsonreference.MustCreateRef(refURI)} +} + +// MarshalJSON marshals this ref into a JSON object +func (r Ref) MarshalJSON() ([]byte, error) { + str := r.String() + if str == "" { + if r.IsRoot() { + return []byte(`{"$ref":""}`), nil + } + return []byte("{}"), nil + } + v := map[string]interface{}{"$ref": str} + return json.Marshal(v) +} + +// UnmarshalJSON unmarshals this ref from a JSON object +func (r *Ref) UnmarshalJSON(d []byte) error { + var v map[string]interface{} + if err := json.Unmarshal(d, &v); err != nil { + return err + } + return r.fromMap(v) +} + +// GobEncode provides a safe gob encoder for Ref +func (r Ref) GobEncode() ([]byte, error) { + var b bytes.Buffer + raw, err := r.MarshalJSON() + if err != nil { + return nil, err + } + err = gob.NewEncoder(&b).Encode(raw) + return b.Bytes(), err +} + +// GobDecode provides a safe gob decoder for Ref +func (r *Ref) GobDecode(b []byte) error { + var raw []byte + buf := bytes.NewBuffer(b) + err := gob.NewDecoder(buf).Decode(&raw) + if err != nil { + return err + } + return json.Unmarshal(raw, r) +} + +func (r *Ref) fromMap(v map[string]interface{}) error { + if v == nil { + return nil + } + + if vv, ok := v["$ref"]; ok { + if str, ok := vv.(string); ok { + ref, err := jsonreference.New(str) + if err != nil { + return err + } + *r = Ref{Ref: ref} + } + } + + return nil +} diff --git a/vendor/github.com/go-openapi/spec/response.go b/vendor/github.com/go-openapi/spec/response.go new file mode 100644 index 000000000..27729c1d9 --- /dev/null +++ b/vendor/github.com/go-openapi/spec/response.go @@ -0,0 +1,131 @@ +// Copyright 2015 go-swagger maintainers +// +// 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 spec + +import ( + "encoding/json" + + "github.com/go-openapi/jsonpointer" + "github.com/go-openapi/swag" +) + +// ResponseProps properties specific to a response +type ResponseProps struct { + Description string `json:"description,omitempty"` + Schema *Schema `json:"schema,omitempty"` + Headers map[string]Header `json:"headers,omitempty"` + Examples map[string]interface{} `json:"examples,omitempty"` +} + +// Response describes a single response from an API Operation. +// +// For more information: http://goo.gl/8us55a#responseObject +type Response struct { + Refable + ResponseProps + VendorExtensible +} + +// JSONLookup look up a value by the json property name +func (r Response) JSONLookup(token string) (interface{}, error) { + if ex, ok := r.Extensions[token]; ok { + return &ex, nil + } + if token == "$ref" { + return &r.Ref, nil + } + ptr, _, err := jsonpointer.GetForToken(r.ResponseProps, token) + return ptr, err +} + +// UnmarshalJSON hydrates this items instance with the data from JSON +func (r *Response) UnmarshalJSON(data []byte) error { + if err := json.Unmarshal(data, &r.ResponseProps); err != nil { + return err + } + if err := json.Unmarshal(data, &r.Refable); err != nil { + return err + } + return json.Unmarshal(data, &r.VendorExtensible) +} + +// MarshalJSON converts this items object to JSON +func (r Response) MarshalJSON() ([]byte, error) { + b1, err := json.Marshal(r.ResponseProps) + if err != nil { + return nil, err + } + b2, err := json.Marshal(r.Refable) + if err != nil { + return nil, err + } + b3, err := json.Marshal(r.VendorExtensible) + if err != nil { + return nil, err + } + return swag.ConcatJSON(b1, b2, b3), nil +} + +// NewResponse creates a new response instance +func NewResponse() *Response { + return new(Response) +} + +// ResponseRef creates a response as a json reference +func ResponseRef(url string) *Response { + resp := NewResponse() + resp.Ref = MustCreateRef(url) + return resp +} + +// WithDescription sets the description on this response, allows for chaining +func (r *Response) WithDescription(description string) *Response { + r.Description = description + return r +} + +// WithSchema sets the schema on this response, allows for chaining. +// Passing a nil argument removes the schema from this response +func (r *Response) WithSchema(schema *Schema) *Response { + r.Schema = schema + return r +} + +// AddHeader adds a header to this response +func (r *Response) AddHeader(name string, header *Header) *Response { + if header == nil { + return r.RemoveHeader(name) + } + if r.Headers == nil { + r.Headers = make(map[string]Header) + } + r.Headers[name] = *header + return r +} + +// RemoveHeader removes a header from this response +func (r *Response) RemoveHeader(name string) *Response { + delete(r.Headers, name) + return r +} + +// AddExample adds an example to this response +func (r *Response) AddExample(mediaType string, example interface{}) *Response { + if r.Examples == nil { + r.Examples = make(map[string]interface{}) + } + r.Examples[mediaType] = example + return r +} diff --git a/vendor/github.com/go-openapi/spec/responses.go b/vendor/github.com/go-openapi/spec/responses.go new file mode 100644 index 000000000..4efb6f868 --- /dev/null +++ b/vendor/github.com/go-openapi/spec/responses.go @@ -0,0 +1,127 @@ +// Copyright 2015 go-swagger maintainers +// +// 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 spec + +import ( + "encoding/json" + "fmt" + "reflect" + "strconv" + + "github.com/go-openapi/swag" +) + +// Responses is a container for the expected responses of an operation. +// The container maps a HTTP response code to the expected response. +// It is not expected from the documentation to necessarily cover all possible HTTP response codes, +// since they may not be known in advance. However, it is expected from the documentation to cover +// a successful operation response and any known errors. +// +// The `default` can be used a default response object for all HTTP codes that are not covered +// individually by the specification. +// +// The `Responses Object` MUST contain at least one response code, and it SHOULD be the response +// for a successful operation call. +// +// For more information: http://goo.gl/8us55a#responsesObject +type Responses struct { + VendorExtensible + ResponsesProps +} + +// JSONLookup implements an interface to customize json pointer lookup +func (r Responses) JSONLookup(token string) (interface{}, error) { + if token == "default" { + return r.Default, nil + } + if ex, ok := r.Extensions[token]; ok { + return &ex, nil + } + if i, err := strconv.Atoi(token); err == nil { + if scr, ok := r.StatusCodeResponses[i]; ok { + return scr, nil + } + } + return nil, fmt.Errorf("object has no field %q", token) +} + +// UnmarshalJSON hydrates this items instance with the data from JSON +func (r *Responses) UnmarshalJSON(data []byte) error { + if err := json.Unmarshal(data, &r.ResponsesProps); err != nil { + return err + } + if err := json.Unmarshal(data, &r.VendorExtensible); err != nil { + return err + } + if reflect.DeepEqual(ResponsesProps{}, r.ResponsesProps) { + r.ResponsesProps = ResponsesProps{} + } + return nil +} + +// MarshalJSON converts this items object to JSON +func (r Responses) MarshalJSON() ([]byte, error) { + b1, err := json.Marshal(r.ResponsesProps) + if err != nil { + return nil, err + } + b2, err := json.Marshal(r.VendorExtensible) + if err != nil { + return nil, err + } + concated := swag.ConcatJSON(b1, b2) + return concated, nil +} + +// ResponsesProps describes all responses for an operation. +// It tells what is the default response and maps all responses with a +// HTTP status code. +type ResponsesProps struct { + Default *Response + StatusCodeResponses map[int]Response +} + +// MarshalJSON marshals responses as JSON +func (r ResponsesProps) MarshalJSON() ([]byte, error) { + toser := map[string]Response{} + if r.Default != nil { + toser["default"] = *r.Default + } + for k, v := range r.StatusCodeResponses { + toser[strconv.Itoa(k)] = v + } + return json.Marshal(toser) +} + +// UnmarshalJSON unmarshals responses from JSON +func (r *ResponsesProps) UnmarshalJSON(data []byte) error { + var res map[string]Response + if err := json.Unmarshal(data, &res); err != nil { + return nil + } + if v, ok := res["default"]; ok { + r.Default = &v + delete(res, "default") + } + for k, v := range res { + if nk, err := strconv.Atoi(k); err == nil { + if r.StatusCodeResponses == nil { + r.StatusCodeResponses = map[int]Response{} + } + r.StatusCodeResponses[nk] = v + } + } + return nil +} diff --git a/vendor/github.com/go-openapi/spec/schema.go b/vendor/github.com/go-openapi/spec/schema.go new file mode 100644 index 000000000..37858ece9 --- /dev/null +++ b/vendor/github.com/go-openapi/spec/schema.go @@ -0,0 +1,596 @@ +// Copyright 2015 go-swagger maintainers +// +// 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 spec + +import ( + "encoding/json" + "fmt" + "net/url" + "strings" + + "github.com/go-openapi/jsonpointer" + "github.com/go-openapi/swag" +) + +// BooleanProperty creates a boolean property +func BooleanProperty() *Schema { + return &Schema{SchemaProps: SchemaProps{Type: []string{"boolean"}}} +} + +// BoolProperty creates a boolean property +func BoolProperty() *Schema { return BooleanProperty() } + +// StringProperty creates a string property +func StringProperty() *Schema { + return &Schema{SchemaProps: SchemaProps{Type: []string{"string"}}} +} + +// CharProperty creates a string property +func CharProperty() *Schema { + return &Schema{SchemaProps: SchemaProps{Type: []string{"string"}}} +} + +// Float64Property creates a float64/double property +func Float64Property() *Schema { + return &Schema{SchemaProps: SchemaProps{Type: []string{"number"}, Format: "double"}} +} + +// Float32Property creates a float32/float property +func Float32Property() *Schema { + return &Schema{SchemaProps: SchemaProps{Type: []string{"number"}, Format: "float"}} +} + +// Int8Property creates an int8 property +func Int8Property() *Schema { + return &Schema{SchemaProps: SchemaProps{Type: []string{"integer"}, Format: "int8"}} +} + +// Int16Property creates an int16 property +func Int16Property() *Schema { + return &Schema{SchemaProps: SchemaProps{Type: []string{"integer"}, Format: "int16"}} +} + +// Int32Property creates an int32 property +func Int32Property() *Schema { + return &Schema{SchemaProps: SchemaProps{Type: []string{"integer"}, Format: "int32"}} +} + +// Int64Property creates an int64 property +func Int64Property() *Schema { + return &Schema{SchemaProps: SchemaProps{Type: []string{"integer"}, Format: "int64"}} +} + +// StrFmtProperty creates a property for the named string format +func StrFmtProperty(format string) *Schema { + return &Schema{SchemaProps: SchemaProps{Type: []string{"string"}, Format: format}} +} + +// DateProperty creates a date property +func DateProperty() *Schema { + return &Schema{SchemaProps: SchemaProps{Type: []string{"string"}, Format: "date"}} +} + +// DateTimeProperty creates a date time property +func DateTimeProperty() *Schema { + return &Schema{SchemaProps: SchemaProps{Type: []string{"string"}, Format: "date-time"}} +} + +// MapProperty creates a map property +func MapProperty(property *Schema) *Schema { + return &Schema{SchemaProps: SchemaProps{Type: []string{"object"}, + AdditionalProperties: &SchemaOrBool{Allows: true, Schema: property}}} +} + +// RefProperty creates a ref property +func RefProperty(name string) *Schema { + return &Schema{SchemaProps: SchemaProps{Ref: MustCreateRef(name)}} +} + +// RefSchema creates a ref property +func RefSchema(name string) *Schema { + return &Schema{SchemaProps: SchemaProps{Ref: MustCreateRef(name)}} +} + +// ArrayProperty creates an array property +func ArrayProperty(items *Schema) *Schema { + if items == nil { + return &Schema{SchemaProps: SchemaProps{Type: []string{"array"}}} + } + return &Schema{SchemaProps: SchemaProps{Items: &SchemaOrArray{Schema: items}, Type: []string{"array"}}} +} + +// ComposedSchema creates a schema with allOf +func ComposedSchema(schemas ...Schema) *Schema { + s := new(Schema) + s.AllOf = schemas + return s +} + +// SchemaURL represents a schema url +type SchemaURL string + +// MarshalJSON marshal this to JSON +func (r SchemaURL) MarshalJSON() ([]byte, error) { + if r == "" { + return []byte("{}"), nil + } + v := map[string]interface{}{"$schema": string(r)} + return json.Marshal(v) +} + +// UnmarshalJSON unmarshal this from JSON +func (r *SchemaURL) UnmarshalJSON(data []byte) error { + var v map[string]interface{} + if err := json.Unmarshal(data, &v); err != nil { + return err + } + return r.fromMap(v) +} + +func (r *SchemaURL) fromMap(v map[string]interface{}) error { + if v == nil { + return nil + } + if vv, ok := v["$schema"]; ok { + if str, ok := vv.(string); ok { + u, err := url.Parse(str) + if err != nil { + return err + } + + *r = SchemaURL(u.String()) + } + } + return nil +} + +// SchemaProps describes a JSON schema (draft 4) +type SchemaProps struct { + ID string `json:"id,omitempty"` + Ref Ref `json:"-"` + Schema SchemaURL `json:"-"` + Description string `json:"description,omitempty"` + Type StringOrArray `json:"type,omitempty"` + Nullable bool `json:"nullable,omitempty"` + Format string `json:"format,omitempty"` + Title string `json:"title,omitempty"` + Default interface{} `json:"default,omitempty"` + Maximum *float64 `json:"maximum,omitempty"` + ExclusiveMaximum bool `json:"exclusiveMaximum,omitempty"` + Minimum *float64 `json:"minimum,omitempty"` + ExclusiveMinimum bool `json:"exclusiveMinimum,omitempty"` + MaxLength *int64 `json:"maxLength,omitempty"` + MinLength *int64 `json:"minLength,omitempty"` + Pattern string `json:"pattern,omitempty"` + MaxItems *int64 `json:"maxItems,omitempty"` + MinItems *int64 `json:"minItems,omitempty"` + UniqueItems bool `json:"uniqueItems,omitempty"` + MultipleOf *float64 `json:"multipleOf,omitempty"` + Enum []interface{} `json:"enum,omitempty"` + MaxProperties *int64 `json:"maxProperties,omitempty"` + MinProperties *int64 `json:"minProperties,omitempty"` + Required []string `json:"required,omitempty"` + Items *SchemaOrArray `json:"items,omitempty"` + AllOf []Schema `json:"allOf,omitempty"` + OneOf []Schema `json:"oneOf,omitempty"` + AnyOf []Schema `json:"anyOf,omitempty"` + Not *Schema `json:"not,omitempty"` + Properties map[string]Schema `json:"properties,omitempty"` + AdditionalProperties *SchemaOrBool `json:"additionalProperties,omitempty"` + PatternProperties map[string]Schema `json:"patternProperties,omitempty"` + Dependencies Dependencies `json:"dependencies,omitempty"` + AdditionalItems *SchemaOrBool `json:"additionalItems,omitempty"` + Definitions Definitions `json:"definitions,omitempty"` +} + +// SwaggerSchemaProps are additional properties supported by swagger schemas, but not JSON-schema (draft 4) +type SwaggerSchemaProps struct { + Discriminator string `json:"discriminator,omitempty"` + ReadOnly bool `json:"readOnly,omitempty"` + XML *XMLObject `json:"xml,omitempty"` + ExternalDocs *ExternalDocumentation `json:"externalDocs,omitempty"` + Example interface{} `json:"example,omitempty"` +} + +// Schema the schema object allows the definition of input and output data types. +// These types can be objects, but also primitives and arrays. +// This object is based on the [JSON Schema Specification Draft 4](http://json-schema.org/) +// and uses a predefined subset of it. +// On top of this subset, there are extensions provided by this specification to allow for more complete documentation. +// +// For more information: http://goo.gl/8us55a#schemaObject +type Schema struct { + VendorExtensible + SchemaProps + SwaggerSchemaProps + ExtraProps map[string]interface{} `json:"-"` +} + +// JSONLookup implements an interface to customize json pointer lookup +func (s Schema) JSONLookup(token string) (interface{}, error) { + if ex, ok := s.Extensions[token]; ok { + return &ex, nil + } + + if ex, ok := s.ExtraProps[token]; ok { + return &ex, nil + } + + r, _, err := jsonpointer.GetForToken(s.SchemaProps, token) + if r != nil || (err != nil && !strings.HasPrefix(err.Error(), "object has no field")) { + return r, err + } + r, _, err = jsonpointer.GetForToken(s.SwaggerSchemaProps, token) + return r, err +} + +// WithID sets the id for this schema, allows for chaining +func (s *Schema) WithID(id string) *Schema { + s.ID = id + return s +} + +// WithTitle sets the title for this schema, allows for chaining +func (s *Schema) WithTitle(title string) *Schema { + s.Title = title + return s +} + +// WithDescription sets the description for this schema, allows for chaining +func (s *Schema) WithDescription(description string) *Schema { + s.Description = description + return s +} + +// WithProperties sets the properties for this schema +func (s *Schema) WithProperties(schemas map[string]Schema) *Schema { + s.Properties = schemas + return s +} + +// SetProperty sets a property on this schema +func (s *Schema) SetProperty(name string, schema Schema) *Schema { + if s.Properties == nil { + s.Properties = make(map[string]Schema) + } + s.Properties[name] = schema + return s +} + +// WithAllOf sets the all of property +func (s *Schema) WithAllOf(schemas ...Schema) *Schema { + s.AllOf = schemas + return s +} + +// WithMaxProperties sets the max number of properties an object can have +func (s *Schema) WithMaxProperties(max int64) *Schema { + s.MaxProperties = &max + return s +} + +// WithMinProperties sets the min number of properties an object must have +func (s *Schema) WithMinProperties(min int64) *Schema { + s.MinProperties = &min + return s +} + +// Typed sets the type of this schema for a single value item +func (s *Schema) Typed(tpe, format string) *Schema { + s.Type = []string{tpe} + s.Format = format + return s +} + +// AddType adds a type with potential format to the types for this schema +func (s *Schema) AddType(tpe, format string) *Schema { + s.Type = append(s.Type, tpe) + if format != "" { + s.Format = format + } + return s +} + +// AsNullable flags this schema as nullable. +func (s *Schema) AsNullable() *Schema { + s.Nullable = true + return s +} + +// CollectionOf a fluent builder method for an array parameter +func (s *Schema) CollectionOf(items Schema) *Schema { + s.Type = []string{jsonArray} + s.Items = &SchemaOrArray{Schema: &items} + return s +} + +// WithDefault sets the default value on this parameter +func (s *Schema) WithDefault(defaultValue interface{}) *Schema { + s.Default = defaultValue + return s +} + +// WithRequired flags this parameter as required +func (s *Schema) WithRequired(items ...string) *Schema { + s.Required = items + return s +} + +// AddRequired adds field names to the required properties array +func (s *Schema) AddRequired(items ...string) *Schema { + s.Required = append(s.Required, items...) + return s +} + +// WithMaxLength sets a max length value +func (s *Schema) WithMaxLength(max int64) *Schema { + s.MaxLength = &max + return s +} + +// WithMinLength sets a min length value +func (s *Schema) WithMinLength(min int64) *Schema { + s.MinLength = &min + return s +} + +// WithPattern sets a pattern value +func (s *Schema) WithPattern(pattern string) *Schema { + s.Pattern = pattern + return s +} + +// WithMultipleOf sets a multiple of value +func (s *Schema) WithMultipleOf(number float64) *Schema { + s.MultipleOf = &number + return s +} + +// WithMaximum sets a maximum number value +func (s *Schema) WithMaximum(max float64, exclusive bool) *Schema { + s.Maximum = &max + s.ExclusiveMaximum = exclusive + return s +} + +// WithMinimum sets a minimum number value +func (s *Schema) WithMinimum(min float64, exclusive bool) *Schema { + s.Minimum = &min + s.ExclusiveMinimum = exclusive + return s +} + +// WithEnum sets a the enum values (replace) +func (s *Schema) WithEnum(values ...interface{}) *Schema { + s.Enum = append([]interface{}{}, values...) + return s +} + +// WithMaxItems sets the max items +func (s *Schema) WithMaxItems(size int64) *Schema { + s.MaxItems = &size + return s +} + +// WithMinItems sets the min items +func (s *Schema) WithMinItems(size int64) *Schema { + s.MinItems = &size + return s +} + +// UniqueValues dictates that this array can only have unique items +func (s *Schema) UniqueValues() *Schema { + s.UniqueItems = true + return s +} + +// AllowDuplicates this array can have duplicates +func (s *Schema) AllowDuplicates() *Schema { + s.UniqueItems = false + return s +} + +// AddToAllOf adds a schema to the allOf property +func (s *Schema) AddToAllOf(schemas ...Schema) *Schema { + s.AllOf = append(s.AllOf, schemas...) + return s +} + +// WithDiscriminator sets the name of the discriminator field +func (s *Schema) WithDiscriminator(discriminator string) *Schema { + s.Discriminator = discriminator + return s +} + +// AsReadOnly flags this schema as readonly +func (s *Schema) AsReadOnly() *Schema { + s.ReadOnly = true + return s +} + +// AsWritable flags this schema as writeable (not read-only) +func (s *Schema) AsWritable() *Schema { + s.ReadOnly = false + return s +} + +// WithExample sets the example for this schema +func (s *Schema) WithExample(example interface{}) *Schema { + s.Example = example + return s +} + +// WithExternalDocs sets/removes the external docs for/from this schema. +// When you pass empty strings as params the external documents will be removed. +// When you pass non-empty string as one value then those values will be used on the external docs object. +// So when you pass a non-empty description, you should also pass the url and vice versa. +func (s *Schema) WithExternalDocs(description, url string) *Schema { + if description == "" && url == "" { + s.ExternalDocs = nil + return s + } + + if s.ExternalDocs == nil { + s.ExternalDocs = &ExternalDocumentation{} + } + s.ExternalDocs.Description = description + s.ExternalDocs.URL = url + return s +} + +// WithXMLName sets the xml name for the object +func (s *Schema) WithXMLName(name string) *Schema { + if s.XML == nil { + s.XML = new(XMLObject) + } + s.XML.Name = name + return s +} + +// WithXMLNamespace sets the xml namespace for the object +func (s *Schema) WithXMLNamespace(namespace string) *Schema { + if s.XML == nil { + s.XML = new(XMLObject) + } + s.XML.Namespace = namespace + return s +} + +// WithXMLPrefix sets the xml prefix for the object +func (s *Schema) WithXMLPrefix(prefix string) *Schema { + if s.XML == nil { + s.XML = new(XMLObject) + } + s.XML.Prefix = prefix + return s +} + +// AsXMLAttribute flags this object as xml attribute +func (s *Schema) AsXMLAttribute() *Schema { + if s.XML == nil { + s.XML = new(XMLObject) + } + s.XML.Attribute = true + return s +} + +// AsXMLElement flags this object as an xml node +func (s *Schema) AsXMLElement() *Schema { + if s.XML == nil { + s.XML = new(XMLObject) + } + s.XML.Attribute = false + return s +} + +// AsWrappedXML flags this object as wrapped, this is mostly useful for array types +func (s *Schema) AsWrappedXML() *Schema { + if s.XML == nil { + s.XML = new(XMLObject) + } + s.XML.Wrapped = true + return s +} + +// AsUnwrappedXML flags this object as an xml node +func (s *Schema) AsUnwrappedXML() *Schema { + if s.XML == nil { + s.XML = new(XMLObject) + } + s.XML.Wrapped = false + return s +} + +// MarshalJSON marshal this to JSON +func (s Schema) MarshalJSON() ([]byte, error) { + b1, err := json.Marshal(s.SchemaProps) + if err != nil { + return nil, fmt.Errorf("schema props %v", err) + } + b2, err := json.Marshal(s.VendorExtensible) + if err != nil { + return nil, fmt.Errorf("vendor props %v", err) + } + b3, err := s.Ref.MarshalJSON() + if err != nil { + return nil, fmt.Errorf("ref prop %v", err) + } + b4, err := s.Schema.MarshalJSON() + if err != nil { + return nil, fmt.Errorf("schema prop %v", err) + } + b5, err := json.Marshal(s.SwaggerSchemaProps) + if err != nil { + return nil, fmt.Errorf("common validations %v", err) + } + var b6 []byte + if s.ExtraProps != nil { + jj, err := json.Marshal(s.ExtraProps) + if err != nil { + return nil, fmt.Errorf("extra props %v", err) + } + b6 = jj + } + return swag.ConcatJSON(b1, b2, b3, b4, b5, b6), nil +} + +// UnmarshalJSON marshal this from JSON +func (s *Schema) UnmarshalJSON(data []byte) error { + props := struct { + SchemaProps + SwaggerSchemaProps + }{} + if err := json.Unmarshal(data, &props); err != nil { + return err + } + + sch := Schema{ + SchemaProps: props.SchemaProps, + SwaggerSchemaProps: props.SwaggerSchemaProps, + } + + var d map[string]interface{} + if err := json.Unmarshal(data, &d); err != nil { + return err + } + + _ = sch.Ref.fromMap(d) + _ = sch.Schema.fromMap(d) + + delete(d, "$ref") + delete(d, "$schema") + for _, pn := range swag.DefaultJSONNameProvider.GetJSONNames(s) { + delete(d, pn) + } + + for k, vv := range d { + lk := strings.ToLower(k) + if strings.HasPrefix(lk, "x-") { + if sch.Extensions == nil { + sch.Extensions = map[string]interface{}{} + } + sch.Extensions[k] = vv + continue + } + if sch.ExtraProps == nil { + sch.ExtraProps = map[string]interface{}{} + } + sch.ExtraProps[k] = vv + } + + *s = sch + + return nil +} diff --git a/vendor/github.com/go-openapi/spec/schema_loader.go b/vendor/github.com/go-openapi/spec/schema_loader.go new file mode 100644 index 000000000..c34a96fa0 --- /dev/null +++ b/vendor/github.com/go-openapi/spec/schema_loader.go @@ -0,0 +1,275 @@ +// Copyright 2015 go-swagger maintainers +// +// 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 spec + +import ( + "encoding/json" + "fmt" + "log" + "net/url" + "reflect" + "strings" + + "github.com/go-openapi/swag" +) + +// PathLoader function to use when loading remote refs +var PathLoader func(string) (json.RawMessage, error) + +func init() { + PathLoader = func(path string) (json.RawMessage, error) { + data, err := swag.LoadFromFileOrHTTP(path) + if err != nil { + return nil, err + } + return json.RawMessage(data), nil + } +} + +// resolverContext allows to share a context during spec processing. +// At the moment, it just holds the index of circular references found. +type resolverContext struct { + // circulars holds all visited circular references, which allows shortcuts. + // NOTE: this is not just a performance improvement: it is required to figure out + // circular references which participate several cycles. + // This structure is privately instantiated and needs not be locked against + // concurrent access, unless we chose to implement a parallel spec walking. + circulars map[string]bool + basePath string +} + +func newResolverContext(originalBasePath string) *resolverContext { + return &resolverContext{ + circulars: make(map[string]bool), + basePath: originalBasePath, // keep the root base path in context + } +} + +type schemaLoader struct { + root interface{} + options *ExpandOptions + cache ResolutionCache + context *resolverContext + loadDoc func(string) (json.RawMessage, error) +} + +func (r *schemaLoader) transitiveResolver(basePath string, ref Ref) (*schemaLoader, error) { + if ref.IsRoot() || ref.HasFragmentOnly { + return r, nil + } + + baseRef, _ := NewRef(basePath) + currentRef := normalizeFileRef(&ref, basePath) + if strings.HasPrefix(currentRef.String(), baseRef.String()) { + return r, nil + } + + // Set a new root to resolve against + rootURL := currentRef.GetURL() + rootURL.Fragment = "" + root, _ := r.cache.Get(rootURL.String()) + + // shallow copy of resolver options to set a new RelativeBase when + // traversing multiple documents + newOptions := r.options + newOptions.RelativeBase = rootURL.String() + debugLog("setting new root: %s", newOptions.RelativeBase) + resolver, err := defaultSchemaLoader(root, newOptions, r.cache, r.context) + if err != nil { + return nil, err + } + + return resolver, nil +} + +func (r *schemaLoader) updateBasePath(transitive *schemaLoader, basePath string) string { + if transitive != r { + debugLog("got a new resolver") + if transitive.options != nil && transitive.options.RelativeBase != "" { + basePath, _ = absPath(transitive.options.RelativeBase) + debugLog("new basePath = %s", basePath) + } + } + return basePath +} + +func (r *schemaLoader) resolveRef(ref *Ref, target interface{}, basePath string) error { + tgt := reflect.ValueOf(target) + if tgt.Kind() != reflect.Ptr { + return fmt.Errorf("resolve ref: target needs to be a pointer") + } + + refURL := ref.GetURL() + if refURL == nil { + return nil + } + + var res interface{} + var data interface{} + var err error + // Resolve against the root if it isn't nil, and if ref is pointing at the root, or has a fragment only which means + // it is pointing somewhere in the root. + root := r.root + if (ref.IsRoot() || ref.HasFragmentOnly) && root == nil && basePath != "" { + if baseRef, erb := NewRef(basePath); erb == nil { + root, _, _, _ = r.load(baseRef.GetURL()) + } + } + if (ref.IsRoot() || ref.HasFragmentOnly) && root != nil { + data = root + } else { + baseRef := normalizeFileRef(ref, basePath) + debugLog("current ref is: %s", ref.String()) + debugLog("current ref normalized file: %s", baseRef.String()) + data, _, _, err = r.load(baseRef.GetURL()) + if err != nil { + return err + } + } + + res = data + if ref.String() != "" { + res, _, err = ref.GetPointer().Get(data) + if err != nil { + return err + } + } + return swag.DynamicJSONToStruct(res, target) +} + +func (r *schemaLoader) load(refURL *url.URL) (interface{}, url.URL, bool, error) { + debugLog("loading schema from url: %s", refURL) + toFetch := *refURL + toFetch.Fragment = "" + + normalized := normalizeAbsPath(toFetch.String()) + + data, fromCache := r.cache.Get(normalized) + if !fromCache { + b, err := r.loadDoc(normalized) + if err != nil { + return nil, url.URL{}, false, err + } + + if err := json.Unmarshal(b, &data); err != nil { + return nil, url.URL{}, false, err + } + r.cache.Set(normalized, data) + } + + return data, toFetch, fromCache, nil +} + +// isCircular detects cycles in sequences of $ref. +// It relies on a private context (which needs not be locked). +func (r *schemaLoader) isCircular(ref *Ref, basePath string, parentRefs ...string) (foundCycle bool) { + normalizedRef := normalizePaths(ref.String(), basePath) + if _, ok := r.context.circulars[normalizedRef]; ok { + // circular $ref has been already detected in another explored cycle + foundCycle = true + return + } + foundCycle = swag.ContainsStringsCI(parentRefs, normalizedRef) + if foundCycle { + r.context.circulars[normalizedRef] = true + } + return +} + +// Resolve resolves a reference against basePath and stores the result in target +// Resolve is not in charge of following references, it only resolves ref by following its URL +// if the schema that ref is referring to has more refs in it. Resolve doesn't resolve them +// if basePath is an empty string, ref is resolved against the root schema stored in the schemaLoader struct +func (r *schemaLoader) Resolve(ref *Ref, target interface{}, basePath string) error { + return r.resolveRef(ref, target, basePath) +} + +func (r *schemaLoader) deref(input interface{}, parentRefs []string, basePath string) error { + var ref *Ref + switch refable := input.(type) { + case *Schema: + ref = &refable.Ref + case *Parameter: + ref = &refable.Ref + case *Response: + ref = &refable.Ref + case *PathItem: + ref = &refable.Ref + default: + return fmt.Errorf("deref: unsupported type %T", input) + } + + curRef := ref.String() + if curRef != "" { + normalizedRef := normalizeFileRef(ref, basePath) + normalizedBasePath := normalizedRef.RemoteURI() + + if r.isCircular(normalizedRef, basePath, parentRefs...) { + return nil + } + + if err := r.resolveRef(ref, input, basePath); r.shouldStopOnError(err) { + return err + } + + // NOTE(fredbi): removed basePath check => needs more testing + if ref.String() != "" && ref.String() != curRef { + parentRefs = append(parentRefs, normalizedRef.String()) + return r.deref(input, parentRefs, normalizedBasePath) + } + } + + return nil +} + +func (r *schemaLoader) shouldStopOnError(err error) bool { + if err != nil && !r.options.ContinueOnError { + return true + } + + if err != nil { + log.Println(err) + } + + return false +} + +func defaultSchemaLoader( + root interface{}, + expandOptions *ExpandOptions, + cache ResolutionCache, + context *resolverContext) (*schemaLoader, error) { + + if cache == nil { + cache = resCache + } + if expandOptions == nil { + expandOptions = &ExpandOptions{} + } + absBase, _ := absPath(expandOptions.RelativeBase) + if context == nil { + context = newResolverContext(absBase) + } + return &schemaLoader{ + root: root, + options: expandOptions, + cache: cache, + context: context, + loadDoc: func(path string) (json.RawMessage, error) { + debugLog("fetching document at %q", path) + return PathLoader(path) + }, + }, nil +} diff --git a/vendor/github.com/go-openapi/spec/security_scheme.go b/vendor/github.com/go-openapi/spec/security_scheme.go new file mode 100644 index 000000000..fe353842a --- /dev/null +++ b/vendor/github.com/go-openapi/spec/security_scheme.go @@ -0,0 +1,140 @@ +// Copyright 2015 go-swagger maintainers +// +// 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 spec + +import ( + "encoding/json" + + "github.com/go-openapi/jsonpointer" + "github.com/go-openapi/swag" +) + +const ( + basic = "basic" + apiKey = "apiKey" + oauth2 = "oauth2" + implicit = "implicit" + password = "password" + application = "application" + accessCode = "accessCode" +) + +// BasicAuth creates a basic auth security scheme +func BasicAuth() *SecurityScheme { + return &SecurityScheme{SecuritySchemeProps: SecuritySchemeProps{Type: basic}} +} + +// APIKeyAuth creates an api key auth security scheme +func APIKeyAuth(fieldName, valueSource string) *SecurityScheme { + return &SecurityScheme{SecuritySchemeProps: SecuritySchemeProps{Type: apiKey, Name: fieldName, In: valueSource}} +} + +// OAuth2Implicit creates an implicit flow oauth2 security scheme +func OAuth2Implicit(authorizationURL string) *SecurityScheme { + return &SecurityScheme{SecuritySchemeProps: SecuritySchemeProps{ + Type: oauth2, + Flow: implicit, + AuthorizationURL: authorizationURL, + }} +} + +// OAuth2Password creates a password flow oauth2 security scheme +func OAuth2Password(tokenURL string) *SecurityScheme { + return &SecurityScheme{SecuritySchemeProps: SecuritySchemeProps{ + Type: oauth2, + Flow: password, + TokenURL: tokenURL, + }} +} + +// OAuth2Application creates an application flow oauth2 security scheme +func OAuth2Application(tokenURL string) *SecurityScheme { + return &SecurityScheme{SecuritySchemeProps: SecuritySchemeProps{ + Type: oauth2, + Flow: application, + TokenURL: tokenURL, + }} +} + +// OAuth2AccessToken creates an access token flow oauth2 security scheme +func OAuth2AccessToken(authorizationURL, tokenURL string) *SecurityScheme { + return &SecurityScheme{SecuritySchemeProps: SecuritySchemeProps{ + Type: oauth2, + Flow: accessCode, + AuthorizationURL: authorizationURL, + TokenURL: tokenURL, + }} +} + +// SecuritySchemeProps describes a swagger security scheme in the securityDefinitions section +type SecuritySchemeProps struct { + Description string `json:"description,omitempty"` + Type string `json:"type"` + Name string `json:"name,omitempty"` // api key + In string `json:"in,omitempty"` // api key + Flow string `json:"flow,omitempty"` // oauth2 + AuthorizationURL string `json:"authorizationUrl,omitempty"` // oauth2 + TokenURL string `json:"tokenUrl,omitempty"` // oauth2 + Scopes map[string]string `json:"scopes,omitempty"` // oauth2 +} + +// AddScope adds a scope to this security scheme +func (s *SecuritySchemeProps) AddScope(scope, description string) { + if s.Scopes == nil { + s.Scopes = make(map[string]string) + } + s.Scopes[scope] = description +} + +// SecurityScheme allows the definition of a security scheme that can be used by the operations. +// Supported schemes are basic authentication, an API key (either as a header or as a query parameter) +// and OAuth2's common flows (implicit, password, application and access code). +// +// For more information: http://goo.gl/8us55a#securitySchemeObject +type SecurityScheme struct { + VendorExtensible + SecuritySchemeProps +} + +// JSONLookup implements an interface to customize json pointer lookup +func (s SecurityScheme) JSONLookup(token string) (interface{}, error) { + if ex, ok := s.Extensions[token]; ok { + return &ex, nil + } + + r, _, err := jsonpointer.GetForToken(s.SecuritySchemeProps, token) + return r, err +} + +// MarshalJSON marshal this to JSON +func (s SecurityScheme) MarshalJSON() ([]byte, error) { + b1, err := json.Marshal(s.SecuritySchemeProps) + if err != nil { + return nil, err + } + b2, err := json.Marshal(s.VendorExtensible) + if err != nil { + return nil, err + } + return swag.ConcatJSON(b1, b2), nil +} + +// UnmarshalJSON marshal this from JSON +func (s *SecurityScheme) UnmarshalJSON(data []byte) error { + if err := json.Unmarshal(data, &s.SecuritySchemeProps); err != nil { + return err + } + return json.Unmarshal(data, &s.VendorExtensible) +} diff --git a/vendor/github.com/go-openapi/spec/spec.go b/vendor/github.com/go-openapi/spec/spec.go new file mode 100644 index 000000000..0bb045bc0 --- /dev/null +++ b/vendor/github.com/go-openapi/spec/spec.go @@ -0,0 +1,86 @@ +// Copyright 2015 go-swagger maintainers +// +// 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 spec + +import "encoding/json" + +//go:generate curl -L --progress -o ./schemas/v2/schema.json http://swagger.io/v2/schema.json +//go:generate curl -L --progress -o ./schemas/jsonschema-draft-04.json http://json-schema.org/draft-04/schema +//go:generate go-bindata -pkg=spec -prefix=./schemas -ignore=.*\.md ./schemas/... +//go:generate perl -pi -e s,Json,JSON,g bindata.go + +const ( + // SwaggerSchemaURL the url for the swagger 2.0 schema to validate specs + SwaggerSchemaURL = "http://swagger.io/v2/schema.json#" + // JSONSchemaURL the url for the json schema schema + JSONSchemaURL = "http://json-schema.org/draft-04/schema#" +) + +var ( + jsonSchema *Schema + swaggerSchema *Schema +) + +func init() { + jsonSchema = MustLoadJSONSchemaDraft04() + swaggerSchema = MustLoadSwagger20Schema() +} + +// MustLoadJSONSchemaDraft04 panics when Swagger20Schema returns an error +func MustLoadJSONSchemaDraft04() *Schema { + d, e := JSONSchemaDraft04() + if e != nil { + panic(e) + } + return d +} + +// JSONSchemaDraft04 loads the json schema document for json shema draft04 +func JSONSchemaDraft04() (*Schema, error) { + b, err := Asset("jsonschema-draft-04.json") + if err != nil { + return nil, err + } + + schema := new(Schema) + if err := json.Unmarshal(b, schema); err != nil { + return nil, err + } + return schema, nil +} + +// MustLoadSwagger20Schema panics when Swagger20Schema returns an error +func MustLoadSwagger20Schema() *Schema { + d, e := Swagger20Schema() + if e != nil { + panic(e) + } + return d +} + +// Swagger20Schema loads the swagger 2.0 schema from the embedded assets +func Swagger20Schema() (*Schema, error) { + + b, err := Asset("v2/schema.json") + if err != nil { + return nil, err + } + + schema := new(Schema) + if err := json.Unmarshal(b, schema); err != nil { + return nil, err + } + return schema, nil +} diff --git a/vendor/github.com/go-openapi/spec/swagger.go b/vendor/github.com/go-openapi/spec/swagger.go new file mode 100644 index 000000000..44722ffd5 --- /dev/null +++ b/vendor/github.com/go-openapi/spec/swagger.go @@ -0,0 +1,448 @@ +// Copyright 2015 go-swagger maintainers +// +// 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 spec + +import ( + "bytes" + "encoding/gob" + "encoding/json" + "fmt" + "strconv" + + "github.com/go-openapi/jsonpointer" + "github.com/go-openapi/swag" +) + +// Swagger this is the root document object for the API specification. +// It combines what previously was the Resource Listing and API Declaration (version 1.2 and earlier) +// together into one document. +// +// For more information: http://goo.gl/8us55a#swagger-object- +type Swagger struct { + VendorExtensible + SwaggerProps +} + +// JSONLookup look up a value by the json property name +func (s Swagger) JSONLookup(token string) (interface{}, error) { + if ex, ok := s.Extensions[token]; ok { + return &ex, nil + } + r, _, err := jsonpointer.GetForToken(s.SwaggerProps, token) + return r, err +} + +// MarshalJSON marshals this swagger structure to json +func (s Swagger) MarshalJSON() ([]byte, error) { + b1, err := json.Marshal(s.SwaggerProps) + if err != nil { + return nil, err + } + b2, err := json.Marshal(s.VendorExtensible) + if err != nil { + return nil, err + } + return swag.ConcatJSON(b1, b2), nil +} + +// UnmarshalJSON unmarshals a swagger spec from json +func (s *Swagger) UnmarshalJSON(data []byte) error { + var sw Swagger + if err := json.Unmarshal(data, &sw.SwaggerProps); err != nil { + return err + } + if err := json.Unmarshal(data, &sw.VendorExtensible); err != nil { + return err + } + *s = sw + return nil +} + +// GobEncode provides a safe gob encoder for Swagger, including extensions +func (s Swagger) GobEncode() ([]byte, error) { + var b bytes.Buffer + raw := struct { + Props SwaggerProps + Ext VendorExtensible + }{ + Props: s.SwaggerProps, + Ext: s.VendorExtensible, + } + err := gob.NewEncoder(&b).Encode(raw) + return b.Bytes(), err +} + +// GobDecode provides a safe gob decoder for Swagger, including extensions +func (s *Swagger) GobDecode(b []byte) error { + var raw struct { + Props SwaggerProps + Ext VendorExtensible + } + buf := bytes.NewBuffer(b) + err := gob.NewDecoder(buf).Decode(&raw) + if err != nil { + return err + } + s.SwaggerProps = raw.Props + s.VendorExtensible = raw.Ext + return nil +} + +// SwaggerProps captures the top-level properties of an Api specification +// +// NOTE: validation rules +// - the scheme, when present must be from [http, https, ws, wss] +// - BasePath must start with a leading "/" +// - Paths is required +type SwaggerProps struct { + ID string `json:"id,omitempty"` + Consumes []string `json:"consumes,omitempty"` + Produces []string `json:"produces,omitempty"` + Schemes []string `json:"schemes,omitempty"` + Swagger string `json:"swagger,omitempty"` + Info *Info `json:"info,omitempty"` + Host string `json:"host,omitempty"` + BasePath string `json:"basePath,omitempty"` + Paths *Paths `json:"paths"` + Definitions Definitions `json:"definitions,omitempty"` + Parameters map[string]Parameter `json:"parameters,omitempty"` + Responses map[string]Response `json:"responses,omitempty"` + SecurityDefinitions SecurityDefinitions `json:"securityDefinitions,omitempty"` + Security []map[string][]string `json:"security,omitempty"` + Tags []Tag `json:"tags,omitempty"` + ExternalDocs *ExternalDocumentation `json:"externalDocs,omitempty"` +} + +type swaggerPropsAlias SwaggerProps + +type gobSwaggerPropsAlias struct { + Security []map[string]struct { + List []string + Pad bool + } + Alias *swaggerPropsAlias + SecurityIsEmpty bool +} + +// GobEncode provides a safe gob encoder for SwaggerProps, including empty security requirements +func (o SwaggerProps) GobEncode() ([]byte, error) { + raw := gobSwaggerPropsAlias{ + Alias: (*swaggerPropsAlias)(&o), + } + + var b bytes.Buffer + if o.Security == nil { + // nil security requirement + err := gob.NewEncoder(&b).Encode(raw) + return b.Bytes(), err + } + + if len(o.Security) == 0 { + // empty, but non-nil security requirement + raw.SecurityIsEmpty = true + raw.Alias.Security = nil + err := gob.NewEncoder(&b).Encode(raw) + return b.Bytes(), err + } + + raw.Security = make([]map[string]struct { + List []string + Pad bool + }, 0, len(o.Security)) + for _, req := range o.Security { + v := make(map[string]struct { + List []string + Pad bool + }, len(req)) + for k, val := range req { + v[k] = struct { + List []string + Pad bool + }{ + List: val, + } + } + raw.Security = append(raw.Security, v) + } + + err := gob.NewEncoder(&b).Encode(raw) + return b.Bytes(), err +} + +// GobDecode provides a safe gob decoder for SwaggerProps, including empty security requirements +func (o *SwaggerProps) GobDecode(b []byte) error { + var raw gobSwaggerPropsAlias + + buf := bytes.NewBuffer(b) + err := gob.NewDecoder(buf).Decode(&raw) + if err != nil { + return err + } + if raw.Alias == nil { + return nil + } + + switch { + case raw.SecurityIsEmpty: + // empty, but non-nil security requirement + raw.Alias.Security = []map[string][]string{} + case len(raw.Alias.Security) == 0: + // nil security requirement + raw.Alias.Security = nil + default: + raw.Alias.Security = make([]map[string][]string, 0, len(raw.Security)) + for _, req := range raw.Security { + v := make(map[string][]string, len(req)) + for k, val := range req { + v[k] = make([]string, 0, len(val.List)) + v[k] = append(v[k], val.List...) + } + raw.Alias.Security = append(raw.Alias.Security, v) + } + } + + *o = *(*SwaggerProps)(raw.Alias) + return nil +} + +// Dependencies represent a dependencies property +type Dependencies map[string]SchemaOrStringArray + +// SchemaOrBool represents a schema or boolean value, is biased towards true for the boolean property +type SchemaOrBool struct { + Allows bool + Schema *Schema +} + +// JSONLookup implements an interface to customize json pointer lookup +func (s SchemaOrBool) JSONLookup(token string) (interface{}, error) { + if token == "allows" { + return s.Allows, nil + } + r, _, err := jsonpointer.GetForToken(s.Schema, token) + return r, err +} + +var jsTrue = []byte("true") +var jsFalse = []byte("false") + +// MarshalJSON convert this object to JSON +func (s SchemaOrBool) MarshalJSON() ([]byte, error) { + if s.Schema != nil { + return json.Marshal(s.Schema) + } + + if s.Schema == nil && !s.Allows { + return jsFalse, nil + } + return jsTrue, nil +} + +// UnmarshalJSON converts this bool or schema object from a JSON structure +func (s *SchemaOrBool) UnmarshalJSON(data []byte) error { + var nw SchemaOrBool + if len(data) >= 4 { + if data[0] == '{' { + var sch Schema + if err := json.Unmarshal(data, &sch); err != nil { + return err + } + nw.Schema = &sch + } + nw.Allows = !(data[0] == 'f' && data[1] == 'a' && data[2] == 'l' && data[3] == 's' && data[4] == 'e') + } + *s = nw + return nil +} + +// SchemaOrStringArray represents a schema or a string array +type SchemaOrStringArray struct { + Schema *Schema + Property []string +} + +// JSONLookup implements an interface to customize json pointer lookup +func (s SchemaOrStringArray) JSONLookup(token string) (interface{}, error) { + r, _, err := jsonpointer.GetForToken(s.Schema, token) + return r, err +} + +// MarshalJSON converts this schema object or array into JSON structure +func (s SchemaOrStringArray) MarshalJSON() ([]byte, error) { + if len(s.Property) > 0 { + return json.Marshal(s.Property) + } + if s.Schema != nil { + return json.Marshal(s.Schema) + } + return []byte("null"), nil +} + +// UnmarshalJSON converts this schema object or array from a JSON structure +func (s *SchemaOrStringArray) UnmarshalJSON(data []byte) error { + var first byte + if len(data) > 1 { + first = data[0] + } + var nw SchemaOrStringArray + if first == '{' { + var sch Schema + if err := json.Unmarshal(data, &sch); err != nil { + return err + } + nw.Schema = &sch + } + if first == '[' { + if err := json.Unmarshal(data, &nw.Property); err != nil { + return err + } + } + *s = nw + return nil +} + +// Definitions contains the models explicitly defined in this spec +// An object to hold data types that can be consumed and produced by operations. +// These data types can be primitives, arrays or models. +// +// For more information: http://goo.gl/8us55a#definitionsObject +type Definitions map[string]Schema + +// SecurityDefinitions a declaration of the security schemes available to be used in the specification. +// This does not enforce the security schemes on the operations and only serves to provide +// the relevant details for each scheme. +// +// For more information: http://goo.gl/8us55a#securityDefinitionsObject +type SecurityDefinitions map[string]*SecurityScheme + +// StringOrArray represents a value that can either be a string +// or an array of strings. Mainly here for serialization purposes +type StringOrArray []string + +// Contains returns true when the value is contained in the slice +func (s StringOrArray) Contains(value string) bool { + for _, str := range s { + if str == value { + return true + } + } + return false +} + +// JSONLookup implements an interface to customize json pointer lookup +func (s SchemaOrArray) JSONLookup(token string) (interface{}, error) { + if _, err := strconv.Atoi(token); err == nil { + r, _, err := jsonpointer.GetForToken(s.Schemas, token) + return r, err + } + r, _, err := jsonpointer.GetForToken(s.Schema, token) + return r, err +} + +// UnmarshalJSON unmarshals this string or array object from a JSON array or JSON string +func (s *StringOrArray) UnmarshalJSON(data []byte) error { + var first byte + if len(data) > 1 { + first = data[0] + } + + if first == '[' { + var parsed []string + if err := json.Unmarshal(data, &parsed); err != nil { + return err + } + *s = StringOrArray(parsed) + return nil + } + + var single interface{} + if err := json.Unmarshal(data, &single); err != nil { + return err + } + if single == nil { + return nil + } + switch v := single.(type) { + case string: + *s = StringOrArray([]string{v}) + return nil + default: + return fmt.Errorf("only string or array is allowed, not %T", single) + } +} + +// MarshalJSON converts this string or array to a JSON array or JSON string +func (s StringOrArray) MarshalJSON() ([]byte, error) { + if len(s) == 1 { + return json.Marshal([]string(s)[0]) + } + return json.Marshal([]string(s)) +} + +// SchemaOrArray represents a value that can either be a Schema +// or an array of Schema. Mainly here for serialization purposes +type SchemaOrArray struct { + Schema *Schema + Schemas []Schema +} + +// Len returns the number of schemas in this property +func (s SchemaOrArray) Len() int { + if s.Schema != nil { + return 1 + } + return len(s.Schemas) +} + +// ContainsType returns true when one of the schemas is of the specified type +func (s *SchemaOrArray) ContainsType(name string) bool { + if s.Schema != nil { + return s.Schema.Type != nil && s.Schema.Type.Contains(name) + } + return false +} + +// MarshalJSON converts this schema object or array into JSON structure +func (s SchemaOrArray) MarshalJSON() ([]byte, error) { + if len(s.Schemas) > 0 { + return json.Marshal(s.Schemas) + } + return json.Marshal(s.Schema) +} + +// UnmarshalJSON converts this schema object or array from a JSON structure +func (s *SchemaOrArray) UnmarshalJSON(data []byte) error { + var nw SchemaOrArray + var first byte + if len(data) > 1 { + first = data[0] + } + if first == '{' { + var sch Schema + if err := json.Unmarshal(data, &sch); err != nil { + return err + } + nw.Schema = &sch + } + if first == '[' { + if err := json.Unmarshal(data, &nw.Schemas); err != nil { + return err + } + } + *s = nw + return nil +} + +// vim:set ft=go noet sts=2 sw=2 ts=2: diff --git a/vendor/github.com/go-openapi/spec/tag.go b/vendor/github.com/go-openapi/spec/tag.go new file mode 100644 index 000000000..faa3d3de1 --- /dev/null +++ b/vendor/github.com/go-openapi/spec/tag.go @@ -0,0 +1,75 @@ +// Copyright 2015 go-swagger maintainers +// +// 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 spec + +import ( + "encoding/json" + + "github.com/go-openapi/jsonpointer" + "github.com/go-openapi/swag" +) + +// TagProps describe a tag entry in the top level tags section of a swagger spec +type TagProps struct { + Description string `json:"description,omitempty"` + Name string `json:"name,omitempty"` + ExternalDocs *ExternalDocumentation `json:"externalDocs,omitempty"` +} + +// NewTag creates a new tag +func NewTag(name, description string, externalDocs *ExternalDocumentation) Tag { + return Tag{TagProps: TagProps{Description: description, Name: name, ExternalDocs: externalDocs}} +} + +// Tag allows adding meta data to a single tag that is used by the +// [Operation Object](http://goo.gl/8us55a#operationObject). +// It is not mandatory to have a Tag Object per tag used there. +// +// For more information: http://goo.gl/8us55a#tagObject +type Tag struct { + VendorExtensible + TagProps +} + +// JSONLookup implements an interface to customize json pointer lookup +func (t Tag) JSONLookup(token string) (interface{}, error) { + if ex, ok := t.Extensions[token]; ok { + return &ex, nil + } + + r, _, err := jsonpointer.GetForToken(t.TagProps, token) + return r, err +} + +// MarshalJSON marshal this to JSON +func (t Tag) MarshalJSON() ([]byte, error) { + b1, err := json.Marshal(t.TagProps) + if err != nil { + return nil, err + } + b2, err := json.Marshal(t.VendorExtensible) + if err != nil { + return nil, err + } + return swag.ConcatJSON(b1, b2), nil +} + +// UnmarshalJSON marshal this from JSON +func (t *Tag) UnmarshalJSON(data []byte) error { + if err := json.Unmarshal(data, &t.TagProps); err != nil { + return err + } + return json.Unmarshal(data, &t.VendorExtensible) +} diff --git a/vendor/github.com/go-openapi/spec/unused.go b/vendor/github.com/go-openapi/spec/unused.go new file mode 100644 index 000000000..aa12b56f6 --- /dev/null +++ b/vendor/github.com/go-openapi/spec/unused.go @@ -0,0 +1,174 @@ +// Copyright 2015 go-swagger maintainers +// +// 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 spec + +/* + +import ( + "net/url" + "os" + "path" + "path/filepath" + + "github.com/go-openapi/jsonpointer" +) + + // Some currently unused functions and definitions that + // used to be part of the expander. + + // Moved here for the record and possible future reuse + +var ( + idPtr, _ = jsonpointer.New("/id") + refPtr, _ = jsonpointer.New("/$ref") +) + +func idFromNode(node interface{}) (*Ref, error) { + if idValue, _, err := idPtr.Get(node); err == nil { + if refStr, ok := idValue.(string); ok && refStr != "" { + idRef, err := NewRef(refStr) + if err != nil { + return nil, err + } + return &idRef, nil + } + } + return nil, nil +} + +func nextRef(startingNode interface{}, startingRef *Ref, ptr *jsonpointer.Pointer) *Ref { + if startingRef == nil { + return nil + } + + if ptr == nil { + return startingRef + } + + ret := startingRef + var idRef *Ref + node := startingNode + + for _, tok := range ptr.DecodedTokens() { + node, _, _ = jsonpointer.GetForToken(node, tok) + if node == nil { + break + } + + idRef, _ = idFromNode(node) + if idRef != nil { + nw, err := ret.Inherits(*idRef) + if err != nil { + break + } + ret = nw + } + + refRef, _, _ := refPtr.Get(node) + if refRef != nil { + var rf Ref + switch value := refRef.(type) { + case string: + rf, _ = NewRef(value) + } + nw, err := ret.Inherits(rf) + if err != nil { + break + } + nwURL := nw.GetURL() + if nwURL.Scheme == "file" || (nwURL.Scheme == "" && nwURL.Host == "") { + nwpt := filepath.ToSlash(nwURL.Path) + if filepath.IsAbs(nwpt) { + _, err := os.Stat(nwpt) + if err != nil { + nwURL.Path = filepath.Join(".", nwpt) + } + } + } + + ret = nw + } + + } + + return ret +} + +// basePathFromSchemaID returns a new basePath based on an existing basePath and a schema ID +func basePathFromSchemaID(oldBasePath, id string) string { + u, err := url.Parse(oldBasePath) + if err != nil { + panic(err) + } + uid, err := url.Parse(id) + if err != nil { + panic(err) + } + + if path.IsAbs(uid.Path) { + return id + } + u.Path = path.Join(path.Dir(u.Path), uid.Path) + return u.String() +} +*/ + +// type ExtraSchemaProps map[string]interface{} + +// // JSONSchema represents a structure that is a json schema draft 04 +// type JSONSchema struct { +// SchemaProps +// ExtraSchemaProps +// } + +// // MarshalJSON marshal this to JSON +// func (s JSONSchema) MarshalJSON() ([]byte, error) { +// b1, err := json.Marshal(s.SchemaProps) +// if err != nil { +// return nil, err +// } +// b2, err := s.Ref.MarshalJSON() +// if err != nil { +// return nil, err +// } +// b3, err := s.Schema.MarshalJSON() +// if err != nil { +// return nil, err +// } +// b4, err := json.Marshal(s.ExtraSchemaProps) +// if err != nil { +// return nil, err +// } +// return swag.ConcatJSON(b1, b2, b3, b4), nil +// } + +// // UnmarshalJSON marshal this from JSON +// func (s *JSONSchema) UnmarshalJSON(data []byte) error { +// var sch JSONSchema +// if err := json.Unmarshal(data, &sch.SchemaProps); err != nil { +// return err +// } +// if err := json.Unmarshal(data, &sch.Ref); err != nil { +// return err +// } +// if err := json.Unmarshal(data, &sch.Schema); err != nil { +// return err +// } +// if err := json.Unmarshal(data, &sch.ExtraSchemaProps); err != nil { +// return err +// } +// *s = sch +// return nil +// } diff --git a/vendor/github.com/go-openapi/spec/xml_object.go b/vendor/github.com/go-openapi/spec/xml_object.go new file mode 100644 index 000000000..945a46703 --- /dev/null +++ b/vendor/github.com/go-openapi/spec/xml_object.go @@ -0,0 +1,68 @@ +// Copyright 2015 go-swagger maintainers +// +// 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 spec + +// XMLObject a metadata object that allows for more fine-tuned XML model definitions. +// +// For more information: http://goo.gl/8us55a#xmlObject +type XMLObject struct { + Name string `json:"name,omitempty"` + Namespace string `json:"namespace,omitempty"` + Prefix string `json:"prefix,omitempty"` + Attribute bool `json:"attribute,omitempty"` + Wrapped bool `json:"wrapped,omitempty"` +} + +// WithName sets the xml name for the object +func (x *XMLObject) WithName(name string) *XMLObject { + x.Name = name + return x +} + +// WithNamespace sets the xml namespace for the object +func (x *XMLObject) WithNamespace(namespace string) *XMLObject { + x.Namespace = namespace + return x +} + +// WithPrefix sets the xml prefix for the object +func (x *XMLObject) WithPrefix(prefix string) *XMLObject { + x.Prefix = prefix + return x +} + +// AsAttribute flags this object as xml attribute +func (x *XMLObject) AsAttribute() *XMLObject { + x.Attribute = true + return x +} + +// AsElement flags this object as an xml node +func (x *XMLObject) AsElement() *XMLObject { + x.Attribute = false + return x +} + +// AsWrapped flags this object as wrapped, this is mostly useful for array types +func (x *XMLObject) AsWrapped() *XMLObject { + x.Wrapped = true + return x +} + +// AsUnwrapped flags this object as an xml node +func (x *XMLObject) AsUnwrapped() *XMLObject { + x.Wrapped = false + return x +} diff --git a/vendor/github.com/go-openapi/swag/.editorconfig b/vendor/github.com/go-openapi/swag/.editorconfig new file mode 100644 index 000000000..3152da69a --- /dev/null +++ b/vendor/github.com/go-openapi/swag/.editorconfig @@ -0,0 +1,26 @@ +# top-most EditorConfig file +root = true + +# Unix-style newlines with a newline ending every file +[*] +end_of_line = lf +insert_final_newline = true +indent_style = space +indent_size = 2 +trim_trailing_whitespace = true + +# Set default charset +[*.{js,py,go,scala,rb,java,html,css,less,sass,md}] +charset = utf-8 + +# Tab indentation (no size specified) +[*.go] +indent_style = tab + +[*.md] +trim_trailing_whitespace = false + +# Matches the exact files either package.json or .travis.yml +[{package.json,.travis.yml}] +indent_style = space +indent_size = 2 diff --git a/vendor/github.com/go-openapi/swag/.gitignore b/vendor/github.com/go-openapi/swag/.gitignore new file mode 100644 index 000000000..d69b53acc --- /dev/null +++ b/vendor/github.com/go-openapi/swag/.gitignore @@ -0,0 +1,4 @@ +secrets.yml +vendor +Godeps +.idea diff --git a/vendor/github.com/go-openapi/swag/.golangci.yml b/vendor/github.com/go-openapi/swag/.golangci.yml new file mode 100644 index 000000000..625c3d6af --- /dev/null +++ b/vendor/github.com/go-openapi/swag/.golangci.yml @@ -0,0 +1,22 @@ +linters-settings: + govet: + check-shadowing: true + golint: + min-confidence: 0 + gocyclo: + min-complexity: 25 + maligned: + suggest-new: true + dupl: + threshold: 100 + goconst: + min-len: 3 + min-occurrences: 2 + +linters: + enable-all: true + disable: + - maligned + - lll + - gochecknoinits + - gochecknoglobals diff --git a/vendor/github.com/go-openapi/swag/.travis.yml b/vendor/github.com/go-openapi/swag/.travis.yml new file mode 100644 index 000000000..aa26d8763 --- /dev/null +++ b/vendor/github.com/go-openapi/swag/.travis.yml @@ -0,0 +1,15 @@ +after_success: +- bash <(curl -s https://codecov.io/bash) +go: +- 1.11.x +- 1.12.x +install: +- GO111MODULE=off go get -u gotest.tools/gotestsum +env: +- GO111MODULE=on +language: go +notifications: + slack: + secure: QUWvCkBBK09GF7YtEvHHVt70JOkdlNBG0nIKu/5qc4/nW5HP8I2w0SEf/XR2je0eED1Qe3L/AfMCWwrEj+IUZc3l4v+ju8X8R3Lomhme0Eb0jd1MTMCuPcBT47YCj0M7RON7vXtbFfm1hFJ/jLe5+9FXz0hpXsR24PJc5ZIi/ogNwkaPqG4BmndzecpSh0vc2FJPZUD9LT0I09REY/vXR0oQAalLkW0asGD5taHZTUZq/kBpsNxaAFrLM23i4mUcf33M5fjLpvx5LRICrX/57XpBrDh2TooBU6Qj3CgoY0uPRYUmSNxbVx1czNzl2JtEpb5yjoxfVPQeg0BvQM00G8LJINISR+ohrjhkZmAqchDupAX+yFrxTtORa78CtnIL6z/aTNlgwwVD8kvL/1pFA/JWYmKDmz93mV/+6wubGzNSQCstzjkFA4/iZEKewKUoRIAi/fxyscP6L/rCpmY/4llZZvrnyTqVbt6URWpopUpH4rwYqreXAtJxJsfBJIeSmUIiDIOMGkCTvyTEW3fWGmGoqWtSHLoaWDyAIGb7azb+KvfpWtEcoPFWfSWU+LGee0A/YsUhBl7ADB9A0CJEuR8q4BPpKpfLwPKSiKSAXL7zDkyjExyhtgqbSl2jS+rKIHOZNL8JkCcTP2MKMVd563C5rC5FMKqu3S9m2b6380E= +script: +- gotestsum -f short-verbose -- -race -coverprofile=coverage.txt -covermode=atomic ./... diff --git a/vendor/github.com/go-openapi/swag/CODE_OF_CONDUCT.md b/vendor/github.com/go-openapi/swag/CODE_OF_CONDUCT.md new file mode 100644 index 000000000..9322b065e --- /dev/null +++ b/vendor/github.com/go-openapi/swag/CODE_OF_CONDUCT.md @@ -0,0 +1,74 @@ +# Contributor Covenant Code of Conduct + +## Our Pledge + +In the interest of fostering an open and welcoming environment, we as +contributors and maintainers pledge to making participation in our project and +our community a harassment-free experience for everyone, regardless of age, body +size, disability, ethnicity, gender identity and expression, level of experience, +nationality, personal appearance, race, religion, or sexual identity and +orientation. + +## Our Standards + +Examples of behavior that contributes to creating a positive environment +include: + +* Using welcoming and inclusive language +* Being respectful of differing viewpoints and experiences +* Gracefully accepting constructive criticism +* Focusing on what is best for the community +* Showing empathy towards other community members + +Examples of unacceptable behavior by participants include: + +* The use of sexualized language or imagery and unwelcome sexual attention or +advances +* Trolling, insulting/derogatory comments, and personal or political attacks +* Public or private harassment +* Publishing others' private information, such as a physical or electronic + address, without explicit permission +* Other conduct which could reasonably be considered inappropriate in a + professional setting + +## Our Responsibilities + +Project maintainers are responsible for clarifying the standards of acceptable +behavior and are expected to take appropriate and fair corrective action in +response to any instances of unacceptable behavior. + +Project maintainers have the right and responsibility to remove, edit, or +reject comments, commits, code, wiki edits, issues, and other contributions +that are not aligned to this Code of Conduct, or to ban temporarily or +permanently any contributor for other behaviors that they deem inappropriate, +threatening, offensive, or harmful. + +## Scope + +This Code of Conduct applies both within project spaces and in public spaces +when an individual is representing the project or its community. Examples of +representing a project or community include using an official project e-mail +address, posting via an official social media account, or acting as an appointed +representative at an online or offline event. Representation of a project may be +further defined and clarified by project maintainers. + +## Enforcement + +Instances of abusive, harassing, or otherwise unacceptable behavior may be +reported by contacting the project team at ivan+abuse@flanders.co.nz. All +complaints will be reviewed and investigated and will result in a response that +is deemed necessary and appropriate to the circumstances. The project team is +obligated to maintain confidentiality with regard to the reporter of an incident. +Further details of specific enforcement policies may be posted separately. + +Project maintainers who do not follow or enforce the Code of Conduct in good +faith may face temporary or permanent repercussions as determined by other +members of the project's leadership. + +## Attribution + +This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, +available at [http://contributor-covenant.org/version/1/4][version] + +[homepage]: http://contributor-covenant.org +[version]: http://contributor-covenant.org/version/1/4/ diff --git a/vendor/github.com/go-openapi/swag/LICENSE b/vendor/github.com/go-openapi/swag/LICENSE new file mode 100644 index 000000000..d64569567 --- /dev/null +++ b/vendor/github.com/go-openapi/swag/LICENSE @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + 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. diff --git a/vendor/github.com/go-openapi/swag/README.md b/vendor/github.com/go-openapi/swag/README.md new file mode 100644 index 000000000..eb60ae80a --- /dev/null +++ b/vendor/github.com/go-openapi/swag/README.md @@ -0,0 +1,22 @@ +# Swag [![Build Status](https://travis-ci.org/go-openapi/swag.svg?branch=master)](https://travis-ci.org/go-openapi/swag) [![codecov](https://codecov.io/gh/go-openapi/swag/branch/master/graph/badge.svg)](https://codecov.io/gh/go-openapi/swag) [![Slack Status](https://slackin.goswagger.io/badge.svg)](https://slackin.goswagger.io) + +[![license](http://img.shields.io/badge/license-Apache%20v2-orange.svg)](https://raw.githubusercontent.com/go-openapi/swag/master/LICENSE) +[![GoDoc](https://godoc.org/github.com/go-openapi/swag?status.svg)](http://godoc.org/github.com/go-openapi/swag) +[![GolangCI](https://golangci.com/badges/github.com/go-openapi/swag.svg)](https://golangci.com) +[![Go Report Card](https://goreportcard.com/badge/github.com/go-openapi/swag)](https://goreportcard.com/report/github.com/go-openapi/swag) + +Contains a bunch of helper functions for go-openapi and go-swagger projects. + +You may also use it standalone for your projects. + +* convert between value and pointers for builtin types +* convert from string to builtin types (wraps strconv) +* fast json concatenation +* search in path +* load from file or http +* name mangling + + +This repo has only few dependencies outside of the standard library: + +* YAML utilities depend on gopkg.in/yaml.v2 diff --git a/vendor/github.com/go-openapi/swag/convert.go b/vendor/github.com/go-openapi/swag/convert.go new file mode 100644 index 000000000..7da35c316 --- /dev/null +++ b/vendor/github.com/go-openapi/swag/convert.go @@ -0,0 +1,208 @@ +// Copyright 2015 go-swagger maintainers +// +// 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 swag + +import ( + "math" + "strconv" + "strings" +) + +// same as ECMA Number.MAX_SAFE_INTEGER and Number.MIN_SAFE_INTEGER +const ( + maxJSONFloat = float64(1<<53 - 1) // 9007199254740991.0 2^53 - 1 + minJSONFloat = -float64(1<<53 - 1) //-9007199254740991.0 -2^53 - 1 + epsilon float64 = 1e-9 +) + +// IsFloat64AJSONInteger allow for integers [-2^53, 2^53-1] inclusive +func IsFloat64AJSONInteger(f float64) bool { + if math.IsNaN(f) || math.IsInf(f, 0) || f < minJSONFloat || f > maxJSONFloat { + return false + } + fa := math.Abs(f) + g := float64(uint64(f)) + ga := math.Abs(g) + + diff := math.Abs(f - g) + + // more info: https://floating-point-gui.de/errors/comparison/#look-out-for-edge-cases + switch { + case f == g: // best case + return true + case f == float64(int64(f)) || f == float64(uint64(f)): // optimistic case + return true + case f == 0 || g == 0 || diff < math.SmallestNonzeroFloat64: // very close to 0 values + return diff < (epsilon * math.SmallestNonzeroFloat64) + } + // check the relative error + return diff/math.Min(fa+ga, math.MaxFloat64) < epsilon +} + +var evaluatesAsTrue map[string]struct{} + +func init() { + evaluatesAsTrue = map[string]struct{}{ + "true": {}, + "1": {}, + "yes": {}, + "ok": {}, + "y": {}, + "on": {}, + "selected": {}, + "checked": {}, + "t": {}, + "enabled": {}, + } +} + +// ConvertBool turn a string into a boolean +func ConvertBool(str string) (bool, error) { + _, ok := evaluatesAsTrue[strings.ToLower(str)] + return ok, nil +} + +// ConvertFloat32 turn a string into a float32 +func ConvertFloat32(str string) (float32, error) { + f, err := strconv.ParseFloat(str, 32) + if err != nil { + return 0, err + } + return float32(f), nil +} + +// ConvertFloat64 turn a string into a float64 +func ConvertFloat64(str string) (float64, error) { + return strconv.ParseFloat(str, 64) +} + +// ConvertInt8 turn a string into int8 boolean +func ConvertInt8(str string) (int8, error) { + i, err := strconv.ParseInt(str, 10, 8) + if err != nil { + return 0, err + } + return int8(i), nil +} + +// ConvertInt16 turn a string into a int16 +func ConvertInt16(str string) (int16, error) { + i, err := strconv.ParseInt(str, 10, 16) + if err != nil { + return 0, err + } + return int16(i), nil +} + +// ConvertInt32 turn a string into a int32 +func ConvertInt32(str string) (int32, error) { + i, err := strconv.ParseInt(str, 10, 32) + if err != nil { + return 0, err + } + return int32(i), nil +} + +// ConvertInt64 turn a string into a int64 +func ConvertInt64(str string) (int64, error) { + return strconv.ParseInt(str, 10, 64) +} + +// ConvertUint8 turn a string into a uint8 +func ConvertUint8(str string) (uint8, error) { + i, err := strconv.ParseUint(str, 10, 8) + if err != nil { + return 0, err + } + return uint8(i), nil +} + +// ConvertUint16 turn a string into a uint16 +func ConvertUint16(str string) (uint16, error) { + i, err := strconv.ParseUint(str, 10, 16) + if err != nil { + return 0, err + } + return uint16(i), nil +} + +// ConvertUint32 turn a string into a uint32 +func ConvertUint32(str string) (uint32, error) { + i, err := strconv.ParseUint(str, 10, 32) + if err != nil { + return 0, err + } + return uint32(i), nil +} + +// ConvertUint64 turn a string into a uint64 +func ConvertUint64(str string) (uint64, error) { + return strconv.ParseUint(str, 10, 64) +} + +// FormatBool turns a boolean into a string +func FormatBool(value bool) string { + return strconv.FormatBool(value) +} + +// FormatFloat32 turns a float32 into a string +func FormatFloat32(value float32) string { + return strconv.FormatFloat(float64(value), 'f', -1, 32) +} + +// FormatFloat64 turns a float64 into a string +func FormatFloat64(value float64) string { + return strconv.FormatFloat(value, 'f', -1, 64) +} + +// FormatInt8 turns an int8 into a string +func FormatInt8(value int8) string { + return strconv.FormatInt(int64(value), 10) +} + +// FormatInt16 turns an int16 into a string +func FormatInt16(value int16) string { + return strconv.FormatInt(int64(value), 10) +} + +// FormatInt32 turns an int32 into a string +func FormatInt32(value int32) string { + return strconv.Itoa(int(value)) +} + +// FormatInt64 turns an int64 into a string +func FormatInt64(value int64) string { + return strconv.FormatInt(value, 10) +} + +// FormatUint8 turns an uint8 into a string +func FormatUint8(value uint8) string { + return strconv.FormatUint(uint64(value), 10) +} + +// FormatUint16 turns an uint16 into a string +func FormatUint16(value uint16) string { + return strconv.FormatUint(uint64(value), 10) +} + +// FormatUint32 turns an uint32 into a string +func FormatUint32(value uint32) string { + return strconv.FormatUint(uint64(value), 10) +} + +// FormatUint64 turns an uint64 into a string +func FormatUint64(value uint64) string { + return strconv.FormatUint(value, 10) +} diff --git a/vendor/github.com/go-openapi/swag/convert_types.go b/vendor/github.com/go-openapi/swag/convert_types.go new file mode 100644 index 000000000..c95e4e78b --- /dev/null +++ b/vendor/github.com/go-openapi/swag/convert_types.go @@ -0,0 +1,595 @@ +package swag + +import "time" + +// This file was taken from the aws go sdk + +// String returns a pointer to of the string value passed in. +func String(v string) *string { + return &v +} + +// StringValue returns the value of the string pointer passed in or +// "" if the pointer is nil. +func StringValue(v *string) string { + if v != nil { + return *v + } + return "" +} + +// StringSlice converts a slice of string values into a slice of +// string pointers +func StringSlice(src []string) []*string { + dst := make([]*string, len(src)) + for i := 0; i < len(src); i++ { + dst[i] = &(src[i]) + } + return dst +} + +// StringValueSlice converts a slice of string pointers into a slice of +// string values +func StringValueSlice(src []*string) []string { + dst := make([]string, len(src)) + for i := 0; i < len(src); i++ { + if src[i] != nil { + dst[i] = *(src[i]) + } + } + return dst +} + +// StringMap converts a string map of string values into a string +// map of string pointers +func StringMap(src map[string]string) map[string]*string { + dst := make(map[string]*string) + for k, val := range src { + v := val + dst[k] = &v + } + return dst +} + +// StringValueMap converts a string map of string pointers into a string +// map of string values +func StringValueMap(src map[string]*string) map[string]string { + dst := make(map[string]string) + for k, val := range src { + if val != nil { + dst[k] = *val + } + } + return dst +} + +// Bool returns a pointer to of the bool value passed in. +func Bool(v bool) *bool { + return &v +} + +// BoolValue returns the value of the bool pointer passed in or +// false if the pointer is nil. +func BoolValue(v *bool) bool { + if v != nil { + return *v + } + return false +} + +// BoolSlice converts a slice of bool values into a slice of +// bool pointers +func BoolSlice(src []bool) []*bool { + dst := make([]*bool, len(src)) + for i := 0; i < len(src); i++ { + dst[i] = &(src[i]) + } + return dst +} + +// BoolValueSlice converts a slice of bool pointers into a slice of +// bool values +func BoolValueSlice(src []*bool) []bool { + dst := make([]bool, len(src)) + for i := 0; i < len(src); i++ { + if src[i] != nil { + dst[i] = *(src[i]) + } + } + return dst +} + +// BoolMap converts a string map of bool values into a string +// map of bool pointers +func BoolMap(src map[string]bool) map[string]*bool { + dst := make(map[string]*bool) + for k, val := range src { + v := val + dst[k] = &v + } + return dst +} + +// BoolValueMap converts a string map of bool pointers into a string +// map of bool values +func BoolValueMap(src map[string]*bool) map[string]bool { + dst := make(map[string]bool) + for k, val := range src { + if val != nil { + dst[k] = *val + } + } + return dst +} + +// Int returns a pointer to of the int value passed in. +func Int(v int) *int { + return &v +} + +// IntValue returns the value of the int pointer passed in or +// 0 if the pointer is nil. +func IntValue(v *int) int { + if v != nil { + return *v + } + return 0 +} + +// IntSlice converts a slice of int values into a slice of +// int pointers +func IntSlice(src []int) []*int { + dst := make([]*int, len(src)) + for i := 0; i < len(src); i++ { + dst[i] = &(src[i]) + } + return dst +} + +// IntValueSlice converts a slice of int pointers into a slice of +// int values +func IntValueSlice(src []*int) []int { + dst := make([]int, len(src)) + for i := 0; i < len(src); i++ { + if src[i] != nil { + dst[i] = *(src[i]) + } + } + return dst +} + +// IntMap converts a string map of int values into a string +// map of int pointers +func IntMap(src map[string]int) map[string]*int { + dst := make(map[string]*int) + for k, val := range src { + v := val + dst[k] = &v + } + return dst +} + +// IntValueMap converts a string map of int pointers into a string +// map of int values +func IntValueMap(src map[string]*int) map[string]int { + dst := make(map[string]int) + for k, val := range src { + if val != nil { + dst[k] = *val + } + } + return dst +} + +// Int32 returns a pointer to of the int64 value passed in. +func Int32(v int32) *int32 { + return &v +} + +// Int32Value returns the value of the int64 pointer passed in or +// 0 if the pointer is nil. +func Int32Value(v *int32) int32 { + if v != nil { + return *v + } + return 0 +} + +// Int32Slice converts a slice of int64 values into a slice of +// int32 pointers +func Int32Slice(src []int32) []*int32 { + dst := make([]*int32, len(src)) + for i := 0; i < len(src); i++ { + dst[i] = &(src[i]) + } + return dst +} + +// Int32ValueSlice converts a slice of int32 pointers into a slice of +// int32 values +func Int32ValueSlice(src []*int32) []int32 { + dst := make([]int32, len(src)) + for i := 0; i < len(src); i++ { + if src[i] != nil { + dst[i] = *(src[i]) + } + } + return dst +} + +// Int32Map converts a string map of int32 values into a string +// map of int32 pointers +func Int32Map(src map[string]int32) map[string]*int32 { + dst := make(map[string]*int32) + for k, val := range src { + v := val + dst[k] = &v + } + return dst +} + +// Int32ValueMap converts a string map of int32 pointers into a string +// map of int32 values +func Int32ValueMap(src map[string]*int32) map[string]int32 { + dst := make(map[string]int32) + for k, val := range src { + if val != nil { + dst[k] = *val + } + } + return dst +} + +// Int64 returns a pointer to of the int64 value passed in. +func Int64(v int64) *int64 { + return &v +} + +// Int64Value returns the value of the int64 pointer passed in or +// 0 if the pointer is nil. +func Int64Value(v *int64) int64 { + if v != nil { + return *v + } + return 0 +} + +// Int64Slice converts a slice of int64 values into a slice of +// int64 pointers +func Int64Slice(src []int64) []*int64 { + dst := make([]*int64, len(src)) + for i := 0; i < len(src); i++ { + dst[i] = &(src[i]) + } + return dst +} + +// Int64ValueSlice converts a slice of int64 pointers into a slice of +// int64 values +func Int64ValueSlice(src []*int64) []int64 { + dst := make([]int64, len(src)) + for i := 0; i < len(src); i++ { + if src[i] != nil { + dst[i] = *(src[i]) + } + } + return dst +} + +// Int64Map converts a string map of int64 values into a string +// map of int64 pointers +func Int64Map(src map[string]int64) map[string]*int64 { + dst := make(map[string]*int64) + for k, val := range src { + v := val + dst[k] = &v + } + return dst +} + +// Int64ValueMap converts a string map of int64 pointers into a string +// map of int64 values +func Int64ValueMap(src map[string]*int64) map[string]int64 { + dst := make(map[string]int64) + for k, val := range src { + if val != nil { + dst[k] = *val + } + } + return dst +} + +// Uint returns a pouinter to of the uint value passed in. +func Uint(v uint) *uint { + return &v +} + +// UintValue returns the value of the uint pouinter passed in or +// 0 if the pouinter is nil. +func UintValue(v *uint) uint { + if v != nil { + return *v + } + return 0 +} + +// UintSlice converts a slice of uint values uinto a slice of +// uint pouinters +func UintSlice(src []uint) []*uint { + dst := make([]*uint, len(src)) + for i := 0; i < len(src); i++ { + dst[i] = &(src[i]) + } + return dst +} + +// UintValueSlice converts a slice of uint pouinters uinto a slice of +// uint values +func UintValueSlice(src []*uint) []uint { + dst := make([]uint, len(src)) + for i := 0; i < len(src); i++ { + if src[i] != nil { + dst[i] = *(src[i]) + } + } + return dst +} + +// UintMap converts a string map of uint values uinto a string +// map of uint pouinters +func UintMap(src map[string]uint) map[string]*uint { + dst := make(map[string]*uint) + for k, val := range src { + v := val + dst[k] = &v + } + return dst +} + +// UintValueMap converts a string map of uint pouinters uinto a string +// map of uint values +func UintValueMap(src map[string]*uint) map[string]uint { + dst := make(map[string]uint) + for k, val := range src { + if val != nil { + dst[k] = *val + } + } + return dst +} + +// Uint32 returns a pouinter to of the uint64 value passed in. +func Uint32(v uint32) *uint32 { + return &v +} + +// Uint32Value returns the value of the uint64 pouinter passed in or +// 0 if the pouinter is nil. +func Uint32Value(v *uint32) uint32 { + if v != nil { + return *v + } + return 0 +} + +// Uint32Slice converts a slice of uint64 values uinto a slice of +// uint32 pouinters +func Uint32Slice(src []uint32) []*uint32 { + dst := make([]*uint32, len(src)) + for i := 0; i < len(src); i++ { + dst[i] = &(src[i]) + } + return dst +} + +// Uint32ValueSlice converts a slice of uint32 pouinters uinto a slice of +// uint32 values +func Uint32ValueSlice(src []*uint32) []uint32 { + dst := make([]uint32, len(src)) + for i := 0; i < len(src); i++ { + if src[i] != nil { + dst[i] = *(src[i]) + } + } + return dst +} + +// Uint32Map converts a string map of uint32 values uinto a string +// map of uint32 pouinters +func Uint32Map(src map[string]uint32) map[string]*uint32 { + dst := make(map[string]*uint32) + for k, val := range src { + v := val + dst[k] = &v + } + return dst +} + +// Uint32ValueMap converts a string map of uint32 pouinters uinto a string +// map of uint32 values +func Uint32ValueMap(src map[string]*uint32) map[string]uint32 { + dst := make(map[string]uint32) + for k, val := range src { + if val != nil { + dst[k] = *val + } + } + return dst +} + +// Uint64 returns a pouinter to of the uint64 value passed in. +func Uint64(v uint64) *uint64 { + return &v +} + +// Uint64Value returns the value of the uint64 pouinter passed in or +// 0 if the pouinter is nil. +func Uint64Value(v *uint64) uint64 { + if v != nil { + return *v + } + return 0 +} + +// Uint64Slice converts a slice of uint64 values uinto a slice of +// uint64 pouinters +func Uint64Slice(src []uint64) []*uint64 { + dst := make([]*uint64, len(src)) + for i := 0; i < len(src); i++ { + dst[i] = &(src[i]) + } + return dst +} + +// Uint64ValueSlice converts a slice of uint64 pouinters uinto a slice of +// uint64 values +func Uint64ValueSlice(src []*uint64) []uint64 { + dst := make([]uint64, len(src)) + for i := 0; i < len(src); i++ { + if src[i] != nil { + dst[i] = *(src[i]) + } + } + return dst +} + +// Uint64Map converts a string map of uint64 values uinto a string +// map of uint64 pouinters +func Uint64Map(src map[string]uint64) map[string]*uint64 { + dst := make(map[string]*uint64) + for k, val := range src { + v := val + dst[k] = &v + } + return dst +} + +// Uint64ValueMap converts a string map of uint64 pouinters uinto a string +// map of uint64 values +func Uint64ValueMap(src map[string]*uint64) map[string]uint64 { + dst := make(map[string]uint64) + for k, val := range src { + if val != nil { + dst[k] = *val + } + } + return dst +} + +// Float64 returns a pointer to of the float64 value passed in. +func Float64(v float64) *float64 { + return &v +} + +// Float64Value returns the value of the float64 pointer passed in or +// 0 if the pointer is nil. +func Float64Value(v *float64) float64 { + if v != nil { + return *v + } + return 0 +} + +// Float64Slice converts a slice of float64 values into a slice of +// float64 pointers +func Float64Slice(src []float64) []*float64 { + dst := make([]*float64, len(src)) + for i := 0; i < len(src); i++ { + dst[i] = &(src[i]) + } + return dst +} + +// Float64ValueSlice converts a slice of float64 pointers into a slice of +// float64 values +func Float64ValueSlice(src []*float64) []float64 { + dst := make([]float64, len(src)) + for i := 0; i < len(src); i++ { + if src[i] != nil { + dst[i] = *(src[i]) + } + } + return dst +} + +// Float64Map converts a string map of float64 values into a string +// map of float64 pointers +func Float64Map(src map[string]float64) map[string]*float64 { + dst := make(map[string]*float64) + for k, val := range src { + v := val + dst[k] = &v + } + return dst +} + +// Float64ValueMap converts a string map of float64 pointers into a string +// map of float64 values +func Float64ValueMap(src map[string]*float64) map[string]float64 { + dst := make(map[string]float64) + for k, val := range src { + if val != nil { + dst[k] = *val + } + } + return dst +} + +// Time returns a pointer to of the time.Time value passed in. +func Time(v time.Time) *time.Time { + return &v +} + +// TimeValue returns the value of the time.Time pointer passed in or +// time.Time{} if the pointer is nil. +func TimeValue(v *time.Time) time.Time { + if v != nil { + return *v + } + return time.Time{} +} + +// TimeSlice converts a slice of time.Time values into a slice of +// time.Time pointers +func TimeSlice(src []time.Time) []*time.Time { + dst := make([]*time.Time, len(src)) + for i := 0; i < len(src); i++ { + dst[i] = &(src[i]) + } + return dst +} + +// TimeValueSlice converts a slice of time.Time pointers into a slice of +// time.Time values +func TimeValueSlice(src []*time.Time) []time.Time { + dst := make([]time.Time, len(src)) + for i := 0; i < len(src); i++ { + if src[i] != nil { + dst[i] = *(src[i]) + } + } + return dst +} + +// TimeMap converts a string map of time.Time values into a string +// map of time.Time pointers +func TimeMap(src map[string]time.Time) map[string]*time.Time { + dst := make(map[string]*time.Time) + for k, val := range src { + v := val + dst[k] = &v + } + return dst +} + +// TimeValueMap converts a string map of time.Time pointers into a string +// map of time.Time values +func TimeValueMap(src map[string]*time.Time) map[string]time.Time { + dst := make(map[string]time.Time) + for k, val := range src { + if val != nil { + dst[k] = *val + } + } + return dst +} diff --git a/vendor/github.com/go-openapi/swag/doc.go b/vendor/github.com/go-openapi/swag/doc.go new file mode 100644 index 000000000..8d2c8c501 --- /dev/null +++ b/vendor/github.com/go-openapi/swag/doc.go @@ -0,0 +1,32 @@ +// Copyright 2015 go-swagger maintainers +// +// 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 swag contains a bunch of helper functions for go-openapi and go-swagger projects. + +You may also use it standalone for your projects. + + * convert between value and pointers for builtin types + * convert from string to builtin types (wraps strconv) + * fast json concatenation + * search in path + * load from file or http + * name mangling + + +This repo has only few dependencies outside of the standard library: + + * YAML utilities depend on gopkg.in/yaml.v2 +*/ +package swag diff --git a/vendor/github.com/go-openapi/swag/go.mod b/vendor/github.com/go-openapi/swag/go.mod new file mode 100644 index 000000000..15bbb0822 --- /dev/null +++ b/vendor/github.com/go-openapi/swag/go.mod @@ -0,0 +1,14 @@ +module github.com/go-openapi/swag + +require ( + github.com/davecgh/go-spew v1.1.1 // indirect + github.com/kr/pretty v0.1.0 // indirect + github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63 + github.com/stretchr/testify v1.3.0 + gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 // indirect + gopkg.in/yaml.v2 v2.2.2 +) + +replace github.com/golang/lint => golang.org/x/lint v0.0.0-20190409202823-959b441ac422 + +replace sourcegraph.com/sourcegraph/go-diff => github.com/sourcegraph/go-diff v0.5.1 diff --git a/vendor/github.com/go-openapi/swag/go.sum b/vendor/github.com/go-openapi/swag/go.sum new file mode 100644 index 000000000..33469f54a --- /dev/null +++ b/vendor/github.com/go-openapi/swag/go.sum @@ -0,0 +1,20 @@ +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= +github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= +github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63 h1:nTT4s92Dgz2HlrB2NaMgvlfqHH39OgMhA7z3PK7PGD4= +github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY= +gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= diff --git a/vendor/github.com/go-openapi/swag/json.go b/vendor/github.com/go-openapi/swag/json.go new file mode 100644 index 000000000..edf93d84c --- /dev/null +++ b/vendor/github.com/go-openapi/swag/json.go @@ -0,0 +1,312 @@ +// Copyright 2015 go-swagger maintainers +// +// 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 swag + +import ( + "bytes" + "encoding/json" + "log" + "reflect" + "strings" + "sync" + + "github.com/mailru/easyjson/jlexer" + "github.com/mailru/easyjson/jwriter" +) + +// nullJSON represents a JSON object with null type +var nullJSON = []byte("null") + +// DefaultJSONNameProvider the default cache for types +var DefaultJSONNameProvider = NewNameProvider() + +const comma = byte(',') + +var closers map[byte]byte + +func init() { + closers = map[byte]byte{ + '{': '}', + '[': ']', + } +} + +type ejMarshaler interface { + MarshalEasyJSON(w *jwriter.Writer) +} + +type ejUnmarshaler interface { + UnmarshalEasyJSON(w *jlexer.Lexer) +} + +// WriteJSON writes json data, prefers finding an appropriate interface to short-circuit the marshaller +// so it takes the fastest option available. +func WriteJSON(data interface{}) ([]byte, error) { + if d, ok := data.(ejMarshaler); ok { + jw := new(jwriter.Writer) + d.MarshalEasyJSON(jw) + return jw.BuildBytes() + } + if d, ok := data.(json.Marshaler); ok { + return d.MarshalJSON() + } + return json.Marshal(data) +} + +// ReadJSON reads json data, prefers finding an appropriate interface to short-circuit the unmarshaller +// so it takes the fastes option available +func ReadJSON(data []byte, value interface{}) error { + trimmedData := bytes.Trim(data, "\x00") + if d, ok := value.(ejUnmarshaler); ok { + jl := &jlexer.Lexer{Data: trimmedData} + d.UnmarshalEasyJSON(jl) + return jl.Error() + } + if d, ok := value.(json.Unmarshaler); ok { + return d.UnmarshalJSON(trimmedData) + } + return json.Unmarshal(trimmedData, value) +} + +// DynamicJSONToStruct converts an untyped json structure into a struct +func DynamicJSONToStruct(data interface{}, target interface{}) error { + // TODO: convert straight to a json typed map (mergo + iterate?) + b, err := WriteJSON(data) + if err != nil { + return err + } + return ReadJSON(b, target) +} + +// ConcatJSON concatenates multiple json objects efficiently +func ConcatJSON(blobs ...[]byte) []byte { + if len(blobs) == 0 { + return nil + } + + last := len(blobs) - 1 + for blobs[last] == nil || bytes.Equal(blobs[last], nullJSON) { + // strips trailing null objects + last-- + if last < 0 { + // there was nothing but "null"s or nil... + return nil + } + } + if last == 0 { + return blobs[0] + } + + var opening, closing byte + var idx, a int + buf := bytes.NewBuffer(nil) + + for i, b := range blobs[:last+1] { + if b == nil || bytes.Equal(b, nullJSON) { + // a null object is in the list: skip it + continue + } + if len(b) > 0 && opening == 0 { // is this an array or an object? + opening, closing = b[0], closers[b[0]] + } + + if opening != '{' && opening != '[' { + continue // don't know how to concatenate non container objects + } + + if len(b) < 3 { // yep empty but also the last one, so closing this thing + if i == last && a > 0 { + if err := buf.WriteByte(closing); err != nil { + log.Println(err) + } + } + continue + } + + idx = 0 + if a > 0 { // we need to join with a comma for everything beyond the first non-empty item + if err := buf.WriteByte(comma); err != nil { + log.Println(err) + } + idx = 1 // this is not the first or the last so we want to drop the leading bracket + } + + if i != last { // not the last one, strip brackets + if _, err := buf.Write(b[idx : len(b)-1]); err != nil { + log.Println(err) + } + } else { // last one, strip only the leading bracket + if _, err := buf.Write(b[idx:]); err != nil { + log.Println(err) + } + } + a++ + } + // somehow it ended up being empty, so provide a default value + if buf.Len() == 0 { + if err := buf.WriteByte(opening); err != nil { + log.Println(err) + } + if err := buf.WriteByte(closing); err != nil { + log.Println(err) + } + } + return buf.Bytes() +} + +// ToDynamicJSON turns an object into a properly JSON typed structure +func ToDynamicJSON(data interface{}) interface{} { + // TODO: convert straight to a json typed map (mergo + iterate?) + b, err := json.Marshal(data) + if err != nil { + log.Println(err) + } + var res interface{} + if err := json.Unmarshal(b, &res); err != nil { + log.Println(err) + } + return res +} + +// FromDynamicJSON turns an object into a properly JSON typed structure +func FromDynamicJSON(data, target interface{}) error { + b, err := json.Marshal(data) + if err != nil { + log.Println(err) + } + return json.Unmarshal(b, target) +} + +// NameProvider represents an object capabale of translating from go property names +// to json property names +// This type is thread-safe. +type NameProvider struct { + lock *sync.Mutex + index map[reflect.Type]nameIndex +} + +type nameIndex struct { + jsonNames map[string]string + goNames map[string]string +} + +// NewNameProvider creates a new name provider +func NewNameProvider() *NameProvider { + return &NameProvider{ + lock: &sync.Mutex{}, + index: make(map[reflect.Type]nameIndex), + } +} + +func buildnameIndex(tpe reflect.Type, idx, reverseIdx map[string]string) { + for i := 0; i < tpe.NumField(); i++ { + targetDes := tpe.Field(i) + + if targetDes.PkgPath != "" { // unexported + continue + } + + if targetDes.Anonymous { // walk embedded structures tree down first + buildnameIndex(targetDes.Type, idx, reverseIdx) + continue + } + + if tag := targetDes.Tag.Get("json"); tag != "" { + + parts := strings.Split(tag, ",") + if len(parts) == 0 { + continue + } + + nm := parts[0] + if nm == "-" { + continue + } + if nm == "" { // empty string means we want to use the Go name + nm = targetDes.Name + } + + idx[nm] = targetDes.Name + reverseIdx[targetDes.Name] = nm + } + } +} + +func newNameIndex(tpe reflect.Type) nameIndex { + var idx = make(map[string]string, tpe.NumField()) + var reverseIdx = make(map[string]string, tpe.NumField()) + + buildnameIndex(tpe, idx, reverseIdx) + return nameIndex{jsonNames: idx, goNames: reverseIdx} +} + +// GetJSONNames gets all the json property names for a type +func (n *NameProvider) GetJSONNames(subject interface{}) []string { + n.lock.Lock() + defer n.lock.Unlock() + tpe := reflect.Indirect(reflect.ValueOf(subject)).Type() + names, ok := n.index[tpe] + if !ok { + names = n.makeNameIndex(tpe) + } + + res := make([]string, 0, len(names.jsonNames)) + for k := range names.jsonNames { + res = append(res, k) + } + return res +} + +// GetJSONName gets the json name for a go property name +func (n *NameProvider) GetJSONName(subject interface{}, name string) (string, bool) { + tpe := reflect.Indirect(reflect.ValueOf(subject)).Type() + return n.GetJSONNameForType(tpe, name) +} + +// GetJSONNameForType gets the json name for a go property name on a given type +func (n *NameProvider) GetJSONNameForType(tpe reflect.Type, name string) (string, bool) { + n.lock.Lock() + defer n.lock.Unlock() + names, ok := n.index[tpe] + if !ok { + names = n.makeNameIndex(tpe) + } + nme, ok := names.goNames[name] + return nme, ok +} + +func (n *NameProvider) makeNameIndex(tpe reflect.Type) nameIndex { + names := newNameIndex(tpe) + n.index[tpe] = names + return names +} + +// GetGoName gets the go name for a json property name +func (n *NameProvider) GetGoName(subject interface{}, name string) (string, bool) { + tpe := reflect.Indirect(reflect.ValueOf(subject)).Type() + return n.GetGoNameForType(tpe, name) +} + +// GetGoNameForType gets the go name for a given type for a json property name +func (n *NameProvider) GetGoNameForType(tpe reflect.Type, name string) (string, bool) { + n.lock.Lock() + defer n.lock.Unlock() + names, ok := n.index[tpe] + if !ok { + names = n.makeNameIndex(tpe) + } + nme, ok := names.jsonNames[name] + return nme, ok +} diff --git a/vendor/github.com/go-openapi/swag/loading.go b/vendor/github.com/go-openapi/swag/loading.go new file mode 100644 index 000000000..70f4fb361 --- /dev/null +++ b/vendor/github.com/go-openapi/swag/loading.go @@ -0,0 +1,80 @@ +// Copyright 2015 go-swagger maintainers +// +// 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 swag + +import ( + "fmt" + "io/ioutil" + "log" + "net/http" + "path/filepath" + "strings" + "time" +) + +// LoadHTTPTimeout the default timeout for load requests +var LoadHTTPTimeout = 30 * time.Second + +// LoadFromFileOrHTTP loads the bytes from a file or a remote http server based on the path passed in +func LoadFromFileOrHTTP(path string) ([]byte, error) { + return LoadStrategy(path, ioutil.ReadFile, loadHTTPBytes(LoadHTTPTimeout))(path) +} + +// LoadFromFileOrHTTPWithTimeout loads the bytes from a file or a remote http server based on the path passed in +// timeout arg allows for per request overriding of the request timeout +func LoadFromFileOrHTTPWithTimeout(path string, timeout time.Duration) ([]byte, error) { + return LoadStrategy(path, ioutil.ReadFile, loadHTTPBytes(timeout))(path) +} + +// LoadStrategy returns a loader function for a given path or uri +func LoadStrategy(path string, local, remote func(string) ([]byte, error)) func(string) ([]byte, error) { + if strings.HasPrefix(path, "http") { + return remote + } + return func(pth string) ([]byte, error) { + upth, err := pathUnescape(pth) + if err != nil { + return nil, err + } + return local(filepath.FromSlash(upth)) + } +} + +func loadHTTPBytes(timeout time.Duration) func(path string) ([]byte, error) { + return func(path string) ([]byte, error) { + client := &http.Client{Timeout: timeout} + req, err := http.NewRequest("GET", path, nil) + if err != nil { + return nil, err + } + resp, err := client.Do(req) + defer func() { + if resp != nil { + if e := resp.Body.Close(); e != nil { + log.Println(e) + } + } + }() + if err != nil { + return nil, err + } + + if resp.StatusCode != http.StatusOK { + return nil, fmt.Errorf("could not access document at %q [%s] ", path, resp.Status) + } + + return ioutil.ReadAll(resp.Body) + } +} diff --git a/vendor/github.com/go-openapi/swag/name_lexem.go b/vendor/github.com/go-openapi/swag/name_lexem.go new file mode 100644 index 000000000..aa7f6a9bb --- /dev/null +++ b/vendor/github.com/go-openapi/swag/name_lexem.go @@ -0,0 +1,87 @@ +// Copyright 2015 go-swagger maintainers +// +// 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 swag + +import "unicode" + +type ( + nameLexem interface { + GetUnsafeGoName() string + GetOriginal() string + IsInitialism() bool + } + + initialismNameLexem struct { + original string + matchedInitialism string + } + + casualNameLexem struct { + original string + } +) + +func newInitialismNameLexem(original, matchedInitialism string) *initialismNameLexem { + return &initialismNameLexem{ + original: original, + matchedInitialism: matchedInitialism, + } +} + +func newCasualNameLexem(original string) *casualNameLexem { + return &casualNameLexem{ + original: original, + } +} + +func (l *initialismNameLexem) GetUnsafeGoName() string { + return l.matchedInitialism +} + +func (l *casualNameLexem) GetUnsafeGoName() string { + var first rune + var rest string + for i, orig := range l.original { + if i == 0 { + first = orig + continue + } + if i > 0 { + rest = l.original[i:] + break + } + } + if len(l.original) > 1 { + return string(unicode.ToUpper(first)) + lower(rest) + } + + return l.original +} + +func (l *initialismNameLexem) GetOriginal() string { + return l.original +} + +func (l *casualNameLexem) GetOriginal() string { + return l.original +} + +func (l *initialismNameLexem) IsInitialism() bool { + return true +} + +func (l *casualNameLexem) IsInitialism() bool { + return false +} diff --git a/vendor/github.com/go-openapi/swag/net.go b/vendor/github.com/go-openapi/swag/net.go new file mode 100644 index 000000000..821235f84 --- /dev/null +++ b/vendor/github.com/go-openapi/swag/net.go @@ -0,0 +1,38 @@ +// Copyright 2015 go-swagger maintainers +// +// 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 swag + +import ( + "net" + "strconv" +) + +// SplitHostPort splits a network address into a host and a port. +// The port is -1 when there is no port to be found +func SplitHostPort(addr string) (host string, port int, err error) { + h, p, err := net.SplitHostPort(addr) + if err != nil { + return "", -1, err + } + if p == "" { + return "", -1, &net.AddrError{Err: "missing port in address", Addr: addr} + } + + pi, err := strconv.Atoi(p) + if err != nil { + return "", -1, err + } + return h, pi, nil +} diff --git a/vendor/github.com/go-openapi/swag/path.go b/vendor/github.com/go-openapi/swag/path.go new file mode 100644 index 000000000..941bd0176 --- /dev/null +++ b/vendor/github.com/go-openapi/swag/path.go @@ -0,0 +1,59 @@ +// Copyright 2015 go-swagger maintainers +// +// 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 swag + +import ( + "os" + "path/filepath" + "runtime" + "strings" +) + +const ( + // GOPATHKey represents the env key for gopath + GOPATHKey = "GOPATH" +) + +// FindInSearchPath finds a package in a provided lists of paths +func FindInSearchPath(searchPath, pkg string) string { + pathsList := filepath.SplitList(searchPath) + for _, path := range pathsList { + if evaluatedPath, err := filepath.EvalSymlinks(filepath.Join(path, "src", pkg)); err == nil { + if _, err := os.Stat(evaluatedPath); err == nil { + return evaluatedPath + } + } + } + return "" +} + +// FindInGoSearchPath finds a package in the $GOPATH:$GOROOT +func FindInGoSearchPath(pkg string) string { + return FindInSearchPath(FullGoSearchPath(), pkg) +} + +// FullGoSearchPath gets the search paths for finding packages +func FullGoSearchPath() string { + allPaths := os.Getenv(GOPATHKey) + if allPaths == "" { + allPaths = filepath.Join(os.Getenv("HOME"), "go") + } + if allPaths != "" { + allPaths = strings.Join([]string{allPaths, runtime.GOROOT()}, ":") + } else { + allPaths = runtime.GOROOT() + } + return allPaths +} diff --git a/vendor/github.com/go-openapi/swag/post_go18.go b/vendor/github.com/go-openapi/swag/post_go18.go new file mode 100644 index 000000000..c2e686d31 --- /dev/null +++ b/vendor/github.com/go-openapi/swag/post_go18.go @@ -0,0 +1,23 @@ +// Copyright 2015 go-swagger maintainers +// +// 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. + +// +build go1.8 + +package swag + +import "net/url" + +func pathUnescape(path string) (string, error) { + return url.PathUnescape(path) +} diff --git a/vendor/github.com/go-openapi/swag/post_go19.go b/vendor/github.com/go-openapi/swag/post_go19.go new file mode 100644 index 000000000..eb2f2d8bc --- /dev/null +++ b/vendor/github.com/go-openapi/swag/post_go19.go @@ -0,0 +1,67 @@ +// Copyright 2015 go-swagger maintainers +// +// 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. + +// +build go1.9 + +package swag + +import ( + "sort" + "sync" +) + +// indexOfInitialisms is a thread-safe implementation of the sorted index of initialisms. +// Since go1.9, this may be implemented with sync.Map. +type indexOfInitialisms struct { + sortMutex *sync.Mutex + index *sync.Map +} + +func newIndexOfInitialisms() *indexOfInitialisms { + return &indexOfInitialisms{ + sortMutex: new(sync.Mutex), + index: new(sync.Map), + } +} + +func (m *indexOfInitialisms) load(initial map[string]bool) *indexOfInitialisms { + m.sortMutex.Lock() + defer m.sortMutex.Unlock() + for k, v := range initial { + m.index.Store(k, v) + } + return m +} + +func (m *indexOfInitialisms) isInitialism(key string) bool { + _, ok := m.index.Load(key) + return ok +} + +func (m *indexOfInitialisms) add(key string) *indexOfInitialisms { + m.index.Store(key, true) + return m +} + +func (m *indexOfInitialisms) sorted() (result []string) { + m.sortMutex.Lock() + defer m.sortMutex.Unlock() + m.index.Range(func(key, value interface{}) bool { + k := key.(string) + result = append(result, k) + return true + }) + sort.Sort(sort.Reverse(byInitialism(result))) + return +} diff --git a/vendor/github.com/go-openapi/swag/pre_go18.go b/vendor/github.com/go-openapi/swag/pre_go18.go new file mode 100644 index 000000000..6607f3393 --- /dev/null +++ b/vendor/github.com/go-openapi/swag/pre_go18.go @@ -0,0 +1,23 @@ +// Copyright 2015 go-swagger maintainers +// +// 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. + +// +build !go1.8 + +package swag + +import "net/url" + +func pathUnescape(path string) (string, error) { + return url.QueryUnescape(path) +} diff --git a/vendor/github.com/go-openapi/swag/pre_go19.go b/vendor/github.com/go-openapi/swag/pre_go19.go new file mode 100644 index 000000000..4bae187d1 --- /dev/null +++ b/vendor/github.com/go-openapi/swag/pre_go19.go @@ -0,0 +1,69 @@ +// Copyright 2015 go-swagger maintainers +// +// 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. + +// +build !go1.9 + +package swag + +import ( + "sort" + "sync" +) + +// indexOfInitialisms is a thread-safe implementation of the sorted index of initialisms. +// Before go1.9, this may be implemented with a mutex on the map. +type indexOfInitialisms struct { + getMutex *sync.Mutex + index map[string]bool +} + +func newIndexOfInitialisms() *indexOfInitialisms { + return &indexOfInitialisms{ + getMutex: new(sync.Mutex), + index: make(map[string]bool, 50), + } +} + +func (m *indexOfInitialisms) load(initial map[string]bool) *indexOfInitialisms { + m.getMutex.Lock() + defer m.getMutex.Unlock() + for k, v := range initial { + m.index[k] = v + } + return m +} + +func (m *indexOfInitialisms) isInitialism(key string) bool { + m.getMutex.Lock() + defer m.getMutex.Unlock() + _, ok := m.index[key] + return ok +} + +func (m *indexOfInitialisms) add(key string) *indexOfInitialisms { + m.getMutex.Lock() + defer m.getMutex.Unlock() + m.index[key] = true + return m +} + +func (m *indexOfInitialisms) sorted() (result []string) { + m.getMutex.Lock() + defer m.getMutex.Unlock() + for k := range m.index { + result = append(result, k) + } + sort.Sort(sort.Reverse(byInitialism(result))) + return +} diff --git a/vendor/github.com/go-openapi/swag/split.go b/vendor/github.com/go-openapi/swag/split.go new file mode 100644 index 000000000..a1825fb7d --- /dev/null +++ b/vendor/github.com/go-openapi/swag/split.go @@ -0,0 +1,262 @@ +// Copyright 2015 go-swagger maintainers +// +// 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 swag + +import ( + "unicode" +) + +var nameReplaceTable = map[rune]string{ + '@': "At ", + '&': "And ", + '|': "Pipe ", + '$': "Dollar ", + '!': "Bang ", + '-': "", + '_': "", +} + +type ( + splitter struct { + postSplitInitialismCheck bool + initialisms []string + } + + splitterOption func(*splitter) *splitter +) + +// split calls the splitter; splitter provides more control and post options +func split(str string) []string { + lexems := newSplitter().split(str) + result := make([]string, 0, len(lexems)) + + for _, lexem := range lexems { + result = append(result, lexem.GetOriginal()) + } + + return result + +} + +func (s *splitter) split(str string) []nameLexem { + return s.toNameLexems(str) +} + +func newSplitter(options ...splitterOption) *splitter { + splitter := &splitter{ + postSplitInitialismCheck: false, + initialisms: initialisms, + } + + for _, option := range options { + splitter = option(splitter) + } + + return splitter +} + +// withPostSplitInitialismCheck allows to catch initialisms after main split process +func withPostSplitInitialismCheck(s *splitter) *splitter { + s.postSplitInitialismCheck = true + return s +} + +type ( + initialismMatch struct { + start, end int + body []rune + complete bool + } + initialismMatches []*initialismMatch +) + +func (s *splitter) toNameLexems(name string) []nameLexem { + nameRunes := []rune(name) + matches := s.gatherInitialismMatches(nameRunes) + return s.mapMatchesToNameLexems(nameRunes, matches) +} + +func (s *splitter) gatherInitialismMatches(nameRunes []rune) initialismMatches { + matches := make(initialismMatches, 0) + + for currentRunePosition, currentRune := range nameRunes { + newMatches := make(initialismMatches, 0, len(matches)) + + // check current initialism matches + for _, match := range matches { + if keepCompleteMatch := match.complete; keepCompleteMatch { + newMatches = append(newMatches, match) + continue + } + + // drop failed match + currentMatchRune := match.body[currentRunePosition-match.start] + if !s.initialismRuneEqual(currentMatchRune, currentRune) { + continue + } + + // try to complete ongoing match + if currentRunePosition-match.start == len(match.body)-1 { + // we are close; the next step is to check the symbol ahead + // if it is a small letter, then it is not the end of match + // but beginning of the next word + + if currentRunePosition < len(nameRunes)-1 { + nextRune := nameRunes[currentRunePosition+1] + if newWord := unicode.IsLower(nextRune); newWord { + // oh ok, it was the start of a new word + continue + } + } + + match.complete = true + match.end = currentRunePosition + } + + newMatches = append(newMatches, match) + } + + // check for new initialism matches + for _, initialism := range s.initialisms { + initialismRunes := []rune(initialism) + if s.initialismRuneEqual(initialismRunes[0], currentRune) { + newMatches = append(newMatches, &initialismMatch{ + start: currentRunePosition, + body: initialismRunes, + complete: false, + }) + } + } + + matches = newMatches + } + + return matches +} + +func (s *splitter) mapMatchesToNameLexems(nameRunes []rune, matches initialismMatches) []nameLexem { + nameLexems := make([]nameLexem, 0) + + var lastAcceptedMatch *initialismMatch + for _, match := range matches { + if !match.complete { + continue + } + + if firstMatch := lastAcceptedMatch == nil; firstMatch { + nameLexems = append(nameLexems, s.breakCasualString(nameRunes[:match.start])...) + nameLexems = append(nameLexems, s.breakInitialism(string(match.body))) + + lastAcceptedMatch = match + + continue + } + + if overlappedMatch := match.start <= lastAcceptedMatch.end; overlappedMatch { + continue + } + + middle := nameRunes[lastAcceptedMatch.end+1 : match.start] + nameLexems = append(nameLexems, s.breakCasualString(middle)...) + nameLexems = append(nameLexems, s.breakInitialism(string(match.body))) + + lastAcceptedMatch = match + } + + // we have not found any accepted matches + if lastAcceptedMatch == nil { + return s.breakCasualString(nameRunes) + } + + if lastAcceptedMatch.end+1 != len(nameRunes) { + rest := nameRunes[lastAcceptedMatch.end+1:] + nameLexems = append(nameLexems, s.breakCasualString(rest)...) + } + + return nameLexems +} + +func (s *splitter) initialismRuneEqual(a, b rune) bool { + return a == b +} + +func (s *splitter) breakInitialism(original string) nameLexem { + return newInitialismNameLexem(original, original) +} + +func (s *splitter) breakCasualString(str []rune) []nameLexem { + segments := make([]nameLexem, 0) + currentSegment := "" + + addCasualNameLexem := func(original string) { + segments = append(segments, newCasualNameLexem(original)) + } + + addInitialismNameLexem := func(original, match string) { + segments = append(segments, newInitialismNameLexem(original, match)) + } + + addNameLexem := func(original string) { + if s.postSplitInitialismCheck { + for _, initialism := range s.initialisms { + if upper(initialism) == upper(original) { + addInitialismNameLexem(original, initialism) + return + } + } + } + + addCasualNameLexem(original) + } + + for _, rn := range string(str) { + if replace, found := nameReplaceTable[rn]; found { + if currentSegment != "" { + addNameLexem(currentSegment) + currentSegment = "" + } + + if replace != "" { + addNameLexem(replace) + } + + continue + } + + if !unicode.In(rn, unicode.L, unicode.M, unicode.N, unicode.Pc) { + if currentSegment != "" { + addNameLexem(currentSegment) + currentSegment = "" + } + + continue + } + + if unicode.IsUpper(rn) { + if currentSegment != "" { + addNameLexem(currentSegment) + } + currentSegment = "" + } + + currentSegment += string(rn) + } + + if currentSegment != "" { + addNameLexem(currentSegment) + } + + return segments +} diff --git a/vendor/github.com/go-openapi/swag/util.go b/vendor/github.com/go-openapi/swag/util.go new file mode 100644 index 000000000..9eac16afb --- /dev/null +++ b/vendor/github.com/go-openapi/swag/util.go @@ -0,0 +1,385 @@ +// Copyright 2015 go-swagger maintainers +// +// 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 swag + +import ( + "reflect" + "strings" + "unicode" +) + +// commonInitialisms are common acronyms that are kept as whole uppercased words. +var commonInitialisms *indexOfInitialisms + +// initialisms is a slice of sorted initialisms +var initialisms []string + +var isInitialism func(string) bool + +// GoNamePrefixFunc sets an optional rule to prefix go names +// which do not start with a letter. +// +// e.g. to help converting "123" into "{prefix}123" +// +// The default is to prefix with "X" +var GoNamePrefixFunc func(string) string + +func init() { + // Taken from https://github.com/golang/lint/blob/3390df4df2787994aea98de825b964ac7944b817/lint.go#L732-L769 + var configuredInitialisms = map[string]bool{ + "ACL": true, + "API": true, + "ASCII": true, + "CPU": true, + "CSS": true, + "DNS": true, + "EOF": true, + "GUID": true, + "HTML": true, + "HTTPS": true, + "HTTP": true, + "ID": true, + "IP": true, + "IPv4": true, + "IPv6": true, + "JSON": true, + "LHS": true, + "OAI": true, + "QPS": true, + "RAM": true, + "RHS": true, + "RPC": true, + "SLA": true, + "SMTP": true, + "SQL": true, + "SSH": true, + "TCP": true, + "TLS": true, + "TTL": true, + "UDP": true, + "UI": true, + "UID": true, + "UUID": true, + "URI": true, + "URL": true, + "UTF8": true, + "VM": true, + "XML": true, + "XMPP": true, + "XSRF": true, + "XSS": true, + } + + // a thread-safe index of initialisms + commonInitialisms = newIndexOfInitialisms().load(configuredInitialisms) + initialisms = commonInitialisms.sorted() + + // a test function + isInitialism = commonInitialisms.isInitialism +} + +const ( + //collectionFormatComma = "csv" + collectionFormatSpace = "ssv" + collectionFormatTab = "tsv" + collectionFormatPipe = "pipes" + collectionFormatMulti = "multi" +) + +// JoinByFormat joins a string array by a known format (e.g. swagger's collectionFormat attribute): +// ssv: space separated value +// tsv: tab separated value +// pipes: pipe (|) separated value +// csv: comma separated value (default) +func JoinByFormat(data []string, format string) []string { + if len(data) == 0 { + return data + } + var sep string + switch format { + case collectionFormatSpace: + sep = " " + case collectionFormatTab: + sep = "\t" + case collectionFormatPipe: + sep = "|" + case collectionFormatMulti: + return data + default: + sep = "," + } + return []string{strings.Join(data, sep)} +} + +// SplitByFormat splits a string by a known format: +// ssv: space separated value +// tsv: tab separated value +// pipes: pipe (|) separated value +// csv: comma separated value (default) +// +func SplitByFormat(data, format string) []string { + if data == "" { + return nil + } + var sep string + switch format { + case collectionFormatSpace: + sep = " " + case collectionFormatTab: + sep = "\t" + case collectionFormatPipe: + sep = "|" + case collectionFormatMulti: + return nil + default: + sep = "," + } + var result []string + for _, s := range strings.Split(data, sep) { + if ts := strings.TrimSpace(s); ts != "" { + result = append(result, ts) + } + } + return result +} + +type byInitialism []string + +func (s byInitialism) Len() int { + return len(s) +} +func (s byInitialism) Swap(i, j int) { + s[i], s[j] = s[j], s[i] +} +func (s byInitialism) Less(i, j int) bool { + if len(s[i]) != len(s[j]) { + return len(s[i]) < len(s[j]) + } + + return strings.Compare(s[i], s[j]) > 0 +} + +// Removes leading whitespaces +func trim(str string) string { + return strings.Trim(str, " ") +} + +// Shortcut to strings.ToUpper() +func upper(str string) string { + return strings.ToUpper(trim(str)) +} + +// Shortcut to strings.ToLower() +func lower(str string) string { + return strings.ToLower(trim(str)) +} + +// Camelize an uppercased word +func Camelize(word string) (camelized string) { + for pos, ru := range []rune(word) { + if pos > 0 { + camelized += string(unicode.ToLower(ru)) + } else { + camelized += string(unicode.ToUpper(ru)) + } + } + return +} + +// ToFileName lowercases and underscores a go type name +func ToFileName(name string) string { + in := split(name) + out := make([]string, 0, len(in)) + + for _, w := range in { + out = append(out, lower(w)) + } + + return strings.Join(out, "_") +} + +// ToCommandName lowercases and underscores a go type name +func ToCommandName(name string) string { + in := split(name) + out := make([]string, 0, len(in)) + + for _, w := range in { + out = append(out, lower(w)) + } + return strings.Join(out, "-") +} + +// ToHumanNameLower represents a code name as a human series of words +func ToHumanNameLower(name string) string { + in := newSplitter(withPostSplitInitialismCheck).split(name) + out := make([]string, 0, len(in)) + + for _, w := range in { + if !w.IsInitialism() { + out = append(out, lower(w.GetOriginal())) + } else { + out = append(out, w.GetOriginal()) + } + } + + return strings.Join(out, " ") +} + +// ToHumanNameTitle represents a code name as a human series of words with the first letters titleized +func ToHumanNameTitle(name string) string { + in := newSplitter(withPostSplitInitialismCheck).split(name) + + out := make([]string, 0, len(in)) + for _, w := range in { + original := w.GetOriginal() + if !w.IsInitialism() { + out = append(out, Camelize(original)) + } else { + out = append(out, original) + } + } + return strings.Join(out, " ") +} + +// ToJSONName camelcases a name which can be underscored or pascal cased +func ToJSONName(name string) string { + in := split(name) + out := make([]string, 0, len(in)) + + for i, w := range in { + if i == 0 { + out = append(out, lower(w)) + continue + } + out = append(out, Camelize(w)) + } + return strings.Join(out, "") +} + +// ToVarName camelcases a name which can be underscored or pascal cased +func ToVarName(name string) string { + res := ToGoName(name) + if isInitialism(res) { + return lower(res) + } + if len(res) <= 1 { + return lower(res) + } + return lower(res[:1]) + res[1:] +} + +// ToGoName translates a swagger name which can be underscored or camel cased to a name that golint likes +func ToGoName(name string) string { + lexems := newSplitter(withPostSplitInitialismCheck).split(name) + + result := "" + for _, lexem := range lexems { + goName := lexem.GetUnsafeGoName() + + // to support old behavior + if lexem.IsInitialism() { + goName = upper(goName) + } + result += goName + } + + if len(result) > 0 { + // Only prefix with X when the first character isn't an ascii letter + first := []rune(result)[0] + if !unicode.IsLetter(first) || (first > unicode.MaxASCII && !unicode.IsUpper(first)) { + if GoNamePrefixFunc == nil { + return "X" + result + } + result = GoNamePrefixFunc(name) + result + } + first = []rune(result)[0] + if unicode.IsLetter(first) && !unicode.IsUpper(first) { + result = string(append([]rune{unicode.ToUpper(first)}, []rune(result)[1:]...)) + } + } + + return result +} + +// ContainsStrings searches a slice of strings for a case-sensitive match +func ContainsStrings(coll []string, item string) bool { + for _, a := range coll { + if a == item { + return true + } + } + return false +} + +// ContainsStringsCI searches a slice of strings for a case-insensitive match +func ContainsStringsCI(coll []string, item string) bool { + for _, a := range coll { + if strings.EqualFold(a, item) { + return true + } + } + return false +} + +type zeroable interface { + IsZero() bool +} + +// IsZero returns true when the value passed into the function is a zero value. +// This allows for safer checking of interface values. +func IsZero(data interface{}) bool { + // check for things that have an IsZero method instead + if vv, ok := data.(zeroable); ok { + return vv.IsZero() + } + // continue with slightly more complex reflection + v := reflect.ValueOf(data) + switch v.Kind() { + case reflect.String: + return v.Len() == 0 + case reflect.Bool: + return !v.Bool() + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + return v.Int() == 0 + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: + return v.Uint() == 0 + case reflect.Float32, reflect.Float64: + return v.Float() == 0 + case reflect.Interface, reflect.Map, reflect.Ptr, reflect.Slice: + return v.IsNil() + case reflect.Struct, reflect.Array: + return reflect.DeepEqual(data, reflect.Zero(v.Type()).Interface()) + case reflect.Invalid: + return true + } + return false +} + +// AddInitialisms add additional initialisms +func AddInitialisms(words ...string) { + for _, word := range words { + //commonInitialisms[upper(word)] = true + commonInitialisms.add(upper(word)) + } + // sort again + initialisms = commonInitialisms.sorted() +} + +// CommandLineOptionsGroup represents a group of user-defined command line options +type CommandLineOptionsGroup struct { + ShortDescription string + LongDescription string + Options interface{} +} diff --git a/vendor/github.com/go-openapi/swag/yaml.go b/vendor/github.com/go-openapi/swag/yaml.go new file mode 100644 index 000000000..435e2948e --- /dev/null +++ b/vendor/github.com/go-openapi/swag/yaml.go @@ -0,0 +1,227 @@ +// Copyright 2015 go-swagger maintainers +// +// 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 swag + +import ( + "encoding/json" + "fmt" + "path/filepath" + "strconv" + + "github.com/mailru/easyjson/jlexer" + "github.com/mailru/easyjson/jwriter" + yaml "gopkg.in/yaml.v2" +) + +// YAMLMatcher matches yaml +func YAMLMatcher(path string) bool { + ext := filepath.Ext(path) + return ext == ".yaml" || ext == ".yml" +} + +// YAMLToJSON converts YAML unmarshaled data into json compatible data +func YAMLToJSON(data interface{}) (json.RawMessage, error) { + jm, err := transformData(data) + if err != nil { + return nil, err + } + b, err := WriteJSON(jm) + return json.RawMessage(b), err +} + +// BytesToYAMLDoc converts a byte slice into a YAML document +func BytesToYAMLDoc(data []byte) (interface{}, error) { + var canary map[interface{}]interface{} // validate this is an object and not a different type + if err := yaml.Unmarshal(data, &canary); err != nil { + return nil, err + } + + var document yaml.MapSlice // preserve order that is present in the document + if err := yaml.Unmarshal(data, &document); err != nil { + return nil, err + } + return document, nil +} + +// JSONMapSlice represent a JSON object, with the order of keys maintained +type JSONMapSlice []JSONMapItem + +// MarshalJSON renders a JSONMapSlice as JSON +func (s JSONMapSlice) MarshalJSON() ([]byte, error) { + w := &jwriter.Writer{Flags: jwriter.NilMapAsEmpty | jwriter.NilSliceAsEmpty} + s.MarshalEasyJSON(w) + return w.BuildBytes() +} + +// MarshalEasyJSON renders a JSONMapSlice as JSON, using easyJSON +func (s JSONMapSlice) MarshalEasyJSON(w *jwriter.Writer) { + w.RawByte('{') + + ln := len(s) + last := ln - 1 + for i := 0; i < ln; i++ { + s[i].MarshalEasyJSON(w) + if i != last { // last item + w.RawByte(',') + } + } + + w.RawByte('}') +} + +// UnmarshalJSON makes a JSONMapSlice from JSON +func (s *JSONMapSlice) UnmarshalJSON(data []byte) error { + l := jlexer.Lexer{Data: data} + s.UnmarshalEasyJSON(&l) + return l.Error() +} + +// UnmarshalEasyJSON makes a JSONMapSlice from JSON, using easyJSON +func (s *JSONMapSlice) UnmarshalEasyJSON(in *jlexer.Lexer) { + if in.IsNull() { + in.Skip() + return + } + + var result JSONMapSlice + in.Delim('{') + for !in.IsDelim('}') { + var mi JSONMapItem + mi.UnmarshalEasyJSON(in) + result = append(result, mi) + } + *s = result +} + +// JSONMapItem represents the value of a key in a JSON object held by JSONMapSlice +type JSONMapItem struct { + Key string + Value interface{} +} + +// MarshalJSON renders a JSONMapItem as JSON +func (s JSONMapItem) MarshalJSON() ([]byte, error) { + w := &jwriter.Writer{Flags: jwriter.NilMapAsEmpty | jwriter.NilSliceAsEmpty} + s.MarshalEasyJSON(w) + return w.BuildBytes() +} + +// MarshalEasyJSON renders a JSONMapItem as JSON, using easyJSON +func (s JSONMapItem) MarshalEasyJSON(w *jwriter.Writer) { + w.String(s.Key) + w.RawByte(':') + w.Raw(WriteJSON(s.Value)) +} + +// UnmarshalJSON makes a JSONMapItem from JSON +func (s *JSONMapItem) UnmarshalJSON(data []byte) error { + l := jlexer.Lexer{Data: data} + s.UnmarshalEasyJSON(&l) + return l.Error() +} + +// UnmarshalEasyJSON makes a JSONMapItem from JSON, using easyJSON +func (s *JSONMapItem) UnmarshalEasyJSON(in *jlexer.Lexer) { + key := in.UnsafeString() + in.WantColon() + value := in.Interface() + in.WantComma() + s.Key = key + s.Value = value +} + +func transformData(input interface{}) (out interface{}, err error) { + switch in := input.(type) { + case yaml.MapSlice: + + o := make(JSONMapSlice, len(in)) + for i, mi := range in { + var nmi JSONMapItem + switch k := mi.Key.(type) { + case string: + nmi.Key = k + case int: + nmi.Key = strconv.Itoa(k) + default: + return nil, fmt.Errorf("types don't match expect map key string or int got: %T", mi.Key) + } + + v, ert := transformData(mi.Value) + if ert != nil { + return nil, ert + } + nmi.Value = v + o[i] = nmi + } + return o, nil + case map[interface{}]interface{}: + o := make(JSONMapSlice, 0, len(in)) + for ke, va := range in { + var nmi JSONMapItem + switch k := ke.(type) { + case string: + nmi.Key = k + case int: + nmi.Key = strconv.Itoa(k) + default: + return nil, fmt.Errorf("types don't match expect map key string or int got: %T", ke) + } + + v, ert := transformData(va) + if ert != nil { + return nil, ert + } + nmi.Value = v + o = append(o, nmi) + } + return o, nil + case []interface{}: + len1 := len(in) + o := make([]interface{}, len1) + for i := 0; i < len1; i++ { + o[i], err = transformData(in[i]) + if err != nil { + return nil, err + } + } + return o, nil + } + return input, nil +} + +// YAMLDoc loads a yaml document from either http or a file and converts it to json +func YAMLDoc(path string) (json.RawMessage, error) { + yamlDoc, err := YAMLData(path) + if err != nil { + return nil, err + } + + data, err := YAMLToJSON(yamlDoc) + if err != nil { + return nil, err + } + + return data, nil +} + +// YAMLData loads a yaml document from either http or a file +func YAMLData(path string) (interface{}, error) { + data, err := LoadFromFileOrHTTP(path) + if err != nil { + return nil, err + } + + return BytesToYAMLDoc(data) +} diff --git a/vendor/github.com/goodrain/rainbond-operator/pkg/apis/addtoscheme_rainbond_v1alpha1.go b/vendor/github.com/goodrain/rainbond-operator/pkg/apis/addtoscheme_rainbond_v1alpha1.go new file mode 100644 index 000000000..ac8d63f21 --- /dev/null +++ b/vendor/github.com/goodrain/rainbond-operator/pkg/apis/addtoscheme_rainbond_v1alpha1.go @@ -0,0 +1,10 @@ +package apis + +import ( + "github.com/goodrain/rainbond-operator/pkg/apis/rainbond/v1alpha1" +) + +func init() { + // Register the types with the Scheme so the components can map objects to GroupVersionKinds and back + AddToSchemes = append(AddToSchemes, v1alpha1.SchemeBuilder.AddToScheme) +} diff --git a/vendor/github.com/goodrain/rainbond-operator/pkg/apis/apis.go b/vendor/github.com/goodrain/rainbond-operator/pkg/apis/apis.go new file mode 100644 index 000000000..07dc96164 --- /dev/null +++ b/vendor/github.com/goodrain/rainbond-operator/pkg/apis/apis.go @@ -0,0 +1,13 @@ +package apis + +import ( + "k8s.io/apimachinery/pkg/runtime" +) + +// AddToSchemes may be used to add all resources defined in the project to a Scheme +var AddToSchemes runtime.SchemeBuilder + +// AddToScheme adds all Resources to the Scheme +func AddToScheme(s *runtime.Scheme) error { + return AddToSchemes.AddToScheme(s) +} diff --git a/vendor/github.com/goodrain/rainbond-operator/pkg/apis/rainbond/group.go b/vendor/github.com/goodrain/rainbond-operator/pkg/apis/rainbond/group.go new file mode 100644 index 000000000..741c2ce37 --- /dev/null +++ b/vendor/github.com/goodrain/rainbond-operator/pkg/apis/rainbond/group.go @@ -0,0 +1,6 @@ +// Package rainbond contains rainbond API versions. +// +// This file ensures Go source parsers acknowledge the rainbond package +// and any child packages. It can be removed if any other Go source files are +// added to this package. +package rainbond diff --git a/vendor/github.com/goodrain/rainbond-operator/pkg/apis/rainbond/v1alpha1/doc.go b/vendor/github.com/goodrain/rainbond-operator/pkg/apis/rainbond/v1alpha1/doc.go new file mode 100644 index 000000000..e90966196 --- /dev/null +++ b/vendor/github.com/goodrain/rainbond-operator/pkg/apis/rainbond/v1alpha1/doc.go @@ -0,0 +1,4 @@ +// Package v1alpha1 contains API Schema definitions for the rainbond v1alpha1 API group +// +k8s:deepcopy-gen=package,register +// +groupName=rainbond.io +package v1alpha1 diff --git a/vendor/github.com/goodrain/rainbond-operator/pkg/apis/rainbond/v1alpha1/rainbondcluster_types.go b/vendor/github.com/goodrain/rainbond-operator/pkg/apis/rainbond/v1alpha1/rainbondcluster_types.go new file mode 100644 index 000000000..fc7ab6cba --- /dev/null +++ b/vendor/github.com/goodrain/rainbond-operator/pkg/apis/rainbond/v1alpha1/rainbondcluster_types.go @@ -0,0 +1,223 @@ +package v1alpha1 + +import ( + "fmt" + + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +// InstallMode is the mode of Rainbond cluster installation +type InstallMode string + +const ( + // InstallationModeWithPackage means some Rainbond images are from the specified image repository, some are from the installation package. + InstallationModeWithPackage InstallMode = "WithPackage" + // InstallationModeWithoutPackage means all Rainbond images are from the specified image repository, not the installation package. + InstallationModeWithoutPackage InstallMode = "WithoutPackage" + + // LabelNodeRolePrefix is a label prefix for node roles + // It's copied over to here until it's merged in core: https://github.com/kubernetes/kubernetes/pull/39112 + LabelNodeRolePrefix = "node-role.kubernetes.io/" + // NodeLabelRole specifies the role of a node + NodeLabelRole = "kubernetes.io/role" +) + +// ImageHub image hub +type ImageHub struct { + Domain string `json:"domain,omitempty"` + Namespace string `json:"namespace,omitempty"` + Username string `json:"username,omitempty"` + Password string `json:"password,omitempty"` +} + +// Database defines the connection information of database. +type Database struct { + Host string `json:"host,omitempty"` + Port int `json:"port,omitempty"` + Username string `json:"username,omitempty"` + Password string `json:"password,omitempty"` +} + +// EtcdConfig defines the configuration of etcd client. +type EtcdConfig struct { + // Endpoints is a list of URLs. + Endpoints []string `json:"endpoints,omitempty"` + // Whether to use tls to connect to etcd + SecretName string `json:"secretName,omitempty"` +} + +// FstabLine represents a line in file /etc/fstab. +type FstabLine struct { + Device string `json:"fileSystem,omitempty"` + MountPoint string `json:"mountPoint,omitempty"` + Type string `json:"type,omitempty"` + Options string `json:"options,omitempty"` + Dump int `json:"dump,omitempty"` + Pass int `json:"pass,omitempty"` +} + +// RainbondShareStorage - +type RainbondShareStorage struct { + StorageClassName string `json:"storageClassName"` + FstabLine *FstabLine `json:"fstabLine"` +} + +// RainbondClusterSpec defines the desired state of RainbondCluster +type RainbondClusterSpec struct { + // Repository of each Rainbond component image, eg. docker.io/rainbond. + // +optional + RainbondImageRepository string `json:"rainbondImageRepository,omitempty"` + // Suffix of component default domain name + SuffixHTTPHost string `json:"suffixHTTPHost"` + // Ingress IP addresses of rbd-gateway. If not specified, + // the IP of the node where the rbd-gateway is located will be used. + GatewayIngressIPs []string `json:"gatewayIngressIPs,omitempty"` + // Information about the node where the gateway is located. + // If not specified, the gateway will run on nodes where all ports do not conflict. + GatewayNodes []NodeAvailPorts `json:"gatewayNodes,omitempty"` + // InstallMode is the mode of Rainbond cluster installation. + InstallMode InstallMode `json:"installMode,omitempty"` + // User-specified private image repository, replacing goodrain.me. + ImageHub *ImageHub `json:"imageHub,omitempty"` + // the storage class that rainbond component will be used. + // rainbond-operator will create one if StorageClassName is empty + StorageClassName string `json:"storageClassName,omitempty"` + // the region database information that rainbond component will be used. + // rainbond-operator will create one if DBInfo is empty + RegionDatabase *Database `json:"regionDatabase,omitempty"` + // the ui database information that rainbond component will be used. + // rainbond-operator will create one if DBInfo is empty + UIDatabase *Database `json:"uiDatabase,omitempty"` + // the etcd connection information that rainbond component will be used. + // rainbond-operator will create one if EtcdConfig is empty + EtcdConfig *EtcdConfig `json:"etcdConfig,omitempty"` + // define install rainbond version, This is usually image tag + InstallVersion string `json:"installVersion,omitempty"` + RainbondShareStorage RainbondShareStorage `json:"rainbondShareStorage,omitempty"` + // Whether the configuration has been completed + ConfigCompleted bool `json:"configCompleted,omitempty"` + //InstallPackageConfig define install package download config + InstallPackageConfig InstallPackageConfig `json:"installPackageConfig,omitempty"` +} + +//InstallPackageConfig define install package download config +type InstallPackageConfig struct { + URL string `json:"url,omitempty"` + MD5 string `json:"md5,omitempty"` +} + +// NodeAvailPorts node avail port +type NodeAvailPorts struct { + NodeName string `json:"nodeName,omitempty"` + NodeIP string `json:"nodeIP,omitempty"` + Ports []int `json:"ports,omitempty"` +} + +// StorageClass storage class +type StorageClass struct { + Name string `json:"name"` + Provisioner string `json:"provisioner"` +} + +// RainbondClusterStatus defines the observed state of RainbondCluster +type RainbondClusterStatus struct { + // Master node name list + MasterNodeNames []string `json:"nodeNames,omitempty"` + NodeAvailPorts []*NodeAvailPorts `json:"NodeAvailPorts,omitempty"` + // List of existing StorageClasses in the cluster + // +optional + StorageClasses []*StorageClass `json:"storageClasses,omitempty"` + // Destination path of the installation package extraction. + MasterRoleLabel string `json:"masterRoleLabel,omitempty"` +} + +// +genclient +// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object + +// RainbondCluster is the Schema for the rainbondclusters API +// +kubebuilder:subresource:status +// +kubebuilder:resource:path=rainbondclusters,scope=Namespaced +type RainbondCluster struct { + metav1.TypeMeta `json:",inline"` + metav1.ObjectMeta `json:"metadata,omitempty"` + + Spec RainbondClusterSpec `json:"spec,omitempty"` + Status *RainbondClusterStatus `json:"status,omitempty"` +} + +// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object + +// RainbondClusterList contains a list of RainbondCluster +type RainbondClusterList struct { + metav1.TypeMeta `json:",inline"` + metav1.ListMeta `json:"metadata,omitempty"` + Items []RainbondCluster `json:"items"` +} + +func init() { + SchemeBuilder.Register(&RainbondCluster{}, &RainbondClusterList{}) +} + +func (in *RainbondCluster) GatewayIngressIP() string { + if len(in.Spec.GatewayIngressIPs) > 0 && in.Spec.GatewayIngressIPs[0] != "" { + return in.Spec.GatewayIngressIPs[0] + } + if len(in.Spec.GatewayNodes) > 0 { + return in.Spec.GatewayNodes[0].NodeIP + } + if in.Status != nil && len(in.Status.NodeAvailPorts) > 0 { + return in.Status.NodeAvailPorts[0].NodeIP + } + return "" +} + +//GatewayIngressIPs get all gateway ips +func (in *RainbondCluster) GatewayIngressIPs() (ips []string) { + // custom ip ,contain eip + if len(in.Spec.GatewayIngressIPs) > 0 && in.Spec.GatewayIngressIPs[0] != "" { + return in.Spec.GatewayIngressIPs + } + // user select gateway node ip + if len(in.Spec.GatewayNodes) > 0 { + for _, node := range in.Spec.GatewayNodes { + ips = append(ips, node.NodeIP) + } + return + } + // all available gateway node ip + if in.Status != nil && len(in.Status.NodeAvailPorts) > 0 { + for _, node := range in.Status.NodeAvailPorts { + ips = append(ips, node.NodeIP) + } + return + } + return nil +} + +func (in *Database) RegionDataSource() string { + return fmt.Sprintf("--mysql=%s:%s@tcp(%s:%d)/region", in.Username, in.Password, in.Host, in.Port) +} + +func (in *RainbondClusterStatus) MasterNodeLabel() map[string]string { + switch in.MasterRoleLabel { + case LabelNodeRolePrefix + "master": + return map[string]string{ + in.MasterRoleLabel: "", + } + case NodeLabelRole: + return map[string]string{ + NodeLabelRole: "master", + } + } + + return nil +} + +func (in *RainbondClusterStatus) FirstMasterNodeLabel() map[string]string { + if len(in.MasterNodeNames) == 0 { + return nil + } + return map[string]string{ + "kubernetes.io/hostname": in.MasterNodeNames[0], + } +} diff --git a/vendor/github.com/goodrain/rainbond-operator/pkg/apis/rainbond/v1alpha1/rainbondpackage_types.go b/vendor/github.com/goodrain/rainbond-operator/pkg/apis/rainbond/v1alpha1/rainbondpackage_types.go new file mode 100644 index 000000000..6f251b945 --- /dev/null +++ b/vendor/github.com/goodrain/rainbond-operator/pkg/apis/rainbond/v1alpha1/rainbondpackage_types.go @@ -0,0 +1,107 @@ +package v1alpha1 + +import ( + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +// RainbondPackageSpec defines the desired state of RainbondPackage +type RainbondPackageSpec struct { + // The path where the rainbond package is located. + PkgPath string `json:"pkgPath"` +} + +// RainbondPackagePhase is a label for the condition of a rainbondcluster at the current time. +type RainbondPackagePhase string + +//PackageConditionType PackageConditionType +type PackageConditionType string + +// These are valid conditions of package. +const ( + // PackageConditionType means this package handle status + Init PackageConditionType = "Init" + DownloadPackage PackageConditionType = "DownloadPackage" + UnpackPackage PackageConditionType = "UnpackPackage" + PushImage PackageConditionType = "PushImage" + Ready PackageConditionType = "Ready" +) + +//PackageConditionStatus condition status +type PackageConditionStatus string + +const ( + //Waiting waiting + Waiting PackageConditionStatus = "Waiting" + //Running Running + Running PackageConditionStatus = "Running" + //Completed Completed + Completed PackageConditionStatus = "Completed" + //Failed Failed + Failed PackageConditionStatus = "Failed" +) + +// PackageCondition contains condition information for package. +type PackageCondition struct { + // Type of package condition. + Type PackageConditionType `json:"type" ` + // Status of the condition, one of True, False, Unknown. + Status PackageConditionStatus `json:"status" ` + // Last time we got an update on a given condition. + // +optional + LastHeartbeatTime metav1.Time `json:"lastHeartbeatTime,omitempty" ` + // Last time the condition transit from one status to another. + // +optional + LastTransitionTime metav1.Time `json:"lastTransitionTime,omitempty" ` + // (brief) reason for the condition's last transition. + // +optional + Reason string `json:"reason,omitempty"` + // Human readable message indicating details about last transition. + // +optional + Message string `json:"message,omitempty"` + // The progress of the condition + // +optional + Progress int `json:"progress,omitempty"` +} + +//RainbondPackageImage image +type RainbondPackageImage struct { + //Name image name + Name string `json:"name,omitempty"` +} + +// RainbondPackageStatus defines the observed state of RainbondPackage +type RainbondPackageStatus struct { + //worker and master maintenance + Conditions []PackageCondition `json:"conditions,omitempty"` + // The number of images that should be load and pushed. + ImagesNumber int32 `json:"imagesNumber"` + // ImagesPushed contains the images have been pushed. + ImagesPushed []RainbondPackageImage `json:"images,omitempty"` +} + +// +genclient +// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object + +// RainbondPackage is the Schema for the rainbondpackages API +// +kubebuilder:subresource:status +// +kubebuilder:resource:path=rainbondpackages,scope=Namespaced +type RainbondPackage struct { + metav1.TypeMeta `json:",inline"` + metav1.ObjectMeta `json:"metadata,omitempty"` + + Spec RainbondPackageSpec `json:"spec,omitempty"` + Status *RainbondPackageStatus `json:"status,omitempty"` +} + +// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object + +// RainbondPackageList contains a list of RainbondPackage +type RainbondPackageList struct { + metav1.TypeMeta `json:",inline"` + metav1.ListMeta `json:"metadata,omitempty"` + Items []RainbondPackage `json:"items"` +} + +func init() { + SchemeBuilder.Register(&RainbondPackage{}, &RainbondPackageList{}) +} diff --git a/vendor/github.com/goodrain/rainbond-operator/pkg/apis/rainbond/v1alpha1/rbdcomponent_types.go b/vendor/github.com/goodrain/rainbond-operator/pkg/apis/rainbond/v1alpha1/rbdcomponent_types.go new file mode 100644 index 000000000..4d7551c23 --- /dev/null +++ b/vendor/github.com/goodrain/rainbond-operator/pkg/apis/rainbond/v1alpha1/rbdcomponent_types.go @@ -0,0 +1,139 @@ +package v1alpha1 + +import ( + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +// LogLevel - +type LogLevel string + +const ( + // LogLevelDebug - + LogLevelDebug LogLevel = "debug" + // LogLevelInfo - + LogLevelInfo LogLevel = "info" + // LogLevelWarning - + LogLevelWarning LogLevel = "warning" + // LogLevelError - + LogLevelError LogLevel = "error" +) + +//ParseLogLevel parse log level +func ParseLogLevel(l string) LogLevel { + switch l { + case "debug": + return LogLevelDebug + case "info": + return LogLevelInfo + case "warning": + return LogLevelWarning + case "error": + return LogLevelError + default: + return LogLevelInfo + } +} + +// RbdComponentSpec defines the desired state of RbdComponent +type RbdComponentSpec struct { + // Number of desired pods. This is a pointer to distinguish between explicit + // zero and not specified. Defaults to 1. + // +optional + Replicas *int32 `json:"replicas,omitempty"` + // type of rainbond component + Type string `json:"type,omitempty"` + // version of rainbond component + Version string `json:"version,omitempty"` + LogLevel LogLevel `json:"logLevel,omitempty"` + // Docker image name. + Image string `json:"image,omitempty"` + // Image pull policy. + // One of Always, Never, IfNotPresent. + // Defaults to Always if :latest tag is specified, or IfNotPresent otherwise. + // Cannot be updated. + ImagePullPolicy corev1.PullPolicy `json:"imagePullPolicy,omitempty"` + // component config map + Configs map[string]string `json:"configs,omitempty"` + PackagePath string `json:"packagePath,omitempty"` + // Whether this component needs to be created first + PriorityComponent bool `json:"priorityComponent"` +} + +// ControllerType - +type ControllerType string + +const ( + // ControllerTypeDeployment - + ControllerTypeDeployment ControllerType = "deployment" + // ControllerTypeDaemonSet - + ControllerTypeDaemonSet ControllerType = "daemonset" + // ControllerTypeStatefulSet - + ControllerTypeStatefulSet ControllerType = "statefuleset" + // ControllerTypeUnknown - + ControllerTypeUnknown ControllerType = "unknown" +) + +func (c ControllerType) String() string { + return string(c) +} + +// RbdComponentStatus defines the observed state of RbdComponent +type RbdComponentStatus struct { + // Type of Controller owned by RbdComponent + ControllerType ControllerType `json:"controllerType"` + // ControllerName represents the Controller associated with RbdComponent + // The controller could be Deployment, StatefulSet or DaemonSet + ControllerName string `json:"controllerName"` + Reason string `json:"reason"` + Message string `json:"message"` +} + +// +genclient +// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object + +// RbdComponent is the Schema for the rbdcomponents API +// +kubebuilder:subresource:status +// +kubebuilder:resource:path=rbdcomponents,scope=Namespaced +type RbdComponent struct { + metav1.TypeMeta `json:",inline"` + metav1.ObjectMeta `json:"metadata,omitempty"` + + Spec RbdComponentSpec `json:"spec,omitempty"` + Status *RbdComponentStatus `json:"status,omitempty"` +} + +// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object + +// RbdComponentList contains a list of RbdComponent +type RbdComponentList struct { + metav1.TypeMeta `json:",inline"` + metav1.ListMeta `json:"metadata,omitempty"` + Items []RbdComponent `json:"items"` +} + +func init() { + SchemeBuilder.Register(&RbdComponent{}, &RbdComponentList{}) +} + +func (in *RbdComponent) GetLabels() map[string]string { + return map[string]string{ + "creator": "Rainbond", + "belongTo": "RainbondOperator", + "name": in.Name, + } +} + +func (in *RbdComponent) ImagePullPolicy() corev1.PullPolicy { + if in.Spec.ImagePullPolicy == "" { + return corev1.PullAlways + } + return in.Spec.ImagePullPolicy +} + +func (in *RbdComponent) LogLevel() LogLevel { + if in.Spec.LogLevel == "" { + return LogLevelInfo + } + return in.Spec.LogLevel +} diff --git a/vendor/github.com/goodrain/rainbond-operator/pkg/apis/rainbond/v1alpha1/register.go b/vendor/github.com/goodrain/rainbond-operator/pkg/apis/rainbond/v1alpha1/register.go new file mode 100644 index 000000000..64ddbd005 --- /dev/null +++ b/vendor/github.com/goodrain/rainbond-operator/pkg/apis/rainbond/v1alpha1/register.go @@ -0,0 +1,25 @@ +// NOTE: Boilerplate only. Ignore this file. + +// Package v1alpha1 contains API Schema definitions for the rainbond v1alpha1 API group +// +k8s:deepcopy-gen=package,register +// +groupName=rainbond.io +package v1alpha1 + +import ( + "k8s.io/apimachinery/pkg/runtime/schema" + "sigs.k8s.io/controller-runtime/pkg/scheme" +) + +var ( + // SchemeGroupVersion is group version used to register these objects + SchemeGroupVersion = schema.GroupVersion{Group: "rainbond.io", Version: "v1alpha1"} + + // SchemeBuilder is used to add go types to the GroupVersionKind scheme + SchemeBuilder = &scheme.Builder{GroupVersion: SchemeGroupVersion} + AddToScheme = SchemeBuilder.AddToScheme +) + +// Resource gets an EtcdCluster GroupResource for a specified resource +func Resource(resource string) schema.GroupResource { + return SchemeGroupVersion.WithResource(resource).GroupResource() +} diff --git a/vendor/github.com/goodrain/rainbond-operator/pkg/apis/rainbond/v1alpha1/zz_generated.deepcopy.go b/vendor/github.com/goodrain/rainbond-operator/pkg/apis/rainbond/v1alpha1/zz_generated.deepcopy.go new file mode 100644 index 000000000..15a508f58 --- /dev/null +++ b/vendor/github.com/goodrain/rainbond-operator/pkg/apis/rainbond/v1alpha1/zz_generated.deepcopy.go @@ -0,0 +1,580 @@ +// +build !ignore_autogenerated + +// RAINBOND, Application Management Platform +// Copyright (C) 2014-2020 Goodrain Co., Ltd. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. For any non-GPL usage of Rainbond, +// one or multiple Commercial Licenses authorized by Goodrain Co., Ltd. +// must be obtained first. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +// Code generated by deepcopy-gen. DO NOT EDIT. + +package v1alpha1 + +import ( + runtime "k8s.io/apimachinery/pkg/runtime" +) + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *Database) DeepCopyInto(out *Database) { + *out = *in + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Database. +func (in *Database) DeepCopy() *Database { + if in == nil { + return nil + } + out := new(Database) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *EtcdConfig) DeepCopyInto(out *EtcdConfig) { + *out = *in + if in.Endpoints != nil { + in, out := &in.Endpoints, &out.Endpoints + *out = make([]string, len(*in)) + copy(*out, *in) + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new EtcdConfig. +func (in *EtcdConfig) DeepCopy() *EtcdConfig { + if in == nil { + return nil + } + out := new(EtcdConfig) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *FstabLine) DeepCopyInto(out *FstabLine) { + *out = *in + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new FstabLine. +func (in *FstabLine) DeepCopy() *FstabLine { + if in == nil { + return nil + } + out := new(FstabLine) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ImageHub) DeepCopyInto(out *ImageHub) { + *out = *in + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ImageHub. +func (in *ImageHub) DeepCopy() *ImageHub { + if in == nil { + return nil + } + out := new(ImageHub) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *InstallPackageConfig) DeepCopyInto(out *InstallPackageConfig) { + *out = *in + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new InstallPackageConfig. +func (in *InstallPackageConfig) DeepCopy() *InstallPackageConfig { + if in == nil { + return nil + } + out := new(InstallPackageConfig) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *NodeAvailPorts) DeepCopyInto(out *NodeAvailPorts) { + *out = *in + if in.Ports != nil { + in, out := &in.Ports, &out.Ports + *out = make([]int, len(*in)) + copy(*out, *in) + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new NodeAvailPorts. +func (in *NodeAvailPorts) DeepCopy() *NodeAvailPorts { + if in == nil { + return nil + } + out := new(NodeAvailPorts) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *PackageCondition) DeepCopyInto(out *PackageCondition) { + *out = *in + in.LastHeartbeatTime.DeepCopyInto(&out.LastHeartbeatTime) + in.LastTransitionTime.DeepCopyInto(&out.LastTransitionTime) + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PackageCondition. +func (in *PackageCondition) DeepCopy() *PackageCondition { + if in == nil { + return nil + } + out := new(PackageCondition) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *RainbondCluster) DeepCopyInto(out *RainbondCluster) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) + in.Spec.DeepCopyInto(&out.Spec) + if in.Status != nil { + in, out := &in.Status, &out.Status + *out = new(RainbondClusterStatus) + (*in).DeepCopyInto(*out) + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new RainbondCluster. +func (in *RainbondCluster) DeepCopy() *RainbondCluster { + if in == nil { + return nil + } + out := new(RainbondCluster) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *RainbondCluster) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *RainbondClusterList) DeepCopyInto(out *RainbondClusterList) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ListMeta.DeepCopyInto(&out.ListMeta) + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]RainbondCluster, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new RainbondClusterList. +func (in *RainbondClusterList) DeepCopy() *RainbondClusterList { + if in == nil { + return nil + } + out := new(RainbondClusterList) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *RainbondClusterList) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *RainbondClusterSpec) DeepCopyInto(out *RainbondClusterSpec) { + *out = *in + if in.GatewayIngressIPs != nil { + in, out := &in.GatewayIngressIPs, &out.GatewayIngressIPs + *out = make([]string, len(*in)) + copy(*out, *in) + } + if in.GatewayNodes != nil { + in, out := &in.GatewayNodes, &out.GatewayNodes + *out = make([]NodeAvailPorts, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + if in.ImageHub != nil { + in, out := &in.ImageHub, &out.ImageHub + *out = new(ImageHub) + **out = **in + } + if in.RegionDatabase != nil { + in, out := &in.RegionDatabase, &out.RegionDatabase + *out = new(Database) + **out = **in + } + if in.UIDatabase != nil { + in, out := &in.UIDatabase, &out.UIDatabase + *out = new(Database) + **out = **in + } + if in.EtcdConfig != nil { + in, out := &in.EtcdConfig, &out.EtcdConfig + *out = new(EtcdConfig) + (*in).DeepCopyInto(*out) + } + in.RainbondShareStorage.DeepCopyInto(&out.RainbondShareStorage) + out.InstallPackageConfig = in.InstallPackageConfig + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new RainbondClusterSpec. +func (in *RainbondClusterSpec) DeepCopy() *RainbondClusterSpec { + if in == nil { + return nil + } + out := new(RainbondClusterSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *RainbondClusterStatus) DeepCopyInto(out *RainbondClusterStatus) { + *out = *in + if in.MasterNodeNames != nil { + in, out := &in.MasterNodeNames, &out.MasterNodeNames + *out = make([]string, len(*in)) + copy(*out, *in) + } + if in.NodeAvailPorts != nil { + in, out := &in.NodeAvailPorts, &out.NodeAvailPorts + *out = make([]*NodeAvailPorts, len(*in)) + for i := range *in { + if (*in)[i] != nil { + in, out := &(*in)[i], &(*out)[i] + *out = new(NodeAvailPorts) + (*in).DeepCopyInto(*out) + } + } + } + if in.StorageClasses != nil { + in, out := &in.StorageClasses, &out.StorageClasses + *out = make([]*StorageClass, len(*in)) + for i := range *in { + if (*in)[i] != nil { + in, out := &(*in)[i], &(*out)[i] + *out = new(StorageClass) + **out = **in + } + } + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new RainbondClusterStatus. +func (in *RainbondClusterStatus) DeepCopy() *RainbondClusterStatus { + if in == nil { + return nil + } + out := new(RainbondClusterStatus) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *RainbondPackage) DeepCopyInto(out *RainbondPackage) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) + out.Spec = in.Spec + if in.Status != nil { + in, out := &in.Status, &out.Status + *out = new(RainbondPackageStatus) + (*in).DeepCopyInto(*out) + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new RainbondPackage. +func (in *RainbondPackage) DeepCopy() *RainbondPackage { + if in == nil { + return nil + } + out := new(RainbondPackage) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *RainbondPackage) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *RainbondPackageImage) DeepCopyInto(out *RainbondPackageImage) { + *out = *in + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new RainbondPackageImage. +func (in *RainbondPackageImage) DeepCopy() *RainbondPackageImage { + if in == nil { + return nil + } + out := new(RainbondPackageImage) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *RainbondPackageList) DeepCopyInto(out *RainbondPackageList) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ListMeta.DeepCopyInto(&out.ListMeta) + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]RainbondPackage, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new RainbondPackageList. +func (in *RainbondPackageList) DeepCopy() *RainbondPackageList { + if in == nil { + return nil + } + out := new(RainbondPackageList) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *RainbondPackageList) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *RainbondPackageSpec) DeepCopyInto(out *RainbondPackageSpec) { + *out = *in + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new RainbondPackageSpec. +func (in *RainbondPackageSpec) DeepCopy() *RainbondPackageSpec { + if in == nil { + return nil + } + out := new(RainbondPackageSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *RainbondPackageStatus) DeepCopyInto(out *RainbondPackageStatus) { + *out = *in + if in.Conditions != nil { + in, out := &in.Conditions, &out.Conditions + *out = make([]PackageCondition, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + if in.ImagesPushed != nil { + in, out := &in.ImagesPushed, &out.ImagesPushed + *out = make([]RainbondPackageImage, len(*in)) + copy(*out, *in) + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new RainbondPackageStatus. +func (in *RainbondPackageStatus) DeepCopy() *RainbondPackageStatus { + if in == nil { + return nil + } + out := new(RainbondPackageStatus) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *RainbondShareStorage) DeepCopyInto(out *RainbondShareStorage) { + *out = *in + if in.FstabLine != nil { + in, out := &in.FstabLine, &out.FstabLine + *out = new(FstabLine) + **out = **in + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new RainbondShareStorage. +func (in *RainbondShareStorage) DeepCopy() *RainbondShareStorage { + if in == nil { + return nil + } + out := new(RainbondShareStorage) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *RbdComponent) DeepCopyInto(out *RbdComponent) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) + in.Spec.DeepCopyInto(&out.Spec) + if in.Status != nil { + in, out := &in.Status, &out.Status + *out = new(RbdComponentStatus) + **out = **in + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new RbdComponent. +func (in *RbdComponent) DeepCopy() *RbdComponent { + if in == nil { + return nil + } + out := new(RbdComponent) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *RbdComponent) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *RbdComponentList) DeepCopyInto(out *RbdComponentList) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ListMeta.DeepCopyInto(&out.ListMeta) + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]RbdComponent, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new RbdComponentList. +func (in *RbdComponentList) DeepCopy() *RbdComponentList { + if in == nil { + return nil + } + out := new(RbdComponentList) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *RbdComponentList) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *RbdComponentSpec) DeepCopyInto(out *RbdComponentSpec) { + *out = *in + if in.Replicas != nil { + in, out := &in.Replicas, &out.Replicas + *out = new(int32) + **out = **in + } + if in.Configs != nil { + in, out := &in.Configs, &out.Configs + *out = make(map[string]string, len(*in)) + for key, val := range *in { + (*out)[key] = val + } + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new RbdComponentSpec. +func (in *RbdComponentSpec) DeepCopy() *RbdComponentSpec { + if in == nil { + return nil + } + out := new(RbdComponentSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *RbdComponentStatus) DeepCopyInto(out *RbdComponentStatus) { + *out = *in + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new RbdComponentStatus. +func (in *RbdComponentStatus) DeepCopy() *RbdComponentStatus { + if in == nil { + return nil + } + out := new(RbdComponentStatus) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *StorageClass) DeepCopyInto(out *StorageClass) { + *out = *in + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new StorageClass. +func (in *StorageClass) DeepCopy() *StorageClass { + if in == nil { + return nil + } + out := new(StorageClass) + in.DeepCopyInto(out) + return out +} diff --git a/vendor/github.com/goodrain/rainbond-operator/pkg/apis/rainbond/v1alpha1/zz_generated.openapi.go b/vendor/github.com/goodrain/rainbond-operator/pkg/apis/rainbond/v1alpha1/zz_generated.openapi.go new file mode 100644 index 000000000..563022dc6 --- /dev/null +++ b/vendor/github.com/goodrain/rainbond-operator/pkg/apis/rainbond/v1alpha1/zz_generated.openapi.go @@ -0,0 +1,13 @@ +// +build !ignore_autogenerated + +// This file was autogenerated by openapi-gen. Do not edit it manually! + +package v1alpha1 + +import ( + common "k8s.io/kube-openapi/pkg/common" +) + +func GetOpenAPIDefinitions(ref common.ReferenceCallback) map[string]common.OpenAPIDefinition { + return map[string]common.OpenAPIDefinition{} +} diff --git a/vendor/github.com/mailru/easyjson/LICENSE b/vendor/github.com/mailru/easyjson/LICENSE new file mode 100644 index 000000000..fbff658f7 --- /dev/null +++ b/vendor/github.com/mailru/easyjson/LICENSE @@ -0,0 +1,7 @@ +Copyright (c) 2016 Mail.Ru Group + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/vendor/github.com/mailru/easyjson/buffer/pool.go b/vendor/github.com/mailru/easyjson/buffer/pool.go new file mode 100644 index 000000000..07fb4bc1f --- /dev/null +++ b/vendor/github.com/mailru/easyjson/buffer/pool.go @@ -0,0 +1,270 @@ +// Package buffer implements a buffer for serialization, consisting of a chain of []byte-s to +// reduce copying and to allow reuse of individual chunks. +package buffer + +import ( + "io" + "sync" +) + +// PoolConfig contains configuration for the allocation and reuse strategy. +type PoolConfig struct { + StartSize int // Minimum chunk size that is allocated. + PooledSize int // Minimum chunk size that is reused, reusing chunks too small will result in overhead. + MaxSize int // Maximum chunk size that will be allocated. +} + +var config = PoolConfig{ + StartSize: 128, + PooledSize: 512, + MaxSize: 32768, +} + +// Reuse pool: chunk size -> pool. +var buffers = map[int]*sync.Pool{} + +func initBuffers() { + for l := config.PooledSize; l <= config.MaxSize; l *= 2 { + buffers[l] = new(sync.Pool) + } +} + +func init() { + initBuffers() +} + +// Init sets up a non-default pooling and allocation strategy. Should be run before serialization is done. +func Init(cfg PoolConfig) { + config = cfg + initBuffers() +} + +// putBuf puts a chunk to reuse pool if it can be reused. +func putBuf(buf []byte) { + size := cap(buf) + if size < config.PooledSize { + return + } + if c := buffers[size]; c != nil { + c.Put(buf[:0]) + } +} + +// getBuf gets a chunk from reuse pool or creates a new one if reuse failed. +func getBuf(size int) []byte { + if size < config.PooledSize { + return make([]byte, 0, size) + } + + if c := buffers[size]; c != nil { + v := c.Get() + if v != nil { + return v.([]byte) + } + } + return make([]byte, 0, size) +} + +// Buffer is a buffer optimized for serialization without extra copying. +type Buffer struct { + + // Buf is the current chunk that can be used for serialization. + Buf []byte + + toPool []byte + bufs [][]byte +} + +// EnsureSpace makes sure that the current chunk contains at least s free bytes, +// possibly creating a new chunk. +func (b *Buffer) EnsureSpace(s int) { + if cap(b.Buf)-len(b.Buf) >= s { + return + } + l := len(b.Buf) + if l > 0 { + if cap(b.toPool) != cap(b.Buf) { + // Chunk was reallocated, toPool can be pooled. + putBuf(b.toPool) + } + if cap(b.bufs) == 0 { + b.bufs = make([][]byte, 0, 8) + } + b.bufs = append(b.bufs, b.Buf) + l = cap(b.toPool) * 2 + } else { + l = config.StartSize + } + + if l > config.MaxSize { + l = config.MaxSize + } + b.Buf = getBuf(l) + b.toPool = b.Buf +} + +// AppendByte appends a single byte to buffer. +func (b *Buffer) AppendByte(data byte) { + if cap(b.Buf) == len(b.Buf) { // EnsureSpace won't be inlined. + b.EnsureSpace(1) + } + b.Buf = append(b.Buf, data) +} + +// AppendBytes appends a byte slice to buffer. +func (b *Buffer) AppendBytes(data []byte) { + for len(data) > 0 { + if cap(b.Buf) == len(b.Buf) { // EnsureSpace won't be inlined. + b.EnsureSpace(1) + } + + sz := cap(b.Buf) - len(b.Buf) + if sz > len(data) { + sz = len(data) + } + + b.Buf = append(b.Buf, data[:sz]...) + data = data[sz:] + } +} + +// AppendBytes appends a string to buffer. +func (b *Buffer) AppendString(data string) { + for len(data) > 0 { + if cap(b.Buf) == len(b.Buf) { // EnsureSpace won't be inlined. + b.EnsureSpace(1) + } + + sz := cap(b.Buf) - len(b.Buf) + if sz > len(data) { + sz = len(data) + } + + b.Buf = append(b.Buf, data[:sz]...) + data = data[sz:] + } +} + +// Size computes the size of a buffer by adding sizes of every chunk. +func (b *Buffer) Size() int { + size := len(b.Buf) + for _, buf := range b.bufs { + size += len(buf) + } + return size +} + +// DumpTo outputs the contents of a buffer to a writer and resets the buffer. +func (b *Buffer) DumpTo(w io.Writer) (written int, err error) { + var n int + for _, buf := range b.bufs { + if err == nil { + n, err = w.Write(buf) + written += n + } + putBuf(buf) + } + + if err == nil { + n, err = w.Write(b.Buf) + written += n + } + putBuf(b.toPool) + + b.bufs = nil + b.Buf = nil + b.toPool = nil + + return +} + +// BuildBytes creates a single byte slice with all the contents of the buffer. Data is +// copied if it does not fit in a single chunk. You can optionally provide one byte +// slice as argument that it will try to reuse. +func (b *Buffer) BuildBytes(reuse ...[]byte) []byte { + if len(b.bufs) == 0 { + ret := b.Buf + b.toPool = nil + b.Buf = nil + return ret + } + + var ret []byte + size := b.Size() + + // If we got a buffer as argument and it is big enought, reuse it. + if len(reuse) == 1 && cap(reuse[0]) >= size { + ret = reuse[0][:0] + } else { + ret = make([]byte, 0, size) + } + for _, buf := range b.bufs { + ret = append(ret, buf...) + putBuf(buf) + } + + ret = append(ret, b.Buf...) + putBuf(b.toPool) + + b.bufs = nil + b.toPool = nil + b.Buf = nil + + return ret +} + +type readCloser struct { + offset int + bufs [][]byte +} + +func (r *readCloser) Read(p []byte) (n int, err error) { + for _, buf := range r.bufs { + // Copy as much as we can. + x := copy(p[n:], buf[r.offset:]) + n += x // Increment how much we filled. + + // Did we empty the whole buffer? + if r.offset+x == len(buf) { + // On to the next buffer. + r.offset = 0 + r.bufs = r.bufs[1:] + + // We can release this buffer. + putBuf(buf) + } else { + r.offset += x + } + + if n == len(p) { + break + } + } + // No buffers left or nothing read? + if len(r.bufs) == 0 { + err = io.EOF + } + return +} + +func (r *readCloser) Close() error { + // Release all remaining buffers. + for _, buf := range r.bufs { + putBuf(buf) + } + // In case Close gets called multiple times. + r.bufs = nil + + return nil +} + +// ReadCloser creates an io.ReadCloser with all the contents of the buffer. +func (b *Buffer) ReadCloser() io.ReadCloser { + ret := &readCloser{0, append(b.bufs, b.Buf)} + + b.bufs = nil + b.toPool = nil + b.Buf = nil + + return ret +} diff --git a/vendor/github.com/mailru/easyjson/jlexer/bytestostr.go b/vendor/github.com/mailru/easyjson/jlexer/bytestostr.go new file mode 100644 index 000000000..ff7b27c5b --- /dev/null +++ b/vendor/github.com/mailru/easyjson/jlexer/bytestostr.go @@ -0,0 +1,24 @@ +// This file will only be included to the build if neither +// easyjson_nounsafe nor appengine build tag is set. See README notes +// for more details. + +//+build !easyjson_nounsafe +//+build !appengine + +package jlexer + +import ( + "reflect" + "unsafe" +) + +// bytesToStr creates a string pointing at the slice to avoid copying. +// +// Warning: the string returned by the function should be used with care, as the whole input data +// chunk may be either blocked from being freed by GC because of a single string or the buffer.Data +// may be garbage-collected even when the string exists. +func bytesToStr(data []byte) string { + h := (*reflect.SliceHeader)(unsafe.Pointer(&data)) + shdr := reflect.StringHeader{Data: h.Data, Len: h.Len} + return *(*string)(unsafe.Pointer(&shdr)) +} diff --git a/vendor/github.com/mailru/easyjson/jlexer/bytestostr_nounsafe.go b/vendor/github.com/mailru/easyjson/jlexer/bytestostr_nounsafe.go new file mode 100644 index 000000000..864d1be67 --- /dev/null +++ b/vendor/github.com/mailru/easyjson/jlexer/bytestostr_nounsafe.go @@ -0,0 +1,13 @@ +// This file is included to the build if any of the buildtags below +// are defined. Refer to README notes for more details. + +//+build easyjson_nounsafe appengine + +package jlexer + +// bytesToStr creates a string normally from []byte +// +// Note that this method is roughly 1.5x slower than using the 'unsafe' method. +func bytesToStr(data []byte) string { + return string(data) +} diff --git a/vendor/github.com/mailru/easyjson/jlexer/error.go b/vendor/github.com/mailru/easyjson/jlexer/error.go new file mode 100644 index 000000000..e90ec40d0 --- /dev/null +++ b/vendor/github.com/mailru/easyjson/jlexer/error.go @@ -0,0 +1,15 @@ +package jlexer + +import "fmt" + +// LexerError implements the error interface and represents all possible errors that can be +// generated during parsing the JSON data. +type LexerError struct { + Reason string + Offset int + Data string +} + +func (l *LexerError) Error() string { + return fmt.Sprintf("parse error: %s near offset %d of '%s'", l.Reason, l.Offset, l.Data) +} diff --git a/vendor/github.com/mailru/easyjson/jlexer/lexer.go b/vendor/github.com/mailru/easyjson/jlexer/lexer.go new file mode 100644 index 000000000..ddd376b84 --- /dev/null +++ b/vendor/github.com/mailru/easyjson/jlexer/lexer.go @@ -0,0 +1,1182 @@ +// Package jlexer contains a JSON lexer implementation. +// +// It is expected that it is mostly used with generated parser code, so the interface is tuned +// for a parser that knows what kind of data is expected. +package jlexer + +import ( + "encoding/base64" + "encoding/json" + "errors" + "fmt" + "io" + "strconv" + "unicode" + "unicode/utf16" + "unicode/utf8" +) + +// tokenKind determines type of a token. +type tokenKind byte + +const ( + tokenUndef tokenKind = iota // No token. + tokenDelim // Delimiter: one of '{', '}', '[' or ']'. + tokenString // A string literal, e.g. "abc\u1234" + tokenNumber // Number literal, e.g. 1.5e5 + tokenBool // Boolean literal: true or false. + tokenNull // null keyword. +) + +// token describes a single token: type, position in the input and value. +type token struct { + kind tokenKind // Type of a token. + + boolValue bool // Value if a boolean literal token. + byteValue []byte // Raw value of a token. + delimValue byte +} + +// Lexer is a JSON lexer: it iterates over JSON tokens in a byte slice. +type Lexer struct { + Data []byte // Input data given to the lexer. + + start int // Start of the current token. + pos int // Current unscanned position in the input stream. + token token // Last scanned token, if token.kind != tokenUndef. + + firstElement bool // Whether current element is the first in array or an object. + wantSep byte // A comma or a colon character, which need to occur before a token. + + UseMultipleErrors bool // If we want to use multiple errors. + fatalError error // Fatal error occurred during lexing. It is usually a syntax error. + multipleErrors []*LexerError // Semantic errors occurred during lexing. Marshalling will be continued after finding this errors. +} + +// FetchToken scans the input for the next token. +func (r *Lexer) FetchToken() { + r.token.kind = tokenUndef + r.start = r.pos + + // Check if r.Data has r.pos element + // If it doesn't, it mean corrupted input data + if len(r.Data) < r.pos { + r.errParse("Unexpected end of data") + return + } + // Determine the type of a token by skipping whitespace and reading the + // first character. + for _, c := range r.Data[r.pos:] { + switch c { + case ':', ',': + if r.wantSep == c { + r.pos++ + r.start++ + r.wantSep = 0 + } else { + r.errSyntax() + } + + case ' ', '\t', '\r', '\n': + r.pos++ + r.start++ + + case '"': + if r.wantSep != 0 { + r.errSyntax() + } + + r.token.kind = tokenString + r.fetchString() + return + + case '{', '[': + if r.wantSep != 0 { + r.errSyntax() + } + r.firstElement = true + r.token.kind = tokenDelim + r.token.delimValue = r.Data[r.pos] + r.pos++ + return + + case '}', ']': + if !r.firstElement && (r.wantSep != ',') { + r.errSyntax() + } + r.wantSep = 0 + r.token.kind = tokenDelim + r.token.delimValue = r.Data[r.pos] + r.pos++ + return + + case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '-': + if r.wantSep != 0 { + r.errSyntax() + } + r.token.kind = tokenNumber + r.fetchNumber() + return + + case 'n': + if r.wantSep != 0 { + r.errSyntax() + } + + r.token.kind = tokenNull + r.fetchNull() + return + + case 't': + if r.wantSep != 0 { + r.errSyntax() + } + + r.token.kind = tokenBool + r.token.boolValue = true + r.fetchTrue() + return + + case 'f': + if r.wantSep != 0 { + r.errSyntax() + } + + r.token.kind = tokenBool + r.token.boolValue = false + r.fetchFalse() + return + + default: + r.errSyntax() + return + } + } + r.fatalError = io.EOF + return +} + +// isTokenEnd returns true if the char can follow a non-delimiter token +func isTokenEnd(c byte) bool { + return c == ' ' || c == '\t' || c == '\r' || c == '\n' || c == '[' || c == ']' || c == '{' || c == '}' || c == ',' || c == ':' +} + +// fetchNull fetches and checks remaining bytes of null keyword. +func (r *Lexer) fetchNull() { + r.pos += 4 + if r.pos > len(r.Data) || + r.Data[r.pos-3] != 'u' || + r.Data[r.pos-2] != 'l' || + r.Data[r.pos-1] != 'l' || + (r.pos != len(r.Data) && !isTokenEnd(r.Data[r.pos])) { + + r.pos -= 4 + r.errSyntax() + } +} + +// fetchTrue fetches and checks remaining bytes of true keyword. +func (r *Lexer) fetchTrue() { + r.pos += 4 + if r.pos > len(r.Data) || + r.Data[r.pos-3] != 'r' || + r.Data[r.pos-2] != 'u' || + r.Data[r.pos-1] != 'e' || + (r.pos != len(r.Data) && !isTokenEnd(r.Data[r.pos])) { + + r.pos -= 4 + r.errSyntax() + } +} + +// fetchFalse fetches and checks remaining bytes of false keyword. +func (r *Lexer) fetchFalse() { + r.pos += 5 + if r.pos > len(r.Data) || + r.Data[r.pos-4] != 'a' || + r.Data[r.pos-3] != 'l' || + r.Data[r.pos-2] != 's' || + r.Data[r.pos-1] != 'e' || + (r.pos != len(r.Data) && !isTokenEnd(r.Data[r.pos])) { + + r.pos -= 5 + r.errSyntax() + } +} + +// fetchNumber scans a number literal token. +func (r *Lexer) fetchNumber() { + hasE := false + afterE := false + hasDot := false + + r.pos++ + for i, c := range r.Data[r.pos:] { + switch { + case c >= '0' && c <= '9': + afterE = false + case c == '.' && !hasDot: + hasDot = true + case (c == 'e' || c == 'E') && !hasE: + hasE = true + hasDot = true + afterE = true + case (c == '+' || c == '-') && afterE: + afterE = false + default: + r.pos += i + if !isTokenEnd(c) { + r.errSyntax() + } else { + r.token.byteValue = r.Data[r.start:r.pos] + } + return + } + } + + r.pos = len(r.Data) + r.token.byteValue = r.Data[r.start:] +} + +// findStringLen tries to scan into the string literal for ending quote char to determine required size. +// The size will be exact if no escapes are present and may be inexact if there are escaped chars. +func findStringLen(data []byte) (isValid, hasEscapes bool, length int) { + delta := 0 + + for i := 0; i < len(data); i++ { + switch data[i] { + case '\\': + i++ + delta++ + if i < len(data) && data[i] == 'u' { + delta++ + } + case '"': + return true, (delta > 0), (i - delta) + } + } + + return false, false, len(data) +} + +// getu4 decodes \uXXXX from the beginning of s, returning the hex value, +// or it returns -1. +func getu4(s []byte) rune { + if len(s) < 6 || s[0] != '\\' || s[1] != 'u' { + return -1 + } + var val rune + for i := 2; i < len(s) && i < 6; i++ { + var v byte + c := s[i] + switch c { + case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9': + v = c - '0' + case 'a', 'b', 'c', 'd', 'e', 'f': + v = c - 'a' + 10 + case 'A', 'B', 'C', 'D', 'E', 'F': + v = c - 'A' + 10 + default: + return -1 + } + + val <<= 4 + val |= rune(v) + } + return val +} + +// processEscape processes a single escape sequence and returns number of bytes processed. +func (r *Lexer) processEscape(data []byte) (int, error) { + if len(data) < 2 { + return 0, fmt.Errorf("syntax error at %v", string(data)) + } + + c := data[1] + switch c { + case '"', '/', '\\': + r.token.byteValue = append(r.token.byteValue, c) + return 2, nil + case 'b': + r.token.byteValue = append(r.token.byteValue, '\b') + return 2, nil + case 'f': + r.token.byteValue = append(r.token.byteValue, '\f') + return 2, nil + case 'n': + r.token.byteValue = append(r.token.byteValue, '\n') + return 2, nil + case 'r': + r.token.byteValue = append(r.token.byteValue, '\r') + return 2, nil + case 't': + r.token.byteValue = append(r.token.byteValue, '\t') + return 2, nil + case 'u': + rr := getu4(data) + if rr < 0 { + return 0, errors.New("syntax error") + } + + read := 6 + if utf16.IsSurrogate(rr) { + rr1 := getu4(data[read:]) + if dec := utf16.DecodeRune(rr, rr1); dec != unicode.ReplacementChar { + read += 6 + rr = dec + } else { + rr = unicode.ReplacementChar + } + } + var d [4]byte + s := utf8.EncodeRune(d[:], rr) + r.token.byteValue = append(r.token.byteValue, d[:s]...) + return read, nil + } + + return 0, errors.New("syntax error") +} + +// fetchString scans a string literal token. +func (r *Lexer) fetchString() { + r.pos++ + data := r.Data[r.pos:] + + isValid, hasEscapes, length := findStringLen(data) + if !isValid { + r.pos += length + r.errParse("unterminated string literal") + return + } + if !hasEscapes { + r.token.byteValue = data[:length] + r.pos += length + 1 + return + } + + r.token.byteValue = make([]byte, 0, length) + p := 0 + for i := 0; i < len(data); { + switch data[i] { + case '"': + r.pos += i + 1 + r.token.byteValue = append(r.token.byteValue, data[p:i]...) + i++ + return + + case '\\': + r.token.byteValue = append(r.token.byteValue, data[p:i]...) + off, err := r.processEscape(data[i:]) + if err != nil { + r.errParse(err.Error()) + return + } + i += off + p = i + + default: + i++ + } + } + r.errParse("unterminated string literal") +} + +// scanToken scans the next token if no token is currently available in the lexer. +func (r *Lexer) scanToken() { + if r.token.kind != tokenUndef || r.fatalError != nil { + return + } + + r.FetchToken() +} + +// consume resets the current token to allow scanning the next one. +func (r *Lexer) consume() { + r.token.kind = tokenUndef + r.token.delimValue = 0 +} + +// Ok returns true if no error (including io.EOF) was encountered during scanning. +func (r *Lexer) Ok() bool { + return r.fatalError == nil +} + +const maxErrorContextLen = 13 + +func (r *Lexer) errParse(what string) { + if r.fatalError == nil { + var str string + if len(r.Data)-r.pos <= maxErrorContextLen { + str = string(r.Data) + } else { + str = string(r.Data[r.pos:r.pos+maxErrorContextLen-3]) + "..." + } + r.fatalError = &LexerError{ + Reason: what, + Offset: r.pos, + Data: str, + } + } +} + +func (r *Lexer) errSyntax() { + r.errParse("syntax error") +} + +func (r *Lexer) errInvalidToken(expected string) { + if r.fatalError != nil { + return + } + if r.UseMultipleErrors { + r.pos = r.start + r.consume() + r.SkipRecursive() + switch expected { + case "[": + r.token.delimValue = ']' + r.token.kind = tokenDelim + case "{": + r.token.delimValue = '}' + r.token.kind = tokenDelim + } + r.addNonfatalError(&LexerError{ + Reason: fmt.Sprintf("expected %s", expected), + Offset: r.start, + Data: string(r.Data[r.start:r.pos]), + }) + return + } + + var str string + if len(r.token.byteValue) <= maxErrorContextLen { + str = string(r.token.byteValue) + } else { + str = string(r.token.byteValue[:maxErrorContextLen-3]) + "..." + } + r.fatalError = &LexerError{ + Reason: fmt.Sprintf("expected %s", expected), + Offset: r.pos, + Data: str, + } +} + +func (r *Lexer) GetPos() int { + return r.pos +} + +// Delim consumes a token and verifies that it is the given delimiter. +func (r *Lexer) Delim(c byte) { + if r.token.kind == tokenUndef && r.Ok() { + r.FetchToken() + } + + if !r.Ok() || r.token.delimValue != c { + r.consume() // errInvalidToken can change token if UseMultipleErrors is enabled. + r.errInvalidToken(string([]byte{c})) + } else { + r.consume() + } +} + +// IsDelim returns true if there was no scanning error and next token is the given delimiter. +func (r *Lexer) IsDelim(c byte) bool { + if r.token.kind == tokenUndef && r.Ok() { + r.FetchToken() + } + return !r.Ok() || r.token.delimValue == c +} + +// Null verifies that the next token is null and consumes it. +func (r *Lexer) Null() { + if r.token.kind == tokenUndef && r.Ok() { + r.FetchToken() + } + if !r.Ok() || r.token.kind != tokenNull { + r.errInvalidToken("null") + } + r.consume() +} + +// IsNull returns true if the next token is a null keyword. +func (r *Lexer) IsNull() bool { + if r.token.kind == tokenUndef && r.Ok() { + r.FetchToken() + } + return r.Ok() && r.token.kind == tokenNull +} + +// Skip skips a single token. +func (r *Lexer) Skip() { + if r.token.kind == tokenUndef && r.Ok() { + r.FetchToken() + } + r.consume() +} + +// SkipRecursive skips next array or object completely, or just skips a single token if not +// an array/object. +// +// Note: no syntax validation is performed on the skipped data. +func (r *Lexer) SkipRecursive() { + r.scanToken() + var start, end byte + + switch r.token.delimValue { + case '{': + start, end = '{', '}' + case '[': + start, end = '[', ']' + default: + r.consume() + return + } + + r.consume() + + level := 1 + inQuotes := false + wasEscape := false + + for i, c := range r.Data[r.pos:] { + switch { + case c == start && !inQuotes: + level++ + case c == end && !inQuotes: + level-- + if level == 0 { + r.pos += i + 1 + return + } + case c == '\\' && inQuotes: + wasEscape = !wasEscape + continue + case c == '"' && inQuotes: + inQuotes = wasEscape + case c == '"': + inQuotes = true + } + wasEscape = false + } + r.pos = len(r.Data) + r.fatalError = &LexerError{ + Reason: "EOF reached while skipping array/object or token", + Offset: r.pos, + Data: string(r.Data[r.pos:]), + } +} + +// Raw fetches the next item recursively as a data slice +func (r *Lexer) Raw() []byte { + r.SkipRecursive() + if !r.Ok() { + return nil + } + return r.Data[r.start:r.pos] +} + +// IsStart returns whether the lexer is positioned at the start +// of an input string. +func (r *Lexer) IsStart() bool { + return r.pos == 0 +} + +// Consumed reads all remaining bytes from the input, publishing an error if +// there is anything but whitespace remaining. +func (r *Lexer) Consumed() { + if r.pos > len(r.Data) || !r.Ok() { + return + } + + for _, c := range r.Data[r.pos:] { + if c != ' ' && c != '\t' && c != '\r' && c != '\n' { + r.AddError(&LexerError{ + Reason: "invalid character '" + string(c) + "' after top-level value", + Offset: r.pos, + Data: string(r.Data[r.pos:]), + }) + return + } + + r.pos++ + r.start++ + } +} + +func (r *Lexer) unsafeString() (string, []byte) { + if r.token.kind == tokenUndef && r.Ok() { + r.FetchToken() + } + if !r.Ok() || r.token.kind != tokenString { + r.errInvalidToken("string") + return "", nil + } + bytes := r.token.byteValue + ret := bytesToStr(r.token.byteValue) + r.consume() + return ret, bytes +} + +// UnsafeString returns the string value if the token is a string literal. +// +// Warning: returned string may point to the input buffer, so the string should not outlive +// the input buffer. Intended pattern of usage is as an argument to a switch statement. +func (r *Lexer) UnsafeString() string { + ret, _ := r.unsafeString() + return ret +} + +// UnsafeBytes returns the byte slice if the token is a string literal. +func (r *Lexer) UnsafeBytes() []byte { + _, ret := r.unsafeString() + return ret +} + +// String reads a string literal. +func (r *Lexer) String() string { + if r.token.kind == tokenUndef && r.Ok() { + r.FetchToken() + } + if !r.Ok() || r.token.kind != tokenString { + r.errInvalidToken("string") + return "" + } + ret := string(r.token.byteValue) + r.consume() + return ret +} + +// Bytes reads a string literal and base64 decodes it into a byte slice. +func (r *Lexer) Bytes() []byte { + if r.token.kind == tokenUndef && r.Ok() { + r.FetchToken() + } + if !r.Ok() || r.token.kind != tokenString { + r.errInvalidToken("string") + return nil + } + ret := make([]byte, base64.StdEncoding.DecodedLen(len(r.token.byteValue))) + n, err := base64.StdEncoding.Decode(ret, r.token.byteValue) + if err != nil { + r.fatalError = &LexerError{ + Reason: err.Error(), + } + return nil + } + + r.consume() + return ret[:n] +} + +// Bool reads a true or false boolean keyword. +func (r *Lexer) Bool() bool { + if r.token.kind == tokenUndef && r.Ok() { + r.FetchToken() + } + if !r.Ok() || r.token.kind != tokenBool { + r.errInvalidToken("bool") + return false + } + ret := r.token.boolValue + r.consume() + return ret +} + +func (r *Lexer) number() string { + if r.token.kind == tokenUndef && r.Ok() { + r.FetchToken() + } + if !r.Ok() || r.token.kind != tokenNumber { + r.errInvalidToken("number") + return "" + } + ret := bytesToStr(r.token.byteValue) + r.consume() + return ret +} + +func (r *Lexer) Uint8() uint8 { + s := r.number() + if !r.Ok() { + return 0 + } + + n, err := strconv.ParseUint(s, 10, 8) + if err != nil { + r.addNonfatalError(&LexerError{ + Offset: r.start, + Reason: err.Error(), + Data: s, + }) + } + return uint8(n) +} + +func (r *Lexer) Uint16() uint16 { + s := r.number() + if !r.Ok() { + return 0 + } + + n, err := strconv.ParseUint(s, 10, 16) + if err != nil { + r.addNonfatalError(&LexerError{ + Offset: r.start, + Reason: err.Error(), + Data: s, + }) + } + return uint16(n) +} + +func (r *Lexer) Uint32() uint32 { + s := r.number() + if !r.Ok() { + return 0 + } + + n, err := strconv.ParseUint(s, 10, 32) + if err != nil { + r.addNonfatalError(&LexerError{ + Offset: r.start, + Reason: err.Error(), + Data: s, + }) + } + return uint32(n) +} + +func (r *Lexer) Uint64() uint64 { + s := r.number() + if !r.Ok() { + return 0 + } + + n, err := strconv.ParseUint(s, 10, 64) + if err != nil { + r.addNonfatalError(&LexerError{ + Offset: r.start, + Reason: err.Error(), + Data: s, + }) + } + return n +} + +func (r *Lexer) Uint() uint { + return uint(r.Uint64()) +} + +func (r *Lexer) Int8() int8 { + s := r.number() + if !r.Ok() { + return 0 + } + + n, err := strconv.ParseInt(s, 10, 8) + if err != nil { + r.addNonfatalError(&LexerError{ + Offset: r.start, + Reason: err.Error(), + Data: s, + }) + } + return int8(n) +} + +func (r *Lexer) Int16() int16 { + s := r.number() + if !r.Ok() { + return 0 + } + + n, err := strconv.ParseInt(s, 10, 16) + if err != nil { + r.addNonfatalError(&LexerError{ + Offset: r.start, + Reason: err.Error(), + Data: s, + }) + } + return int16(n) +} + +func (r *Lexer) Int32() int32 { + s := r.number() + if !r.Ok() { + return 0 + } + + n, err := strconv.ParseInt(s, 10, 32) + if err != nil { + r.addNonfatalError(&LexerError{ + Offset: r.start, + Reason: err.Error(), + Data: s, + }) + } + return int32(n) +} + +func (r *Lexer) Int64() int64 { + s := r.number() + if !r.Ok() { + return 0 + } + + n, err := strconv.ParseInt(s, 10, 64) + if err != nil { + r.addNonfatalError(&LexerError{ + Offset: r.start, + Reason: err.Error(), + Data: s, + }) + } + return n +} + +func (r *Lexer) Int() int { + return int(r.Int64()) +} + +func (r *Lexer) Uint8Str() uint8 { + s, b := r.unsafeString() + if !r.Ok() { + return 0 + } + + n, err := strconv.ParseUint(s, 10, 8) + if err != nil { + r.addNonfatalError(&LexerError{ + Offset: r.start, + Reason: err.Error(), + Data: string(b), + }) + } + return uint8(n) +} + +func (r *Lexer) Uint16Str() uint16 { + s, b := r.unsafeString() + if !r.Ok() { + return 0 + } + + n, err := strconv.ParseUint(s, 10, 16) + if err != nil { + r.addNonfatalError(&LexerError{ + Offset: r.start, + Reason: err.Error(), + Data: string(b), + }) + } + return uint16(n) +} + +func (r *Lexer) Uint32Str() uint32 { + s, b := r.unsafeString() + if !r.Ok() { + return 0 + } + + n, err := strconv.ParseUint(s, 10, 32) + if err != nil { + r.addNonfatalError(&LexerError{ + Offset: r.start, + Reason: err.Error(), + Data: string(b), + }) + } + return uint32(n) +} + +func (r *Lexer) Uint64Str() uint64 { + s, b := r.unsafeString() + if !r.Ok() { + return 0 + } + + n, err := strconv.ParseUint(s, 10, 64) + if err != nil { + r.addNonfatalError(&LexerError{ + Offset: r.start, + Reason: err.Error(), + Data: string(b), + }) + } + return n +} + +func (r *Lexer) UintStr() uint { + return uint(r.Uint64Str()) +} + +func (r *Lexer) UintptrStr() uintptr { + return uintptr(r.Uint64Str()) +} + +func (r *Lexer) Int8Str() int8 { + s, b := r.unsafeString() + if !r.Ok() { + return 0 + } + + n, err := strconv.ParseInt(s, 10, 8) + if err != nil { + r.addNonfatalError(&LexerError{ + Offset: r.start, + Reason: err.Error(), + Data: string(b), + }) + } + return int8(n) +} + +func (r *Lexer) Int16Str() int16 { + s, b := r.unsafeString() + if !r.Ok() { + return 0 + } + + n, err := strconv.ParseInt(s, 10, 16) + if err != nil { + r.addNonfatalError(&LexerError{ + Offset: r.start, + Reason: err.Error(), + Data: string(b), + }) + } + return int16(n) +} + +func (r *Lexer) Int32Str() int32 { + s, b := r.unsafeString() + if !r.Ok() { + return 0 + } + + n, err := strconv.ParseInt(s, 10, 32) + if err != nil { + r.addNonfatalError(&LexerError{ + Offset: r.start, + Reason: err.Error(), + Data: string(b), + }) + } + return int32(n) +} + +func (r *Lexer) Int64Str() int64 { + s, b := r.unsafeString() + if !r.Ok() { + return 0 + } + + n, err := strconv.ParseInt(s, 10, 64) + if err != nil { + r.addNonfatalError(&LexerError{ + Offset: r.start, + Reason: err.Error(), + Data: string(b), + }) + } + return n +} + +func (r *Lexer) IntStr() int { + return int(r.Int64Str()) +} + +func (r *Lexer) Float32() float32 { + s := r.number() + if !r.Ok() { + return 0 + } + + n, err := strconv.ParseFloat(s, 32) + if err != nil { + r.addNonfatalError(&LexerError{ + Offset: r.start, + Reason: err.Error(), + Data: s, + }) + } + return float32(n) +} + +func (r *Lexer) Float32Str() float32 { + s, b := r.unsafeString() + if !r.Ok() { + return 0 + } + n, err := strconv.ParseFloat(s, 32) + if err != nil { + r.addNonfatalError(&LexerError{ + Offset: r.start, + Reason: err.Error(), + Data: string(b), + }) + } + return float32(n) +} + +func (r *Lexer) Float64() float64 { + s := r.number() + if !r.Ok() { + return 0 + } + + n, err := strconv.ParseFloat(s, 64) + if err != nil { + r.addNonfatalError(&LexerError{ + Offset: r.start, + Reason: err.Error(), + Data: s, + }) + } + return n +} + +func (r *Lexer) Float64Str() float64 { + s, b := r.unsafeString() + if !r.Ok() { + return 0 + } + n, err := strconv.ParseFloat(s, 64) + if err != nil { + r.addNonfatalError(&LexerError{ + Offset: r.start, + Reason: err.Error(), + Data: string(b), + }) + } + return n +} + +func (r *Lexer) Error() error { + return r.fatalError +} + +func (r *Lexer) AddError(e error) { + if r.fatalError == nil { + r.fatalError = e + } +} + +func (r *Lexer) AddNonFatalError(e error) { + r.addNonfatalError(&LexerError{ + Offset: r.start, + Data: string(r.Data[r.start:r.pos]), + Reason: e.Error(), + }) +} + +func (r *Lexer) addNonfatalError(err *LexerError) { + if r.UseMultipleErrors { + // We don't want to add errors with the same offset. + if len(r.multipleErrors) != 0 && r.multipleErrors[len(r.multipleErrors)-1].Offset == err.Offset { + return + } + r.multipleErrors = append(r.multipleErrors, err) + return + } + r.fatalError = err +} + +func (r *Lexer) GetNonFatalErrors() []*LexerError { + return r.multipleErrors +} + +// JsonNumber fetches and json.Number from 'encoding/json' package. +// Both int, float or string, contains them are valid values +func (r *Lexer) JsonNumber() json.Number { + if r.token.kind == tokenUndef && r.Ok() { + r.FetchToken() + } + if !r.Ok() { + r.errInvalidToken("json.Number") + return json.Number("") + } + + switch r.token.kind { + case tokenString: + return json.Number(r.String()) + case tokenNumber: + return json.Number(r.Raw()) + case tokenNull: + r.Null() + return json.Number("") + default: + r.errSyntax() + return json.Number("") + } +} + +// Interface fetches an interface{} analogous to the 'encoding/json' package. +func (r *Lexer) Interface() interface{} { + if r.token.kind == tokenUndef && r.Ok() { + r.FetchToken() + } + + if !r.Ok() { + return nil + } + switch r.token.kind { + case tokenString: + return r.String() + case tokenNumber: + return r.Float64() + case tokenBool: + return r.Bool() + case tokenNull: + r.Null() + return nil + } + + if r.token.delimValue == '{' { + r.consume() + + ret := map[string]interface{}{} + for !r.IsDelim('}') { + key := r.String() + r.WantColon() + ret[key] = r.Interface() + r.WantComma() + } + r.Delim('}') + + if r.Ok() { + return ret + } else { + return nil + } + } else if r.token.delimValue == '[' { + r.consume() + + ret := []interface{}{} + for !r.IsDelim(']') { + ret = append(ret, r.Interface()) + r.WantComma() + } + r.Delim(']') + + if r.Ok() { + return ret + } else { + return nil + } + } + r.errSyntax() + return nil +} + +// WantComma requires a comma to be present before fetching next token. +func (r *Lexer) WantComma() { + r.wantSep = ',' + r.firstElement = false +} + +// WantColon requires a colon to be present before fetching next token. +func (r *Lexer) WantColon() { + r.wantSep = ':' + r.firstElement = false +} diff --git a/vendor/github.com/mailru/easyjson/jwriter/writer.go b/vendor/github.com/mailru/easyjson/jwriter/writer.go new file mode 100644 index 000000000..b9ed7ccaa --- /dev/null +++ b/vendor/github.com/mailru/easyjson/jwriter/writer.go @@ -0,0 +1,390 @@ +// Package jwriter contains a JSON writer. +package jwriter + +import ( + "io" + "strconv" + "unicode/utf8" + + "github.com/mailru/easyjson/buffer" +) + +// Flags describe various encoding options. The behavior may be actually implemented in the encoder, but +// Flags field in Writer is used to set and pass them around. +type Flags int + +const ( + NilMapAsEmpty Flags = 1 << iota // Encode nil map as '{}' rather than 'null'. + NilSliceAsEmpty // Encode nil slice as '[]' rather than 'null'. +) + +// Writer is a JSON writer. +type Writer struct { + Flags Flags + + Error error + Buffer buffer.Buffer + NoEscapeHTML bool +} + +// Size returns the size of the data that was written out. +func (w *Writer) Size() int { + return w.Buffer.Size() +} + +// DumpTo outputs the data to given io.Writer, resetting the buffer. +func (w *Writer) DumpTo(out io.Writer) (written int, err error) { + return w.Buffer.DumpTo(out) +} + +// BuildBytes returns writer data as a single byte slice. You can optionally provide one byte slice +// as argument that it will try to reuse. +func (w *Writer) BuildBytes(reuse ...[]byte) ([]byte, error) { + if w.Error != nil { + return nil, w.Error + } + + return w.Buffer.BuildBytes(reuse...), nil +} + +// ReadCloser returns an io.ReadCloser that can be used to read the data. +// ReadCloser also resets the buffer. +func (w *Writer) ReadCloser() (io.ReadCloser, error) { + if w.Error != nil { + return nil, w.Error + } + + return w.Buffer.ReadCloser(), nil +} + +// RawByte appends raw binary data to the buffer. +func (w *Writer) RawByte(c byte) { + w.Buffer.AppendByte(c) +} + +// RawByte appends raw binary data to the buffer. +func (w *Writer) RawString(s string) { + w.Buffer.AppendString(s) +} + +// Raw appends raw binary data to the buffer or sets the error if it is given. Useful for +// calling with results of MarshalJSON-like functions. +func (w *Writer) Raw(data []byte, err error) { + switch { + case w.Error != nil: + return + case err != nil: + w.Error = err + case len(data) > 0: + w.Buffer.AppendBytes(data) + default: + w.RawString("null") + } +} + +// RawText encloses raw binary data in quotes and appends in to the buffer. +// Useful for calling with results of MarshalText-like functions. +func (w *Writer) RawText(data []byte, err error) { + switch { + case w.Error != nil: + return + case err != nil: + w.Error = err + case len(data) > 0: + w.String(string(data)) + default: + w.RawString("null") + } +} + +// Base64Bytes appends data to the buffer after base64 encoding it +func (w *Writer) Base64Bytes(data []byte) { + if data == nil { + w.Buffer.AppendString("null") + return + } + w.Buffer.AppendByte('"') + w.base64(data) + w.Buffer.AppendByte('"') +} + +func (w *Writer) Uint8(n uint8) { + w.Buffer.EnsureSpace(3) + w.Buffer.Buf = strconv.AppendUint(w.Buffer.Buf, uint64(n), 10) +} + +func (w *Writer) Uint16(n uint16) { + w.Buffer.EnsureSpace(5) + w.Buffer.Buf = strconv.AppendUint(w.Buffer.Buf, uint64(n), 10) +} + +func (w *Writer) Uint32(n uint32) { + w.Buffer.EnsureSpace(10) + w.Buffer.Buf = strconv.AppendUint(w.Buffer.Buf, uint64(n), 10) +} + +func (w *Writer) Uint(n uint) { + w.Buffer.EnsureSpace(20) + w.Buffer.Buf = strconv.AppendUint(w.Buffer.Buf, uint64(n), 10) +} + +func (w *Writer) Uint64(n uint64) { + w.Buffer.EnsureSpace(20) + w.Buffer.Buf = strconv.AppendUint(w.Buffer.Buf, n, 10) +} + +func (w *Writer) Int8(n int8) { + w.Buffer.EnsureSpace(4) + w.Buffer.Buf = strconv.AppendInt(w.Buffer.Buf, int64(n), 10) +} + +func (w *Writer) Int16(n int16) { + w.Buffer.EnsureSpace(6) + w.Buffer.Buf = strconv.AppendInt(w.Buffer.Buf, int64(n), 10) +} + +func (w *Writer) Int32(n int32) { + w.Buffer.EnsureSpace(11) + w.Buffer.Buf = strconv.AppendInt(w.Buffer.Buf, int64(n), 10) +} + +func (w *Writer) Int(n int) { + w.Buffer.EnsureSpace(21) + w.Buffer.Buf = strconv.AppendInt(w.Buffer.Buf, int64(n), 10) +} + +func (w *Writer) Int64(n int64) { + w.Buffer.EnsureSpace(21) + w.Buffer.Buf = strconv.AppendInt(w.Buffer.Buf, n, 10) +} + +func (w *Writer) Uint8Str(n uint8) { + w.Buffer.EnsureSpace(3) + w.Buffer.Buf = append(w.Buffer.Buf, '"') + w.Buffer.Buf = strconv.AppendUint(w.Buffer.Buf, uint64(n), 10) + w.Buffer.Buf = append(w.Buffer.Buf, '"') +} + +func (w *Writer) Uint16Str(n uint16) { + w.Buffer.EnsureSpace(5) + w.Buffer.Buf = append(w.Buffer.Buf, '"') + w.Buffer.Buf = strconv.AppendUint(w.Buffer.Buf, uint64(n), 10) + w.Buffer.Buf = append(w.Buffer.Buf, '"') +} + +func (w *Writer) Uint32Str(n uint32) { + w.Buffer.EnsureSpace(10) + w.Buffer.Buf = append(w.Buffer.Buf, '"') + w.Buffer.Buf = strconv.AppendUint(w.Buffer.Buf, uint64(n), 10) + w.Buffer.Buf = append(w.Buffer.Buf, '"') +} + +func (w *Writer) UintStr(n uint) { + w.Buffer.EnsureSpace(20) + w.Buffer.Buf = append(w.Buffer.Buf, '"') + w.Buffer.Buf = strconv.AppendUint(w.Buffer.Buf, uint64(n), 10) + w.Buffer.Buf = append(w.Buffer.Buf, '"') +} + +func (w *Writer) Uint64Str(n uint64) { + w.Buffer.EnsureSpace(20) + w.Buffer.Buf = append(w.Buffer.Buf, '"') + w.Buffer.Buf = strconv.AppendUint(w.Buffer.Buf, n, 10) + w.Buffer.Buf = append(w.Buffer.Buf, '"') +} + +func (w *Writer) UintptrStr(n uintptr) { + w.Buffer.EnsureSpace(20) + w.Buffer.Buf = append(w.Buffer.Buf, '"') + w.Buffer.Buf = strconv.AppendUint(w.Buffer.Buf, uint64(n), 10) + w.Buffer.Buf = append(w.Buffer.Buf, '"') +} + +func (w *Writer) Int8Str(n int8) { + w.Buffer.EnsureSpace(4) + w.Buffer.Buf = append(w.Buffer.Buf, '"') + w.Buffer.Buf = strconv.AppendInt(w.Buffer.Buf, int64(n), 10) + w.Buffer.Buf = append(w.Buffer.Buf, '"') +} + +func (w *Writer) Int16Str(n int16) { + w.Buffer.EnsureSpace(6) + w.Buffer.Buf = append(w.Buffer.Buf, '"') + w.Buffer.Buf = strconv.AppendInt(w.Buffer.Buf, int64(n), 10) + w.Buffer.Buf = append(w.Buffer.Buf, '"') +} + +func (w *Writer) Int32Str(n int32) { + w.Buffer.EnsureSpace(11) + w.Buffer.Buf = append(w.Buffer.Buf, '"') + w.Buffer.Buf = strconv.AppendInt(w.Buffer.Buf, int64(n), 10) + w.Buffer.Buf = append(w.Buffer.Buf, '"') +} + +func (w *Writer) IntStr(n int) { + w.Buffer.EnsureSpace(21) + w.Buffer.Buf = append(w.Buffer.Buf, '"') + w.Buffer.Buf = strconv.AppendInt(w.Buffer.Buf, int64(n), 10) + w.Buffer.Buf = append(w.Buffer.Buf, '"') +} + +func (w *Writer) Int64Str(n int64) { + w.Buffer.EnsureSpace(21) + w.Buffer.Buf = append(w.Buffer.Buf, '"') + w.Buffer.Buf = strconv.AppendInt(w.Buffer.Buf, n, 10) + w.Buffer.Buf = append(w.Buffer.Buf, '"') +} + +func (w *Writer) Float32(n float32) { + w.Buffer.EnsureSpace(20) + w.Buffer.Buf = strconv.AppendFloat(w.Buffer.Buf, float64(n), 'g', -1, 32) +} + +func (w *Writer) Float32Str(n float32) { + w.Buffer.EnsureSpace(20) + w.Buffer.Buf = append(w.Buffer.Buf, '"') + w.Buffer.Buf = strconv.AppendFloat(w.Buffer.Buf, float64(n), 'g', -1, 32) + w.Buffer.Buf = append(w.Buffer.Buf, '"') +} + +func (w *Writer) Float64(n float64) { + w.Buffer.EnsureSpace(20) + w.Buffer.Buf = strconv.AppendFloat(w.Buffer.Buf, n, 'g', -1, 64) +} + +func (w *Writer) Float64Str(n float64) { + w.Buffer.EnsureSpace(20) + w.Buffer.Buf = append(w.Buffer.Buf, '"') + w.Buffer.Buf = strconv.AppendFloat(w.Buffer.Buf, float64(n), 'g', -1, 64) + w.Buffer.Buf = append(w.Buffer.Buf, '"') +} + +func (w *Writer) Bool(v bool) { + w.Buffer.EnsureSpace(5) + if v { + w.Buffer.Buf = append(w.Buffer.Buf, "true"...) + } else { + w.Buffer.Buf = append(w.Buffer.Buf, "false"...) + } +} + +const chars = "0123456789abcdef" + +func isNotEscapedSingleChar(c byte, escapeHTML bool) bool { + // Note: might make sense to use a table if there are more chars to escape. With 4 chars + // it benchmarks the same. + if escapeHTML { + return c != '<' && c != '>' && c != '&' && c != '\\' && c != '"' && c >= 0x20 && c < utf8.RuneSelf + } else { + return c != '\\' && c != '"' && c >= 0x20 && c < utf8.RuneSelf + } +} + +func (w *Writer) String(s string) { + w.Buffer.AppendByte('"') + + // Portions of the string that contain no escapes are appended as + // byte slices. + + p := 0 // last non-escape symbol + + for i := 0; i < len(s); { + c := s[i] + + if isNotEscapedSingleChar(c, !w.NoEscapeHTML) { + // single-width character, no escaping is required + i++ + continue + } else if c < utf8.RuneSelf { + // single-with character, need to escape + w.Buffer.AppendString(s[p:i]) + switch c { + case '\t': + w.Buffer.AppendString(`\t`) + case '\r': + w.Buffer.AppendString(`\r`) + case '\n': + w.Buffer.AppendString(`\n`) + case '\\': + w.Buffer.AppendString(`\\`) + case '"': + w.Buffer.AppendString(`\"`) + default: + w.Buffer.AppendString(`\u00`) + w.Buffer.AppendByte(chars[c>>4]) + w.Buffer.AppendByte(chars[c&0xf]) + } + + i++ + p = i + continue + } + + // broken utf + runeValue, runeWidth := utf8.DecodeRuneInString(s[i:]) + if runeValue == utf8.RuneError && runeWidth == 1 { + w.Buffer.AppendString(s[p:i]) + w.Buffer.AppendString(`\ufffd`) + i++ + p = i + continue + } + + // jsonp stuff - tab separator and line separator + if runeValue == '\u2028' || runeValue == '\u2029' { + w.Buffer.AppendString(s[p:i]) + w.Buffer.AppendString(`\u202`) + w.Buffer.AppendByte(chars[runeValue&0xf]) + i += runeWidth + p = i + continue + } + i += runeWidth + } + w.Buffer.AppendString(s[p:]) + w.Buffer.AppendByte('"') +} + +const encode = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/" +const padChar = '=' + +func (w *Writer) base64(in []byte) { + + if len(in) == 0 { + return + } + + w.Buffer.EnsureSpace(((len(in)-1)/3 + 1) * 4) + + si := 0 + n := (len(in) / 3) * 3 + + for si < n { + // Convert 3x 8bit source bytes into 4 bytes + val := uint(in[si+0])<<16 | uint(in[si+1])<<8 | uint(in[si+2]) + + w.Buffer.Buf = append(w.Buffer.Buf, encode[val>>18&0x3F], encode[val>>12&0x3F], encode[val>>6&0x3F], encode[val&0x3F]) + + si += 3 + } + + remain := len(in) - si + if remain == 0 { + return + } + + // Add the remaining small block + val := uint(in[si+0]) << 16 + if remain == 2 { + val |= uint(in[si+1]) << 8 + } + + w.Buffer.Buf = append(w.Buffer.Buf, encode[val>>18&0x3F], encode[val>>12&0x3F]) + + switch remain { + case 2: + w.Buffer.Buf = append(w.Buffer.Buf, encode[val>>6&0x3F], byte(padChar)) + case 1: + w.Buffer.Buf = append(w.Buffer.Buf, byte(padChar), byte(padChar)) + } +} diff --git a/vendor/k8s.io/kube-openapi/pkg/common/common.go b/vendor/k8s.io/kube-openapi/pkg/common/common.go new file mode 100644 index 000000000..f1c87c300 --- /dev/null +++ b/vendor/k8s.io/kube-openapi/pkg/common/common.go @@ -0,0 +1,192 @@ +/* +Copyright 2016 The Kubernetes Authors. + +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 common + +import ( + "net/http" + "strings" + + "github.com/emicklei/go-restful" + "github.com/go-openapi/spec" +) + +const ( + // TODO: Make this configurable. + ExtensionPrefix = "x-kubernetes-" + ExtensionV2Schema = ExtensionPrefix + "v2-schema" +) + +// OpenAPIDefinition describes single type. Normally these definitions are auto-generated using gen-openapi. +type OpenAPIDefinition struct { + Schema spec.Schema + Dependencies []string +} + +type ReferenceCallback func(path string) spec.Ref + +// GetOpenAPIDefinitions is collection of all definitions. +type GetOpenAPIDefinitions func(ReferenceCallback) map[string]OpenAPIDefinition + +// OpenAPIDefinitionGetter gets openAPI definitions for a given type. If a type implements this interface, +// the definition returned by it will be used, otherwise the auto-generated definitions will be used. See +// GetOpenAPITypeFormat for more information about trade-offs of using this interface or GetOpenAPITypeFormat method when +// possible. +type OpenAPIDefinitionGetter interface { + OpenAPIDefinition() *OpenAPIDefinition +} + +type OpenAPIV3DefinitionGetter interface { + OpenAPIV3Definition() *OpenAPIDefinition +} + +type PathHandler interface { + Handle(path string, handler http.Handler) +} + +// Config is set of configuration for openAPI spec generation. +type Config struct { + // List of supported protocols such as https, http, etc. + ProtocolList []string + + // Info is general information about the API. + Info *spec.Info + + // DefaultResponse will be used if an operation does not have any responses listed. It + // will show up as ... "responses" : {"default" : $DefaultResponse} in the spec. + DefaultResponse *spec.Response + + // ResponseDefinitions will be added to "responses" under the top-level swagger object. This is an object + // that holds responses definitions that can be used across operations. This property does not define + // global responses for all operations. For more info please refer: + // https://github.com/OAI/OpenAPI-Specification/blob/master/versions/2.0.md#fixed-fields + ResponseDefinitions map[string]spec.Response + + // CommonResponses will be added as a response to all operation specs. This is a good place to add common + // responses such as authorization failed. + CommonResponses map[int]spec.Response + + // List of webservice's path prefixes to ignore + IgnorePrefixes []string + + // OpenAPIDefinitions should provide definition for all models used by routes. Failure to provide this map + // or any of the models will result in spec generation failure. + GetDefinitions GetOpenAPIDefinitions + + // GetOperationIDAndTags returns operation id and tags for a restful route. It is an optional function to customize operation IDs. + GetOperationIDAndTags func(r *restful.Route) (string, []string, error) + + // GetDefinitionName returns a friendly name for a definition base on the serving path. parameter `name` is the full name of the definition. + // It is an optional function to customize model names. + GetDefinitionName func(name string) (string, spec.Extensions) + + // PostProcessSpec runs after the spec is ready to serve. It allows a final modification to the spec before serving. + PostProcessSpec func(*spec.Swagger) (*spec.Swagger, error) + + // SecurityDefinitions is list of all security definitions for OpenAPI service. If this is not nil, the user of config + // is responsible to provide DefaultSecurity and (maybe) add unauthorized response to CommonResponses. + SecurityDefinitions *spec.SecurityDefinitions + + // DefaultSecurity for all operations. This will pass as spec.SwaggerProps.Security to OpenAPI. + // For most cases, this will be list of acceptable definitions in SecurityDefinitions. + DefaultSecurity []map[string][]string +} + +var schemaTypeFormatMap = map[string][]string{ + "uint": {"integer", "int32"}, + "uint8": {"integer", "byte"}, + "uint16": {"integer", "int32"}, + "uint32": {"integer", "int64"}, + "uint64": {"integer", "int64"}, + "int": {"integer", "int32"}, + "int8": {"integer", "byte"}, + "int16": {"integer", "int32"}, + "int32": {"integer", "int32"}, + "int64": {"integer", "int64"}, + "byte": {"integer", "byte"}, + "float64": {"number", "double"}, + "float32": {"number", "float"}, + "bool": {"boolean", ""}, + "time.Time": {"string", "date-time"}, + "string": {"string", ""}, + "integer": {"integer", ""}, + "number": {"number", ""}, + "boolean": {"boolean", ""}, + "[]byte": {"string", "byte"}, // base64 encoded characters + "interface{}": {"object", ""}, +} + +// This function is a reference for converting go (or any custom type) to a simple open API type,format pair. There are +// two ways to customize spec for a type. If you add it here, a type will be converted to a simple type and the type +// comment (the comment that is added before type definition) will be lost. The spec will still have the property +// comment. The second way is to implement OpenAPIDefinitionGetter interface. That function can customize the spec (so +// the spec does not need to be simple type,format) or can even return a simple type,format (e.g. IntOrString). For simple +// type formats, the benefit of adding OpenAPIDefinitionGetter interface is to keep both type and property documentation. +// Example: +// type Sample struct { +// ... +// // port of the server +// port IntOrString +// ... +// } +// // IntOrString documentation... +// type IntOrString { ... } +// +// Adding IntOrString to this function: +// "port" : { +// format: "string", +// type: "int-or-string", +// Description: "port of the server" +// } +// +// Implement OpenAPIDefinitionGetter for IntOrString: +// +// "port" : { +// $Ref: "#/definitions/IntOrString" +// Description: "port of the server" +// } +// ... +// definitions: +// { +// "IntOrString": { +// format: "string", +// type: "int-or-string", +// Description: "IntOrString documentation..." // new +// } +// } +// +func GetOpenAPITypeFormat(typeName string) (string, string) { + mapped, ok := schemaTypeFormatMap[typeName] + if !ok { + return "", "" + } + return mapped[0], mapped[1] +} + +func EscapeJsonPointer(p string) string { + // Escaping reference name using rfc6901 + p = strings.Replace(p, "~", "~0", -1) + p = strings.Replace(p, "/", "~1", -1) + return p +} + +func EmbedOpenAPIDefinitionIntoV2Extension(main OpenAPIDefinition, embedded OpenAPIDefinition) OpenAPIDefinition { + if main.Schema.Extensions == nil { + main.Schema.Extensions = make(map[string]interface{}) + } + main.Schema.Extensions[ExtensionV2Schema] = embedded.Schema + return main +} diff --git a/vendor/k8s.io/kube-openapi/pkg/common/doc.go b/vendor/k8s.io/kube-openapi/pkg/common/doc.go new file mode 100644 index 000000000..2ba6d247b --- /dev/null +++ b/vendor/k8s.io/kube-openapi/pkg/common/doc.go @@ -0,0 +1,19 @@ +/* +Copyright 2016 The Kubernetes Authors. + +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 common holds shared code and types between open API code +// generator and spec generator. +package common diff --git a/vendor/sigs.k8s.io/controller-runtime/pkg/scheme/scheme.go b/vendor/sigs.k8s.io/controller-runtime/pkg/scheme/scheme.go new file mode 100644 index 000000000..cd6031967 --- /dev/null +++ b/vendor/sigs.k8s.io/controller-runtime/pkg/scheme/scheme.go @@ -0,0 +1,94 @@ +/* +Copyright 2018 The Kubernetes Authors. + +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 scheme contains utilities for gradually building Schemes, +// which contain information associating Go types with Kubernetes +// groups, versions, and kinds. +// +// Each API group should define a utility function +// called AddToScheme for adding its types to a Scheme: +// +// // in package myapigroupv1... +// var ( +// SchemeGroupVersion = schema.GroupVersion{Group: "my.api.group", Version: "v1"} +// SchemeBuilder = &scheme.Builder{GroupVersion: SchemeGroupVersion} +// AddToScheme = SchemeBuilder.AddToScheme +// ) +// +// func init() { +// SchemeBuilder.Register(&MyType{}, &MyTypeList) +// } +// var ( +// scheme *runtime.Scheme = runtime.NewScheme() +// ) +// +// This also true of the built-in Kubernetes types. Then, in the entrypoint for +// your manager, assemble the scheme containing exactly the types you need. +// For instance, if our controller needs types from the core/v1 API group (e.g. Pod), +// plus types from my.api.group/v1: +// +// func init() { +// myapigroupv1.AddToScheme(scheme) +// kubernetesscheme.AddToScheme(scheme) +// } +// +// func main() { +// mgr := controllers.NewManager(controllers.GetConfigOrDie(), manager.Options{ +// Scheme: scheme, +// }) +// // ... +// } +// +package scheme + +import ( + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/runtime/schema" +) + +// Builder builds a new Scheme for mapping go types to Kubernetes GroupVersionKinds. +type Builder struct { + GroupVersion schema.GroupVersion + runtime.SchemeBuilder +} + +// Register adds one or objects to the SchemeBuilder so they can be added to a Scheme. Register mutates bld. +func (bld *Builder) Register(object ...runtime.Object) *Builder { + bld.SchemeBuilder.Register(func(scheme *runtime.Scheme) error { + scheme.AddKnownTypes(bld.GroupVersion, object...) + metav1.AddToGroupVersion(scheme, bld.GroupVersion) + return nil + }) + return bld +} + +// RegisterAll registers all types from the Builder argument. RegisterAll mutates bld. +func (bld *Builder) RegisterAll(b *Builder) *Builder { + bld.SchemeBuilder = append(bld.SchemeBuilder, b.SchemeBuilder...) + return bld +} + +// AddToScheme adds all registered types to s. +func (bld *Builder) AddToScheme(s *runtime.Scheme) error { + return bld.SchemeBuilder.AddToScheme(s) +} + +// Build returns a new Scheme containing the registered types. +func (bld *Builder) Build() (*runtime.Scheme, error) { + s := runtime.NewScheme() + return s, bld.AddToScheme(s) +} From 4f01354facae51d9e9115714acd4dc3e55540e7c Mon Sep 17 00:00:00 2001 From: barnettZQG Date: Fri, 14 Feb 2020 16:40:32 +0800 Subject: [PATCH 27/47] change rainbond kube client --- grctl/clients/kubernetes.go | 12 +- grctl/cmd/cluster.go | 2 +- grctl/cmd/install.go | 2 +- util/k8s/k8s.go | 62 ------ .../clientset/versioned/clientset.go | 99 +++++++++ .../pkg/generated/clientset/versioned/doc.go | 22 ++ .../versioned/fake/clientset_generated.go | 84 ++++++++ .../generated/clientset/versioned/fake/doc.go | 22 ++ .../clientset/versioned/fake/register.go | 58 ++++++ .../clientset/versioned/scheme/doc.go | 22 ++ .../clientset/versioned/scheme/register.go | 58 ++++++ .../versioned/typed/rainbond/v1alpha1/doc.go | 22 ++ .../typed/rainbond/v1alpha1/fake/doc.go | 22 ++ .../v1alpha1/fake/fake_rainbond_client.go | 50 +++++ .../v1alpha1/fake/fake_rainbondcluster.go | 142 +++++++++++++ .../v1alpha1/fake/fake_rainbondpackage.go | 142 +++++++++++++ .../v1alpha1/fake/fake_rbdcomponent.go | 142 +++++++++++++ .../rainbond/v1alpha1/generated_expansion.go | 27 +++ .../rainbond/v1alpha1/rainbond_client.go | 101 +++++++++ .../rainbond/v1alpha1/rainbondcluster.go | 193 ++++++++++++++++++ .../rainbond/v1alpha1/rainbondpackage.go | 193 ++++++++++++++++++ .../typed/rainbond/v1alpha1/rbdcomponent.go | 193 ++++++++++++++++++ .../informers/externalversions/factory.go | 182 +++++++++++++++++ .../informers/externalversions/generic.go | 68 ++++++ .../internalinterfaces/factory_interfaces.go | 42 ++++ .../externalversions/rainbond/interface.go | 48 +++++ .../rainbond/v1alpha1/interface.go | 61 ++++++ .../rainbond/v1alpha1/rainbondcluster.go | 91 +++++++++ .../rainbond/v1alpha1/rainbondpackage.go | 91 +++++++++ .../rainbond/v1alpha1/rbdcomponent.go | 91 +++++++++ .../rainbond/v1alpha1/expansion_generated.go | 45 ++++ .../rainbond/v1alpha1/rainbondcluster.go | 96 +++++++++ .../rainbond/v1alpha1/rainbondpackage.go | 96 +++++++++ .../listers/rainbond/v1alpha1/rbdcomponent.go | 96 +++++++++ 34 files changed, 2605 insertions(+), 72 deletions(-) create mode 100644 vendor/github.com/goodrain/rainbond-operator/pkg/generated/clientset/versioned/clientset.go create mode 100644 vendor/github.com/goodrain/rainbond-operator/pkg/generated/clientset/versioned/doc.go create mode 100644 vendor/github.com/goodrain/rainbond-operator/pkg/generated/clientset/versioned/fake/clientset_generated.go create mode 100644 vendor/github.com/goodrain/rainbond-operator/pkg/generated/clientset/versioned/fake/doc.go create mode 100644 vendor/github.com/goodrain/rainbond-operator/pkg/generated/clientset/versioned/fake/register.go create mode 100644 vendor/github.com/goodrain/rainbond-operator/pkg/generated/clientset/versioned/scheme/doc.go create mode 100644 vendor/github.com/goodrain/rainbond-operator/pkg/generated/clientset/versioned/scheme/register.go create mode 100644 vendor/github.com/goodrain/rainbond-operator/pkg/generated/clientset/versioned/typed/rainbond/v1alpha1/doc.go create mode 100644 vendor/github.com/goodrain/rainbond-operator/pkg/generated/clientset/versioned/typed/rainbond/v1alpha1/fake/doc.go create mode 100644 vendor/github.com/goodrain/rainbond-operator/pkg/generated/clientset/versioned/typed/rainbond/v1alpha1/fake/fake_rainbond_client.go create mode 100644 vendor/github.com/goodrain/rainbond-operator/pkg/generated/clientset/versioned/typed/rainbond/v1alpha1/fake/fake_rainbondcluster.go create mode 100644 vendor/github.com/goodrain/rainbond-operator/pkg/generated/clientset/versioned/typed/rainbond/v1alpha1/fake/fake_rainbondpackage.go create mode 100644 vendor/github.com/goodrain/rainbond-operator/pkg/generated/clientset/versioned/typed/rainbond/v1alpha1/fake/fake_rbdcomponent.go create mode 100644 vendor/github.com/goodrain/rainbond-operator/pkg/generated/clientset/versioned/typed/rainbond/v1alpha1/generated_expansion.go create mode 100644 vendor/github.com/goodrain/rainbond-operator/pkg/generated/clientset/versioned/typed/rainbond/v1alpha1/rainbond_client.go create mode 100644 vendor/github.com/goodrain/rainbond-operator/pkg/generated/clientset/versioned/typed/rainbond/v1alpha1/rainbondcluster.go create mode 100644 vendor/github.com/goodrain/rainbond-operator/pkg/generated/clientset/versioned/typed/rainbond/v1alpha1/rainbondpackage.go create mode 100644 vendor/github.com/goodrain/rainbond-operator/pkg/generated/clientset/versioned/typed/rainbond/v1alpha1/rbdcomponent.go create mode 100644 vendor/github.com/goodrain/rainbond-operator/pkg/generated/informers/externalversions/factory.go create mode 100644 vendor/github.com/goodrain/rainbond-operator/pkg/generated/informers/externalversions/generic.go create mode 100644 vendor/github.com/goodrain/rainbond-operator/pkg/generated/informers/externalversions/internalinterfaces/factory_interfaces.go create mode 100644 vendor/github.com/goodrain/rainbond-operator/pkg/generated/informers/externalversions/rainbond/interface.go create mode 100644 vendor/github.com/goodrain/rainbond-operator/pkg/generated/informers/externalversions/rainbond/v1alpha1/interface.go create mode 100644 vendor/github.com/goodrain/rainbond-operator/pkg/generated/informers/externalversions/rainbond/v1alpha1/rainbondcluster.go create mode 100644 vendor/github.com/goodrain/rainbond-operator/pkg/generated/informers/externalversions/rainbond/v1alpha1/rainbondpackage.go create mode 100644 vendor/github.com/goodrain/rainbond-operator/pkg/generated/informers/externalversions/rainbond/v1alpha1/rbdcomponent.go create mode 100644 vendor/github.com/goodrain/rainbond-operator/pkg/generated/listers/rainbond/v1alpha1/expansion_generated.go create mode 100644 vendor/github.com/goodrain/rainbond-operator/pkg/generated/listers/rainbond/v1alpha1/rainbondcluster.go create mode 100644 vendor/github.com/goodrain/rainbond-operator/pkg/generated/listers/rainbond/v1alpha1/rainbondpackage.go create mode 100644 vendor/github.com/goodrain/rainbond-operator/pkg/generated/listers/rainbond/v1alpha1/rbdcomponent.go diff --git a/grctl/clients/kubernetes.go b/grctl/clients/kubernetes.go index e283aa7be..05a832ae8 100644 --- a/grctl/clients/kubernetes.go +++ b/grctl/clients/kubernetes.go @@ -24,6 +24,7 @@ import ( "path" "github.com/Sirupsen/logrus" + "github.com/goodrain/rainbond-operator/pkg/generated/clientset/versioned" "github.com/goodrain/rainbond/builder/sources" k8sutil "github.com/goodrain/rainbond/util/k8s" "k8s.io/client-go/kubernetes" @@ -32,8 +33,8 @@ import ( //K8SClient K8SClient var K8SClient kubernetes.Interface -//CRClient rainbond custom resource client -var CRClient k8sutil.RainbondCustomResourceClient +//RainbondKubeClient rainbond custom resource client +var RainbondKubeClient versioned.Interface //InitClient init k8s client func InitClient(kubeconfig string) error { @@ -59,11 +60,6 @@ func InitClient(kubeconfig string) error { logrus.Error("Create kubernetes client error.", err.Error()) return err } - - CRClient, err = k8sutil.NewRainbondCustomResourceClient(config) - if err != nil { - logrus.Error("Create kubernetes rest client error.", err.Error()) - return err - } + RainbondKubeClient = versioned.NewForConfigOrDie(config) return nil } diff --git a/grctl/cmd/cluster.go b/grctl/cmd/cluster.go index 86d763fde..4e76cc036 100644 --- a/grctl/cmd/cluster.go +++ b/grctl/cmd/cluster.go @@ -241,7 +241,7 @@ func printComponentStatus() { } func printConfig(c *cli.Context) error { - config, err := clients.CRClient.GetRainbondCluster(metav1.GetOptions{}) + config, err := clients.RainbondKubeClient.RainbondV1alpha1().RainbondClusters("rbd-system").Get("rainbondcluster", metav1.GetOptions{}) if err != nil { showError(err.Error()) } diff --git a/grctl/cmd/install.go b/grctl/cmd/install.go index e07419006..99b2f4aa7 100644 --- a/grctl/cmd/install.go +++ b/grctl/cmd/install.go @@ -42,7 +42,7 @@ func NewCmdInstall() cli.Command { } regionAPIIP := c.StringSlice("gateway-ip") if len(regionAPIIP) == 0 { - cluster, err := clients.CRClient.GetRainbondCluster(metav1.GetOptions{}) + cluster, err := clients.RainbondKubeClient.RainbondV1alpha1().RainbondClusters("rbd-system").Get("rainbondcluster", metav1.GetOptions{}) if err != nil { showError(fmt.Sprintf("get rainbond cluster config failure %s", err.Error())) } diff --git a/util/k8s/k8s.go b/util/k8s/k8s.go index 203c2e464..4a3532cb6 100644 --- a/util/k8s/k8s.go +++ b/util/k8s/k8s.go @@ -116,65 +116,3 @@ func DefListEventsByPod(clientset kubernetes.Interface, pod *corev1.Pod) *corev1 events, _ := clientset.CoreV1().Events(pod.GetNamespace()).Search(scheme.Scheme, ref) return events } - -//ConfigureRainbondClient config rainbond custom resource -func ConfigureRainbondClient(config *rest.Config) { - groupversion := rainbondv1alpha1.SchemeGroupVersion - config.GroupVersion = &groupversion - config.APIPath = "/apis" - config.NegotiatedSerializer = scheme.Codecs.WithoutConversion() - if config.UserAgent == "" { - config.UserAgent = rest.DefaultKubernetesUserAgent() - } -} - -//RainbondCluster rainbond cluster -type RainbondCluster interface { - GetRainbondCluster(options metav1.GetOptions) (*rainbondv1alpha1.RainbondCluster, error) -} - -// RainbondCustomResourceClient client -type RainbondCustomResourceClient interface { - RESTClient() rest.Interface - RainbondCluster -} - -type rclient struct { - restClient *rest.RESTClient - namespace string -} - -func (r *rclient) GetRainbondCluster(options metav1.GetOptions) (*rainbondv1alpha1.RainbondCluster, error) { - var cluster rainbondv1alpha1.RainbondCluster - err := r.restClient.Get(). - Namespace(r.namespace). - Resource("rainbondclusters"). - Name("rainbondcluster"). - VersionedParams(&options, scheme.ParameterCodec). - Do().Into(&cluster) - if err != nil { - return nil, err - } - return &cluster, nil -} - -func (r *rclient) RESTClient() rest.Interface { - if r == nil { - return nil - } - return r.restClient -} - -//NewRainbondCustomResourceClient new rainbond cr client -func NewRainbondCustomResourceClient(config *rest.Config) (RainbondCustomResourceClient, error) { - copyConfig := *config - ConfigureRainbondClient(©Config) - restClient, err := NewRestClient(©Config) - if err != nil { - return nil, err - } - return &rclient{ - restClient: restClient, - namespace: "rbd-system", - }, nil -} diff --git a/vendor/github.com/goodrain/rainbond-operator/pkg/generated/clientset/versioned/clientset.go b/vendor/github.com/goodrain/rainbond-operator/pkg/generated/clientset/versioned/clientset.go new file mode 100644 index 000000000..7c9fb7493 --- /dev/null +++ b/vendor/github.com/goodrain/rainbond-operator/pkg/generated/clientset/versioned/clientset.go @@ -0,0 +1,99 @@ +// RAINBOND, Application Management Platform +// Copyright (C) 2014-2020 Goodrain Co., Ltd. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. For any non-GPL usage of Rainbond, +// one or multiple Commercial Licenses authorized by Goodrain Co., Ltd. +// must be obtained first. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +// Code generated by client-gen. DO NOT EDIT. + +package versioned + +import ( + "fmt" + + rainbondv1alpha1 "github.com/goodrain/rainbond-operator/pkg/generated/clientset/versioned/typed/rainbond/v1alpha1" + discovery "k8s.io/client-go/discovery" + rest "k8s.io/client-go/rest" + flowcontrol "k8s.io/client-go/util/flowcontrol" +) + +type Interface interface { + Discovery() discovery.DiscoveryInterface + RainbondV1alpha1() rainbondv1alpha1.RainbondV1alpha1Interface +} + +// Clientset contains the clients for groups. Each group has exactly one +// version included in a Clientset. +type Clientset struct { + *discovery.DiscoveryClient + rainbondV1alpha1 *rainbondv1alpha1.RainbondV1alpha1Client +} + +// RainbondV1alpha1 retrieves the RainbondV1alpha1Client +func (c *Clientset) RainbondV1alpha1() rainbondv1alpha1.RainbondV1alpha1Interface { + return c.rainbondV1alpha1 +} + +// Discovery retrieves the DiscoveryClient +func (c *Clientset) Discovery() discovery.DiscoveryInterface { + if c == nil { + return nil + } + return c.DiscoveryClient +} + +// NewForConfig creates a new Clientset for the given config. +// If config's RateLimiter is not set and QPS and Burst are acceptable, +// NewForConfig will generate a rate-limiter in configShallowCopy. +func NewForConfig(c *rest.Config) (*Clientset, error) { + configShallowCopy := *c + if configShallowCopy.RateLimiter == nil && configShallowCopy.QPS > 0 { + if configShallowCopy.Burst <= 0 { + return nil, fmt.Errorf("Burst is required to be greater than 0 when RateLimiter is not set and QPS is set to greater than 0") + } + configShallowCopy.RateLimiter = flowcontrol.NewTokenBucketRateLimiter(configShallowCopy.QPS, configShallowCopy.Burst) + } + var cs Clientset + var err error + cs.rainbondV1alpha1, err = rainbondv1alpha1.NewForConfig(&configShallowCopy) + if err != nil { + return nil, err + } + + cs.DiscoveryClient, err = discovery.NewDiscoveryClientForConfig(&configShallowCopy) + if err != nil { + return nil, err + } + return &cs, nil +} + +// NewForConfigOrDie creates a new Clientset for the given config and +// panics if there is an error in the config. +func NewForConfigOrDie(c *rest.Config) *Clientset { + var cs Clientset + cs.rainbondV1alpha1 = rainbondv1alpha1.NewForConfigOrDie(c) + + cs.DiscoveryClient = discovery.NewDiscoveryClientForConfigOrDie(c) + return &cs +} + +// New creates a new Clientset for the given RESTClient. +func New(c rest.Interface) *Clientset { + var cs Clientset + cs.rainbondV1alpha1 = rainbondv1alpha1.New(c) + + cs.DiscoveryClient = discovery.NewDiscoveryClient(c) + return &cs +} diff --git a/vendor/github.com/goodrain/rainbond-operator/pkg/generated/clientset/versioned/doc.go b/vendor/github.com/goodrain/rainbond-operator/pkg/generated/clientset/versioned/doc.go new file mode 100644 index 000000000..334d68972 --- /dev/null +++ b/vendor/github.com/goodrain/rainbond-operator/pkg/generated/clientset/versioned/doc.go @@ -0,0 +1,22 @@ +// RAINBOND, Application Management Platform +// Copyright (C) 2014-2020 Goodrain Co., Ltd. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. For any non-GPL usage of Rainbond, +// one or multiple Commercial Licenses authorized by Goodrain Co., Ltd. +// must be obtained first. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +// Code generated by client-gen. DO NOT EDIT. + +// This package has the automatically generated clientset. +package versioned diff --git a/vendor/github.com/goodrain/rainbond-operator/pkg/generated/clientset/versioned/fake/clientset_generated.go b/vendor/github.com/goodrain/rainbond-operator/pkg/generated/clientset/versioned/fake/clientset_generated.go new file mode 100644 index 000000000..032f4319c --- /dev/null +++ b/vendor/github.com/goodrain/rainbond-operator/pkg/generated/clientset/versioned/fake/clientset_generated.go @@ -0,0 +1,84 @@ +// RAINBOND, Application Management Platform +// Copyright (C) 2014-2020 Goodrain Co., Ltd. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. For any non-GPL usage of Rainbond, +// one or multiple Commercial Licenses authorized by Goodrain Co., Ltd. +// must be obtained first. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +// Code generated by client-gen. DO NOT EDIT. + +package fake + +import ( + clientset "github.com/goodrain/rainbond-operator/pkg/generated/clientset/versioned" + rainbondv1alpha1 "github.com/goodrain/rainbond-operator/pkg/generated/clientset/versioned/typed/rainbond/v1alpha1" + fakerainbondv1alpha1 "github.com/goodrain/rainbond-operator/pkg/generated/clientset/versioned/typed/rainbond/v1alpha1/fake" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/watch" + "k8s.io/client-go/discovery" + fakediscovery "k8s.io/client-go/discovery/fake" + "k8s.io/client-go/testing" +) + +// NewSimpleClientset returns a clientset that will respond with the provided objects. +// It's backed by a very simple object tracker that processes creates, updates and deletions as-is, +// without applying any validations and/or defaults. It shouldn't be considered a replacement +// for a real clientset and is mostly useful in simple unit tests. +func NewSimpleClientset(objects ...runtime.Object) *Clientset { + o := testing.NewObjectTracker(scheme, codecs.UniversalDecoder()) + for _, obj := range objects { + if err := o.Add(obj); err != nil { + panic(err) + } + } + + cs := &Clientset{tracker: o} + cs.discovery = &fakediscovery.FakeDiscovery{Fake: &cs.Fake} + cs.AddReactor("*", "*", testing.ObjectReaction(o)) + cs.AddWatchReactor("*", func(action testing.Action) (handled bool, ret watch.Interface, err error) { + gvr := action.GetResource() + ns := action.GetNamespace() + watch, err := o.Watch(gvr, ns) + if err != nil { + return false, nil, err + } + return true, watch, nil + }) + + return cs +} + +// Clientset implements clientset.Interface. Meant to be embedded into a +// struct to get a default implementation. This makes faking out just the method +// you want to test easier. +type Clientset struct { + testing.Fake + discovery *fakediscovery.FakeDiscovery + tracker testing.ObjectTracker +} + +func (c *Clientset) Discovery() discovery.DiscoveryInterface { + return c.discovery +} + +func (c *Clientset) Tracker() testing.ObjectTracker { + return c.tracker +} + +var _ clientset.Interface = &Clientset{} + +// RainbondV1alpha1 retrieves the RainbondV1alpha1Client +func (c *Clientset) RainbondV1alpha1() rainbondv1alpha1.RainbondV1alpha1Interface { + return &fakerainbondv1alpha1.FakeRainbondV1alpha1{Fake: &c.Fake} +} diff --git a/vendor/github.com/goodrain/rainbond-operator/pkg/generated/clientset/versioned/fake/doc.go b/vendor/github.com/goodrain/rainbond-operator/pkg/generated/clientset/versioned/fake/doc.go new file mode 100644 index 000000000..907759f32 --- /dev/null +++ b/vendor/github.com/goodrain/rainbond-operator/pkg/generated/clientset/versioned/fake/doc.go @@ -0,0 +1,22 @@ +// RAINBOND, Application Management Platform +// Copyright (C) 2014-2020 Goodrain Co., Ltd. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. For any non-GPL usage of Rainbond, +// one or multiple Commercial Licenses authorized by Goodrain Co., Ltd. +// must be obtained first. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +// Code generated by client-gen. DO NOT EDIT. + +// This package has the automatically generated fake clientset. +package fake diff --git a/vendor/github.com/goodrain/rainbond-operator/pkg/generated/clientset/versioned/fake/register.go b/vendor/github.com/goodrain/rainbond-operator/pkg/generated/clientset/versioned/fake/register.go new file mode 100644 index 000000000..d1105f886 --- /dev/null +++ b/vendor/github.com/goodrain/rainbond-operator/pkg/generated/clientset/versioned/fake/register.go @@ -0,0 +1,58 @@ +// RAINBOND, Application Management Platform +// Copyright (C) 2014-2020 Goodrain Co., Ltd. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. For any non-GPL usage of Rainbond, +// one or multiple Commercial Licenses authorized by Goodrain Co., Ltd. +// must be obtained first. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +// Code generated by client-gen. DO NOT EDIT. + +package fake + +import ( + rainbondv1alpha1 "github.com/goodrain/rainbond-operator/pkg/apis/rainbond/v1alpha1" + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + runtime "k8s.io/apimachinery/pkg/runtime" + schema "k8s.io/apimachinery/pkg/runtime/schema" + serializer "k8s.io/apimachinery/pkg/runtime/serializer" + utilruntime "k8s.io/apimachinery/pkg/util/runtime" +) + +var scheme = runtime.NewScheme() +var codecs = serializer.NewCodecFactory(scheme) +var parameterCodec = runtime.NewParameterCodec(scheme) +var localSchemeBuilder = runtime.SchemeBuilder{ + rainbondv1alpha1.AddToScheme, +} + +// AddToScheme adds all types of this clientset into the given scheme. This allows composition +// of clientsets, like in: +// +// import ( +// "k8s.io/client-go/kubernetes" +// clientsetscheme "k8s.io/client-go/kubernetes/scheme" +// aggregatorclientsetscheme "k8s.io/kube-aggregator/pkg/client/clientset_generated/clientset/scheme" +// ) +// +// kclientset, _ := kubernetes.NewForConfig(c) +// _ = aggregatorclientsetscheme.AddToScheme(clientsetscheme.Scheme) +// +// After this, RawExtensions in Kubernetes types will serialize kube-aggregator types +// correctly. +var AddToScheme = localSchemeBuilder.AddToScheme + +func init() { + v1.AddToGroupVersion(scheme, schema.GroupVersion{Version: "v1"}) + utilruntime.Must(AddToScheme(scheme)) +} diff --git a/vendor/github.com/goodrain/rainbond-operator/pkg/generated/clientset/versioned/scheme/doc.go b/vendor/github.com/goodrain/rainbond-operator/pkg/generated/clientset/versioned/scheme/doc.go new file mode 100644 index 000000000..0392755e2 --- /dev/null +++ b/vendor/github.com/goodrain/rainbond-operator/pkg/generated/clientset/versioned/scheme/doc.go @@ -0,0 +1,22 @@ +// RAINBOND, Application Management Platform +// Copyright (C) 2014-2020 Goodrain Co., Ltd. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. For any non-GPL usage of Rainbond, +// one or multiple Commercial Licenses authorized by Goodrain Co., Ltd. +// must be obtained first. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +// Code generated by client-gen. DO NOT EDIT. + +// This package contains the scheme of the automatically generated clientset. +package scheme diff --git a/vendor/github.com/goodrain/rainbond-operator/pkg/generated/clientset/versioned/scheme/register.go b/vendor/github.com/goodrain/rainbond-operator/pkg/generated/clientset/versioned/scheme/register.go new file mode 100644 index 000000000..7ea54ac06 --- /dev/null +++ b/vendor/github.com/goodrain/rainbond-operator/pkg/generated/clientset/versioned/scheme/register.go @@ -0,0 +1,58 @@ +// RAINBOND, Application Management Platform +// Copyright (C) 2014-2020 Goodrain Co., Ltd. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. For any non-GPL usage of Rainbond, +// one or multiple Commercial Licenses authorized by Goodrain Co., Ltd. +// must be obtained first. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +// Code generated by client-gen. DO NOT EDIT. + +package scheme + +import ( + rainbondv1alpha1 "github.com/goodrain/rainbond-operator/pkg/apis/rainbond/v1alpha1" + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + runtime "k8s.io/apimachinery/pkg/runtime" + schema "k8s.io/apimachinery/pkg/runtime/schema" + serializer "k8s.io/apimachinery/pkg/runtime/serializer" + utilruntime "k8s.io/apimachinery/pkg/util/runtime" +) + +var Scheme = runtime.NewScheme() +var Codecs = serializer.NewCodecFactory(Scheme) +var ParameterCodec = runtime.NewParameterCodec(Scheme) +var localSchemeBuilder = runtime.SchemeBuilder{ + rainbondv1alpha1.AddToScheme, +} + +// AddToScheme adds all types of this clientset into the given scheme. This allows composition +// of clientsets, like in: +// +// import ( +// "k8s.io/client-go/kubernetes" +// clientsetscheme "k8s.io/client-go/kubernetes/scheme" +// aggregatorclientsetscheme "k8s.io/kube-aggregator/pkg/client/clientset_generated/clientset/scheme" +// ) +// +// kclientset, _ := kubernetes.NewForConfig(c) +// _ = aggregatorclientsetscheme.AddToScheme(clientsetscheme.Scheme) +// +// After this, RawExtensions in Kubernetes types will serialize kube-aggregator types +// correctly. +var AddToScheme = localSchemeBuilder.AddToScheme + +func init() { + v1.AddToGroupVersion(Scheme, schema.GroupVersion{Version: "v1"}) + utilruntime.Must(AddToScheme(Scheme)) +} diff --git a/vendor/github.com/goodrain/rainbond-operator/pkg/generated/clientset/versioned/typed/rainbond/v1alpha1/doc.go b/vendor/github.com/goodrain/rainbond-operator/pkg/generated/clientset/versioned/typed/rainbond/v1alpha1/doc.go new file mode 100644 index 000000000..a9692d234 --- /dev/null +++ b/vendor/github.com/goodrain/rainbond-operator/pkg/generated/clientset/versioned/typed/rainbond/v1alpha1/doc.go @@ -0,0 +1,22 @@ +// RAINBOND, Application Management Platform +// Copyright (C) 2014-2020 Goodrain Co., Ltd. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. For any non-GPL usage of Rainbond, +// one or multiple Commercial Licenses authorized by Goodrain Co., Ltd. +// must be obtained first. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +// Code generated by client-gen. DO NOT EDIT. + +// This package has the automatically generated typed clients. +package v1alpha1 diff --git a/vendor/github.com/goodrain/rainbond-operator/pkg/generated/clientset/versioned/typed/rainbond/v1alpha1/fake/doc.go b/vendor/github.com/goodrain/rainbond-operator/pkg/generated/clientset/versioned/typed/rainbond/v1alpha1/fake/doc.go new file mode 100644 index 000000000..07a9864cb --- /dev/null +++ b/vendor/github.com/goodrain/rainbond-operator/pkg/generated/clientset/versioned/typed/rainbond/v1alpha1/fake/doc.go @@ -0,0 +1,22 @@ +// RAINBOND, Application Management Platform +// Copyright (C) 2014-2020 Goodrain Co., Ltd. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. For any non-GPL usage of Rainbond, +// one or multiple Commercial Licenses authorized by Goodrain Co., Ltd. +// must be obtained first. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +// Code generated by client-gen. DO NOT EDIT. + +// Package fake has the automatically generated clients. +package fake diff --git a/vendor/github.com/goodrain/rainbond-operator/pkg/generated/clientset/versioned/typed/rainbond/v1alpha1/fake/fake_rainbond_client.go b/vendor/github.com/goodrain/rainbond-operator/pkg/generated/clientset/versioned/typed/rainbond/v1alpha1/fake/fake_rainbond_client.go new file mode 100644 index 000000000..4b3c27747 --- /dev/null +++ b/vendor/github.com/goodrain/rainbond-operator/pkg/generated/clientset/versioned/typed/rainbond/v1alpha1/fake/fake_rainbond_client.go @@ -0,0 +1,50 @@ +// RAINBOND, Application Management Platform +// Copyright (C) 2014-2020 Goodrain Co., Ltd. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. For any non-GPL usage of Rainbond, +// one or multiple Commercial Licenses authorized by Goodrain Co., Ltd. +// must be obtained first. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +// Code generated by client-gen. DO NOT EDIT. + +package fake + +import ( + v1alpha1 "github.com/goodrain/rainbond-operator/pkg/generated/clientset/versioned/typed/rainbond/v1alpha1" + rest "k8s.io/client-go/rest" + testing "k8s.io/client-go/testing" +) + +type FakeRainbondV1alpha1 struct { + *testing.Fake +} + +func (c *FakeRainbondV1alpha1) RainbondClusters(namespace string) v1alpha1.RainbondClusterInterface { + return &FakeRainbondClusters{c, namespace} +} + +func (c *FakeRainbondV1alpha1) RainbondPackages(namespace string) v1alpha1.RainbondPackageInterface { + return &FakeRainbondPackages{c, namespace} +} + +func (c *FakeRainbondV1alpha1) RbdComponents(namespace string) v1alpha1.RbdComponentInterface { + return &FakeRbdComponents{c, namespace} +} + +// RESTClient returns a RESTClient that is used to communicate +// with API server by this client implementation. +func (c *FakeRainbondV1alpha1) RESTClient() rest.Interface { + var ret *rest.RESTClient + return ret +} diff --git a/vendor/github.com/goodrain/rainbond-operator/pkg/generated/clientset/versioned/typed/rainbond/v1alpha1/fake/fake_rainbondcluster.go b/vendor/github.com/goodrain/rainbond-operator/pkg/generated/clientset/versioned/typed/rainbond/v1alpha1/fake/fake_rainbondcluster.go new file mode 100644 index 000000000..4d07d82fd --- /dev/null +++ b/vendor/github.com/goodrain/rainbond-operator/pkg/generated/clientset/versioned/typed/rainbond/v1alpha1/fake/fake_rainbondcluster.go @@ -0,0 +1,142 @@ +// RAINBOND, Application Management Platform +// Copyright (C) 2014-2020 Goodrain Co., Ltd. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. For any non-GPL usage of Rainbond, +// one or multiple Commercial Licenses authorized by Goodrain Co., Ltd. +// must be obtained first. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +// Code generated by client-gen. DO NOT EDIT. + +package fake + +import ( + v1alpha1 "github.com/goodrain/rainbond-operator/pkg/apis/rainbond/v1alpha1" + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + labels "k8s.io/apimachinery/pkg/labels" + schema "k8s.io/apimachinery/pkg/runtime/schema" + types "k8s.io/apimachinery/pkg/types" + watch "k8s.io/apimachinery/pkg/watch" + testing "k8s.io/client-go/testing" +) + +// FakeRainbondClusters implements RainbondClusterInterface +type FakeRainbondClusters struct { + Fake *FakeRainbondV1alpha1 + ns string +} + +var rainbondclustersResource = schema.GroupVersionResource{Group: "rainbond.io", Version: "v1alpha1", Resource: "rainbondclusters"} + +var rainbondclustersKind = schema.GroupVersionKind{Group: "rainbond.io", Version: "v1alpha1", Kind: "RainbondCluster"} + +// Get takes name of the rainbondCluster, and returns the corresponding rainbondCluster object, and an error if there is any. +func (c *FakeRainbondClusters) Get(name string, options v1.GetOptions) (result *v1alpha1.RainbondCluster, err error) { + obj, err := c.Fake. + Invokes(testing.NewGetAction(rainbondclustersResource, c.ns, name), &v1alpha1.RainbondCluster{}) + + if obj == nil { + return nil, err + } + return obj.(*v1alpha1.RainbondCluster), err +} + +// List takes label and field selectors, and returns the list of RainbondClusters that match those selectors. +func (c *FakeRainbondClusters) List(opts v1.ListOptions) (result *v1alpha1.RainbondClusterList, err error) { + obj, err := c.Fake. + Invokes(testing.NewListAction(rainbondclustersResource, rainbondclustersKind, c.ns, opts), &v1alpha1.RainbondClusterList{}) + + if obj == nil { + return nil, err + } + + label, _, _ := testing.ExtractFromListOptions(opts) + if label == nil { + label = labels.Everything() + } + list := &v1alpha1.RainbondClusterList{ListMeta: obj.(*v1alpha1.RainbondClusterList).ListMeta} + for _, item := range obj.(*v1alpha1.RainbondClusterList).Items { + if label.Matches(labels.Set(item.Labels)) { + list.Items = append(list.Items, item) + } + } + return list, err +} + +// Watch returns a watch.Interface that watches the requested rainbondClusters. +func (c *FakeRainbondClusters) Watch(opts v1.ListOptions) (watch.Interface, error) { + return c.Fake. + InvokesWatch(testing.NewWatchAction(rainbondclustersResource, c.ns, opts)) + +} + +// Create takes the representation of a rainbondCluster and creates it. Returns the server's representation of the rainbondCluster, and an error, if there is any. +func (c *FakeRainbondClusters) Create(rainbondCluster *v1alpha1.RainbondCluster) (result *v1alpha1.RainbondCluster, err error) { + obj, err := c.Fake. + Invokes(testing.NewCreateAction(rainbondclustersResource, c.ns, rainbondCluster), &v1alpha1.RainbondCluster{}) + + if obj == nil { + return nil, err + } + return obj.(*v1alpha1.RainbondCluster), err +} + +// Update takes the representation of a rainbondCluster and updates it. Returns the server's representation of the rainbondCluster, and an error, if there is any. +func (c *FakeRainbondClusters) Update(rainbondCluster *v1alpha1.RainbondCluster) (result *v1alpha1.RainbondCluster, err error) { + obj, err := c.Fake. + Invokes(testing.NewUpdateAction(rainbondclustersResource, c.ns, rainbondCluster), &v1alpha1.RainbondCluster{}) + + if obj == nil { + return nil, err + } + return obj.(*v1alpha1.RainbondCluster), err +} + +// UpdateStatus was generated because the type contains a Status member. +// Add a +genclient:noStatus comment above the type to avoid generating UpdateStatus(). +func (c *FakeRainbondClusters) UpdateStatus(rainbondCluster *v1alpha1.RainbondCluster) (*v1alpha1.RainbondCluster, error) { + obj, err := c.Fake. + Invokes(testing.NewUpdateSubresourceAction(rainbondclustersResource, "status", c.ns, rainbondCluster), &v1alpha1.RainbondCluster{}) + + if obj == nil { + return nil, err + } + return obj.(*v1alpha1.RainbondCluster), err +} + +// Delete takes name of the rainbondCluster and deletes it. Returns an error if one occurs. +func (c *FakeRainbondClusters) Delete(name string, options *v1.DeleteOptions) error { + _, err := c.Fake. + Invokes(testing.NewDeleteAction(rainbondclustersResource, c.ns, name), &v1alpha1.RainbondCluster{}) + + return err +} + +// DeleteCollection deletes a collection of objects. +func (c *FakeRainbondClusters) DeleteCollection(options *v1.DeleteOptions, listOptions v1.ListOptions) error { + action := testing.NewDeleteCollectionAction(rainbondclustersResource, c.ns, listOptions) + + _, err := c.Fake.Invokes(action, &v1alpha1.RainbondClusterList{}) + return err +} + +// Patch applies the patch and returns the patched rainbondCluster. +func (c *FakeRainbondClusters) Patch(name string, pt types.PatchType, data []byte, subresources ...string) (result *v1alpha1.RainbondCluster, err error) { + obj, err := c.Fake. + Invokes(testing.NewPatchSubresourceAction(rainbondclustersResource, c.ns, name, pt, data, subresources...), &v1alpha1.RainbondCluster{}) + + if obj == nil { + return nil, err + } + return obj.(*v1alpha1.RainbondCluster), err +} diff --git a/vendor/github.com/goodrain/rainbond-operator/pkg/generated/clientset/versioned/typed/rainbond/v1alpha1/fake/fake_rainbondpackage.go b/vendor/github.com/goodrain/rainbond-operator/pkg/generated/clientset/versioned/typed/rainbond/v1alpha1/fake/fake_rainbondpackage.go new file mode 100644 index 000000000..859455f58 --- /dev/null +++ b/vendor/github.com/goodrain/rainbond-operator/pkg/generated/clientset/versioned/typed/rainbond/v1alpha1/fake/fake_rainbondpackage.go @@ -0,0 +1,142 @@ +// RAINBOND, Application Management Platform +// Copyright (C) 2014-2020 Goodrain Co., Ltd. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. For any non-GPL usage of Rainbond, +// one or multiple Commercial Licenses authorized by Goodrain Co., Ltd. +// must be obtained first. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +// Code generated by client-gen. DO NOT EDIT. + +package fake + +import ( + v1alpha1 "github.com/goodrain/rainbond-operator/pkg/apis/rainbond/v1alpha1" + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + labels "k8s.io/apimachinery/pkg/labels" + schema "k8s.io/apimachinery/pkg/runtime/schema" + types "k8s.io/apimachinery/pkg/types" + watch "k8s.io/apimachinery/pkg/watch" + testing "k8s.io/client-go/testing" +) + +// FakeRainbondPackages implements RainbondPackageInterface +type FakeRainbondPackages struct { + Fake *FakeRainbondV1alpha1 + ns string +} + +var rainbondpackagesResource = schema.GroupVersionResource{Group: "rainbond.io", Version: "v1alpha1", Resource: "rainbondpackages"} + +var rainbondpackagesKind = schema.GroupVersionKind{Group: "rainbond.io", Version: "v1alpha1", Kind: "RainbondPackage"} + +// Get takes name of the rainbondPackage, and returns the corresponding rainbondPackage object, and an error if there is any. +func (c *FakeRainbondPackages) Get(name string, options v1.GetOptions) (result *v1alpha1.RainbondPackage, err error) { + obj, err := c.Fake. + Invokes(testing.NewGetAction(rainbondpackagesResource, c.ns, name), &v1alpha1.RainbondPackage{}) + + if obj == nil { + return nil, err + } + return obj.(*v1alpha1.RainbondPackage), err +} + +// List takes label and field selectors, and returns the list of RainbondPackages that match those selectors. +func (c *FakeRainbondPackages) List(opts v1.ListOptions) (result *v1alpha1.RainbondPackageList, err error) { + obj, err := c.Fake. + Invokes(testing.NewListAction(rainbondpackagesResource, rainbondpackagesKind, c.ns, opts), &v1alpha1.RainbondPackageList{}) + + if obj == nil { + return nil, err + } + + label, _, _ := testing.ExtractFromListOptions(opts) + if label == nil { + label = labels.Everything() + } + list := &v1alpha1.RainbondPackageList{ListMeta: obj.(*v1alpha1.RainbondPackageList).ListMeta} + for _, item := range obj.(*v1alpha1.RainbondPackageList).Items { + if label.Matches(labels.Set(item.Labels)) { + list.Items = append(list.Items, item) + } + } + return list, err +} + +// Watch returns a watch.Interface that watches the requested rainbondPackages. +func (c *FakeRainbondPackages) Watch(opts v1.ListOptions) (watch.Interface, error) { + return c.Fake. + InvokesWatch(testing.NewWatchAction(rainbondpackagesResource, c.ns, opts)) + +} + +// Create takes the representation of a rainbondPackage and creates it. Returns the server's representation of the rainbondPackage, and an error, if there is any. +func (c *FakeRainbondPackages) Create(rainbondPackage *v1alpha1.RainbondPackage) (result *v1alpha1.RainbondPackage, err error) { + obj, err := c.Fake. + Invokes(testing.NewCreateAction(rainbondpackagesResource, c.ns, rainbondPackage), &v1alpha1.RainbondPackage{}) + + if obj == nil { + return nil, err + } + return obj.(*v1alpha1.RainbondPackage), err +} + +// Update takes the representation of a rainbondPackage and updates it. Returns the server's representation of the rainbondPackage, and an error, if there is any. +func (c *FakeRainbondPackages) Update(rainbondPackage *v1alpha1.RainbondPackage) (result *v1alpha1.RainbondPackage, err error) { + obj, err := c.Fake. + Invokes(testing.NewUpdateAction(rainbondpackagesResource, c.ns, rainbondPackage), &v1alpha1.RainbondPackage{}) + + if obj == nil { + return nil, err + } + return obj.(*v1alpha1.RainbondPackage), err +} + +// UpdateStatus was generated because the type contains a Status member. +// Add a +genclient:noStatus comment above the type to avoid generating UpdateStatus(). +func (c *FakeRainbondPackages) UpdateStatus(rainbondPackage *v1alpha1.RainbondPackage) (*v1alpha1.RainbondPackage, error) { + obj, err := c.Fake. + Invokes(testing.NewUpdateSubresourceAction(rainbondpackagesResource, "status", c.ns, rainbondPackage), &v1alpha1.RainbondPackage{}) + + if obj == nil { + return nil, err + } + return obj.(*v1alpha1.RainbondPackage), err +} + +// Delete takes name of the rainbondPackage and deletes it. Returns an error if one occurs. +func (c *FakeRainbondPackages) Delete(name string, options *v1.DeleteOptions) error { + _, err := c.Fake. + Invokes(testing.NewDeleteAction(rainbondpackagesResource, c.ns, name), &v1alpha1.RainbondPackage{}) + + return err +} + +// DeleteCollection deletes a collection of objects. +func (c *FakeRainbondPackages) DeleteCollection(options *v1.DeleteOptions, listOptions v1.ListOptions) error { + action := testing.NewDeleteCollectionAction(rainbondpackagesResource, c.ns, listOptions) + + _, err := c.Fake.Invokes(action, &v1alpha1.RainbondPackageList{}) + return err +} + +// Patch applies the patch and returns the patched rainbondPackage. +func (c *FakeRainbondPackages) Patch(name string, pt types.PatchType, data []byte, subresources ...string) (result *v1alpha1.RainbondPackage, err error) { + obj, err := c.Fake. + Invokes(testing.NewPatchSubresourceAction(rainbondpackagesResource, c.ns, name, pt, data, subresources...), &v1alpha1.RainbondPackage{}) + + if obj == nil { + return nil, err + } + return obj.(*v1alpha1.RainbondPackage), err +} diff --git a/vendor/github.com/goodrain/rainbond-operator/pkg/generated/clientset/versioned/typed/rainbond/v1alpha1/fake/fake_rbdcomponent.go b/vendor/github.com/goodrain/rainbond-operator/pkg/generated/clientset/versioned/typed/rainbond/v1alpha1/fake/fake_rbdcomponent.go new file mode 100644 index 000000000..3184939bf --- /dev/null +++ b/vendor/github.com/goodrain/rainbond-operator/pkg/generated/clientset/versioned/typed/rainbond/v1alpha1/fake/fake_rbdcomponent.go @@ -0,0 +1,142 @@ +// RAINBOND, Application Management Platform +// Copyright (C) 2014-2020 Goodrain Co., Ltd. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. For any non-GPL usage of Rainbond, +// one or multiple Commercial Licenses authorized by Goodrain Co., Ltd. +// must be obtained first. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +// Code generated by client-gen. DO NOT EDIT. + +package fake + +import ( + v1alpha1 "github.com/goodrain/rainbond-operator/pkg/apis/rainbond/v1alpha1" + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + labels "k8s.io/apimachinery/pkg/labels" + schema "k8s.io/apimachinery/pkg/runtime/schema" + types "k8s.io/apimachinery/pkg/types" + watch "k8s.io/apimachinery/pkg/watch" + testing "k8s.io/client-go/testing" +) + +// FakeRbdComponents implements RbdComponentInterface +type FakeRbdComponents struct { + Fake *FakeRainbondV1alpha1 + ns string +} + +var rbdcomponentsResource = schema.GroupVersionResource{Group: "rainbond.io", Version: "v1alpha1", Resource: "rbdcomponents"} + +var rbdcomponentsKind = schema.GroupVersionKind{Group: "rainbond.io", Version: "v1alpha1", Kind: "RbdComponent"} + +// Get takes name of the rbdComponent, and returns the corresponding rbdComponent object, and an error if there is any. +func (c *FakeRbdComponents) Get(name string, options v1.GetOptions) (result *v1alpha1.RbdComponent, err error) { + obj, err := c.Fake. + Invokes(testing.NewGetAction(rbdcomponentsResource, c.ns, name), &v1alpha1.RbdComponent{}) + + if obj == nil { + return nil, err + } + return obj.(*v1alpha1.RbdComponent), err +} + +// List takes label and field selectors, and returns the list of RbdComponents that match those selectors. +func (c *FakeRbdComponents) List(opts v1.ListOptions) (result *v1alpha1.RbdComponentList, err error) { + obj, err := c.Fake. + Invokes(testing.NewListAction(rbdcomponentsResource, rbdcomponentsKind, c.ns, opts), &v1alpha1.RbdComponentList{}) + + if obj == nil { + return nil, err + } + + label, _, _ := testing.ExtractFromListOptions(opts) + if label == nil { + label = labels.Everything() + } + list := &v1alpha1.RbdComponentList{ListMeta: obj.(*v1alpha1.RbdComponentList).ListMeta} + for _, item := range obj.(*v1alpha1.RbdComponentList).Items { + if label.Matches(labels.Set(item.Labels)) { + list.Items = append(list.Items, item) + } + } + return list, err +} + +// Watch returns a watch.Interface that watches the requested rbdComponents. +func (c *FakeRbdComponents) Watch(opts v1.ListOptions) (watch.Interface, error) { + return c.Fake. + InvokesWatch(testing.NewWatchAction(rbdcomponentsResource, c.ns, opts)) + +} + +// Create takes the representation of a rbdComponent and creates it. Returns the server's representation of the rbdComponent, and an error, if there is any. +func (c *FakeRbdComponents) Create(rbdComponent *v1alpha1.RbdComponent) (result *v1alpha1.RbdComponent, err error) { + obj, err := c.Fake. + Invokes(testing.NewCreateAction(rbdcomponentsResource, c.ns, rbdComponent), &v1alpha1.RbdComponent{}) + + if obj == nil { + return nil, err + } + return obj.(*v1alpha1.RbdComponent), err +} + +// Update takes the representation of a rbdComponent and updates it. Returns the server's representation of the rbdComponent, and an error, if there is any. +func (c *FakeRbdComponents) Update(rbdComponent *v1alpha1.RbdComponent) (result *v1alpha1.RbdComponent, err error) { + obj, err := c.Fake. + Invokes(testing.NewUpdateAction(rbdcomponentsResource, c.ns, rbdComponent), &v1alpha1.RbdComponent{}) + + if obj == nil { + return nil, err + } + return obj.(*v1alpha1.RbdComponent), err +} + +// UpdateStatus was generated because the type contains a Status member. +// Add a +genclient:noStatus comment above the type to avoid generating UpdateStatus(). +func (c *FakeRbdComponents) UpdateStatus(rbdComponent *v1alpha1.RbdComponent) (*v1alpha1.RbdComponent, error) { + obj, err := c.Fake. + Invokes(testing.NewUpdateSubresourceAction(rbdcomponentsResource, "status", c.ns, rbdComponent), &v1alpha1.RbdComponent{}) + + if obj == nil { + return nil, err + } + return obj.(*v1alpha1.RbdComponent), err +} + +// Delete takes name of the rbdComponent and deletes it. Returns an error if one occurs. +func (c *FakeRbdComponents) Delete(name string, options *v1.DeleteOptions) error { + _, err := c.Fake. + Invokes(testing.NewDeleteAction(rbdcomponentsResource, c.ns, name), &v1alpha1.RbdComponent{}) + + return err +} + +// DeleteCollection deletes a collection of objects. +func (c *FakeRbdComponents) DeleteCollection(options *v1.DeleteOptions, listOptions v1.ListOptions) error { + action := testing.NewDeleteCollectionAction(rbdcomponentsResource, c.ns, listOptions) + + _, err := c.Fake.Invokes(action, &v1alpha1.RbdComponentList{}) + return err +} + +// Patch applies the patch and returns the patched rbdComponent. +func (c *FakeRbdComponents) Patch(name string, pt types.PatchType, data []byte, subresources ...string) (result *v1alpha1.RbdComponent, err error) { + obj, err := c.Fake. + Invokes(testing.NewPatchSubresourceAction(rbdcomponentsResource, c.ns, name, pt, data, subresources...), &v1alpha1.RbdComponent{}) + + if obj == nil { + return nil, err + } + return obj.(*v1alpha1.RbdComponent), err +} diff --git a/vendor/github.com/goodrain/rainbond-operator/pkg/generated/clientset/versioned/typed/rainbond/v1alpha1/generated_expansion.go b/vendor/github.com/goodrain/rainbond-operator/pkg/generated/clientset/versioned/typed/rainbond/v1alpha1/generated_expansion.go new file mode 100644 index 000000000..03a23694b --- /dev/null +++ b/vendor/github.com/goodrain/rainbond-operator/pkg/generated/clientset/versioned/typed/rainbond/v1alpha1/generated_expansion.go @@ -0,0 +1,27 @@ +// RAINBOND, Application Management Platform +// Copyright (C) 2014-2020 Goodrain Co., Ltd. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. For any non-GPL usage of Rainbond, +// one or multiple Commercial Licenses authorized by Goodrain Co., Ltd. +// must be obtained first. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +// Code generated by client-gen. DO NOT EDIT. + +package v1alpha1 + +type RainbondClusterExpansion interface{} + +type RainbondPackageExpansion interface{} + +type RbdComponentExpansion interface{} diff --git a/vendor/github.com/goodrain/rainbond-operator/pkg/generated/clientset/versioned/typed/rainbond/v1alpha1/rainbond_client.go b/vendor/github.com/goodrain/rainbond-operator/pkg/generated/clientset/versioned/typed/rainbond/v1alpha1/rainbond_client.go new file mode 100644 index 000000000..bb1d22db2 --- /dev/null +++ b/vendor/github.com/goodrain/rainbond-operator/pkg/generated/clientset/versioned/typed/rainbond/v1alpha1/rainbond_client.go @@ -0,0 +1,101 @@ +// RAINBOND, Application Management Platform +// Copyright (C) 2014-2020 Goodrain Co., Ltd. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. For any non-GPL usage of Rainbond, +// one or multiple Commercial Licenses authorized by Goodrain Co., Ltd. +// must be obtained first. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +// Code generated by client-gen. DO NOT EDIT. + +package v1alpha1 + +import ( + v1alpha1 "github.com/goodrain/rainbond-operator/pkg/apis/rainbond/v1alpha1" + "github.com/goodrain/rainbond-operator/pkg/generated/clientset/versioned/scheme" + rest "k8s.io/client-go/rest" +) + +type RainbondV1alpha1Interface interface { + RESTClient() rest.Interface + RainbondClustersGetter + RainbondPackagesGetter + RbdComponentsGetter +} + +// RainbondV1alpha1Client is used to interact with features provided by the rainbond.io group. +type RainbondV1alpha1Client struct { + restClient rest.Interface +} + +func (c *RainbondV1alpha1Client) RainbondClusters(namespace string) RainbondClusterInterface { + return newRainbondClusters(c, namespace) +} + +func (c *RainbondV1alpha1Client) RainbondPackages(namespace string) RainbondPackageInterface { + return newRainbondPackages(c, namespace) +} + +func (c *RainbondV1alpha1Client) RbdComponents(namespace string) RbdComponentInterface { + return newRbdComponents(c, namespace) +} + +// NewForConfig creates a new RainbondV1alpha1Client for the given config. +func NewForConfig(c *rest.Config) (*RainbondV1alpha1Client, error) { + config := *c + if err := setConfigDefaults(&config); err != nil { + return nil, err + } + client, err := rest.RESTClientFor(&config) + if err != nil { + return nil, err + } + return &RainbondV1alpha1Client{client}, nil +} + +// NewForConfigOrDie creates a new RainbondV1alpha1Client for the given config and +// panics if there is an error in the config. +func NewForConfigOrDie(c *rest.Config) *RainbondV1alpha1Client { + client, err := NewForConfig(c) + if err != nil { + panic(err) + } + return client +} + +// New creates a new RainbondV1alpha1Client for the given RESTClient. +func New(c rest.Interface) *RainbondV1alpha1Client { + return &RainbondV1alpha1Client{c} +} + +func setConfigDefaults(config *rest.Config) error { + gv := v1alpha1.SchemeGroupVersion + config.GroupVersion = &gv + config.APIPath = "/apis" + config.NegotiatedSerializer = scheme.Codecs.WithoutConversion() + + if config.UserAgent == "" { + config.UserAgent = rest.DefaultKubernetesUserAgent() + } + + return nil +} + +// RESTClient returns a RESTClient that is used to communicate +// with API server by this client implementation. +func (c *RainbondV1alpha1Client) RESTClient() rest.Interface { + if c == nil { + return nil + } + return c.restClient +} diff --git a/vendor/github.com/goodrain/rainbond-operator/pkg/generated/clientset/versioned/typed/rainbond/v1alpha1/rainbondcluster.go b/vendor/github.com/goodrain/rainbond-operator/pkg/generated/clientset/versioned/typed/rainbond/v1alpha1/rainbondcluster.go new file mode 100644 index 000000000..20e92eb7f --- /dev/null +++ b/vendor/github.com/goodrain/rainbond-operator/pkg/generated/clientset/versioned/typed/rainbond/v1alpha1/rainbondcluster.go @@ -0,0 +1,193 @@ +// RAINBOND, Application Management Platform +// Copyright (C) 2014-2020 Goodrain Co., Ltd. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. For any non-GPL usage of Rainbond, +// one or multiple Commercial Licenses authorized by Goodrain Co., Ltd. +// must be obtained first. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +// Code generated by client-gen. DO NOT EDIT. + +package v1alpha1 + +import ( + "time" + + v1alpha1 "github.com/goodrain/rainbond-operator/pkg/apis/rainbond/v1alpha1" + scheme "github.com/goodrain/rainbond-operator/pkg/generated/clientset/versioned/scheme" + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + types "k8s.io/apimachinery/pkg/types" + watch "k8s.io/apimachinery/pkg/watch" + rest "k8s.io/client-go/rest" +) + +// RainbondClustersGetter has a method to return a RainbondClusterInterface. +// A group's client should implement this interface. +type RainbondClustersGetter interface { + RainbondClusters(namespace string) RainbondClusterInterface +} + +// RainbondClusterInterface has methods to work with RainbondCluster resources. +type RainbondClusterInterface interface { + Create(*v1alpha1.RainbondCluster) (*v1alpha1.RainbondCluster, error) + Update(*v1alpha1.RainbondCluster) (*v1alpha1.RainbondCluster, error) + UpdateStatus(*v1alpha1.RainbondCluster) (*v1alpha1.RainbondCluster, error) + Delete(name string, options *v1.DeleteOptions) error + DeleteCollection(options *v1.DeleteOptions, listOptions v1.ListOptions) error + Get(name string, options v1.GetOptions) (*v1alpha1.RainbondCluster, error) + List(opts v1.ListOptions) (*v1alpha1.RainbondClusterList, error) + Watch(opts v1.ListOptions) (watch.Interface, error) + Patch(name string, pt types.PatchType, data []byte, subresources ...string) (result *v1alpha1.RainbondCluster, err error) + RainbondClusterExpansion +} + +// rainbondClusters implements RainbondClusterInterface +type rainbondClusters struct { + client rest.Interface + ns string +} + +// newRainbondClusters returns a RainbondClusters +func newRainbondClusters(c *RainbondV1alpha1Client, namespace string) *rainbondClusters { + return &rainbondClusters{ + client: c.RESTClient(), + ns: namespace, + } +} + +// Get takes name of the rainbondCluster, and returns the corresponding rainbondCluster object, and an error if there is any. +func (c *rainbondClusters) Get(name string, options v1.GetOptions) (result *v1alpha1.RainbondCluster, err error) { + result = &v1alpha1.RainbondCluster{} + err = c.client.Get(). + Namespace(c.ns). + Resource("rainbondclusters"). + Name(name). + VersionedParams(&options, scheme.ParameterCodec). + Do(). + Into(result) + return +} + +// List takes label and field selectors, and returns the list of RainbondClusters that match those selectors. +func (c *rainbondClusters) List(opts v1.ListOptions) (result *v1alpha1.RainbondClusterList, err error) { + var timeout time.Duration + if opts.TimeoutSeconds != nil { + timeout = time.Duration(*opts.TimeoutSeconds) * time.Second + } + result = &v1alpha1.RainbondClusterList{} + err = c.client.Get(). + Namespace(c.ns). + Resource("rainbondclusters"). + VersionedParams(&opts, scheme.ParameterCodec). + Timeout(timeout). + Do(). + Into(result) + return +} + +// Watch returns a watch.Interface that watches the requested rainbondClusters. +func (c *rainbondClusters) Watch(opts v1.ListOptions) (watch.Interface, error) { + var timeout time.Duration + if opts.TimeoutSeconds != nil { + timeout = time.Duration(*opts.TimeoutSeconds) * time.Second + } + opts.Watch = true + return c.client.Get(). + Namespace(c.ns). + Resource("rainbondclusters"). + VersionedParams(&opts, scheme.ParameterCodec). + Timeout(timeout). + Watch() +} + +// Create takes the representation of a rainbondCluster and creates it. Returns the server's representation of the rainbondCluster, and an error, if there is any. +func (c *rainbondClusters) Create(rainbondCluster *v1alpha1.RainbondCluster) (result *v1alpha1.RainbondCluster, err error) { + result = &v1alpha1.RainbondCluster{} + err = c.client.Post(). + Namespace(c.ns). + Resource("rainbondclusters"). + Body(rainbondCluster). + Do(). + Into(result) + return +} + +// Update takes the representation of a rainbondCluster and updates it. Returns the server's representation of the rainbondCluster, and an error, if there is any. +func (c *rainbondClusters) Update(rainbondCluster *v1alpha1.RainbondCluster) (result *v1alpha1.RainbondCluster, err error) { + result = &v1alpha1.RainbondCluster{} + err = c.client.Put(). + Namespace(c.ns). + Resource("rainbondclusters"). + Name(rainbondCluster.Name). + Body(rainbondCluster). + Do(). + Into(result) + return +} + +// UpdateStatus was generated because the type contains a Status member. +// Add a +genclient:noStatus comment above the type to avoid generating UpdateStatus(). + +func (c *rainbondClusters) UpdateStatus(rainbondCluster *v1alpha1.RainbondCluster) (result *v1alpha1.RainbondCluster, err error) { + result = &v1alpha1.RainbondCluster{} + err = c.client.Put(). + Namespace(c.ns). + Resource("rainbondclusters"). + Name(rainbondCluster.Name). + SubResource("status"). + Body(rainbondCluster). + Do(). + Into(result) + return +} + +// Delete takes name of the rainbondCluster and deletes it. Returns an error if one occurs. +func (c *rainbondClusters) Delete(name string, options *v1.DeleteOptions) error { + return c.client.Delete(). + Namespace(c.ns). + Resource("rainbondclusters"). + Name(name). + Body(options). + Do(). + Error() +} + +// DeleteCollection deletes a collection of objects. +func (c *rainbondClusters) DeleteCollection(options *v1.DeleteOptions, listOptions v1.ListOptions) error { + var timeout time.Duration + if listOptions.TimeoutSeconds != nil { + timeout = time.Duration(*listOptions.TimeoutSeconds) * time.Second + } + return c.client.Delete(). + Namespace(c.ns). + Resource("rainbondclusters"). + VersionedParams(&listOptions, scheme.ParameterCodec). + Timeout(timeout). + Body(options). + Do(). + Error() +} + +// Patch applies the patch and returns the patched rainbondCluster. +func (c *rainbondClusters) Patch(name string, pt types.PatchType, data []byte, subresources ...string) (result *v1alpha1.RainbondCluster, err error) { + result = &v1alpha1.RainbondCluster{} + err = c.client.Patch(pt). + Namespace(c.ns). + Resource("rainbondclusters"). + SubResource(subresources...). + Name(name). + Body(data). + Do(). + Into(result) + return +} diff --git a/vendor/github.com/goodrain/rainbond-operator/pkg/generated/clientset/versioned/typed/rainbond/v1alpha1/rainbondpackage.go b/vendor/github.com/goodrain/rainbond-operator/pkg/generated/clientset/versioned/typed/rainbond/v1alpha1/rainbondpackage.go new file mode 100644 index 000000000..8f973160c --- /dev/null +++ b/vendor/github.com/goodrain/rainbond-operator/pkg/generated/clientset/versioned/typed/rainbond/v1alpha1/rainbondpackage.go @@ -0,0 +1,193 @@ +// RAINBOND, Application Management Platform +// Copyright (C) 2014-2020 Goodrain Co., Ltd. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. For any non-GPL usage of Rainbond, +// one or multiple Commercial Licenses authorized by Goodrain Co., Ltd. +// must be obtained first. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +// Code generated by client-gen. DO NOT EDIT. + +package v1alpha1 + +import ( + "time" + + v1alpha1 "github.com/goodrain/rainbond-operator/pkg/apis/rainbond/v1alpha1" + scheme "github.com/goodrain/rainbond-operator/pkg/generated/clientset/versioned/scheme" + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + types "k8s.io/apimachinery/pkg/types" + watch "k8s.io/apimachinery/pkg/watch" + rest "k8s.io/client-go/rest" +) + +// RainbondPackagesGetter has a method to return a RainbondPackageInterface. +// A group's client should implement this interface. +type RainbondPackagesGetter interface { + RainbondPackages(namespace string) RainbondPackageInterface +} + +// RainbondPackageInterface has methods to work with RainbondPackage resources. +type RainbondPackageInterface interface { + Create(*v1alpha1.RainbondPackage) (*v1alpha1.RainbondPackage, error) + Update(*v1alpha1.RainbondPackage) (*v1alpha1.RainbondPackage, error) + UpdateStatus(*v1alpha1.RainbondPackage) (*v1alpha1.RainbondPackage, error) + Delete(name string, options *v1.DeleteOptions) error + DeleteCollection(options *v1.DeleteOptions, listOptions v1.ListOptions) error + Get(name string, options v1.GetOptions) (*v1alpha1.RainbondPackage, error) + List(opts v1.ListOptions) (*v1alpha1.RainbondPackageList, error) + Watch(opts v1.ListOptions) (watch.Interface, error) + Patch(name string, pt types.PatchType, data []byte, subresources ...string) (result *v1alpha1.RainbondPackage, err error) + RainbondPackageExpansion +} + +// rainbondPackages implements RainbondPackageInterface +type rainbondPackages struct { + client rest.Interface + ns string +} + +// newRainbondPackages returns a RainbondPackages +func newRainbondPackages(c *RainbondV1alpha1Client, namespace string) *rainbondPackages { + return &rainbondPackages{ + client: c.RESTClient(), + ns: namespace, + } +} + +// Get takes name of the rainbondPackage, and returns the corresponding rainbondPackage object, and an error if there is any. +func (c *rainbondPackages) Get(name string, options v1.GetOptions) (result *v1alpha1.RainbondPackage, err error) { + result = &v1alpha1.RainbondPackage{} + err = c.client.Get(). + Namespace(c.ns). + Resource("rainbondpackages"). + Name(name). + VersionedParams(&options, scheme.ParameterCodec). + Do(). + Into(result) + return +} + +// List takes label and field selectors, and returns the list of RainbondPackages that match those selectors. +func (c *rainbondPackages) List(opts v1.ListOptions) (result *v1alpha1.RainbondPackageList, err error) { + var timeout time.Duration + if opts.TimeoutSeconds != nil { + timeout = time.Duration(*opts.TimeoutSeconds) * time.Second + } + result = &v1alpha1.RainbondPackageList{} + err = c.client.Get(). + Namespace(c.ns). + Resource("rainbondpackages"). + VersionedParams(&opts, scheme.ParameterCodec). + Timeout(timeout). + Do(). + Into(result) + return +} + +// Watch returns a watch.Interface that watches the requested rainbondPackages. +func (c *rainbondPackages) Watch(opts v1.ListOptions) (watch.Interface, error) { + var timeout time.Duration + if opts.TimeoutSeconds != nil { + timeout = time.Duration(*opts.TimeoutSeconds) * time.Second + } + opts.Watch = true + return c.client.Get(). + Namespace(c.ns). + Resource("rainbondpackages"). + VersionedParams(&opts, scheme.ParameterCodec). + Timeout(timeout). + Watch() +} + +// Create takes the representation of a rainbondPackage and creates it. Returns the server's representation of the rainbondPackage, and an error, if there is any. +func (c *rainbondPackages) Create(rainbondPackage *v1alpha1.RainbondPackage) (result *v1alpha1.RainbondPackage, err error) { + result = &v1alpha1.RainbondPackage{} + err = c.client.Post(). + Namespace(c.ns). + Resource("rainbondpackages"). + Body(rainbondPackage). + Do(). + Into(result) + return +} + +// Update takes the representation of a rainbondPackage and updates it. Returns the server's representation of the rainbondPackage, and an error, if there is any. +func (c *rainbondPackages) Update(rainbondPackage *v1alpha1.RainbondPackage) (result *v1alpha1.RainbondPackage, err error) { + result = &v1alpha1.RainbondPackage{} + err = c.client.Put(). + Namespace(c.ns). + Resource("rainbondpackages"). + Name(rainbondPackage.Name). + Body(rainbondPackage). + Do(). + Into(result) + return +} + +// UpdateStatus was generated because the type contains a Status member. +// Add a +genclient:noStatus comment above the type to avoid generating UpdateStatus(). + +func (c *rainbondPackages) UpdateStatus(rainbondPackage *v1alpha1.RainbondPackage) (result *v1alpha1.RainbondPackage, err error) { + result = &v1alpha1.RainbondPackage{} + err = c.client.Put(). + Namespace(c.ns). + Resource("rainbondpackages"). + Name(rainbondPackage.Name). + SubResource("status"). + Body(rainbondPackage). + Do(). + Into(result) + return +} + +// Delete takes name of the rainbondPackage and deletes it. Returns an error if one occurs. +func (c *rainbondPackages) Delete(name string, options *v1.DeleteOptions) error { + return c.client.Delete(). + Namespace(c.ns). + Resource("rainbondpackages"). + Name(name). + Body(options). + Do(). + Error() +} + +// DeleteCollection deletes a collection of objects. +func (c *rainbondPackages) DeleteCollection(options *v1.DeleteOptions, listOptions v1.ListOptions) error { + var timeout time.Duration + if listOptions.TimeoutSeconds != nil { + timeout = time.Duration(*listOptions.TimeoutSeconds) * time.Second + } + return c.client.Delete(). + Namespace(c.ns). + Resource("rainbondpackages"). + VersionedParams(&listOptions, scheme.ParameterCodec). + Timeout(timeout). + Body(options). + Do(). + Error() +} + +// Patch applies the patch and returns the patched rainbondPackage. +func (c *rainbondPackages) Patch(name string, pt types.PatchType, data []byte, subresources ...string) (result *v1alpha1.RainbondPackage, err error) { + result = &v1alpha1.RainbondPackage{} + err = c.client.Patch(pt). + Namespace(c.ns). + Resource("rainbondpackages"). + SubResource(subresources...). + Name(name). + Body(data). + Do(). + Into(result) + return +} diff --git a/vendor/github.com/goodrain/rainbond-operator/pkg/generated/clientset/versioned/typed/rainbond/v1alpha1/rbdcomponent.go b/vendor/github.com/goodrain/rainbond-operator/pkg/generated/clientset/versioned/typed/rainbond/v1alpha1/rbdcomponent.go new file mode 100644 index 000000000..4a6e22090 --- /dev/null +++ b/vendor/github.com/goodrain/rainbond-operator/pkg/generated/clientset/versioned/typed/rainbond/v1alpha1/rbdcomponent.go @@ -0,0 +1,193 @@ +// RAINBOND, Application Management Platform +// Copyright (C) 2014-2020 Goodrain Co., Ltd. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. For any non-GPL usage of Rainbond, +// one or multiple Commercial Licenses authorized by Goodrain Co., Ltd. +// must be obtained first. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +// Code generated by client-gen. DO NOT EDIT. + +package v1alpha1 + +import ( + "time" + + v1alpha1 "github.com/goodrain/rainbond-operator/pkg/apis/rainbond/v1alpha1" + scheme "github.com/goodrain/rainbond-operator/pkg/generated/clientset/versioned/scheme" + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + types "k8s.io/apimachinery/pkg/types" + watch "k8s.io/apimachinery/pkg/watch" + rest "k8s.io/client-go/rest" +) + +// RbdComponentsGetter has a method to return a RbdComponentInterface. +// A group's client should implement this interface. +type RbdComponentsGetter interface { + RbdComponents(namespace string) RbdComponentInterface +} + +// RbdComponentInterface has methods to work with RbdComponent resources. +type RbdComponentInterface interface { + Create(*v1alpha1.RbdComponent) (*v1alpha1.RbdComponent, error) + Update(*v1alpha1.RbdComponent) (*v1alpha1.RbdComponent, error) + UpdateStatus(*v1alpha1.RbdComponent) (*v1alpha1.RbdComponent, error) + Delete(name string, options *v1.DeleteOptions) error + DeleteCollection(options *v1.DeleteOptions, listOptions v1.ListOptions) error + Get(name string, options v1.GetOptions) (*v1alpha1.RbdComponent, error) + List(opts v1.ListOptions) (*v1alpha1.RbdComponentList, error) + Watch(opts v1.ListOptions) (watch.Interface, error) + Patch(name string, pt types.PatchType, data []byte, subresources ...string) (result *v1alpha1.RbdComponent, err error) + RbdComponentExpansion +} + +// rbdComponents implements RbdComponentInterface +type rbdComponents struct { + client rest.Interface + ns string +} + +// newRbdComponents returns a RbdComponents +func newRbdComponents(c *RainbondV1alpha1Client, namespace string) *rbdComponents { + return &rbdComponents{ + client: c.RESTClient(), + ns: namespace, + } +} + +// Get takes name of the rbdComponent, and returns the corresponding rbdComponent object, and an error if there is any. +func (c *rbdComponents) Get(name string, options v1.GetOptions) (result *v1alpha1.RbdComponent, err error) { + result = &v1alpha1.RbdComponent{} + err = c.client.Get(). + Namespace(c.ns). + Resource("rbdcomponents"). + Name(name). + VersionedParams(&options, scheme.ParameterCodec). + Do(). + Into(result) + return +} + +// List takes label and field selectors, and returns the list of RbdComponents that match those selectors. +func (c *rbdComponents) List(opts v1.ListOptions) (result *v1alpha1.RbdComponentList, err error) { + var timeout time.Duration + if opts.TimeoutSeconds != nil { + timeout = time.Duration(*opts.TimeoutSeconds) * time.Second + } + result = &v1alpha1.RbdComponentList{} + err = c.client.Get(). + Namespace(c.ns). + Resource("rbdcomponents"). + VersionedParams(&opts, scheme.ParameterCodec). + Timeout(timeout). + Do(). + Into(result) + return +} + +// Watch returns a watch.Interface that watches the requested rbdComponents. +func (c *rbdComponents) Watch(opts v1.ListOptions) (watch.Interface, error) { + var timeout time.Duration + if opts.TimeoutSeconds != nil { + timeout = time.Duration(*opts.TimeoutSeconds) * time.Second + } + opts.Watch = true + return c.client.Get(). + Namespace(c.ns). + Resource("rbdcomponents"). + VersionedParams(&opts, scheme.ParameterCodec). + Timeout(timeout). + Watch() +} + +// Create takes the representation of a rbdComponent and creates it. Returns the server's representation of the rbdComponent, and an error, if there is any. +func (c *rbdComponents) Create(rbdComponent *v1alpha1.RbdComponent) (result *v1alpha1.RbdComponent, err error) { + result = &v1alpha1.RbdComponent{} + err = c.client.Post(). + Namespace(c.ns). + Resource("rbdcomponents"). + Body(rbdComponent). + Do(). + Into(result) + return +} + +// Update takes the representation of a rbdComponent and updates it. Returns the server's representation of the rbdComponent, and an error, if there is any. +func (c *rbdComponents) Update(rbdComponent *v1alpha1.RbdComponent) (result *v1alpha1.RbdComponent, err error) { + result = &v1alpha1.RbdComponent{} + err = c.client.Put(). + Namespace(c.ns). + Resource("rbdcomponents"). + Name(rbdComponent.Name). + Body(rbdComponent). + Do(). + Into(result) + return +} + +// UpdateStatus was generated because the type contains a Status member. +// Add a +genclient:noStatus comment above the type to avoid generating UpdateStatus(). + +func (c *rbdComponents) UpdateStatus(rbdComponent *v1alpha1.RbdComponent) (result *v1alpha1.RbdComponent, err error) { + result = &v1alpha1.RbdComponent{} + err = c.client.Put(). + Namespace(c.ns). + Resource("rbdcomponents"). + Name(rbdComponent.Name). + SubResource("status"). + Body(rbdComponent). + Do(). + Into(result) + return +} + +// Delete takes name of the rbdComponent and deletes it. Returns an error if one occurs. +func (c *rbdComponents) Delete(name string, options *v1.DeleteOptions) error { + return c.client.Delete(). + Namespace(c.ns). + Resource("rbdcomponents"). + Name(name). + Body(options). + Do(). + Error() +} + +// DeleteCollection deletes a collection of objects. +func (c *rbdComponents) DeleteCollection(options *v1.DeleteOptions, listOptions v1.ListOptions) error { + var timeout time.Duration + if listOptions.TimeoutSeconds != nil { + timeout = time.Duration(*listOptions.TimeoutSeconds) * time.Second + } + return c.client.Delete(). + Namespace(c.ns). + Resource("rbdcomponents"). + VersionedParams(&listOptions, scheme.ParameterCodec). + Timeout(timeout). + Body(options). + Do(). + Error() +} + +// Patch applies the patch and returns the patched rbdComponent. +func (c *rbdComponents) Patch(name string, pt types.PatchType, data []byte, subresources ...string) (result *v1alpha1.RbdComponent, err error) { + result = &v1alpha1.RbdComponent{} + err = c.client.Patch(pt). + Namespace(c.ns). + Resource("rbdcomponents"). + SubResource(subresources...). + Name(name). + Body(data). + Do(). + Into(result) + return +} diff --git a/vendor/github.com/goodrain/rainbond-operator/pkg/generated/informers/externalversions/factory.go b/vendor/github.com/goodrain/rainbond-operator/pkg/generated/informers/externalversions/factory.go new file mode 100644 index 000000000..a206b96ea --- /dev/null +++ b/vendor/github.com/goodrain/rainbond-operator/pkg/generated/informers/externalversions/factory.go @@ -0,0 +1,182 @@ +// RAINBOND, Application Management Platform +// Copyright (C) 2014-2020 Goodrain Co., Ltd. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. For any non-GPL usage of Rainbond, +// one or multiple Commercial Licenses authorized by Goodrain Co., Ltd. +// must be obtained first. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +// Code generated by informer-gen. DO NOT EDIT. + +package externalversions + +import ( + reflect "reflect" + sync "sync" + time "time" + + versioned "github.com/goodrain/rainbond-operator/pkg/generated/clientset/versioned" + internalinterfaces "github.com/goodrain/rainbond-operator/pkg/generated/informers/externalversions/internalinterfaces" + rainbond "github.com/goodrain/rainbond-operator/pkg/generated/informers/externalversions/rainbond" + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + runtime "k8s.io/apimachinery/pkg/runtime" + schema "k8s.io/apimachinery/pkg/runtime/schema" + cache "k8s.io/client-go/tools/cache" +) + +// SharedInformerOption defines the functional option type for SharedInformerFactory. +type SharedInformerOption func(*sharedInformerFactory) *sharedInformerFactory + +type sharedInformerFactory struct { + client versioned.Interface + namespace string + tweakListOptions internalinterfaces.TweakListOptionsFunc + lock sync.Mutex + defaultResync time.Duration + customResync map[reflect.Type]time.Duration + + informers map[reflect.Type]cache.SharedIndexInformer + // startedInformers is used for tracking which informers have been started. + // This allows Start() to be called multiple times safely. + startedInformers map[reflect.Type]bool +} + +// WithCustomResyncConfig sets a custom resync period for the specified informer types. +func WithCustomResyncConfig(resyncConfig map[v1.Object]time.Duration) SharedInformerOption { + return func(factory *sharedInformerFactory) *sharedInformerFactory { + for k, v := range resyncConfig { + factory.customResync[reflect.TypeOf(k)] = v + } + return factory + } +} + +// WithTweakListOptions sets a custom filter on all listers of the configured SharedInformerFactory. +func WithTweakListOptions(tweakListOptions internalinterfaces.TweakListOptionsFunc) SharedInformerOption { + return func(factory *sharedInformerFactory) *sharedInformerFactory { + factory.tweakListOptions = tweakListOptions + return factory + } +} + +// WithNamespace limits the SharedInformerFactory to the specified namespace. +func WithNamespace(namespace string) SharedInformerOption { + return func(factory *sharedInformerFactory) *sharedInformerFactory { + factory.namespace = namespace + return factory + } +} + +// NewSharedInformerFactory constructs a new instance of sharedInformerFactory for all namespaces. +func NewSharedInformerFactory(client versioned.Interface, defaultResync time.Duration) SharedInformerFactory { + return NewSharedInformerFactoryWithOptions(client, defaultResync) +} + +// NewFilteredSharedInformerFactory constructs a new instance of sharedInformerFactory. +// Listers obtained via this SharedInformerFactory will be subject to the same filters +// as specified here. +// Deprecated: Please use NewSharedInformerFactoryWithOptions instead +func NewFilteredSharedInformerFactory(client versioned.Interface, defaultResync time.Duration, namespace string, tweakListOptions internalinterfaces.TweakListOptionsFunc) SharedInformerFactory { + return NewSharedInformerFactoryWithOptions(client, defaultResync, WithNamespace(namespace), WithTweakListOptions(tweakListOptions)) +} + +// NewSharedInformerFactoryWithOptions constructs a new instance of a SharedInformerFactory with additional options. +func NewSharedInformerFactoryWithOptions(client versioned.Interface, defaultResync time.Duration, options ...SharedInformerOption) SharedInformerFactory { + factory := &sharedInformerFactory{ + client: client, + namespace: v1.NamespaceAll, + defaultResync: defaultResync, + informers: make(map[reflect.Type]cache.SharedIndexInformer), + startedInformers: make(map[reflect.Type]bool), + customResync: make(map[reflect.Type]time.Duration), + } + + // Apply all options + for _, opt := range options { + factory = opt(factory) + } + + return factory +} + +// Start initializes all requested informers. +func (f *sharedInformerFactory) Start(stopCh <-chan struct{}) { + f.lock.Lock() + defer f.lock.Unlock() + + for informerType, informer := range f.informers { + if !f.startedInformers[informerType] { + go informer.Run(stopCh) + f.startedInformers[informerType] = true + } + } +} + +// WaitForCacheSync waits for all started informers' cache were synced. +func (f *sharedInformerFactory) WaitForCacheSync(stopCh <-chan struct{}) map[reflect.Type]bool { + informers := func() map[reflect.Type]cache.SharedIndexInformer { + f.lock.Lock() + defer f.lock.Unlock() + + informers := map[reflect.Type]cache.SharedIndexInformer{} + for informerType, informer := range f.informers { + if f.startedInformers[informerType] { + informers[informerType] = informer + } + } + return informers + }() + + res := map[reflect.Type]bool{} + for informType, informer := range informers { + res[informType] = cache.WaitForCacheSync(stopCh, informer.HasSynced) + } + return res +} + +// InternalInformerFor returns the SharedIndexInformer for obj using an internal +// client. +func (f *sharedInformerFactory) InformerFor(obj runtime.Object, newFunc internalinterfaces.NewInformerFunc) cache.SharedIndexInformer { + f.lock.Lock() + defer f.lock.Unlock() + + informerType := reflect.TypeOf(obj) + informer, exists := f.informers[informerType] + if exists { + return informer + } + + resyncPeriod, exists := f.customResync[informerType] + if !exists { + resyncPeriod = f.defaultResync + } + + informer = newFunc(f.client, resyncPeriod) + f.informers[informerType] = informer + + return informer +} + +// SharedInformerFactory provides shared informers for resources in all known +// API group versions. +type SharedInformerFactory interface { + internalinterfaces.SharedInformerFactory + ForResource(resource schema.GroupVersionResource) (GenericInformer, error) + WaitForCacheSync(stopCh <-chan struct{}) map[reflect.Type]bool + + Rainbond() rainbond.Interface +} + +func (f *sharedInformerFactory) Rainbond() rainbond.Interface { + return rainbond.New(f, f.namespace, f.tweakListOptions) +} diff --git a/vendor/github.com/goodrain/rainbond-operator/pkg/generated/informers/externalversions/generic.go b/vendor/github.com/goodrain/rainbond-operator/pkg/generated/informers/externalversions/generic.go new file mode 100644 index 000000000..f1a9cbf3c --- /dev/null +++ b/vendor/github.com/goodrain/rainbond-operator/pkg/generated/informers/externalversions/generic.go @@ -0,0 +1,68 @@ +// RAINBOND, Application Management Platform +// Copyright (C) 2014-2020 Goodrain Co., Ltd. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. For any non-GPL usage of Rainbond, +// one or multiple Commercial Licenses authorized by Goodrain Co., Ltd. +// must be obtained first. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +// Code generated by informer-gen. DO NOT EDIT. + +package externalversions + +import ( + "fmt" + + v1alpha1 "github.com/goodrain/rainbond-operator/pkg/apis/rainbond/v1alpha1" + schema "k8s.io/apimachinery/pkg/runtime/schema" + cache "k8s.io/client-go/tools/cache" +) + +// GenericInformer is type of SharedIndexInformer which will locate and delegate to other +// sharedInformers based on type +type GenericInformer interface { + Informer() cache.SharedIndexInformer + Lister() cache.GenericLister +} + +type genericInformer struct { + informer cache.SharedIndexInformer + resource schema.GroupResource +} + +// Informer returns the SharedIndexInformer. +func (f *genericInformer) Informer() cache.SharedIndexInformer { + return f.informer +} + +// Lister returns the GenericLister. +func (f *genericInformer) Lister() cache.GenericLister { + return cache.NewGenericLister(f.Informer().GetIndexer(), f.resource) +} + +// ForResource gives generic access to a shared informer of the matching type +// TODO extend this to unknown resources with a client pool +func (f *sharedInformerFactory) ForResource(resource schema.GroupVersionResource) (GenericInformer, error) { + switch resource { + // Group=rainbond.io, Version=v1alpha1 + case v1alpha1.SchemeGroupVersion.WithResource("rainbondclusters"): + return &genericInformer{resource: resource.GroupResource(), informer: f.Rainbond().V1alpha1().RainbondClusters().Informer()}, nil + case v1alpha1.SchemeGroupVersion.WithResource("rainbondpackages"): + return &genericInformer{resource: resource.GroupResource(), informer: f.Rainbond().V1alpha1().RainbondPackages().Informer()}, nil + case v1alpha1.SchemeGroupVersion.WithResource("rbdcomponents"): + return &genericInformer{resource: resource.GroupResource(), informer: f.Rainbond().V1alpha1().RbdComponents().Informer()}, nil + + } + + return nil, fmt.Errorf("no informer found for %v", resource) +} diff --git a/vendor/github.com/goodrain/rainbond-operator/pkg/generated/informers/externalversions/internalinterfaces/factory_interfaces.go b/vendor/github.com/goodrain/rainbond-operator/pkg/generated/informers/externalversions/internalinterfaces/factory_interfaces.go new file mode 100644 index 000000000..7d8db371d --- /dev/null +++ b/vendor/github.com/goodrain/rainbond-operator/pkg/generated/informers/externalversions/internalinterfaces/factory_interfaces.go @@ -0,0 +1,42 @@ +// RAINBOND, Application Management Platform +// Copyright (C) 2014-2020 Goodrain Co., Ltd. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. For any non-GPL usage of Rainbond, +// one or multiple Commercial Licenses authorized by Goodrain Co., Ltd. +// must be obtained first. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +// Code generated by informer-gen. DO NOT EDIT. + +package internalinterfaces + +import ( + time "time" + + versioned "github.com/goodrain/rainbond-operator/pkg/generated/clientset/versioned" + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + runtime "k8s.io/apimachinery/pkg/runtime" + cache "k8s.io/client-go/tools/cache" +) + +// NewInformerFunc takes versioned.Interface and time.Duration to return a SharedIndexInformer. +type NewInformerFunc func(versioned.Interface, time.Duration) cache.SharedIndexInformer + +// SharedInformerFactory a small interface to allow for adding an informer without an import cycle +type SharedInformerFactory interface { + Start(stopCh <-chan struct{}) + InformerFor(obj runtime.Object, newFunc NewInformerFunc) cache.SharedIndexInformer +} + +// TweakListOptionsFunc is a function that transforms a v1.ListOptions. +type TweakListOptionsFunc func(*v1.ListOptions) diff --git a/vendor/github.com/goodrain/rainbond-operator/pkg/generated/informers/externalversions/rainbond/interface.go b/vendor/github.com/goodrain/rainbond-operator/pkg/generated/informers/externalversions/rainbond/interface.go new file mode 100644 index 000000000..f2eb6c19a --- /dev/null +++ b/vendor/github.com/goodrain/rainbond-operator/pkg/generated/informers/externalversions/rainbond/interface.go @@ -0,0 +1,48 @@ +// RAINBOND, Application Management Platform +// Copyright (C) 2014-2020 Goodrain Co., Ltd. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. For any non-GPL usage of Rainbond, +// one or multiple Commercial Licenses authorized by Goodrain Co., Ltd. +// must be obtained first. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +// Code generated by informer-gen. DO NOT EDIT. + +package rainbond + +import ( + internalinterfaces "github.com/goodrain/rainbond-operator/pkg/generated/informers/externalversions/internalinterfaces" + v1alpha1 "github.com/goodrain/rainbond-operator/pkg/generated/informers/externalversions/rainbond/v1alpha1" +) + +// Interface provides access to each of this group's versions. +type Interface interface { + // V1alpha1 provides access to shared informers for resources in V1alpha1. + V1alpha1() v1alpha1.Interface +} + +type group struct { + factory internalinterfaces.SharedInformerFactory + namespace string + tweakListOptions internalinterfaces.TweakListOptionsFunc +} + +// New returns a new Interface. +func New(f internalinterfaces.SharedInformerFactory, namespace string, tweakListOptions internalinterfaces.TweakListOptionsFunc) Interface { + return &group{factory: f, namespace: namespace, tweakListOptions: tweakListOptions} +} + +// V1alpha1 returns a new v1alpha1.Interface. +func (g *group) V1alpha1() v1alpha1.Interface { + return v1alpha1.New(g.factory, g.namespace, g.tweakListOptions) +} diff --git a/vendor/github.com/goodrain/rainbond-operator/pkg/generated/informers/externalversions/rainbond/v1alpha1/interface.go b/vendor/github.com/goodrain/rainbond-operator/pkg/generated/informers/externalversions/rainbond/v1alpha1/interface.go new file mode 100644 index 000000000..99ee0c2b9 --- /dev/null +++ b/vendor/github.com/goodrain/rainbond-operator/pkg/generated/informers/externalversions/rainbond/v1alpha1/interface.go @@ -0,0 +1,61 @@ +// RAINBOND, Application Management Platform +// Copyright (C) 2014-2020 Goodrain Co., Ltd. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. For any non-GPL usage of Rainbond, +// one or multiple Commercial Licenses authorized by Goodrain Co., Ltd. +// must be obtained first. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +// Code generated by informer-gen. DO NOT EDIT. + +package v1alpha1 + +import ( + internalinterfaces "github.com/goodrain/rainbond-operator/pkg/generated/informers/externalversions/internalinterfaces" +) + +// Interface provides access to all the informers in this group version. +type Interface interface { + // RainbondClusters returns a RainbondClusterInformer. + RainbondClusters() RainbondClusterInformer + // RainbondPackages returns a RainbondPackageInformer. + RainbondPackages() RainbondPackageInformer + // RbdComponents returns a RbdComponentInformer. + RbdComponents() RbdComponentInformer +} + +type version struct { + factory internalinterfaces.SharedInformerFactory + namespace string + tweakListOptions internalinterfaces.TweakListOptionsFunc +} + +// New returns a new Interface. +func New(f internalinterfaces.SharedInformerFactory, namespace string, tweakListOptions internalinterfaces.TweakListOptionsFunc) Interface { + return &version{factory: f, namespace: namespace, tweakListOptions: tweakListOptions} +} + +// RainbondClusters returns a RainbondClusterInformer. +func (v *version) RainbondClusters() RainbondClusterInformer { + return &rainbondClusterInformer{factory: v.factory, namespace: v.namespace, tweakListOptions: v.tweakListOptions} +} + +// RainbondPackages returns a RainbondPackageInformer. +func (v *version) RainbondPackages() RainbondPackageInformer { + return &rainbondPackageInformer{factory: v.factory, namespace: v.namespace, tweakListOptions: v.tweakListOptions} +} + +// RbdComponents returns a RbdComponentInformer. +func (v *version) RbdComponents() RbdComponentInformer { + return &rbdComponentInformer{factory: v.factory, namespace: v.namespace, tweakListOptions: v.tweakListOptions} +} diff --git a/vendor/github.com/goodrain/rainbond-operator/pkg/generated/informers/externalversions/rainbond/v1alpha1/rainbondcluster.go b/vendor/github.com/goodrain/rainbond-operator/pkg/generated/informers/externalversions/rainbond/v1alpha1/rainbondcluster.go new file mode 100644 index 000000000..1d8bc3df0 --- /dev/null +++ b/vendor/github.com/goodrain/rainbond-operator/pkg/generated/informers/externalversions/rainbond/v1alpha1/rainbondcluster.go @@ -0,0 +1,91 @@ +// RAINBOND, Application Management Platform +// Copyright (C) 2014-2020 Goodrain Co., Ltd. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. For any non-GPL usage of Rainbond, +// one or multiple Commercial Licenses authorized by Goodrain Co., Ltd. +// must be obtained first. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +// Code generated by informer-gen. DO NOT EDIT. + +package v1alpha1 + +import ( + time "time" + + rainbondv1alpha1 "github.com/goodrain/rainbond-operator/pkg/apis/rainbond/v1alpha1" + versioned "github.com/goodrain/rainbond-operator/pkg/generated/clientset/versioned" + internalinterfaces "github.com/goodrain/rainbond-operator/pkg/generated/informers/externalversions/internalinterfaces" + v1alpha1 "github.com/goodrain/rainbond-operator/pkg/generated/listers/rainbond/v1alpha1" + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + runtime "k8s.io/apimachinery/pkg/runtime" + watch "k8s.io/apimachinery/pkg/watch" + cache "k8s.io/client-go/tools/cache" +) + +// RainbondClusterInformer provides access to a shared informer and lister for +// RainbondClusters. +type RainbondClusterInformer interface { + Informer() cache.SharedIndexInformer + Lister() v1alpha1.RainbondClusterLister +} + +type rainbondClusterInformer struct { + factory internalinterfaces.SharedInformerFactory + tweakListOptions internalinterfaces.TweakListOptionsFunc + namespace string +} + +// NewRainbondClusterInformer constructs a new informer for RainbondCluster type. +// Always prefer using an informer factory to get a shared informer instead of getting an independent +// one. This reduces memory footprint and number of connections to the server. +func NewRainbondClusterInformer(client versioned.Interface, namespace string, resyncPeriod time.Duration, indexers cache.Indexers) cache.SharedIndexInformer { + return NewFilteredRainbondClusterInformer(client, namespace, resyncPeriod, indexers, nil) +} + +// NewFilteredRainbondClusterInformer constructs a new informer for RainbondCluster type. +// Always prefer using an informer factory to get a shared informer instead of getting an independent +// one. This reduces memory footprint and number of connections to the server. +func NewFilteredRainbondClusterInformer(client versioned.Interface, namespace string, resyncPeriod time.Duration, indexers cache.Indexers, tweakListOptions internalinterfaces.TweakListOptionsFunc) cache.SharedIndexInformer { + return cache.NewSharedIndexInformer( + &cache.ListWatch{ + ListFunc: func(options v1.ListOptions) (runtime.Object, error) { + if tweakListOptions != nil { + tweakListOptions(&options) + } + return client.RainbondV1alpha1().RainbondClusters(namespace).List(options) + }, + WatchFunc: func(options v1.ListOptions) (watch.Interface, error) { + if tweakListOptions != nil { + tweakListOptions(&options) + } + return client.RainbondV1alpha1().RainbondClusters(namespace).Watch(options) + }, + }, + &rainbondv1alpha1.RainbondCluster{}, + resyncPeriod, + indexers, + ) +} + +func (f *rainbondClusterInformer) defaultInformer(client versioned.Interface, resyncPeriod time.Duration) cache.SharedIndexInformer { + return NewFilteredRainbondClusterInformer(client, f.namespace, resyncPeriod, cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc}, f.tweakListOptions) +} + +func (f *rainbondClusterInformer) Informer() cache.SharedIndexInformer { + return f.factory.InformerFor(&rainbondv1alpha1.RainbondCluster{}, f.defaultInformer) +} + +func (f *rainbondClusterInformer) Lister() v1alpha1.RainbondClusterLister { + return v1alpha1.NewRainbondClusterLister(f.Informer().GetIndexer()) +} diff --git a/vendor/github.com/goodrain/rainbond-operator/pkg/generated/informers/externalversions/rainbond/v1alpha1/rainbondpackage.go b/vendor/github.com/goodrain/rainbond-operator/pkg/generated/informers/externalversions/rainbond/v1alpha1/rainbondpackage.go new file mode 100644 index 000000000..f95fc220b --- /dev/null +++ b/vendor/github.com/goodrain/rainbond-operator/pkg/generated/informers/externalversions/rainbond/v1alpha1/rainbondpackage.go @@ -0,0 +1,91 @@ +// RAINBOND, Application Management Platform +// Copyright (C) 2014-2020 Goodrain Co., Ltd. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. For any non-GPL usage of Rainbond, +// one or multiple Commercial Licenses authorized by Goodrain Co., Ltd. +// must be obtained first. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +// Code generated by informer-gen. DO NOT EDIT. + +package v1alpha1 + +import ( + time "time" + + rainbondv1alpha1 "github.com/goodrain/rainbond-operator/pkg/apis/rainbond/v1alpha1" + versioned "github.com/goodrain/rainbond-operator/pkg/generated/clientset/versioned" + internalinterfaces "github.com/goodrain/rainbond-operator/pkg/generated/informers/externalversions/internalinterfaces" + v1alpha1 "github.com/goodrain/rainbond-operator/pkg/generated/listers/rainbond/v1alpha1" + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + runtime "k8s.io/apimachinery/pkg/runtime" + watch "k8s.io/apimachinery/pkg/watch" + cache "k8s.io/client-go/tools/cache" +) + +// RainbondPackageInformer provides access to a shared informer and lister for +// RainbondPackages. +type RainbondPackageInformer interface { + Informer() cache.SharedIndexInformer + Lister() v1alpha1.RainbondPackageLister +} + +type rainbondPackageInformer struct { + factory internalinterfaces.SharedInformerFactory + tweakListOptions internalinterfaces.TweakListOptionsFunc + namespace string +} + +// NewRainbondPackageInformer constructs a new informer for RainbondPackage type. +// Always prefer using an informer factory to get a shared informer instead of getting an independent +// one. This reduces memory footprint and number of connections to the server. +func NewRainbondPackageInformer(client versioned.Interface, namespace string, resyncPeriod time.Duration, indexers cache.Indexers) cache.SharedIndexInformer { + return NewFilteredRainbondPackageInformer(client, namespace, resyncPeriod, indexers, nil) +} + +// NewFilteredRainbondPackageInformer constructs a new informer for RainbondPackage type. +// Always prefer using an informer factory to get a shared informer instead of getting an independent +// one. This reduces memory footprint and number of connections to the server. +func NewFilteredRainbondPackageInformer(client versioned.Interface, namespace string, resyncPeriod time.Duration, indexers cache.Indexers, tweakListOptions internalinterfaces.TweakListOptionsFunc) cache.SharedIndexInformer { + return cache.NewSharedIndexInformer( + &cache.ListWatch{ + ListFunc: func(options v1.ListOptions) (runtime.Object, error) { + if tweakListOptions != nil { + tweakListOptions(&options) + } + return client.RainbondV1alpha1().RainbondPackages(namespace).List(options) + }, + WatchFunc: func(options v1.ListOptions) (watch.Interface, error) { + if tweakListOptions != nil { + tweakListOptions(&options) + } + return client.RainbondV1alpha1().RainbondPackages(namespace).Watch(options) + }, + }, + &rainbondv1alpha1.RainbondPackage{}, + resyncPeriod, + indexers, + ) +} + +func (f *rainbondPackageInformer) defaultInformer(client versioned.Interface, resyncPeriod time.Duration) cache.SharedIndexInformer { + return NewFilteredRainbondPackageInformer(client, f.namespace, resyncPeriod, cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc}, f.tweakListOptions) +} + +func (f *rainbondPackageInformer) Informer() cache.SharedIndexInformer { + return f.factory.InformerFor(&rainbondv1alpha1.RainbondPackage{}, f.defaultInformer) +} + +func (f *rainbondPackageInformer) Lister() v1alpha1.RainbondPackageLister { + return v1alpha1.NewRainbondPackageLister(f.Informer().GetIndexer()) +} diff --git a/vendor/github.com/goodrain/rainbond-operator/pkg/generated/informers/externalversions/rainbond/v1alpha1/rbdcomponent.go b/vendor/github.com/goodrain/rainbond-operator/pkg/generated/informers/externalversions/rainbond/v1alpha1/rbdcomponent.go new file mode 100644 index 000000000..3ad1836c7 --- /dev/null +++ b/vendor/github.com/goodrain/rainbond-operator/pkg/generated/informers/externalversions/rainbond/v1alpha1/rbdcomponent.go @@ -0,0 +1,91 @@ +// RAINBOND, Application Management Platform +// Copyright (C) 2014-2020 Goodrain Co., Ltd. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. For any non-GPL usage of Rainbond, +// one or multiple Commercial Licenses authorized by Goodrain Co., Ltd. +// must be obtained first. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +// Code generated by informer-gen. DO NOT EDIT. + +package v1alpha1 + +import ( + time "time" + + rainbondv1alpha1 "github.com/goodrain/rainbond-operator/pkg/apis/rainbond/v1alpha1" + versioned "github.com/goodrain/rainbond-operator/pkg/generated/clientset/versioned" + internalinterfaces "github.com/goodrain/rainbond-operator/pkg/generated/informers/externalversions/internalinterfaces" + v1alpha1 "github.com/goodrain/rainbond-operator/pkg/generated/listers/rainbond/v1alpha1" + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + runtime "k8s.io/apimachinery/pkg/runtime" + watch "k8s.io/apimachinery/pkg/watch" + cache "k8s.io/client-go/tools/cache" +) + +// RbdComponentInformer provides access to a shared informer and lister for +// RbdComponents. +type RbdComponentInformer interface { + Informer() cache.SharedIndexInformer + Lister() v1alpha1.RbdComponentLister +} + +type rbdComponentInformer struct { + factory internalinterfaces.SharedInformerFactory + tweakListOptions internalinterfaces.TweakListOptionsFunc + namespace string +} + +// NewRbdComponentInformer constructs a new informer for RbdComponent type. +// Always prefer using an informer factory to get a shared informer instead of getting an independent +// one. This reduces memory footprint and number of connections to the server. +func NewRbdComponentInformer(client versioned.Interface, namespace string, resyncPeriod time.Duration, indexers cache.Indexers) cache.SharedIndexInformer { + return NewFilteredRbdComponentInformer(client, namespace, resyncPeriod, indexers, nil) +} + +// NewFilteredRbdComponentInformer constructs a new informer for RbdComponent type. +// Always prefer using an informer factory to get a shared informer instead of getting an independent +// one. This reduces memory footprint and number of connections to the server. +func NewFilteredRbdComponentInformer(client versioned.Interface, namespace string, resyncPeriod time.Duration, indexers cache.Indexers, tweakListOptions internalinterfaces.TweakListOptionsFunc) cache.SharedIndexInformer { + return cache.NewSharedIndexInformer( + &cache.ListWatch{ + ListFunc: func(options v1.ListOptions) (runtime.Object, error) { + if tweakListOptions != nil { + tweakListOptions(&options) + } + return client.RainbondV1alpha1().RbdComponents(namespace).List(options) + }, + WatchFunc: func(options v1.ListOptions) (watch.Interface, error) { + if tweakListOptions != nil { + tweakListOptions(&options) + } + return client.RainbondV1alpha1().RbdComponents(namespace).Watch(options) + }, + }, + &rainbondv1alpha1.RbdComponent{}, + resyncPeriod, + indexers, + ) +} + +func (f *rbdComponentInformer) defaultInformer(client versioned.Interface, resyncPeriod time.Duration) cache.SharedIndexInformer { + return NewFilteredRbdComponentInformer(client, f.namespace, resyncPeriod, cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc}, f.tweakListOptions) +} + +func (f *rbdComponentInformer) Informer() cache.SharedIndexInformer { + return f.factory.InformerFor(&rainbondv1alpha1.RbdComponent{}, f.defaultInformer) +} + +func (f *rbdComponentInformer) Lister() v1alpha1.RbdComponentLister { + return v1alpha1.NewRbdComponentLister(f.Informer().GetIndexer()) +} diff --git a/vendor/github.com/goodrain/rainbond-operator/pkg/generated/listers/rainbond/v1alpha1/expansion_generated.go b/vendor/github.com/goodrain/rainbond-operator/pkg/generated/listers/rainbond/v1alpha1/expansion_generated.go new file mode 100644 index 000000000..6e183d7ac --- /dev/null +++ b/vendor/github.com/goodrain/rainbond-operator/pkg/generated/listers/rainbond/v1alpha1/expansion_generated.go @@ -0,0 +1,45 @@ +// RAINBOND, Application Management Platform +// Copyright (C) 2014-2020 Goodrain Co., Ltd. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. For any non-GPL usage of Rainbond, +// one or multiple Commercial Licenses authorized by Goodrain Co., Ltd. +// must be obtained first. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +// Code generated by lister-gen. DO NOT EDIT. + +package v1alpha1 + +// RainbondClusterListerExpansion allows custom methods to be added to +// RainbondClusterLister. +type RainbondClusterListerExpansion interface{} + +// RainbondClusterNamespaceListerExpansion allows custom methods to be added to +// RainbondClusterNamespaceLister. +type RainbondClusterNamespaceListerExpansion interface{} + +// RainbondPackageListerExpansion allows custom methods to be added to +// RainbondPackageLister. +type RainbondPackageListerExpansion interface{} + +// RainbondPackageNamespaceListerExpansion allows custom methods to be added to +// RainbondPackageNamespaceLister. +type RainbondPackageNamespaceListerExpansion interface{} + +// RbdComponentListerExpansion allows custom methods to be added to +// RbdComponentLister. +type RbdComponentListerExpansion interface{} + +// RbdComponentNamespaceListerExpansion allows custom methods to be added to +// RbdComponentNamespaceLister. +type RbdComponentNamespaceListerExpansion interface{} diff --git a/vendor/github.com/goodrain/rainbond-operator/pkg/generated/listers/rainbond/v1alpha1/rainbondcluster.go b/vendor/github.com/goodrain/rainbond-operator/pkg/generated/listers/rainbond/v1alpha1/rainbondcluster.go new file mode 100644 index 000000000..f722e2e38 --- /dev/null +++ b/vendor/github.com/goodrain/rainbond-operator/pkg/generated/listers/rainbond/v1alpha1/rainbondcluster.go @@ -0,0 +1,96 @@ +// RAINBOND, Application Management Platform +// Copyright (C) 2014-2020 Goodrain Co., Ltd. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. For any non-GPL usage of Rainbond, +// one or multiple Commercial Licenses authorized by Goodrain Co., Ltd. +// must be obtained first. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +// Code generated by lister-gen. DO NOT EDIT. + +package v1alpha1 + +import ( + v1alpha1 "github.com/goodrain/rainbond-operator/pkg/apis/rainbond/v1alpha1" + "k8s.io/apimachinery/pkg/api/errors" + "k8s.io/apimachinery/pkg/labels" + "k8s.io/client-go/tools/cache" +) + +// RainbondClusterLister helps list RainbondClusters. +type RainbondClusterLister interface { + // List lists all RainbondClusters in the indexer. + List(selector labels.Selector) (ret []*v1alpha1.RainbondCluster, err error) + // RainbondClusters returns an object that can list and get RainbondClusters. + RainbondClusters(namespace string) RainbondClusterNamespaceLister + RainbondClusterListerExpansion +} + +// rainbondClusterLister implements the RainbondClusterLister interface. +type rainbondClusterLister struct { + indexer cache.Indexer +} + +// NewRainbondClusterLister returns a new RainbondClusterLister. +func NewRainbondClusterLister(indexer cache.Indexer) RainbondClusterLister { + return &rainbondClusterLister{indexer: indexer} +} + +// List lists all RainbondClusters in the indexer. +func (s *rainbondClusterLister) List(selector labels.Selector) (ret []*v1alpha1.RainbondCluster, err error) { + err = cache.ListAll(s.indexer, selector, func(m interface{}) { + ret = append(ret, m.(*v1alpha1.RainbondCluster)) + }) + return ret, err +} + +// RainbondClusters returns an object that can list and get RainbondClusters. +func (s *rainbondClusterLister) RainbondClusters(namespace string) RainbondClusterNamespaceLister { + return rainbondClusterNamespaceLister{indexer: s.indexer, namespace: namespace} +} + +// RainbondClusterNamespaceLister helps list and get RainbondClusters. +type RainbondClusterNamespaceLister interface { + // List lists all RainbondClusters in the indexer for a given namespace. + List(selector labels.Selector) (ret []*v1alpha1.RainbondCluster, err error) + // Get retrieves the RainbondCluster from the indexer for a given namespace and name. + Get(name string) (*v1alpha1.RainbondCluster, error) + RainbondClusterNamespaceListerExpansion +} + +// rainbondClusterNamespaceLister implements the RainbondClusterNamespaceLister +// interface. +type rainbondClusterNamespaceLister struct { + indexer cache.Indexer + namespace string +} + +// List lists all RainbondClusters in the indexer for a given namespace. +func (s rainbondClusterNamespaceLister) List(selector labels.Selector) (ret []*v1alpha1.RainbondCluster, err error) { + err = cache.ListAllByNamespace(s.indexer, s.namespace, selector, func(m interface{}) { + ret = append(ret, m.(*v1alpha1.RainbondCluster)) + }) + return ret, err +} + +// Get retrieves the RainbondCluster from the indexer for a given namespace and name. +func (s rainbondClusterNamespaceLister) Get(name string) (*v1alpha1.RainbondCluster, error) { + obj, exists, err := s.indexer.GetByKey(s.namespace + "/" + name) + if err != nil { + return nil, err + } + if !exists { + return nil, errors.NewNotFound(v1alpha1.Resource("rainbondcluster"), name) + } + return obj.(*v1alpha1.RainbondCluster), nil +} diff --git a/vendor/github.com/goodrain/rainbond-operator/pkg/generated/listers/rainbond/v1alpha1/rainbondpackage.go b/vendor/github.com/goodrain/rainbond-operator/pkg/generated/listers/rainbond/v1alpha1/rainbondpackage.go new file mode 100644 index 000000000..0c21d3402 --- /dev/null +++ b/vendor/github.com/goodrain/rainbond-operator/pkg/generated/listers/rainbond/v1alpha1/rainbondpackage.go @@ -0,0 +1,96 @@ +// RAINBOND, Application Management Platform +// Copyright (C) 2014-2020 Goodrain Co., Ltd. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. For any non-GPL usage of Rainbond, +// one or multiple Commercial Licenses authorized by Goodrain Co., Ltd. +// must be obtained first. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +// Code generated by lister-gen. DO NOT EDIT. + +package v1alpha1 + +import ( + v1alpha1 "github.com/goodrain/rainbond-operator/pkg/apis/rainbond/v1alpha1" + "k8s.io/apimachinery/pkg/api/errors" + "k8s.io/apimachinery/pkg/labels" + "k8s.io/client-go/tools/cache" +) + +// RainbondPackageLister helps list RainbondPackages. +type RainbondPackageLister interface { + // List lists all RainbondPackages in the indexer. + List(selector labels.Selector) (ret []*v1alpha1.RainbondPackage, err error) + // RainbondPackages returns an object that can list and get RainbondPackages. + RainbondPackages(namespace string) RainbondPackageNamespaceLister + RainbondPackageListerExpansion +} + +// rainbondPackageLister implements the RainbondPackageLister interface. +type rainbondPackageLister struct { + indexer cache.Indexer +} + +// NewRainbondPackageLister returns a new RainbondPackageLister. +func NewRainbondPackageLister(indexer cache.Indexer) RainbondPackageLister { + return &rainbondPackageLister{indexer: indexer} +} + +// List lists all RainbondPackages in the indexer. +func (s *rainbondPackageLister) List(selector labels.Selector) (ret []*v1alpha1.RainbondPackage, err error) { + err = cache.ListAll(s.indexer, selector, func(m interface{}) { + ret = append(ret, m.(*v1alpha1.RainbondPackage)) + }) + return ret, err +} + +// RainbondPackages returns an object that can list and get RainbondPackages. +func (s *rainbondPackageLister) RainbondPackages(namespace string) RainbondPackageNamespaceLister { + return rainbondPackageNamespaceLister{indexer: s.indexer, namespace: namespace} +} + +// RainbondPackageNamespaceLister helps list and get RainbondPackages. +type RainbondPackageNamespaceLister interface { + // List lists all RainbondPackages in the indexer for a given namespace. + List(selector labels.Selector) (ret []*v1alpha1.RainbondPackage, err error) + // Get retrieves the RainbondPackage from the indexer for a given namespace and name. + Get(name string) (*v1alpha1.RainbondPackage, error) + RainbondPackageNamespaceListerExpansion +} + +// rainbondPackageNamespaceLister implements the RainbondPackageNamespaceLister +// interface. +type rainbondPackageNamespaceLister struct { + indexer cache.Indexer + namespace string +} + +// List lists all RainbondPackages in the indexer for a given namespace. +func (s rainbondPackageNamespaceLister) List(selector labels.Selector) (ret []*v1alpha1.RainbondPackage, err error) { + err = cache.ListAllByNamespace(s.indexer, s.namespace, selector, func(m interface{}) { + ret = append(ret, m.(*v1alpha1.RainbondPackage)) + }) + return ret, err +} + +// Get retrieves the RainbondPackage from the indexer for a given namespace and name. +func (s rainbondPackageNamespaceLister) Get(name string) (*v1alpha1.RainbondPackage, error) { + obj, exists, err := s.indexer.GetByKey(s.namespace + "/" + name) + if err != nil { + return nil, err + } + if !exists { + return nil, errors.NewNotFound(v1alpha1.Resource("rainbondpackage"), name) + } + return obj.(*v1alpha1.RainbondPackage), nil +} diff --git a/vendor/github.com/goodrain/rainbond-operator/pkg/generated/listers/rainbond/v1alpha1/rbdcomponent.go b/vendor/github.com/goodrain/rainbond-operator/pkg/generated/listers/rainbond/v1alpha1/rbdcomponent.go new file mode 100644 index 000000000..68327185e --- /dev/null +++ b/vendor/github.com/goodrain/rainbond-operator/pkg/generated/listers/rainbond/v1alpha1/rbdcomponent.go @@ -0,0 +1,96 @@ +// RAINBOND, Application Management Platform +// Copyright (C) 2014-2020 Goodrain Co., Ltd. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. For any non-GPL usage of Rainbond, +// one or multiple Commercial Licenses authorized by Goodrain Co., Ltd. +// must be obtained first. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +// Code generated by lister-gen. DO NOT EDIT. + +package v1alpha1 + +import ( + v1alpha1 "github.com/goodrain/rainbond-operator/pkg/apis/rainbond/v1alpha1" + "k8s.io/apimachinery/pkg/api/errors" + "k8s.io/apimachinery/pkg/labels" + "k8s.io/client-go/tools/cache" +) + +// RbdComponentLister helps list RbdComponents. +type RbdComponentLister interface { + // List lists all RbdComponents in the indexer. + List(selector labels.Selector) (ret []*v1alpha1.RbdComponent, err error) + // RbdComponents returns an object that can list and get RbdComponents. + RbdComponents(namespace string) RbdComponentNamespaceLister + RbdComponentListerExpansion +} + +// rbdComponentLister implements the RbdComponentLister interface. +type rbdComponentLister struct { + indexer cache.Indexer +} + +// NewRbdComponentLister returns a new RbdComponentLister. +func NewRbdComponentLister(indexer cache.Indexer) RbdComponentLister { + return &rbdComponentLister{indexer: indexer} +} + +// List lists all RbdComponents in the indexer. +func (s *rbdComponentLister) List(selector labels.Selector) (ret []*v1alpha1.RbdComponent, err error) { + err = cache.ListAll(s.indexer, selector, func(m interface{}) { + ret = append(ret, m.(*v1alpha1.RbdComponent)) + }) + return ret, err +} + +// RbdComponents returns an object that can list and get RbdComponents. +func (s *rbdComponentLister) RbdComponents(namespace string) RbdComponentNamespaceLister { + return rbdComponentNamespaceLister{indexer: s.indexer, namespace: namespace} +} + +// RbdComponentNamespaceLister helps list and get RbdComponents. +type RbdComponentNamespaceLister interface { + // List lists all RbdComponents in the indexer for a given namespace. + List(selector labels.Selector) (ret []*v1alpha1.RbdComponent, err error) + // Get retrieves the RbdComponent from the indexer for a given namespace and name. + Get(name string) (*v1alpha1.RbdComponent, error) + RbdComponentNamespaceListerExpansion +} + +// rbdComponentNamespaceLister implements the RbdComponentNamespaceLister +// interface. +type rbdComponentNamespaceLister struct { + indexer cache.Indexer + namespace string +} + +// List lists all RbdComponents in the indexer for a given namespace. +func (s rbdComponentNamespaceLister) List(selector labels.Selector) (ret []*v1alpha1.RbdComponent, err error) { + err = cache.ListAllByNamespace(s.indexer, s.namespace, selector, func(m interface{}) { + ret = append(ret, m.(*v1alpha1.RbdComponent)) + }) + return ret, err +} + +// Get retrieves the RbdComponent from the indexer for a given namespace and name. +func (s rbdComponentNamespaceLister) Get(name string) (*v1alpha1.RbdComponent, error) { + obj, exists, err := s.indexer.GetByKey(s.namespace + "/" + name) + if err != nil { + return nil, err + } + if !exists { + return nil, errors.NewNotFound(v1alpha1.Resource("rbdcomponent"), name) + } + return obj.(*v1alpha1.RbdComponent), nil +} From 4f70f33e8014d38f911cc08b4aaef4da49a68de0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=87=A1=E7=BE=8A=E7=BE=8A?= Date: Fri, 14 Feb 2020 18:47:32 +0800 Subject: [PATCH 28/47] fix local storage --- node/nodem/node_manager.go | 2 +- .../master/volumes/provider/lib/controller/controller.go | 2 +- worker/master/volumes/provider/rainbondsslc.go | 8 ++++---- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/node/nodem/node_manager.go b/node/nodem/node_manager.go index f5a24c574..cbf9ff126 100644 --- a/node/nodem/node_manager.go +++ b/node/nodem/node_manager.go @@ -324,7 +324,7 @@ func (n *NodeManager) getInitLabel(node *client.HostNode) map[string]string { node.HostName = hostname } labels["rainbond_node_hostname"] = node.HostName - labels["rainbond_node_ip"] = node.InternalIP + labels["kubernetes.io/hostname"] = node.InternalIP return labels } diff --git a/worker/master/volumes/provider/lib/controller/controller.go b/worker/master/volumes/provider/lib/controller/controller.go index 12be09341..35cbd14ff 100644 --- a/worker/master/volumes/provider/lib/controller/controller.go +++ b/worker/master/volumes/provider/lib/controller/controller.go @@ -515,7 +515,7 @@ func NewProvisionController( case "rainbondslsc": nodeIP := func() string { for _, me := range pv.Spec.NodeAffinity.Required.NodeSelectorTerms[0].MatchExpressions { - if me.Key != "rainbond_node_ip" { + if me.Key != "kubernetes.io/hostname" { continue } return me.Values[0] diff --git a/worker/master/volumes/provider/rainbondsslc.go b/worker/master/volumes/provider/rainbondsslc.go index edd6ba937..2f869299a 100644 --- a/worker/master/volumes/provider/rainbondsslc.go +++ b/worker/master/volumes/provider/rainbondsslc.go @@ -192,8 +192,8 @@ func (p *rainbondsslcProvisioner) Provision(options controller.VolumeOptions) (* if options.SelectedNode == nil { return nil, fmt.Errorf("do not select an appropriate node for local volume") } - if _, ok := options.SelectedNode.Labels["rainbond_node_ip"]; !ok { - return nil, fmt.Errorf("select node(%s) do not have label rainbond_node_ip ", options.SelectedNode.Name) + if _, ok := options.SelectedNode.Labels["kubernetes.io/hostname"]; !ok { + return nil, fmt.Errorf("select node(%s) do not have label kubernetes.io/hostname ", options.SelectedNode.Name) } } path, err := p.createPath(options) @@ -228,9 +228,9 @@ func (p *rainbondsslcProvisioner) Provision(options controller.VolumeOptions) (* v1.NodeSelectorTerm{ MatchExpressions: []v1.NodeSelectorRequirement{ v1.NodeSelectorRequirement{ - Key: "rainbond_node_ip", + Key: "kubernetes.io/hostname", Operator: v1.NodeSelectorOpIn, - Values: []string{options.SelectedNode.Labels["rainbond_node_ip"]}, + Values: []string{options.SelectedNode.Labels["kubernetes.io/hostname"]}, }, }, }, From a5e6a2f9285add22eef2e5fd74ee02ab16583dde Mon Sep 17 00:00:00 2001 From: barnettZQG Date: Sat, 15 Feb 2020 11:02:10 +0800 Subject: [PATCH 29/47] fix ES_DEFAULT_EXEC_ARGS bug --- webcli/app/app.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/webcli/app/app.go b/webcli/app/app.go index 9e13982db..6fe520066 100644 --- a/webcli/app/app.go +++ b/webcli/app/app.go @@ -26,6 +26,7 @@ import ( "fmt" "net" "net/http" + "strings" "sync" "text/template" @@ -332,7 +333,7 @@ func (app *App) GetDefaultContainerName(namespace, podname string) (string, []st if len(pod.Spec.Containers) > 0 { for _, env := range pod.Spec.Containers[0].Env { if env.Name == "ES_DEFAULT_EXEC_ARGS" { - args = []string{env.Name} + args = strings.Split(env.Value, " ") } } return pod.Spec.Containers[0].Name, args, nil From a29d42e6f286227056aa2f8ae7ecfb11ee5b14e1 Mon Sep 17 00:00:00 2001 From: GLYASAI Date: Sat, 15 Feb 2020 13:26:30 +0800 Subject: [PATCH 30/47] docker certificates and hosts --- cmd/node/server/server.go | 11 +++++++---- node/nodem/docker/docker.go | 11 +++-------- util/constants/constants.go | 6 ++++++ 3 files changed, 16 insertions(+), 12 deletions(-) create mode 100644 util/constants/constants.go diff --git a/cmd/node/server/server.go b/cmd/node/server/server.go index 12bf90a03..aafe497bd 100644 --- a/cmd/node/server/server.go +++ b/cmd/node/server/server.go @@ -23,6 +23,7 @@ import ( "fmt" "github.com/goodrain/rainbond/discover.v2" "github.com/goodrain/rainbond/node/initiate" + "github.com/goodrain/rainbond/util/constants" "k8s.io/client-go/kubernetes" "os" "os/signal" @@ -103,11 +104,13 @@ func Run(cfg *option.Conf) error { } defer kubecli.Stop() - hostManager, err := initiate.NewHostManager(cfg, k8sDiscover) - if err != nil { - return fmt.Errorf("create new host manager: %v", err) + if cfg.ImageRepositoryHost == constants.DefImageRepository { + hostManager, err := initiate.NewHostManager(cfg, k8sDiscover) + if err != nil { + return fmt.Errorf("create new host manager: %v", err) + } + hostManager.Start() } - hostManager.Start() logrus.Debugf("rbd-namespace=%s; rbd-docker-secret=%s", os.Getenv("RBD_NAMESPACE"), os.Getenv("RBD_DOCKER_SECRET")) // sync docker inscure registries cert info into all rainbond node diff --git a/node/nodem/docker/docker.go b/node/nodem/docker/docker.go index e1c4c3152..b226cf0a2 100644 --- a/node/nodem/docker/docker.go +++ b/node/nodem/docker/docker.go @@ -12,8 +12,6 @@ import ( "k8s.io/client-go/kubernetes" ) -var defaultSecretName = "rbd-docker-secret" -var defaultNamespace = "rbd-system" var defaultFileName = "server.crt" var defaultFilePath = "/etc/docker/certs.d/goodrain.me" @@ -21,11 +19,8 @@ var defaultFilePath = "/etc/docker/certs.d/goodrain.me" func SyncDockerCertFromSecret(clientset kubernetes.Interface, namespace, secretName string) error { namespace = strings.TrimSpace(namespace) secretName = strings.TrimSpace(secretName) - if namespace == "" { - namespace = defaultNamespace - } - if secretName == "" { - secretName = defaultSecretName + if namespace == "" || secretName == "" { + return nil } secretInfo, err := clientset.CoreV1().Secrets(namespace).Get(secretName, metav1.GetOptions{}) if err != nil { @@ -37,7 +32,7 @@ func SyncDockerCertFromSecret(clientset kubernetes.Interface, namespace, secretN } } else { - logrus.Warnf("docker secret:%s do not contain cert info", defaultSecretName) + logrus.Warnf("docker secret: %s/%s do not contain cert info", secretName, namespace) } return nil } diff --git a/util/constants/constants.go b/util/constants/constants.go new file mode 100644 index 000000000..e05b57e72 --- /dev/null +++ b/util/constants/constants.go @@ -0,0 +1,6 @@ +package constants + +const ( + // DefImageRepository default private image repository + DefImageRepository = "gooodrain.me" +) From 32122af52be8314055fb14c5f8d645876842cdd7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=87=A1=E7=BE=8A=E7=BE=8A?= Date: Sat, 15 Feb 2020 14:38:30 +0800 Subject: [PATCH 31/47] add etcd ca args --- cmd/gateway/option/option.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/cmd/gateway/option/option.go b/cmd/gateway/option/option.go index dc5744199..9571a343a 100644 --- a/cmd/gateway/option/option.go +++ b/cmd/gateway/option/option.go @@ -118,6 +118,9 @@ func (g *GWServer) AddFlags(fs *pflag.FlagSet) { // etcd fs.StringSliceVar(&g.EtcdEndpoint, "etcd-endpoints", []string{"http://127.0.0.1:2379"}, "etcd cluster endpoints.") fs.IntVar(&g.EtcdTimeout, "etcd-timeout", 10, "etcd http timeout seconds") + fs.StringVar(&g.EtcdCaFile, "etcd-ca", "", "etcd tls ca file ") + fs.StringVar(&g.EtcdCertFile, "etcd-cert", "", "etcd tls cert file") + fs.StringVar(&g.EtcdKeyFile, "etcd-key", "", "etcd http tls cert key file") // health check fs.StringVar(&g.HealthPath, "health-path", "/healthz", "absolute path to the kubeconfig file") fs.DurationVar(&g.HealthCheckTimeout, "health-check-timeout", 10, `Time limit, in seconds, for a probe to health-check-path to succeed.`) From 73375372c64af40c9aa66195c38abeb4b5b1bb4e Mon Sep 17 00:00:00 2001 From: GLYASAI Date: Sat, 15 Feb 2020 17:02:50 +0800 Subject: [PATCH 32/47] fix typos --- util/constants/constants.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/util/constants/constants.go b/util/constants/constants.go index e05b57e72..fa1a3aec7 100644 --- a/util/constants/constants.go +++ b/util/constants/constants.go @@ -2,5 +2,5 @@ package constants const ( // DefImageRepository default private image repository - DefImageRepository = "gooodrain.me" + DefImageRepository = "goodrain.me" ) From a4662aea162e61a674616faad5680960d5265201 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=87=A1=E7=BE=8A=E7=BE=8A?= Date: Sat, 15 Feb 2020 23:07:24 +0800 Subject: [PATCH 33/47] check etcd args and append --- cmd/monitor/option/option.go | 3 +++ cmd/mq/option/option.go | 3 +++ cmd/webcli/option/option.go | 3 +++ 3 files changed, 9 insertions(+) diff --git a/cmd/monitor/option/option.go b/cmd/monitor/option/option.go index 01ada57f3..b7972e744 100644 --- a/cmd/monitor/option/option.go +++ b/cmd/monitor/option/option.go @@ -136,6 +136,9 @@ func NewConfig() *Config { //AddFlag monitor flag func (c *Config) AddFlag(cmd *pflag.FlagSet) { cmd.StringVar(&c.EtcdEndpointsLine, "etcd-endpoints", c.EtcdEndpointsLine, "etcd endpoints list.") + cmd.StringVar(&c.EtcdCaFile, "etcd-ca", "", "etcd tls ca file ") + cmd.StringVar(&c.EtcdCertFile, "etcd-cert", "", "etcd tls cert file") + cmd.StringVar(&c.EtcdKeyFile, "etcd-key", "", "etcd http tls cert key file") cmd.StringVar(&c.AdvertiseAddr, "advertise-addr", c.AdvertiseAddr, "advertise address, and registry into etcd.") cmd.IntVar(&c.CadvisorListenPort, "cadvisor-listen-port", c.CadvisorListenPort, "kubelet cadvisor listen port in all node") cmd.StringSliceVar(&c.AlertManagerURL, "alertmanager-address", c.AlertManagerURL, "AlertManager url.") diff --git a/cmd/mq/option/option.go b/cmd/mq/option/option.go index a376c5ee5..02397638c 100644 --- a/cmd/mq/option/option.go +++ b/cmd/mq/option/option.go @@ -54,6 +54,9 @@ func (a *MQServer) AddFlags(fs *pflag.FlagSet) { fs.StringVar(&a.LogLevel, "log-level", "info", "the mq log level") fs.StringSliceVar(&a.EtcdEndPoints, "etcd-endpoints", []string{"http://127.0.0.1:2379"}, "etcd v3 cluster endpoints.") fs.IntVar(&a.EtcdTimeout, "etcd-timeout", 10, "etcd http timeout seconds") + fs.StringVar(&a.EtcdCaFile, "etcd-ca", "", "etcd tls ca file ") + fs.StringVar(&a.EtcdCertFile, "etcd-cert", "", "etcd tls cert file") + fs.StringVar(&a.EtcdKeyFile, "etcd-key", "", "etcd http tls cert key file") fs.StringVar(&a.EtcdPrefix, "etcd-prefix", "/mq", "the etcd data save key prefix ") fs.IntVar(&a.APIPort, "api-port", 6300, "the api server listen port") fs.StringVar(&a.RunMode, "mode", "grpc", "the api server run mode grpc or http") diff --git a/cmd/webcli/option/option.go b/cmd/webcli/option/option.go index 53359e145..9fef1a01d 100644 --- a/cmd/webcli/option/option.go +++ b/cmd/webcli/option/option.go @@ -55,6 +55,9 @@ func NewWebCliServer() *WebCliServer { func (a *WebCliServer) AddFlags(fs *pflag.FlagSet) { fs.StringVar(&a.LogLevel, "log-level", "info", "the webcli log level") fs.StringSliceVar(&a.EtcdEndPoints, "etcd-endpoints", []string{"http://127.0.0.1:2379"}, "etcd v3 cluster endpoints.") + fs.StringVar(&a.EtcdCaFile, "etcd-ca", "", "etcd tls ca file ") + fs.StringVar(&a.EtcdCertFile, "etcd-cert", "", "etcd tls cert file") + fs.StringVar(&a.EtcdKeyFile, "etcd-key", "", "etcd http tls cert key file") fs.StringVar(&a.Address, "address", "0.0.0.0", "server listen address") fs.StringVar(&a.HostIP, "hostIP", "", "Current node Intranet IP") fs.StringVar(&a.HostName, "hostName", "", "Current node host name") From 34528a9f35eddf7fdeb98bb07bdb720b7dfd6f38 Mon Sep 17 00:00:00 2001 From: GLYASAI Date: Mon, 17 Feb 2020 07:10:36 +0800 Subject: [PATCH 34/47] api certificate file not found --- grctl/cmd/cmd.go | 15 +++++++++++++++ grctl/cmd/install.go | 2 +- 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/grctl/cmd/cmd.go b/grctl/cmd/cmd.go index 0d2ccc3ea..cc3bd138a 100644 --- a/grctl/cmd/cmd.go +++ b/grctl/cmd/cmd.go @@ -65,6 +65,21 @@ func Common(c *cli.Context) { } +//Common Common +func CommonWithoutRegion(c *cli.Context) { + config, err := conf.LoadConfig(c) + if err != nil { + logrus.Warn("Load config file error.", err.Error()) + } + kc := c.GlobalString("kubeconfig") + if kc != "" { + config.Kubernets.KubeConf = kc + } + if err := clients.InitClient(config.Kubernets.KubeConf); err != nil { + logrus.Errorf("error config k8s,details %s", err.Error()) + } +} + // fatal prints the message (if provided) and then exits. If V(2) or greater, // glog.Fatal is invoked for extended information. func fatal(msg string, code int) { diff --git a/grctl/cmd/install.go b/grctl/cmd/install.go index 99b2f4aa7..772248e5b 100644 --- a/grctl/cmd/install.go +++ b/grctl/cmd/install.go @@ -35,7 +35,7 @@ func NewCmdInstall() cli.Command { }, Usage: "grctl install", Action: func(c *cli.Context) error { - Common(c) + CommonWithoutRegion(c) apiClientSecrit, err := clients.K8SClient.CoreV1().Secrets("rbd-system").Get("rbd-api-client-cert", metav1.GetOptions{}) if err != nil { showError(fmt.Sprintf("get region api tls secret failure %s", err.Error())) From a065ab265e42929e46f876bbbfc301f957e8733f Mon Sep 17 00:00:00 2001 From: barnettZQG Date: Tue, 18 Feb 2020 13:55:17 +0800 Subject: [PATCH 35/47] support set os type windows --- builder/parser/docker_compose.go | 2 ++ builder/parser/docker_run.go | 2 ++ builder/parser/parser.go | 3 ++- builder/parser/source_code.go | 2 ++ cmd/builder/option/option.go | 4 ++++ 5 files changed, 12 insertions(+), 1 deletion(-) diff --git a/builder/parser/docker_compose.go b/builder/parser/docker_compose.go index 026f52859..275abb5cf 100644 --- a/builder/parser/docker_compose.go +++ b/builder/parser/docker_compose.go @@ -20,6 +20,7 @@ package parser import ( "fmt" + "runtime" "strings" "github.com/Sirupsen/logrus" @@ -222,6 +223,7 @@ func (d *DockerComposeParse) GetServiceInfo() []ServiceInfo { ServiceDeployType: service.deployType, Name: service.name, Cname: service.name, + OS: runtime.GOOS, } if service.memory != 0 { si.Memory = service.memory diff --git a/builder/parser/docker_run.go b/builder/parser/docker_run.go index d64594f57..06cea483a 100644 --- a/builder/parser/docker_run.go +++ b/builder/parser/docker_run.go @@ -27,6 +27,7 @@ import ( "github.com/goodrain/rainbond/db/model" "github.com/goodrain/rainbond/event" "github.com/goodrain/rainbond/util" + "runtime" "strconv" "strings" //"github.com/docker/docker/client" ) @@ -275,6 +276,7 @@ func (d *DockerRunOrImageParse) GetServiceInfo() []ServiceInfo { Branchs: d.GetBranchs(), Memory: d.memory, ServiceDeployType: d.deployType, + OS: runtime.GOOS, } if serviceInfo.Memory == 0 { serviceInfo.Memory = 512 diff --git a/builder/parser/parser.go b/builder/parser/parser.go index b533fe965..d033ebcf0 100644 --- a/builder/parser/parser.go +++ b/builder/parser/parser.go @@ -179,7 +179,8 @@ type ServiceInfo struct { ImageAlias string `json:"image_alias,omitempty"` //For third party services Endpoints []*discovery.Endpoint `json:"endpoints,omitempty"` - + //os type,default linux + OS string `json:"os"` Name string `json:"name,omitempty"` // module name Cname string `json:"cname,omitempty"` // service cname Packaging string `json:"packaging,omitempty"` diff --git a/builder/parser/source_code.go b/builder/parser/source_code.go index 563a8ed64..132a9970e 100644 --- a/builder/parser/source_code.go +++ b/builder/parser/source_code.go @@ -21,6 +21,7 @@ package parser import ( "fmt" "path" + "runtime" "strconv" "strings" @@ -486,6 +487,7 @@ func (d *SourceCodeParse) GetServiceInfo() []ServiceInfo { Memory: d.memory, Lang: d.GetLang(), ServiceDeployType: util.StatelessServiceType, + OS: runtime.GOOS, } var res []ServiceInfo if d.isMulti && d.services != nil && len(d.services) > 0 { diff --git a/cmd/builder/option/option.go b/cmd/builder/option/option.go index 444d0412f..58e2a1920 100644 --- a/cmd/builder/option/option.go +++ b/cmd/builder/option/option.go @@ -20,6 +20,7 @@ package option import ( "fmt" + "runtime" "github.com/Sirupsen/logrus" "github.com/goodrain/rainbond/mq/client" @@ -96,5 +97,8 @@ func (a *Builder) CheckConfig() error { if a.Topic != client.BuilderTopic && a.Topic != client.WindowsBuilderTopic { return fmt.Errorf("Topic is only suppory `%s` and `%s`", client.BuilderTopic, client.WindowsBuilderTopic) } + if runtime.GOOS == "windows" { + a.Topic = "windows_builder" + } return nil } From 61e4b2df4e173eef55ba827d9dbc41548c13f7ab Mon Sep 17 00:00:00 2001 From: barnettZQG Date: Tue, 18 Feb 2020 17:23:51 +0800 Subject: [PATCH 36/47] create os label where create service --- api/handler/service.go | 11 +++++++++++ api/model/model.go | 4 ++++ 2 files changed, 15 insertions(+) diff --git a/api/handler/service.go b/api/handler/service.go index 25e7f7d82..f348e29bc 100644 --- a/api/handler/service.go +++ b/api/handler/service.go @@ -645,6 +645,17 @@ func (s *ServiceAction) ServiceCreate(sc *api_model.ServiceStruct) error { tx.Rollback() return err } + if sc.OSType == "windows" { + if err := db.GetManager().TenantServiceLabelDaoTransactions(tx).AddModel(&dbmodel.TenantServiceLable{ + ServiceID: ts.ServiceID, + LabelKey: core_model.LabelKeyNodeSelector, + LabelValue: sc.OSType, + }); err != nil { + logrus.Errorf("add label %s=%s %v error, %v", core_model.LabelKeyNodeSelector, sc.OSType, ts.ServiceID, err) + tx.Rollback() + return err + } + } // sc.Endpoints can't be nil // sc.Endpoints.Discovery or sc.Endpoints.Static can't be nil if sc.Kind == dbmodel.ServiceKindThirdParty.String() { // TODO: validate request data diff --git a/api/model/model.go b/api/model/model.go index b5595dbb7..74c732cd7 100644 --- a/api/model/model.go +++ b/api/model/model.go @@ -323,6 +323,10 @@ type ServiceStruct struct { // required: false ServiceOrigin string `json:"service_origin" validate:"service_origin"` Kind string `json:"kind" validate:"kind|in:internal,third_party"` + //OSType runtime os type + // in: body + // required: false + OSType string `json:"os_type" validate:"os_type|in:windows,linux"` ServiceLabel string `json:"service_label" validate:"service_label|in:StatelessServiceType,StatefulServiceType"` NodeLabel string `json:"node_label" validate:"node_label"` From 4393dddc63c8f4092e903cc4b6a05409b644d821 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=87=A1=E7=BE=8A=E7=BE=8A?= Date: Tue, 18 Feb 2020 22:41:18 +0800 Subject: [PATCH 37/47] update tenant_service struct, add service_type --- api/controller/resources.go | 3 +- api/handler/group/group_backup.go | 4 +- api/handler/service.go | 73 +++++--------------------- api/handler/service_handler.go | 1 - api/model/model.go | 8 ++- builder/exector/groupapp_backup.go | 10 ---- builder/exector/groupapp_restore.go | 3 +- builder/parser/docker_compose.go | 44 ++++++++-------- builder/parser/docker_run.go | 20 +++---- builder/parser/parser.go | 30 +++++------ builder/parser/source_code.go | 18 +++---- db/dao/dao.go | 1 + db/db_test.go | 4 +- db/model/tenant.go | 42 ++++++++++++++- db/mysql/dao/tenants.go | 46 +++++++++------- worker/appm/conversion/gateway_test.go | 3 -- worker/appm/conversion/service.go | 7 +-- worker/appm/conversion/version.go | 5 -- worker/appm/volume/volume.go | 5 -- 19 files changed, 154 insertions(+), 173 deletions(-) diff --git a/api/controller/resources.go b/api/controller/resources.go index 08b2e1705..6c0c5f692 100644 --- a/api/controller/resources.go +++ b/api/controller/resources.go @@ -660,6 +660,7 @@ func (t *TenantStruct) CreateService(w http.ResponseWriter, r *http.Request) { tenantID := r.Context().Value(middleware.ContextKey("tenant_id")).(string) ss.TenantID = tenantID + ss.ServiceType = ss.ExtendMethod if err := handler.GetServiceManager().ServiceCreate(&ss); err != nil { if strings.Contains(err.Error(), "is exist in tenant") { httputil.ReturnError(r, w, 400, fmt.Sprintf("create service error, %v", err)) @@ -692,7 +693,7 @@ func (t *TenantStruct) UpdateService(w http.ResponseWriter, r *http.Request) { // schema: // "$ref": "#/responses/commandResponse" // description: 统一返回格式 - + // TODO fanyangyang 支持组件类型的修改 logrus.Debugf("trans update service service") //目前提供三个元素的修改 rules := validator.MapData{ diff --git a/api/handler/group/group_backup.go b/api/handler/group/group_backup.go index ff3d5492a..76955b982 100644 --- a/api/handler/group/group_backup.go +++ b/api/handler/group/group_backup.go @@ -230,11 +230,11 @@ func (h *BackupHandle) snapshot(ids []string, sourceDir string) error { ServiceID: id, } status := h.statusCli.GetStatus(id) - serviceType, err := db.GetManager().TenantServiceLabelDao().GetTenantServiceTypeLabel(id) + serviceInfo, err := db.GetManager().TenantServiceDao().GetServiceTypeById(id) if err != nil { return fmt.Errorf("Get service deploy type error,%s", err.Error()) } - if status != v1.CLOSED && serviceType != nil && serviceType.LabelValue == core_util.StatefulServiceType { + if status != v1.CLOSED && serviceInfo != nil && serviceInfo.IsState() { // TODO fanyangyang根据组件类型确定是否支持 return fmt.Errorf("Statefulset app must be closed before backup,%s", err.Error()) } data.ServiceStatus = status diff --git a/api/handler/service.go b/api/handler/service.go index 861370d46..c1ed3622f 100644 --- a/api/handler/service.go +++ b/api/handler/service.go @@ -37,7 +37,6 @@ import ( "github.com/goodrain/rainbond/cmd/api/option" "github.com/goodrain/rainbond/db" dberrors "github.com/goodrain/rainbond/db/errors" - core_model "github.com/goodrain/rainbond/db/model" dbmodel "github.com/goodrain/rainbond/db/model" "github.com/goodrain/rainbond/event" eventutil "github.com/goodrain/rainbond/eventlog/util" @@ -249,17 +248,12 @@ func (s *ServiceAction) AddLabel(l *api_model.LabelsStruct, serviceID string) er tx.Rollback() } }() + //V5.2: do not support service type label for _, label := range l.Labels { - var labelModel dbmodel.TenantServiceLable - switch label.LabelKey { - case core_model.LabelKeyServiceType: - labelModel.ServiceID = serviceID - labelModel.LabelKey = core_model.LabelKeyServiceType - labelModel.LabelValue = chekeServiceLabel(label.LabelValue) - default: - labelModel.ServiceID = serviceID - labelModel.LabelKey = label.LabelKey - labelModel.LabelValue = label.LabelValue + labelModel := dbmodel.TenantServiceLable{ + ServiceID: serviceID, + LabelKey: label.LabelKey, + LabelValue: label.LabelValue, } if err := db.GetManager().TenantServiceLabelDaoTransactions(tx).AddModel(&labelModel); err != nil { tx.Rollback() @@ -291,17 +285,12 @@ func (s *ServiceAction) UpdateLabel(l *api_model.LabelsStruct, serviceID string) tx.Rollback() return err } + // V5.2 do not support service type label // add new labels - var labelModel dbmodel.TenantServiceLable - switch label.LabelKey { - case core_model.LabelKeyServiceType: - labelModel.ServiceID = serviceID - labelModel.LabelKey = core_model.LabelKeyServiceType - labelModel.LabelValue = chekeServiceLabel(label.LabelValue) - default: - labelModel.ServiceID = serviceID - labelModel.LabelKey = label.LabelKey - labelModel.LabelValue = label.LabelValue + labelModel := dbmodel.TenantServiceLable{ + ServiceID: serviceID, + LabelKey: label.LabelKey, + LabelValue: label.LabelValue, } if err := db.GetManager().TenantServiceLabelDaoTransactions(tx).AddModel(&labelModel); err != nil { logrus.Errorf("error adding new labels: %v", err) @@ -342,24 +331,6 @@ func (s *ServiceAction) DeleteLabel(l *api_model.LabelsStruct, serviceID string) return nil } -//UpdateServiceLabel UpdateLabel -func (s *ServiceAction) UpdateServiceLabel(serviceID, value string) error { - sls, err := db.GetManager().TenantServiceLabelDao().GetTenantServiceLabel(serviceID) - if err != nil { - return err - } - if len(sls) > 0 { - for _, sl := range sls { - sl.ServiceID = serviceID - sl.LabelKey = core_model.LabelKeyServiceType - value = chekeServiceLabel(value) - sl.LabelValue = value - return db.GetManager().TenantServiceLabelDao().UpdateModel(sl) - } - } - return fmt.Errorf("Get tenant service label error") -} - //StartStopService start service func (s *ServiceAction) StartStopService(sss *api_model.StartStopStruct) error { services, err := db.GetManager().TenantServiceDao().GetServiceByID(sss.ServiceID) @@ -635,16 +606,6 @@ func (s *ServiceAction) ServiceCreate(sc *api_model.ServiceStruct) error { } } } - //set app label - if err := db.GetManager().TenantServiceLabelDaoTransactions(tx).AddModel(&dbmodel.TenantServiceLable{ - ServiceID: ts.ServiceID, - LabelKey: core_model.LabelKeyServiceType, - LabelValue: sc.ServiceLabel, - }); err != nil { - logrus.Errorf("add label %v error, %v", ts.ServiceID, err) - tx.Rollback() - return err - } // sc.Endpoints can't be nil // sc.Endpoints.Discovery or sc.Endpoints.Static can't be nil if sc.Kind == dbmodel.ServiceKindThirdParty.String() { // TODO: validate request data @@ -1343,11 +1304,11 @@ func (s *ServiceAction) VolumnVar(tsv *dbmodel.TenantServiceVolume, tenantID, fi tsv.HostPath = fmt.Sprintf("%s/tenant/%s/service/%s%s", sharePath, tenantID, tsv.ServiceID, tsv.VolumePath) //本地文件存储 case dbmodel.LocalVolumeType.String(): - serviceType, err := db.GetManager().TenantServiceLabelDao().GetTenantServiceTypeLabel(tsv.ServiceID) + serviceInfo, err := db.GetManager().TenantServiceDao().GetServiceTypeById(tsv.ServiceID) if err != nil { return util.CreateAPIHandleErrorFromDBError("service type", err) } - if serviceType == nil || serviceType.LabelValue != core_util.StatefulServiceType { + if serviceInfo == nil || serviceInfo.IsState() { // TODO fanyangyang 根据组件类型确定是否支持 return util.CreateAPIHandleError(400, fmt.Errorf("应用类型不为有状态应用.不支持本地存储")) } tsv.HostPath = fmt.Sprintf("%s/tenant/%s/service/%s%s", localPath, tenantID, tsv.ServiceID, tsv.VolumePath) @@ -2173,13 +2134,3 @@ func CheckMapKey(rebody map[string]interface{}, key string, defaultValue interfa rebody[key] = defaultValue return rebody } - -func chekeServiceLabel(v string) string { - if strings.Contains(v, "有状态") { - return core_util.StatefulServiceType - } - if strings.Contains(v, "无状态") { - return core_util.StatelessServiceType - } - return v -} diff --git a/api/handler/service_handler.go b/api/handler/service_handler.go index 0cd52361b..b05a9354e 100644 --- a/api/handler/service_handler.go +++ b/api/handler/service_handler.go @@ -33,7 +33,6 @@ type ServiceHandler interface { AddLabel(l *api_model.LabelsStruct, serviceID string) error DeleteLabel(l *api_model.LabelsStruct, serviceID string) error UpdateLabel(l *api_model.LabelsStruct, serviceID string) error - UpdateServiceLabel(serviceID, value string) error StartStopService(s *api_model.StartStopStruct) error ServiceVertical(v *model.VerticalScalingTaskBody) error ServiceHorizontal(h *model.HorizontalScalingTaskBody) error diff --git a/api/model/model.go b/api/model/model.go index b5595dbb7..8c003accb 100644 --- a/api/model/model.go +++ b/api/model/model.go @@ -258,6 +258,10 @@ type ServiceStruct struct { // in: body // required: true ServiceAlias string `json:"service_alias" validate:"service_alias"` + // 组件类型 + // in: body + // required: true + ServiceType string `json:"service_type" validate:"service_type"` // 服务描述 // in: body // required: false @@ -286,7 +290,7 @@ type ServiceStruct struct { // in: body // required: false ContainerEnv string `json:"container_env" validate:"container_env"` - // 扩容方式;0:无状态;1:有状态;2:分区 + // 扩容方式;0:无状态;1:有状态;2:分区(v5.2用于接收组件的类型) // in: body // required: false ExtendMethod string `json:"extend_method" validate:"extend_method"` @@ -323,7 +327,7 @@ type ServiceStruct struct { // required: false ServiceOrigin string `json:"service_origin" validate:"service_origin"` Kind string `json:"kind" validate:"kind|in:internal,third_party"` - + // V5.2 弃用 ServiceLabel string `json:"service_label" validate:"service_label|in:StatelessServiceType,StatefulServiceType"` NodeLabel string `json:"node_label" validate:"node_label"` Operator string `json:"operator" validate:"operator"` diff --git a/builder/exector/groupapp_backup.go b/builder/exector/groupapp_backup.go index 88c83d021..6fa94119a 100644 --- a/builder/exector/groupapp_backup.go +++ b/builder/exector/groupapp_backup.go @@ -426,13 +426,3 @@ func GetVolumeDir() (string, string) { } return localPath, sharePath } - -//GetServiceType get service deploy type -func GetServiceType(labels []*dbmodel.TenantServiceLable) string { - for _, l := range labels { - if l.LabelKey == dbmodel.LabelKeyServiceType { - return l.LabelValue - } - } - return util.StatelessServiceType -} diff --git a/builder/exector/groupapp_restore.go b/builder/exector/groupapp_restore.go index 2574ac46a..e630b4832 100644 --- a/builder/exector/groupapp_restore.go +++ b/builder/exector/groupapp_restore.go @@ -213,8 +213,9 @@ func (b *BackupAPPRestore) restoreVersionAndData(backup *dbmodel.AppBackup, appS os.MkdirAll(volume.HostPath, 0777) continue } + //if app type is statefulset, change pod hostpath - if GetServiceType(app.ServiceLabel) == util.StatefulServiceType { + if app.Service.IsState() { // TODO fanyangyang 备份时需要备份组件的类型 //Next two level directory list, err := util.GetDirList(tmpDir, 2) if err != nil { diff --git a/builder/parser/docker_compose.go b/builder/parser/docker_compose.go index 026f52859..5ccc98bf5 100644 --- a/builder/parser/docker_compose.go +++ b/builder/parser/docker_compose.go @@ -44,17 +44,17 @@ type DockerComposeParse struct { //ServiceInfoFromDC service info from dockercompose type ServiceInfoFromDC struct { - ports map[int]*types.Port - volumes map[string]*types.Volume - envs map[string]*types.Env - source string - memory int - image Image - args []string - depends []string - imageAlias string - deployType string - name string + ports map[int]*types.Port + volumes map[string]*types.Volume + envs map[string]*types.Env + source string + memory int + image Image + args []string + depends []string + imageAlias string + serviceType string + name string } //GetPorts 获取端口列表 @@ -163,7 +163,7 @@ func (d *DockerComposeParse) Parse() ParseErrorList { if sc.DependsON != nil { service.depends = sc.DependsON } - service.deployType = DetermineDeployType(service.image) + service.serviceType = DetermineDeployType(service.image) d.services[kev] = &service } for serviceName, service := range d.services { @@ -212,16 +212,16 @@ func (d *DockerComposeParse) GetServiceInfo() []ServiceInfo { var sis []ServiceInfo for _, service := range d.services { si := ServiceInfo{ - Ports: service.GetPorts(), - Envs: service.GetEnvs(), - Volumes: service.GetVolumes(), - Image: service.image, - Args: service.args, - DependServices: service.depends, - ImageAlias: service.imageAlias, - ServiceDeployType: service.deployType, - Name: service.name, - Cname: service.name, + Ports: service.GetPorts(), + Envs: service.GetEnvs(), + Volumes: service.GetVolumes(), + Image: service.image, + Args: service.args, + DependServices: service.depends, + ImageAlias: service.imageAlias, + ServiceType: service.serviceType, + Name: service.name, + Cname: service.name, } if service.memory != 0 { si.Memory = service.memory diff --git a/builder/parser/docker_run.go b/builder/parser/docker_run.go index d64594f57..e8abb1556 100644 --- a/builder/parser/docker_run.go +++ b/builder/parser/docker_run.go @@ -38,7 +38,7 @@ type DockerRunOrImageParse struct { volumes map[string]*types.Volume envs map[string]*types.Env source string - deployType string + serviceType string memory int image Image args []string @@ -133,7 +133,7 @@ func (d *DockerRunOrImageParse) Parse() ParseErrorList { } } } - d.deployType = DetermineDeployType(d.image) + d.serviceType = DetermineDeployType(d.image) return d.errors } @@ -267,14 +267,14 @@ func (d *DockerRunOrImageParse) GetMemory() int { //GetServiceInfo 获取service info func (d *DockerRunOrImageParse) GetServiceInfo() []ServiceInfo { serviceInfo := ServiceInfo{ - Ports: d.GetPorts(), - Envs: d.GetEnvs(), - Volumes: d.GetVolumes(), - Image: d.GetImage(), - Args: d.GetArgs(), - Branchs: d.GetBranchs(), - Memory: d.memory, - ServiceDeployType: d.deployType, + Ports: d.GetPorts(), + Envs: d.GetEnvs(), + Volumes: d.GetVolumes(), + Image: d.GetImage(), + Args: d.GetArgs(), + Branchs: d.GetBranchs(), + Memory: d.memory, + ServiceType: d.serviceType, } if serviceInfo.Memory == 0 { serviceInfo.Memory = 512 diff --git a/builder/parser/parser.go b/builder/parser/parser.go index b533fe965..738d79439 100644 --- a/builder/parser/parser.go +++ b/builder/parser/parser.go @@ -20,6 +20,7 @@ package parser import ( "fmt" + dbmodel "github.com/goodrain/rainbond/db/model" "strings" "github.com/Sirupsen/logrus" @@ -28,7 +29,6 @@ import ( "github.com/goodrain/rainbond/builder/parser/discovery" "github.com/goodrain/rainbond/builder/parser/types" "github.com/goodrain/rainbond/builder/sources" - "github.com/goodrain/rainbond/util" "k8s.io/apimachinery/pkg/api/resource" ) @@ -165,18 +165,18 @@ type Lang string //ServiceInfo 智能获取的应用信息 type ServiceInfo struct { - ID string `json:"id,omitempty"` - Ports []types.Port `json:"ports,omitempty"` - Envs []types.Env `json:"envs,omitempty"` - Volumes []types.Volume `json:"volumes,omitempty"` - Image Image `json:"image,omitempty"` - Args []string `json:"args,omitempty"` - DependServices []string `json:"depends,omitempty"` - ServiceDeployType string `json:"deploy_type,omitempty"` - Branchs []string `json:"branchs,omitempty"` - Memory int `json:"memory,omitempty"` - Lang code.Lang `json:"language,omitempty"` - ImageAlias string `json:"image_alias,omitempty"` + ID string `json:"id,omitempty"` + Ports []types.Port `json:"ports,omitempty"` + Envs []types.Env `json:"envs,omitempty"` + Volumes []types.Volume `json:"volumes,omitempty"` + Image Image `json:"image,omitempty"` + Args []string `json:"args,omitempty"` + DependServices []string `json:"depends,omitempty"` + ServiceType string `json:"service_type,omitempty"` + Branchs []string `json:"branchs,omitempty"` + Memory int `json:"memory,omitempty"` + Lang code.Lang `json:"language,omitempty"` + ImageAlias string `json:"image_alias,omitempty"` //For third party services Endpoints []*discovery.Endpoint `json:"endpoints,omitempty"` @@ -236,10 +236,10 @@ var dbImageKey = []string{ func DetermineDeployType(imageName Image) string { for _, key := range dbImageKey { if strings.ToLower(imageName.GetSimpleName()) == key { - return util.StatefulServiceType + return dbmodel.ServiceTypeStatelessSingleton.String() // TODO fanyangyang 通过镜像要挂载的存储确定更细致的组件类型 } } - return util.StatelessServiceType + return dbmodel.ServiceTypeStatelessSingleton.String() } //readmemory diff --git a/builder/parser/source_code.go b/builder/parser/source_code.go index 563a8ed64..c5f42b82f 100644 --- a/builder/parser/source_code.go +++ b/builder/parser/source_code.go @@ -477,15 +477,15 @@ func (d *SourceCodeParse) GetLang() code.Lang { //GetServiceInfo 获取service info func (d *SourceCodeParse) GetServiceInfo() []ServiceInfo { serviceInfo := ServiceInfo{ - Ports: d.GetPorts(), - Envs: d.GetEnvs(), - Volumes: d.GetVolumes(), - Image: d.GetImage(), - Args: d.GetArgs(), - Branchs: d.GetBranchs(), - Memory: d.memory, - Lang: d.GetLang(), - ServiceDeployType: util.StatelessServiceType, + Ports: d.GetPorts(), + Envs: d.GetEnvs(), + Volumes: d.GetVolumes(), + Image: d.GetImage(), + Args: d.GetArgs(), + Branchs: d.GetBranchs(), + Memory: d.memory, + Lang: d.GetLang(), + ServiceType: model.ServiceTypeStatelessSingleton.String(), // TODO fanyangyang 使用存储确定组件类型 } var res []ServiceInfo if d.isMulti && d.services != nil && len(d.services) > 0 { diff --git a/db/dao/dao.go b/db/dao/dao.go index ce36c9a14..110076099 100644 --- a/db/dao/dao.go +++ b/db/dao/dao.go @@ -99,6 +99,7 @@ type TenantServiceDao interface { UpdateDeployVersion(serviceID, deployversion string) error ListThirdPartyServices() ([]*model.TenantServices, error) ListServicesByTenantID(tenantID string) ([]*model.TenantServices, error) + GetServiceTypeById(serviceID string) (*model.TenantServices, error) } //TenantServiceDeleteDao TenantServiceDeleteDao diff --git a/db/db_test.go b/db/db_test.go index 88e1f071c..02bdd52e4 100644 --- a/db/db_test.go +++ b/db/db_test.go @@ -155,8 +155,8 @@ func TestSetServiceLabel(t *testing.T) { t.Fatal(err) } label := model.TenantServiceLable{ - LabelKey: model.LabelKeyServiceType, - LabelValue: util.StatefulServiceType, + LabelKey: "labelkey", + LabelValue: "labelvalue", ServiceID: "889bb1f028f655bebd545f24aa184a0b", } label.CreatedAt = time.Now() diff --git a/db/model/tenant.go b/db/model/tenant.go index e6b29f02a..2d1f7ebb1 100644 --- a/db/model/tenant.go +++ b/db/model/tenant.go @@ -85,6 +85,41 @@ func (s ServiceKind) String() string { return string(s) } +// ServiceType type of service +type ServiceType string + +// String imple String +func (s ServiceType) String() string { + return string(s) +} + +// TODO fanyangyang 根据组件简单判断是否是有状态 +// IsState is state service or stateless service +func (ts TenantServices) IsState() bool { + if ts.ServiceType == "" { + return false + } + if ts.ServiceType == ServiceTypeStatelessSingleton.String() || ts.ServiceType == ServiceTypeStatelessMultiple.String() { + return false + } + return true +} + +// ServiceTypeUnknown unknown +var ServiceTypeUnknown ServiceType = "unknown" + +//ServiceTypeStatelessSingleton stateless_singleton +var ServiceTypeStatelessSingleton ServiceType = "stateless_singleton" + +// ServiceTypeStatelessMultiple stateless_multiple +var ServiceTypeStatelessMultiple ServiceType = "stateless_multiple" + +// ServiceTypeStateSingleton state_singleton +var ServiceTypeStateSingleton ServiceType = "state_singleton" + +// ServiceTypeStateMultiple state_multiple +var ServiceTypeStateMultiple ServiceType = "state_multiple" + //TenantServices app service base info type TenantServices struct { Model @@ -96,6 +131,8 @@ type TenantServices struct { ServiceAlias string `gorm:"column:service_alias;size:30" json:"service_alias"` // service regist endpoint name(host name), used of statefulset ServiceName string `gorm:"column:service_name;size:100" json:"service_name"` + // Service type now service support stateless_singleton/stateless_multiple/state_singleton/state_multiple + ServiceType string `gorm:"column:service_type;size:32" json:"service_type"` // 服务描述 Comment string `gorm:"column:comment" json:"comment"` // 容器CPU权重 @@ -105,7 +142,7 @@ type TenantServices struct { //UpgradeMethod service upgrade controller type //such as : `Rolling` `OnDelete` UpgradeMethod string `gorm:"column:upgrade_method;default:'Rolling'" json:"upgrade_method"` - // 扩容方式;0:无状态;1:有状态;2:分区 + // 扩容方式;0:无状态;1:有状态;2:分区(V5.2已弃用) ExtendMethod string `gorm:"column:extend_method;default:'stateless';" json:"extend_method"` // 节点数 Replicas int `gorm:"column:replicas;default:1" json:"replicas"` @@ -197,6 +234,8 @@ type TenantServicesDelete struct { ServiceAlias string `gorm:"column:service_alias;size:30" json:"service_alias"` // service regist endpoint name(host name), used of statefulset ServiceName string `gorm:"column:service_name;size:100" json:"service_name"` + // Service type now service support stateless_singleton/stateless_multiple/state_singleton/state_multiple + ServiceType string `gorm:"column:service_type;size:20" json:"service_type"` // 服务描述 Comment string `gorm:"column:comment" json:"comment"` // 容器CPU权重 @@ -423,6 +462,7 @@ var LabelKeyNodeSelector = "node-selector" //LabelKeyNodeAffinity 节点亲和标签 var LabelKeyNodeAffinity = "node-affinity" +// TODO fanyangyang 待删除,组件类型记录在tenant_service表中 //LabelKeyServiceType 应用部署类型标签 var LabelKeyServiceType = "service-type" diff --git a/db/mysql/dao/tenants.go b/db/mysql/dao/tenants.go index 5625c2af9..fc8007804 100644 --- a/db/mysql/dao/tenants.go +++ b/db/mysql/dao/tenants.go @@ -172,6 +172,27 @@ type TenantServicesDaoImpl struct { DB *gorm.DB } +// GetServiceTypeById get service type by service id +func (t *TenantServicesDaoImpl) GetServiceTypeById(serviceID string) (*model.TenantServices, error) { + var service model.TenantServices + if err := t.DB.Select("tenant_id, service_id, service_alias, service_type").Where("service_id=?", serviceID).Find(&service).Error; err != nil { + return nil, err + } + if service.ServiceType == "" { + // for before V5.2 version + logrus.Infof("get low version service[%s] type", serviceID) + rows, err := t.DB.Raw("select label_value from tenant_services_label where service_id=? and label_key=?", serviceID, "service-type").Rows() + if err != nil { + return nil, err + } + defer rows.Close() + for rows.Next() { + rows.Scan(&service.ServiceType) + } + } + return &service, nil +} + //GetAllServicesID get all service sample info func (t *TenantServicesDaoImpl) GetAllServicesID() ([]*model.TenantServices, error) { var services []*model.TenantServices @@ -1322,22 +1343,12 @@ type ServiceLabelDaoImpl struct { func (t *ServiceLabelDaoImpl) AddModel(mo model.Interface) error { label := mo.(*model.TenantServiceLable) var oldLabel model.TenantServiceLable - if label.LabelKey == model.LabelKeyServiceType { //LabelKeyServiceType 只能有一条 - if ok := t.DB.Where("service_id = ? and label_key=?", label.ServiceID, label.LabelKey).Find(&oldLabel).RecordNotFound(); ok { - if err := t.DB.Create(label).Error; err != nil { - return err - } - } else { - return fmt.Errorf("label key %s of service %s is exist", label.LabelKey, label.ServiceID) + if ok := t.DB.Where("service_id = ? and label_key=? and label_value=?", label.ServiceID, label.LabelKey, label.LabelValue).Find(&oldLabel).RecordNotFound(); ok { + if err := t.DB.Create(label).Error; err != nil { + return err } } else { - if ok := t.DB.Where("service_id = ? and label_key=? and label_value=?", label.ServiceID, label.LabelKey, label.LabelValue).Find(&oldLabel).RecordNotFound(); ok { - if err := t.DB.Create(label).Error; err != nil { - return err - } - } else { - return fmt.Errorf("label key %s value %s of service %s is exist", label.LabelKey, label.LabelValue, label.ServiceID) - } + return fmt.Errorf("label key %s value %s of service %s is exist", label.LabelKey, label.LabelValue, label.ServiceID) } return nil } @@ -1439,15 +1450,10 @@ func (t *ServiceLabelDaoImpl) GetTenantServiceAffinityLabel(serviceID string) ([ return labels, nil } +// no usages func. get tenant service type use TenantServiceDao.GetServiceTypeById(serviceID string) //GetTenantServiceTypeLabel GetTenantServiceTypeLabel func (t *ServiceLabelDaoImpl) GetTenantServiceTypeLabel(serviceID string) (*model.TenantServiceLable, error) { var label model.TenantServiceLable - if err := t.DB.Where("service_id=? and label_key=?", serviceID, model.LabelKeyServiceType).Find(&label).Error; err != nil { - if err == gorm.ErrRecordNotFound { - return nil, nil - } - return nil, err - } return &label, nil } diff --git a/worker/appm/conversion/gateway_test.go b/worker/appm/conversion/gateway_test.go index 53e4404cc..7fc52d5e2 100644 --- a/worker/appm/conversion/gateway_test.go +++ b/worker/appm/conversion/gateway_test.go @@ -163,7 +163,6 @@ func TestApplyTcpRule(t *testing.T) { Comment: "application info", ContainerCPU: 20, ContainerMemory: 128, - ExtendMethod: "stateless", Replicas: 1, DeployVersion: "20181022200709", Category: "application", @@ -289,7 +288,6 @@ func TestAppServiceBuild_ApplyHttpRule(t *testing.T) { Comment: "application info", ContainerCPU: 20, ContainerMemory: 128, - ExtendMethod: "stateless", Replicas: 1, DeployVersion: "20181022200709", Category: "application", @@ -430,7 +428,6 @@ func TestAppServiceBuild_ApplyHttpRuleWithCertificate(t *testing.T) { Comment: "application info", ContainerCPU: 20, ContainerMemory: 128, - ExtendMethod: "stateless", Replicas: 1, DeployVersion: "20181022200709", Category: "application", diff --git a/worker/appm/conversion/service.go b/worker/appm/conversion/service.go index 82b23237e..09f354a77 100644 --- a/worker/appm/conversion/service.go +++ b/worker/appm/conversion/service.go @@ -110,7 +110,8 @@ func TenantServiceBase(as *v1.AppService, dbmanager db.Manager) error { if tenantService.Kind == dbmodel.ServiceKindThirdParty.String() { return nil } - serviceType, err := dbmanager.TenantServiceLabelDao().GetTenantServiceTypeLabel(as.ServiceID) + //TODO fanyangyang 根据组件类型确定是否支持 + serviceInfo, err := dbmanager.TenantServiceDao().GetServiceTypeById(as.ServiceID) if err != nil { return fmt.Errorf("get service type info failure %s", err.Error()) } @@ -118,11 +119,11 @@ func TenantServiceBase(as *v1.AppService, dbmanager db.Manager) error { if label != nil { as.IsWindowsService = true } - if serviceType == nil || serviceType.LabelValue == util.StatelessServiceType { + if serviceInfo == nil || !serviceInfo.IsState() { initBaseDeployment(as, tenantService) return nil } - if serviceType.LabelValue == util.StatefulServiceType { + if serviceInfo.IsState() { initBaseStatefulSet(as, tenantService) return nil } diff --git a/worker/appm/conversion/version.go b/worker/appm/conversion/version.go index ea79351e2..567c46d05 100644 --- a/worker/appm/conversion/version.go +++ b/worker/appm/conversion/version.go @@ -279,16 +279,11 @@ func createEnv(as *v1.AppService, dbmanager db.Manager) (*[]corev1.EnvVar, error as.ExtensionSet[strings.ToLower(e.AttrName[3:])] = e.AttrValue } } - svc, err := dbmanager.TenantServiceDao().GetServiceByID(as.ServiceID) - if err != nil { - return nil, err - } //set default env envs = append(envs, corev1.EnvVar{Name: "TENANT_ID", Value: as.TenantID}) envs = append(envs, corev1.EnvVar{Name: "SERVICE_ID", Value: as.ServiceID}) envs = append(envs, corev1.EnvVar{Name: "MEMORY_SIZE", Value: getMemoryType(as.ContainerMemory)}) envs = append(envs, corev1.EnvVar{Name: "SERVICE_NAME", Value: as.ServiceAlias}) - envs = append(envs, corev1.EnvVar{Name: "SERVICE_EXTEND_METHOD", Value: svc.ExtendMethod}) envs = append(envs, corev1.EnvVar{Name: "SERVICE_POD_NUM", Value: strconv.Itoa(as.Replicas)}) envs = append(envs, corev1.EnvVar{Name: "HOST_IP", ValueFrom: &corev1.EnvVarSource{ FieldRef: &corev1.ObjectFieldSelector{ diff --git a/worker/appm/volume/volume.go b/worker/appm/volume/volume.go index 699ba2eab..e29c65198 100644 --- a/worker/appm/volume/volume.go +++ b/worker/appm/volume/volume.go @@ -350,16 +350,11 @@ func createEnv(as *v1.AppService, dbmanager db.Manager) (*[]corev1.EnvVar, error as.ExtensionSet[strings.ToLower(e.AttrName[3:])] = e.AttrValue } } - svc, err := dbmanager.TenantServiceDao().GetServiceByID(as.ServiceID) - if err != nil { - return nil, err - } //set default env envs = append(envs, corev1.EnvVar{Name: "TENANT_ID", Value: as.TenantID}) envs = append(envs, corev1.EnvVar{Name: "SERVICE_ID", Value: as.ServiceID}) envs = append(envs, corev1.EnvVar{Name: "MEMORY_SIZE", Value: getMemoryType(as.ContainerMemory)}) envs = append(envs, corev1.EnvVar{Name: "SERVICE_NAME", Value: as.ServiceAlias}) - envs = append(envs, corev1.EnvVar{Name: "SERVICE_EXTEND_METHOD", Value: svc.ExtendMethod}) envs = append(envs, corev1.EnvVar{Name: "SERVICE_POD_NUM", Value: strconv.Itoa(as.Replicas)}) envs = append(envs, corev1.EnvVar{Name: "HOST_IP", ValueFrom: &corev1.EnvVarSource{ FieldRef: &corev1.ObjectFieldSelector{ From 51045e9a2bb16e3b0ffc463655768369b465f049 Mon Sep 17 00:00:00 2001 From: barnettZQG Date: Wed, 19 Feb 2020 16:32:19 +0800 Subject: [PATCH 38/47] fix domain third service health check bug --- api/db/db.go | 3 ++- util/prober/probes/http.go | 2 +- worker/appm/prober/prober_test.go | 8 ++++++++ worker/appm/thirdparty/thirdparty.go | 12 ++++++++---- 4 files changed, 19 insertions(+), 6 deletions(-) create mode 100644 worker/appm/prober/prober_test.go diff --git a/api/db/db.go b/api/db/db.go index be762e7c4..ca3ac1179 100644 --- a/api/db/db.go +++ b/api/db/db.go @@ -167,6 +167,7 @@ func dbInit() error { if err := begin.Where("class_level=? and prefix=?", "server_source", "/v2/show").Find(&rac).Error; err != nil { if err == gorm.ErrRecordNotFound { data := map[string]string{ + "/v2/cluster": "server_source", "/v2/show": "server_source", "/v2/opentsdb": "server_source", "/v2/resources": "server_source", @@ -176,7 +177,7 @@ func dbInit() error { "/v2/port": "server_source", "/api/v1": "server_source", "/v2/events": "server_source", - "/v2/gateway/ips": "server_source", + "/v2/gateway/ips": "server_source", "/v2/gateway/ports": "server_source", "/v2/nodes": "node_manager", "/v2/job": "node_manager", diff --git a/util/prober/probes/http.go b/util/prober/probes/http.go index 653c1cb90..f10658f2c 100644 --- a/util/prober/probes/http.go +++ b/util/prober/probes/http.go @@ -115,7 +115,7 @@ func (h *HTTPProbe) GetHTTPHealth() map[string]string { } logrus.Debugf("http probe check address; %s", address) resp, err := c.Get(addr.String()) - if resp.Body != nil { + if resp != nil && resp.Body != nil { defer resp.Body.Close() } if err != nil { diff --git a/worker/appm/prober/prober_test.go b/worker/appm/prober/prober_test.go new file mode 100644 index 000000000..26212a14b --- /dev/null +++ b/worker/appm/prober/prober_test.go @@ -0,0 +1,8 @@ +package prober + +import "testing" + +func TestParseTCPHostAddress(t *testing.T) { + re := parseTCPHostAddress("rm-2ze0xlsi14xz6q6sz.mysql.rds.aliyuncs.com", 3306) + t.Log(re) +} diff --git a/worker/appm/thirdparty/thirdparty.go b/worker/appm/thirdparty/thirdparty.go index 053ca8ece..3773e0974 100644 --- a/worker/appm/thirdparty/thirdparty.go +++ b/worker/appm/thirdparty/thirdparty.go @@ -415,6 +415,7 @@ func (t *thirdparty) createSubsetForAllEndpoint(as *v1.AppService, rbdep *v1.Rbd func (t *thirdparty) runUpdate(event discovery.Event) { updateAddress := func(as *v1.AppService, rbdep *v1.RbdEndpoint, ready bool) { + ad := validation.SplitEndpointAddress(rbdep.IP) for _, ep := range as.GetEndpoints(true) { var needUpdate bool for idx, subset := range ep.Subsets { @@ -424,8 +425,8 @@ func (t *thirdparty) runUpdate(event discovery.Event) { address = subset.NotReadyAddresses } for i, addr := range address { - if fmt.Sprintf("%s_%d", addr.IP, port.Port) == fmt.Sprintf("%s_%d", rbdep.IP, rbdep.Port) { - ad := validation.SplitEndpointAddress(rbdep.IP) + ipequal := fmt.Sprintf("%s_%d", addr.IP, port.Port) == fmt.Sprintf("%s_%d", rbdep.IP, rbdep.Port) + if (addr.IP == "1.1.1.1" && validation.IsDomainNotIP(ad)) || ipequal { if validation.IsDomainNotIP(ad) { rbdep.IP = "1.1.1.1" } @@ -434,6 +435,7 @@ func (t *thirdparty) runUpdate(event discovery.Event) { break } } + logrus.Debugf("not found need update address by %s", fmt.Sprintf("%s_%d", rbdep.IP, rbdep.Port)) } } if needUpdate { @@ -445,6 +447,8 @@ func (t *thirdparty) runUpdate(event discovery.Event) { } // do not have multiple ports, multiple addresses removeAddress := func(as *v1.AppService, rbdep *v1.RbdEndpoint) { + + ad := validation.SplitEndpointAddress(rbdep.IP) for _, ep := range as.GetEndpoints(true) { var needUpdate bool var newSubsets []corev1.EndpointSubset @@ -453,13 +457,13 @@ func (t *thirdparty) runUpdate(event discovery.Event) { for i, port := range subset.Ports { address := append(subset.Addresses, subset.NotReadyAddresses...) for j, addr := range address { - if fmt.Sprintf("%s_%d", addr.IP, port.Port) == fmt.Sprintf("%s_%d", rbdep.IP, rbdep.Port) { + ipequal := fmt.Sprintf("%s_%d", addr.IP, port.Port) == fmt.Sprintf("%s_%d", rbdep.IP, rbdep.Port) + if (addr.IP == "1.1.1.1" && validation.IsDomainNotIP(ad)) || ipequal { //multiple port remove port, Instead remove the address if len(subset.Ports) > 1 { subset.Ports = append(subset.Ports[:i], subset.Ports[:i]...) newSubsets = append(newSubsets, subset) } else { - ad := validation.SplitEndpointAddress(rbdep.IP) if validation.IsDomainNotIP(ad) { rbdep.IP = "1.1.1.1" } From 867e2e975e93340554f617a21dff224ed1c6dd3e Mon Sep 17 00:00:00 2001 From: barnettZQG Date: Wed, 19 Feb 2020 18:03:07 +0800 Subject: [PATCH 39/47] fix build version conflict bug --- builder/build/build.go | 14 ++------------ builder/build/dockerfile_build.go | 2 +- builder/build/netcore_build.go | 2 +- builder/exector/build_from_image_run.go | 13 +++---------- 4 files changed, 7 insertions(+), 24 deletions(-) diff --git a/builder/build/build.go b/builder/build/build.go index 29f1eabb0..f6ef0c315 100644 --- a/builder/build/build.go +++ b/builder/build/build.go @@ -20,7 +20,6 @@ package build import ( "fmt" - "regexp" "strings" "github.com/goodrain/rainbond/builder" @@ -105,15 +104,6 @@ func GetBuild(lang code.Lang) (Build, error) { } //CreateImageName create image name -func CreateImageName(repoURL, serviceAlias, deployversion string) string { - reg := regexp.MustCompile(`.*(?:\:|\/)([\w\-\.]+)/([\w\-\.]+)\.git`) - rc := reg.FindSubmatch([]byte(repoURL)) - var name string - if len(rc) == 3 { - name = fmt.Sprintf("%s_%s_%s", serviceAlias, string(rc[1]), string(rc[2])) - } else { - name = fmt.Sprintf("%s_%s", serviceAlias, "rainbondbuild") - } - buildImageName := strings.ToLower(fmt.Sprintf("%s/%s:%s", builder.REGISTRYDOMAIN, name, deployversion)) - return buildImageName +func CreateImageName(serviceID, deployversion string) string { + return strings.ToLower(fmt.Sprintf("%s/%s:%s", builder.REGISTRYDOMAIN, serviceID, deployversion)) } diff --git a/builder/build/dockerfile_build.go b/builder/build/dockerfile_build.go index 234386f26..37ce53ce4 100644 --- a/builder/build/dockerfile_build.go +++ b/builder/build/dockerfile_build.go @@ -46,7 +46,7 @@ func (d *dockerfileBuild) Build(re *Request) (*Response, error) { re.Logger.Error(fmt.Sprintf("Parse dockerfile error"), map[string]string{"step": "builder-exector"}) return nil, err } - buildImageName := CreateImageName(re.RepositoryURL, re.ServiceAlias, re.DeployVersion) + buildImageName := CreateImageName(re.ServiceID, re.DeployVersion) buildOptions := types.ImageBuildOptions{ Tags: []string{buildImageName}, diff --git a/builder/build/netcore_build.go b/builder/build/netcore_build.go index a9ff685f0..36390322a 100644 --- a/builder/build/netcore_build.go +++ b/builder/build/netcore_build.go @@ -81,7 +81,7 @@ func (d *netcoreBuild) Build(re *Request) (*Response, error) { return nil, fmt.Errorf("write default build dockerfile error:%s", err.Error()) } d.sourceDir = re.SourceDir - d.imageName = CreateImageName(re.RepositoryURL, re.ServiceAlias, re.DeployVersion) + d.imageName = CreateImageName(re.ServiceID, re.DeployVersion) d.buildImageName = d.imageName + "_build" //build code buildOptions := types.ImageBuildOptions{ diff --git a/builder/exector/build_from_image_run.go b/builder/exector/build_from_image_run.go index a50cd3d8d..9c53bee72 100644 --- a/builder/exector/build_from_image_run.go +++ b/builder/exector/build_from_image_run.go @@ -25,11 +25,11 @@ import ( "github.com/Sirupsen/logrus" "github.com/docker/docker/client" "github.com/goodrain/rainbond/builder" + "github.com/goodrain/rainbond/builder/build" "github.com/goodrain/rainbond/builder/sources" "github.com/goodrain/rainbond/db" "github.com/goodrain/rainbond/event" - "github.com/tidwall/gjson" //"github.com/docker/docker/api/types" - //"github.com/docker/docker/client" + "github.com/tidwall/gjson" ) //ImageBuildItem ImageBuildItem @@ -79,7 +79,7 @@ func (i *ImageBuildItem) Run(timeout time.Duration) error { i.Logger.Error(fmt.Sprintf("获取指定镜像: %s失败", i.Image), map[string]string{"step": "builder-exector", "status": "failure"}) return err } - localImageURL := i.ImageNameHandler(i.Image) + localImageURL := build.CreateImageName(i.ServiceID, i.DeployVersion) if err := sources.ImageTag(i.DockerClient, i.Image, localImageURL, i.Logger, 1); err != nil { logrus.Errorf("change image tag error: %s", err.Error()) i.Logger.Error(fmt.Sprintf("修改镜像tag: %s -> %s 失败", i.Image, localImageURL), map[string]string{"step": "builder-exector", "status": "failure"}) @@ -99,13 +99,6 @@ func (i *ImageBuildItem) Run(timeout time.Duration) error { return nil } -//ImageNameHandler 根据平台配置处理镜像名称 -func (i *ImageBuildItem) ImageNameHandler(source string) string { - imageModel := sources.ImageNameHandle(source) - localImageURL := fmt.Sprintf("%s/%s:%s", builder.REGISTRYDOMAIN, imageModel.Name, i.DeployVersion) - return localImageURL -} - //StorageVersionInfo 存储version信息 func (i *ImageBuildItem) StorageVersionInfo(imageURL string) error { version, err := db.GetManager().VersionInfoDao().GetVersionByDeployVersion(i.DeployVersion, i.ServiceID) From 862c0ed5d536df3f637101c81033cddbb166a333 Mon Sep 17 00:00:00 2001 From: barnettZQG Date: Wed, 19 Feb 2020 18:03:51 +0800 Subject: [PATCH 40/47] fix dependent domain class third party component startup sequence control bug --- cmd/init-probe/healthy/depends_health.go | 53 +++++++++++++++---- cmd/init-probe/healthy/depends_health_test.go | 15 ++++-- 2 files changed, 55 insertions(+), 13 deletions(-) diff --git a/cmd/init-probe/healthy/depends_health.go b/cmd/init-probe/healthy/depends_health.go index eaa26eaa6..c47bd48d8 100644 --- a/cmd/init-probe/healthy/depends_health.go +++ b/cmd/init-probe/healthy/depends_health.go @@ -30,6 +30,7 @@ import ( "github.com/envoyproxy/go-control-plane/envoy/api/v2/core" endpointapi "github.com/envoyproxy/go-control-plane/envoy/api/v2/endpoint" envoyv2 "github.com/goodrain/rainbond/node/core/envoy/v2" + "github.com/goodrain/rainbond/util" "google.golang.org/grpc" ) @@ -39,16 +40,18 @@ import ( //------- cds: discover all dependent services //------- sds: every service has at least one Ready instance type DependServiceHealthController struct { - listeners []v2.Listener - clusters []v2.Cluster - sdsHost []v2.ClusterLoadAssignment - interval time.Duration - envoyDiscoverVersion string //only support v2 - checkFunc []func() bool - endpointClient v2.EndpointDiscoveryServiceClient - dependServiceCount int - clusterID string - dependServiceNames []string + listeners []v2.Listener + clusters []v2.Cluster + sdsHost []v2.ClusterLoadAssignment + interval time.Duration + envoyDiscoverVersion string //only support v2 + checkFunc []func() bool + endpointClient v2.EndpointDiscoveryServiceClient + clusterClient v2.ClusterDiscoveryServiceClient + dependServiceCount int + clusterID string + dependServiceNames []string + ignoreCheckEndpointsClusterName []string } //NewDependServiceHealthController create a controller @@ -77,6 +80,7 @@ func NewDependServiceHealthController() (*DependServiceHealthController, error) return nil, err } dsc.endpointClient = v2.NewEndpointDiscoveryServiceClient(cli) + dsc.clusterClient = v2.NewClusterDiscoveryServiceClient(cli) nameIDs := strings.Split(os.Getenv("DEPEND_SERVICE"), ",") for _, nameID := range nameIDs { if len(strings.Split(nameID, ":")) > 0 { @@ -115,11 +119,36 @@ func (d *DependServiceHealthController) checkListener() bool { } func (d *DependServiceHealthController) checkClusters() bool { + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + res, err := d.clusterClient.FetchClusters(ctx, &v2.DiscoveryRequest{ + Node: &core.Node{ + Cluster: d.clusterID, + Id: d.clusterID, + }, + }) + if err != nil { + logrus.Errorf("discover depend services cluster failure %s", err.Error()) + return false + } + clusters := envoyv2.ParseClustersResource(res.Resources) + d.ignoreCheckEndpointsClusterName = nil + for _, cluster := range clusters { + if cluster.Type == v2.Cluster_LOGICAL_DNS { + d.ignoreCheckEndpointsClusterName = append(d.ignoreCheckEndpointsClusterName, cluster.Name) + } + } + d.clusters = clusters return true } func (d *DependServiceHealthController) checkEDS() bool { logrus.Infof("start checking eds; dependent service cluster names: %s", d.dependServiceNames) + if len(d.clusters) == len(d.ignoreCheckEndpointsClusterName) { + logrus.Info("all dependent services is domain third service.") + return true + } + ctx, cancel := context.WithCancel(context.Background()) defer cancel() res, err := d.endpointClient.FetchEndpoints(ctx, &v2.DiscoveryRequest{ @@ -147,7 +176,11 @@ func (d *DependServiceHealthController) checkEDS() bool { if ready, exist := readyClusters[serviceName]; exist && ready { continue } + ready := func() bool { + if util.StringArrayContains(d.ignoreCheckEndpointsClusterName, cla.ClusterName) { + return true + } if len(cla.Endpoints) > 0 && len(cla.Endpoints[0].LbEndpoints) > 0 { // first LbEndpoints healthy is not nil. so endpoint is not notreadyaddress if host, ok := cla.Endpoints[0].LbEndpoints[0].HostIdentifier.(*endpointapi.LbEndpoint_Endpoint); ok { diff --git a/cmd/init-probe/healthy/depends_health_test.go b/cmd/init-probe/healthy/depends_health_test.go index e1e155198..63cf596d4 100644 --- a/cmd/init-probe/healthy/depends_health_test.go +++ b/cmd/init-probe/healthy/depends_health_test.go @@ -20,6 +20,7 @@ package healthy import ( "context" + "fmt" "testing" yaml "gopkg.in/yaml.v2" @@ -33,9 +34,9 @@ import ( v2 "github.com/envoyproxy/go-control-plane/envoy/api/v2" ) -var testClusterID = "2bf54c5a0b5a48a890e2dda8635cb507_2591d9904fc4480c9c012037697f98c6_grc9e8e3" +var testClusterID = "8cd9214e6b3d4476942b600f41bfefea_tcpmeshd3d6a722b632b854b6c232e4895e0cc6_gr5e0cc6" -var testXDSHost = "192.168.195.1:6101" +var testXDSHost = "39.104.66.227:6101" // var testClusterID = "2bf54c5a0b5a48a890e2dda8635cb507_tcpmeshed6827c0afdda50599b4108105c9e8e3_grc9e8e3" //var testXDSHost = "127.0.0.1:6101" @@ -87,7 +88,12 @@ func TestClientCluster(t *testing.T) { } t.Logf("version %s", res.GetVersionInfo()) clusters := envoyv2.ParseClustersResource(res.Resources) - printYaml(t, clusters) + for _, cluster := range clusters { + if cluster.Type == v2.Cluster_LOGICAL_DNS { + fmt.Println(cluster.Name) + } + printYaml(t, cluster) + } } func printYaml(t *testing.T, data interface{}) { @@ -117,6 +123,9 @@ func TestClientEndpoint(t *testing.T) { } t.Logf("version %s", res.GetVersionInfo()) endpoints := envoyv2.ParseLocalityLbEndpointsResource(res.Resources) + for _, e := range endpoints { + fmt.Println(e.GetClusterName()) + } printYaml(t, endpoints) } From ef5e85a654a5964ff67d9c4b10c0dc855e00803b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=87=A1=E7=BE=8A=E7=BE=8A?= Date: Wed, 19 Feb 2020 23:02:25 +0800 Subject: [PATCH 41/47] fix service type while check service --- builder/parser/parser.go | 4 ++-- builder/parser/source_code.go | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/builder/parser/parser.go b/builder/parser/parser.go index 738d79439..7560f5d8f 100644 --- a/builder/parser/parser.go +++ b/builder/parser/parser.go @@ -236,10 +236,10 @@ var dbImageKey = []string{ func DetermineDeployType(imageName Image) string { for _, key := range dbImageKey { if strings.ToLower(imageName.GetSimpleName()) == key { - return dbmodel.ServiceTypeStatelessSingleton.String() // TODO fanyangyang 通过镜像要挂载的存储确定更细致的组件类型 + return dbmodel.ServiceTypeStatelessSingleton.String() } } - return dbmodel.ServiceTypeStatelessSingleton.String() + return dbmodel.ServiceTypeStatelessMultiple.String() } //readmemory diff --git a/builder/parser/source_code.go b/builder/parser/source_code.go index c5f42b82f..a37d11c9c 100644 --- a/builder/parser/source_code.go +++ b/builder/parser/source_code.go @@ -485,7 +485,7 @@ func (d *SourceCodeParse) GetServiceInfo() []ServiceInfo { Branchs: d.GetBranchs(), Memory: d.memory, Lang: d.GetLang(), - ServiceType: model.ServiceTypeStatelessSingleton.String(), // TODO fanyangyang 使用存储确定组件类型 + ServiceType: model.ServiceTypeStatelessMultiple.String(), } var res []ServiceInfo if d.isMulti && d.services != nil && len(d.services) > 0 { From ba7a110fccb34f951197868148944c3b33567fb2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=87=A1=E7=BE=8A=E7=BE=8A?= Date: Thu, 20 Feb 2020 16:15:40 +0800 Subject: [PATCH 42/47] region change service type --- api/controller/resources.go | 1 + api/handler/service.go | 22 ++++++++++++++++++++++ db/model/tenant.go | 31 +++++++++++++++++++++++++++++-- 3 files changed, 52 insertions(+), 2 deletions(-) diff --git a/api/controller/resources.go b/api/controller/resources.go index 6c0c5f692..9c350979a 100644 --- a/api/controller/resources.go +++ b/api/controller/resources.go @@ -701,6 +701,7 @@ func (t *TenantStruct) UpdateService(w http.ResponseWriter, r *http.Request) { "image_name": []string{}, "container_memory": []string{}, "service_name": []string{}, + "extend_method": []string{}, } data, ok := httputil.ValidatorRequestMapAndErrorResponse(r, w, rules, nil) if !ok { diff --git a/api/handler/service.go b/api/handler/service.go index c1ed3622f..baaa5b38c 100644 --- a/api/handler/service.go +++ b/api/handler/service.go @@ -701,6 +701,28 @@ func (s *ServiceAction) ServiceUpdate(sc map[string]interface{}) error { if sc["service_name"] != nil { ts.ServiceName = sc["service_name"].(string) } + if sc["extend_method"] != nil { + extendMethod := sc["extend_method"].(string) + if ts.Replicas > 1 && dbmodel.ServiceType(extendMethod).IsSingleton() { + err := fmt.Errorf("service[%s] replicas > 1, can't change service typ to stateless_singleton", ts.ServiceAlias) + return err + } + volumes, err := db.GetManager().TenantServiceVolumeDao().GetTenantServiceVolumesByServiceID(ts.ServiceID) + if err != nil { + return err + } + for _, vo := range volumes { + if vo.VolumeType == dbmodel.LocalVolumeType.String() && !dbmodel.ServiceType(extendMethod).IsState() { + err := fmt.Errorf("service[%s] has local volume type, can't change type to stateless", ts.ServiceAlias) + return err + } + if vo.AccessMode == "RWO" && !dbmodel.ServiceType(extendMethod).IsState() { + err := fmt.Errorf("service[%s] volume[%s] access_mode is RWO, can't change type to stateless", ts.ServiceAlias, vo.VolumeName) + return err + } + } + ts.ServiceType = extendMethod + } //update service if err := db.GetManager().TenantServiceDao().UpdateModel(ts); err != nil { logrus.Errorf("update service error, %v", err) diff --git a/db/model/tenant.go b/db/model/tenant.go index 2d1f7ebb1..045c4f380 100644 --- a/db/model/tenant.go +++ b/db/model/tenant.go @@ -93,16 +93,43 @@ func (s ServiceType) String() string { return string(s) } +// IsState is state type or not +func (s ServiceType) IsState() bool { + if s == "" { + return false + } + if s == ServiceTypeStatelessSingleton || s == ServiceTypeStatelessMultiple { + return false + } + return true +} + +// IsSingleton is singleton or not +func (s ServiceType) IsSingleton() bool { + if s == "" { + return false + } + if s == ServiceTypeStatelessMultiple || s == ServiceTypeStateMultiple { + return false + } + return true +} + // TODO fanyangyang 根据组件简单判断是否是有状态 // IsState is state service or stateless service func (ts TenantServices) IsState() bool { if ts.ServiceType == "" { return false } - if ts.ServiceType == ServiceTypeStatelessSingleton.String() || ts.ServiceType == ServiceTypeStatelessMultiple.String() { + return ServiceType(ts.ServiceType).IsState() +} + +// IsSingleton is singleton or multiple service +func (ts TenantServices) IsSingleton() bool { + if ts.ServiceType == "" { return false } - return true + return ServiceType(ts.ServiceType).IsSingleton() } // ServiceTypeUnknown unknown From 6ab470e491c9305ef881a59118525ab744d48bb6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=87=A1=E7=BE=8A=E7=BE=8A?= Date: Thu, 20 Feb 2020 17:51:48 +0800 Subject: [PATCH 43/47] fix add volume validate, and change service type validate --- api/handler/service.go | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/api/handler/service.go b/api/handler/service.go index baaa5b38c..d3a9db0f9 100644 --- a/api/handler/service.go +++ b/api/handler/service.go @@ -712,6 +712,9 @@ func (s *ServiceAction) ServiceUpdate(sc map[string]interface{}) error { return err } for _, vo := range volumes { + if vo.VolumeType == dbmodel.ShareFileVolumeType.String() || vo.VolumeType == dbmodel.MemoryFSVolumeType.String() { + continue + } if vo.VolumeType == dbmodel.LocalVolumeType.String() && !dbmodel.ServiceType(extendMethod).IsState() { err := fmt.Errorf("service[%s] has local volume type, can't change type to stateless", ts.ServiceAlias) return err @@ -721,6 +724,7 @@ func (s *ServiceAction) ServiceUpdate(sc map[string]interface{}) error { return err } } + ts.ExtendMethod = extendMethod ts.ServiceType = extendMethod } //update service @@ -1330,7 +1334,7 @@ func (s *ServiceAction) VolumnVar(tsv *dbmodel.TenantServiceVolume, tenantID, fi if err != nil { return util.CreateAPIHandleErrorFromDBError("service type", err) } - if serviceInfo == nil || serviceInfo.IsState() { // TODO fanyangyang 根据组件类型确定是否支持 + if serviceInfo == nil || !serviceInfo.IsState() { return util.CreateAPIHandleError(400, fmt.Errorf("应用类型不为有状态应用.不支持本地存储")) } tsv.HostPath = fmt.Sprintf("%s/tenant/%s/service/%s%s", localPath, tenantID, tsv.ServiceID, tsv.VolumePath) From b5e629a5fa624423b52d9321e691958ebd023027 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=87=A1=E7=BE=8A=E7=BE=8A?= Date: Thu, 20 Feb 2020 23:27:48 +0800 Subject: [PATCH 44/47] update change service type --- api/handler/service.go | 8 +++++--- builder/exector/groupapp_backup_test.go | 15 +++++++++++++++ builder/parser/parser.go | 2 +- 3 files changed, 21 insertions(+), 4 deletions(-) diff --git a/api/handler/service.go b/api/handler/service.go index d3a9db0f9..b8fceb273 100644 --- a/api/handler/service.go +++ b/api/handler/service.go @@ -733,9 +733,11 @@ func (s *ServiceAction) ServiceUpdate(sc map[string]interface{}) error { return err } //update service version - if err := db.GetManager().VersionInfoDao().UpdateModel(version); err != nil { - logrus.Errorf("update version error, %v", err) - return err + if version != nil { + if err := db.GetManager().VersionInfoDao().UpdateModel(version); err != nil { + logrus.Errorf("update version error, %v", err) + return err + } } return nil } diff --git a/builder/exector/groupapp_backup_test.go b/builder/exector/groupapp_backup_test.go index 344014b30..669e4cc5e 100644 --- a/builder/exector/groupapp_backup_test.go +++ b/builder/exector/groupapp_backup_test.go @@ -20,7 +20,9 @@ package exector import ( "fmt" + dbmodel "github.com/goodrain/rainbond/db/model" "github.com/goodrain/rainbond/util" + "strings" "testing" ) @@ -59,3 +61,16 @@ func TestUploadPkg2(t *testing.T) { t.Error(err) } } + +func TestBackupServiceVolume(t *testing.T) { + volume := dbmodel.TenantServiceVolume{} + sourceDir := "" + serviceID := "" + dstDir := fmt.Sprintf("%s/data_%s/%s.zip", sourceDir, serviceID, strings.Replace(volume.VolumeName, "/", "", -1)) + hostPath := volume.HostPath + if hostPath != "" && !util.DirIsEmpty(hostPath) { + if err := util.Zip(hostPath, dstDir); err != nil { + t.Fatalf("backup service(%s) volume(%s) data error.%s", serviceID, volume.VolumeName, err.Error()) + } + } +} diff --git a/builder/parser/parser.go b/builder/parser/parser.go index 7560f5d8f..6f08d82c0 100644 --- a/builder/parser/parser.go +++ b/builder/parser/parser.go @@ -236,7 +236,7 @@ var dbImageKey = []string{ func DetermineDeployType(imageName Image) string { for _, key := range dbImageKey { if strings.ToLower(imageName.GetSimpleName()) == key { - return dbmodel.ServiceTypeStatelessSingleton.String() + return dbmodel.ServiceTypeStateSingleton.String() } } return dbmodel.ServiceTypeStatelessMultiple.String() From 51efc6c6cb93c42da3de51050bb0c9202c6313c0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=87=A1=E7=BE=8A=E7=BE=8A?= Date: Fri, 21 Feb 2020 20:59:59 +0800 Subject: [PATCH 45/47] enterprise running servicees --- api/api_routers/version2/v2Routers.go | 1 + api/controller/enterprise.go | 19 +++++++++++++++++++ api/handler/service.go | 25 +++++++++++++++++++++++++ api/handler/service_handler.go | 1 + api/model/model.go | 12 ++++++++++++ 5 files changed, 58 insertions(+) create mode 100644 api/controller/enterprise.go diff --git a/api/api_routers/version2/v2Routers.go b/api/api_routers/version2/v2Routers.go index 7161c2a1e..da50b66f5 100644 --- a/api/api_routers/version2/v2Routers.go +++ b/api/api_routers/version2/v2Routers.go @@ -59,6 +59,7 @@ func (v2 *V2) Routes() chi.Router { r.Post("/volume-options", controller.VolumeSetVar) r.Delete("/volume-options/{volume_type}", controller.DeleteVolumeType) r.Put("/volume-options/{volume_type}", controller.UpdateVolumeType) + r.Post("/enterprise/running-services", controller.GetRunningServices) return r } diff --git a/api/controller/enterprise.go b/api/controller/enterprise.go new file mode 100644 index 000000000..a0810bffc --- /dev/null +++ b/api/controller/enterprise.go @@ -0,0 +1,19 @@ +package controller + +import ( + "github.com/goodrain/rainbond/api/handler" + apimodel "github.com/goodrain/rainbond/api/model" + httputil "github.com/goodrain/rainbond/util/http" + "net/http" +) + +//GetRunningServices list all running service ids +func GetRunningServices(w http.ResponseWriter, r *http.Request) { + req := apimodel.EnterpriseTenantListStruct{} + ok := httputil.ValidatorRequestStructAndErrorResponse(r, w, &req.Body, nil) + if !ok { + return + } + runningList := handler.GetServiceManager().GetMultiTenantsRunningServices(req.Body.TenantIDs) + httputil.ReturnNoFomart(r, w, 200, map[string]interface{}{"service_ids": runningList}) +} diff --git a/api/handler/service.go b/api/handler/service.go index 861370d46..30ed60a65 100644 --- a/api/handler/service.go +++ b/api/handler/service.go @@ -22,6 +22,7 @@ import ( "encoding/json" "errors" "fmt" + typesv1 "github.com/goodrain/rainbond/worker/appm/types/v1" "net/http" "os" "strconv" @@ -1659,6 +1660,30 @@ func (s *ServiceAction) GetServicesStatus(tenantID string, serviceIDs []string) return nil } +// GetMultiTenantsRunningServices get running services +func (s *ServiceAction) GetMultiTenantsRunningServices(tenants []string) []string { + if len(tenants) == 0 { + return []string{} + } + services, err := db.GetManager().TenantServiceDao().GetServicesByTenantIDs(tenants) + if err != nil { + logrus.Errorf("list tenants servicee failed: %s", err.Error()) + return []string{} + } + var serviceIDs []string + for _, svc := range services { + serviceIDs = append(serviceIDs, svc.ServiceID) + } + statusList := s.statusCli.GetStatuss(strings.Join(serviceIDs, ",")) + retServices := make([]string, 0, 10) + for service, status := range statusList { + if status == typesv1.RUNNING { + retServices = append(retServices, service) + } + } + return retServices +} + //CreateTenant create tenant func (s *ServiceAction) CreateTenant(t *dbmodel.Tenants) error { if ten, _ := db.GetManager().TenantDao().GetTenantIDByName(t.Name); ten != nil { diff --git a/api/handler/service_handler.go b/api/handler/service_handler.go index 0cd52361b..5b5f1b7a1 100644 --- a/api/handler/service_handler.go +++ b/api/handler/service_handler.go @@ -59,6 +59,7 @@ type ServiceHandler interface { RollBack(rs *api_model.RollbackStruct) error GetStatus(serviceID string) (*api_model.StatusList, error) GetServicesStatus(tenantID string, services []string) map[string]string + GetMultiTenantsRunningServices(servicIDs []string) []string CreateTenant(*dbmodel.Tenants) error CreateTenandIDAndName(eid string) (string, string, error) GetPods(serviceID string) (*K8sPodInfos, error) diff --git a/api/model/model.go b/api/model/model.go index b5595dbb7..88b70cb86 100644 --- a/api/model/model.go +++ b/api/model/model.go @@ -809,6 +809,18 @@ type StatusServiceListStruct struct { } } +//EnterpriseTenantListStruct enterprise tenants list +type EnterpriseTenantListStruct struct { + // in: body + // required: true + Body struct { + // 需要获取状态的服务ID列表,若不指定,返回租户所有应用的状态 + // in: body + // required: true + TenantIDs []string `json:"tenant_ids" validate:"tenant_ids|required"` + } +} + //AddServiceLabelStruct AddServiceLabelStruct //swagger:parameters addServiceLabel updateServiceLabel type AddServiceLabelStruct struct { From 9a4af3e7873e5edc0dc9cc856d72d35a8e2ff0f5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=87=A1=E7=BE=8A=E7=BE=8A?= Date: Fri, 21 Feb 2020 21:10:19 +0800 Subject: [PATCH 46/47] fix comment --- api/model/model.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/api/model/model.go b/api/model/model.go index 88b70cb86..e1a283abe 100644 --- a/api/model/model.go +++ b/api/model/model.go @@ -814,7 +814,7 @@ type EnterpriseTenantListStruct struct { // in: body // required: true Body struct { - // 需要获取状态的服务ID列表,若不指定,返回租户所有应用的状态 + // 需要获取状态的租户ID列表,若不指定,返回空列表 // in: body // required: true TenantIDs []string `json:"tenant_ids" validate:"tenant_ids|required"` From 0ff4a17ee02bb4479cc43aaed0ef11a76568ef6e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=87=A1=E7=BE=8A=E7=BE=8A?= Date: Fri, 21 Feb 2020 22:43:28 +0800 Subject: [PATCH 47/47] update get enterprise running services --- api/api_routers/version2/v2Routers.go | 8 +++++++- api/controller/enterprise.go | 10 +++------- api/handler/service.go | 15 ++++++++++++--- api/handler/service_handler.go | 2 +- api/model/model.go | 12 ------------ db/dao/dao.go | 5 +++++ db/db.go | 1 + db/db_mock.go | 14 +++++++++++++- db/mysql/dao/enterprise.go | 22 ++++++++++++++++++++++ db/mysql/dao_impl.go | 7 +++++++ 10 files changed, 71 insertions(+), 25 deletions(-) create mode 100644 db/mysql/dao/enterprise.go diff --git a/api/api_routers/version2/v2Routers.go b/api/api_routers/version2/v2Routers.go index da50b66f5..40271ff6d 100644 --- a/api/api_routers/version2/v2Routers.go +++ b/api/api_routers/version2/v2Routers.go @@ -59,7 +59,13 @@ func (v2 *V2) Routes() chi.Router { r.Post("/volume-options", controller.VolumeSetVar) r.Delete("/volume-options/{volume_type}", controller.DeleteVolumeType) r.Put("/volume-options/{volume_type}", controller.UpdateVolumeType) - r.Post("/enterprise/running-services", controller.GetRunningServices) + r.Mount("/enterprise/{enterprise_id}", v2.enterpriseRouter()) + return r +} + +func (v2 *V2) enterpriseRouter() chi.Router { + r := chi.NewRouter() + r.Get("/running-services", controller.GetRunningServices) return r } diff --git a/api/controller/enterprise.go b/api/controller/enterprise.go index a0810bffc..443f86395 100644 --- a/api/controller/enterprise.go +++ b/api/controller/enterprise.go @@ -1,19 +1,15 @@ package controller import ( + "github.com/go-chi/chi" "github.com/goodrain/rainbond/api/handler" - apimodel "github.com/goodrain/rainbond/api/model" httputil "github.com/goodrain/rainbond/util/http" "net/http" ) //GetRunningServices list all running service ids func GetRunningServices(w http.ResponseWriter, r *http.Request) { - req := apimodel.EnterpriseTenantListStruct{} - ok := httputil.ValidatorRequestStructAndErrorResponse(r, w, &req.Body, nil) - if !ok { - return - } - runningList := handler.GetServiceManager().GetMultiTenantsRunningServices(req.Body.TenantIDs) + enterpriseID := chi.URLParam(r, "enterprise_id") + runningList := handler.GetServiceManager().GetEnterpriseRunningServices(enterpriseID) httputil.ReturnNoFomart(r, w, 200, map[string]interface{}{"service_ids": runningList}) } diff --git a/api/handler/service.go b/api/handler/service.go index 30ed60a65..7fb52ac2b 100644 --- a/api/handler/service.go +++ b/api/handler/service.go @@ -1661,11 +1661,20 @@ func (s *ServiceAction) GetServicesStatus(tenantID string, serviceIDs []string) } // GetMultiTenantsRunningServices get running services -func (s *ServiceAction) GetMultiTenantsRunningServices(tenants []string) []string { - if len(tenants) == 0 { +func (s *ServiceAction) GetEnterpriseRunningServices(enterpriseID string) []string { + var tenantIDs []string + tenants, err := db.GetManager().EnterpriseDao().GetEnterpriseTenants(enterpriseID) + if err != nil { + logrus.Errorf("list tenant failed: %s", err.Error()) return []string{} } - services, err := db.GetManager().TenantServiceDao().GetServicesByTenantIDs(tenants) + for _, tenant := range tenants { + tenantIDs = append(tenantIDs, tenant.UUID) + } + if len(tenantIDs) == 0 { + return []string{} + } + services, err := db.GetManager().TenantServiceDao().GetServicesByTenantIDs(tenantIDs) if err != nil { logrus.Errorf("list tenants servicee failed: %s", err.Error()) return []string{} diff --git a/api/handler/service_handler.go b/api/handler/service_handler.go index 5b5f1b7a1..9a0b993c5 100644 --- a/api/handler/service_handler.go +++ b/api/handler/service_handler.go @@ -59,7 +59,7 @@ type ServiceHandler interface { RollBack(rs *api_model.RollbackStruct) error GetStatus(serviceID string) (*api_model.StatusList, error) GetServicesStatus(tenantID string, services []string) map[string]string - GetMultiTenantsRunningServices(servicIDs []string) []string + GetEnterpriseRunningServices(enterpriseID string) []string CreateTenant(*dbmodel.Tenants) error CreateTenandIDAndName(eid string) (string, string, error) GetPods(serviceID string) (*K8sPodInfos, error) diff --git a/api/model/model.go b/api/model/model.go index e1a283abe..b5595dbb7 100644 --- a/api/model/model.go +++ b/api/model/model.go @@ -809,18 +809,6 @@ type StatusServiceListStruct struct { } } -//EnterpriseTenantListStruct enterprise tenants list -type EnterpriseTenantListStruct struct { - // in: body - // required: true - Body struct { - // 需要获取状态的租户ID列表,若不指定,返回空列表 - // in: body - // required: true - TenantIDs []string `json:"tenant_ids" validate:"tenant_ids|required"` - } -} - //AddServiceLabelStruct AddServiceLabelStruct //swagger:parameters addServiceLabel updateServiceLabel type AddServiceLabelStruct struct { diff --git a/db/dao/dao.go b/db/dao/dao.go index ce36c9a14..0609dcf4c 100644 --- a/db/dao/dao.go +++ b/db/dao/dao.go @@ -41,6 +41,11 @@ type DelDao interface { DeleteModel(serviceID string, arg ...interface{}) error } +// EnterpriseDao enterprise dao +type EnterpriseDao interface { + GetEnterpriseTenants(enterpriseID string) ([]*model.Tenants, error) +} + //TenantDao tenant dao type TenantDao interface { Dao diff --git a/db/db.go b/db/db.go index 4edf2bfc6..32a58fa37 100644 --- a/db/db.go +++ b/db/db.go @@ -36,6 +36,7 @@ type Manager interface { VolumeTypeDao() dao.VolumeTypeDao LicenseDao() dao.LicenseDao AppDao() dao.AppDao + EnterpriseDao() dao.EnterpriseDao TenantDao() dao.TenantDao TenantDaoTransactions(db *gorm.DB) dao.TenantDao TenantServiceDao() dao.TenantServiceDao diff --git a/db/db_mock.go b/db/db_mock.go index 7dda78459..d3aa3c1cb 100644 --- a/db/db_mock.go +++ b/db/db_mock.go @@ -1,5 +1,5 @@ // Code generated by MockGen. DO NOT EDIT. -// Source: db/db.go +// Source: db.go // Package db is a generated GoMock package. package db @@ -106,6 +106,18 @@ func (mr *MockManagerMockRecorder) AppDao() *gomock.Call { return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AppDao", reflect.TypeOf((*MockManager)(nil).AppDao)) } +// EnterpriseDao mocks base method +func (m *MockManager) EnterpriseDao() dao.EnterpriseDao { + ret := m.ctrl.Call(m, "EnterpriseDao") + ret0, _ := ret[0].(dao.EnterpriseDao) + return ret0 +} + +// EnterpriseDao indicates an expected call of EnterpriseDao +func (mr *MockManagerMockRecorder) EnterpriseDao() *gomock.Call { + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "EnterpriseDao", reflect.TypeOf((*MockManager)(nil).EnterpriseDao)) +} + // TenantDao mocks base method func (m *MockManager) TenantDao() dao.TenantDao { ret := m.ctrl.Call(m, "TenantDao") diff --git a/db/mysql/dao/enterprise.go b/db/mysql/dao/enterprise.go new file mode 100644 index 000000000..d671d4f01 --- /dev/null +++ b/db/mysql/dao/enterprise.go @@ -0,0 +1,22 @@ +package dao + +import ( + "github.com/goodrain/rainbond/db/model" + "github.com/jinzhu/gorm" +) + +//EnterpriseDaoImpl 租户信息管理 +type EnterpriseDaoImpl struct { + DB *gorm.DB +} + +func (e *EnterpriseDaoImpl) GetEnterpriseTenants(enterpriseID string) ([]*model.Tenants, error) { + var tenants []*model.Tenants + if enterpriseID == "" { + return []*model.Tenants{}, nil + } + if err := e.DB.Where("eid= ?", enterpriseID).Find(&tenants).Error; err != nil { + return nil, err + } + return tenants, nil +} diff --git a/db/mysql/dao_impl.go b/db/mysql/dao_impl.go index c28a8292d..7ae95e368 100644 --- a/db/mysql/dao_impl.go +++ b/db/mysql/dao_impl.go @@ -39,6 +39,13 @@ func (m *Manager) LicenseDao() dao.LicenseDao { } } +// EnterpriseDao enterprise dao +func (m *Manager) EnterpriseDao() dao.EnterpriseDao { + return &mysqldao.EnterpriseDaoImpl{ + DB: m.db, + } +} + //TenantDao 租户数据 func (m *Manager) TenantDao() dao.TenantDao { return &mysqldao.TenantDaoImpl{