From a4edf385663b2e412e1fd64f7d45e1ee01749798 Mon Sep 17 00:00:00 2001 From: Sida Chen Date: Mon, 5 Jun 2017 10:37:29 -0400 Subject: [PATCH] api: v2 api with gRPC and gRPC-gateway Newly designed API defines Ancestry as a set of layers and shrinked the api to only the most used apis: post ancestry, get layer, get notification, delete notification Fixes #98 --- .travis.yml | 4 + api/api.go | 13 + api/token/token.go | 49 ++ api/v1/models.go | 30 +- api/v1/routes.go | 9 +- api/v2/clairpb/Makefile | 17 + api/v2/clairpb/clair.pb.go | 872 ++++++++++++++++++++++++++++++ api/v2/clairpb/clair.pb.gw.go | 346 ++++++++++++ api/v2/clairpb/clair.proto | 149 +++++ api/v2/clairpb/clair.swagger.json | 371 +++++++++++++ api/v2/clairpb/convert.go | 165 ++++++ api/v2/rpc.go | 256 +++++++++ api/v2/server.go | 223 ++++++++ cmd/clair/config.go | 1 + cmd/clair/main.go | 1 + config.example.yaml | 2 +- glide.lock | 65 ++- glide.yaml | 1 + 18 files changed, 2535 insertions(+), 39 deletions(-) create mode 100644 api/token/token.go create mode 100644 api/v2/clairpb/Makefile create mode 100644 api/v2/clairpb/clair.pb.go create mode 100644 api/v2/clairpb/clair.pb.gw.go create mode 100644 api/v2/clairpb/clair.proto create mode 100644 api/v2/clairpb/clair.swagger.json create mode 100644 api/v2/clairpb/convert.go create mode 100644 api/v2/rpc.go create mode 100644 api/v2/server.go diff --git a/.travis.yml b/.travis.yml index 0566a4b1..576da9fc 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,4 +1,8 @@ language: go + +go: + - 1.8 + sudo: required install: diff --git a/api/api.go b/api/api.go index b2cb028d..cf1a6a6d 100644 --- a/api/api.go +++ b/api/api.go @@ -26,6 +26,7 @@ import ( log "github.com/sirupsen/logrus" "github.com/tylerb/graceful" + "github.com/coreos/clair/api/v2" "github.com/coreos/clair/database" "github.com/coreos/clair/pkg/stopper" ) @@ -35,12 +36,24 @@ const timeoutResponse = `{"Error":{"Message":"Clair failed to respond within the // Config is the configuration for the API service. type Config struct { Port int + GrpcPort int HealthPort int Timeout time.Duration PaginationKey string CertFile, KeyFile, CAFile string } +func RunV2(cfg *Config, store database.Datastore) { + tlsConfig, err := tlsClientConfig(cfg.CAFile) + if err != nil { + log.WithError(err).Fatal("could not initialize client cert authentication") + } + if tlsConfig != nil { + log.Info("main API configured with client certificate authentication") + } + v2.Run(cfg.GrpcPort, tlsConfig, cfg.PaginationKey, cfg.CertFile, cfg.KeyFile, store) +} + func Run(cfg *Config, store database.Datastore, st *stopper.Stopper) { defer st.End() diff --git a/api/token/token.go b/api/token/token.go new file mode 100644 index 00000000..3492e712 --- /dev/null +++ b/api/token/token.go @@ -0,0 +1,49 @@ +// Copyright 2017 clair 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 token implements encryption/decryption for json encoded interfaces +package token + +import ( + "bytes" + "encoding/json" + "errors" + "time" + + "github.com/fernet/fernet-go" +) + +// Unmarshal decrypts a token using provided key +// and decode the result into interface. +func Unmarshal(token string, key string, v interface{}) error { + k, _ := fernet.DecodeKey(key) + msg := fernet.VerifyAndDecrypt([]byte(token), time.Hour, []*fernet.Key{k}) + if msg == nil { + return errors.New("invalid or expired pagination token") + } + + return json.NewDecoder(bytes.NewBuffer(msg)).Decode(&v) +} + +// Marshal encodes an interface into json bytes and encrypts it. +func Marshal(v interface{}, key string) ([]byte, error) { + var buf bytes.Buffer + err := json.NewEncoder(&buf).Encode(v) + if err != nil { + return nil, err + } + + k, _ := fernet.DecodeKey(key) + return fernet.EncryptAndSign(buf.Bytes(), k) +} diff --git a/api/v1/models.go b/api/v1/models.go index b050b428..2a1b8065 100644 --- a/api/v1/models.go +++ b/api/v1/models.go @@ -15,14 +15,9 @@ package v1 import ( - "bytes" - "encoding/json" - "errors" "fmt" - "time" - - "github.com/fernet/fernet-go" + "github.com/coreos/clair/api/token" "github.com/coreos/clair/database" "github.com/coreos/clair/ext/versionfmt" ) @@ -227,7 +222,7 @@ func NotificationFromDatabaseModel(dbNotification database.VulnerabilityNotifica var nextPageStr string if nextPage != database.NoVulnerabilityNotificationPage { - nextPageBytes, _ := tokenMarshal(nextPage, key) + nextPageBytes, _ := token.Marshal(nextPage, key) nextPageStr = string(nextPageBytes) } @@ -320,24 +315,3 @@ type FeatureEnvelope struct { Features *[]Feature `json:"Features,omitempty"` Error *Error `json:"Error,omitempty"` } - -func tokenUnmarshal(token string, key string, v interface{}) error { - k, _ := fernet.DecodeKey(key) - msg := fernet.VerifyAndDecrypt([]byte(token), time.Hour, []*fernet.Key{k}) - if msg == nil { - return errors.New("invalid or expired pagination token") - } - - return json.NewDecoder(bytes.NewBuffer(msg)).Decode(&v) -} - -func tokenMarshal(v interface{}, key string) ([]byte, error) { - var buf bytes.Buffer - err := json.NewEncoder(&buf).Encode(v) - if err != nil { - return nil, err - } - - k, _ := fernet.DecodeKey(key) - return fernet.EncryptAndSign(buf.Bytes(), k) -} diff --git a/api/v1/routes.go b/api/v1/routes.go index f44dde67..9a5f6bb3 100644 --- a/api/v1/routes.go +++ b/api/v1/routes.go @@ -27,6 +27,7 @@ import ( log "github.com/sirupsen/logrus" "github.com/coreos/clair" + "github.com/coreos/clair/api/token" "github.com/coreos/clair/database" "github.com/coreos/clair/pkg/commonerr" "github.com/coreos/clair/pkg/tarutil" @@ -209,7 +210,7 @@ func getVulnerabilities(w http.ResponseWriter, r *http.Request, p httprouter.Par page := 0 pageStrs, pageExists := query["page"] if pageExists { - err = tokenUnmarshal(pageStrs[0], ctx.PaginationKey, &page) + err = token.Unmarshal(pageStrs[0], ctx.PaginationKey, &page) if err != nil { writeResponse(w, r, http.StatusBadRequest, VulnerabilityEnvelope{Error: &Error{"invalid page format: " + err.Error()}}) return getNotificationRoute, http.StatusBadRequest @@ -239,7 +240,7 @@ func getVulnerabilities(w http.ResponseWriter, r *http.Request, p httprouter.Par var nextPageStr string if nextPage != -1 { - nextPageBytes, err := tokenMarshal(nextPage, ctx.PaginationKey) + nextPageBytes, err := token.Marshal(nextPage, ctx.PaginationKey) if err != nil { writeResponse(w, r, http.StatusBadRequest, VulnerabilityEnvelope{Error: &Error{"failed to marshal token: " + err.Error()}}) return getNotificationRoute, http.StatusBadRequest @@ -452,14 +453,14 @@ func getNotification(w http.ResponseWriter, r *http.Request, p httprouter.Params page := database.VulnerabilityNotificationFirstPage pageStrs, pageExists := query["page"] if pageExists { - err := tokenUnmarshal(pageStrs[0], ctx.PaginationKey, &page) + err := token.Unmarshal(pageStrs[0], ctx.PaginationKey, &page) if err != nil { writeResponse(w, r, http.StatusBadRequest, NotificationEnvelope{Error: &Error{"invalid page format: " + err.Error()}}) return getNotificationRoute, http.StatusBadRequest } pageToken = pageStrs[0] } else { - pageTokenBytes, err := tokenMarshal(page, ctx.PaginationKey) + pageTokenBytes, err := token.Marshal(page, ctx.PaginationKey) if err != nil { writeResponse(w, r, http.StatusBadRequest, NotificationEnvelope{Error: &Error{"failed to marshal token: " + err.Error()}}) return getNotificationRoute, http.StatusBadRequest diff --git a/api/v2/clairpb/Makefile b/api/v2/clairpb/Makefile new file mode 100644 index 00000000..8c193baa --- /dev/null +++ b/api/v2/clairpb/Makefile @@ -0,0 +1,17 @@ +all: + protoc -I/usr/local/include -I. \ + -I${GOPATH}/src \ + -I${GOPATH}/src/github.com/grpc-ecosystem/grpc-gateway/third_party/googleapis \ + --go_out=plugins=grpc:. \ + clair.proto + protoc -I/usr/local/include -I. \ + -I${GOPATH}/src \ + -I${GOPATH}/src/github.com/grpc-ecosystem/grpc-gateway/third_party/googleapis \ + --grpc-gateway_out=logtostderr=true:. \ + clair.proto + protoc -I/usr/local/include -I. \ + -I${GOPATH}/src \ + -I${GOPATH}/src/github.com/grpc-ecosystem/grpc-gateway/third_party/googleapis \ + --swagger_out=logtostderr=true:. \ + clair.proto + go generate . \ No newline at end of file diff --git a/api/v2/clairpb/clair.pb.go b/api/v2/clairpb/clair.pb.go new file mode 100644 index 00000000..6e5255d4 --- /dev/null +++ b/api/v2/clairpb/clair.pb.go @@ -0,0 +1,872 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// source: clair.proto + +/* +Package clairpb is a generated protocol buffer package. + +It is generated from these files: + clair.proto + +It has these top-level messages: + Vulnerability + Feature + Ancestry + LayersIntroducingVulnerabilty + OrderedLayerName + Layer + Notification + Page + PostAncestryRequest + PostAncestryResponse + GetAncestryRequest + GetAncestryResponse + GetNotificationRequest + GetNotificationResponse + DeleteNotificationRequest +*/ +package clairpb + +import proto "github.com/golang/protobuf/proto" +import fmt "fmt" +import math "math" +import _ "google.golang.org/genproto/googleapis/api/annotations" +import google_protobuf1 "github.com/golang/protobuf/ptypes/empty" + +import ( + context "golang.org/x/net/context" + grpc "google.golang.org/grpc" +) + +// Reference imports to suppress errors if they are not otherwise used. +var _ = proto.Marshal +var _ = fmt.Errorf +var _ = math.Inf + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the proto package it is being compiled against. +// A compilation error at this line likely means your copy of the +// proto package needs to be updated. +const _ = proto.ProtoPackageIsVersion2 // please upgrade the proto package + +type Vulnerability struct { + Name string `protobuf:"bytes,1,opt,name=name" json:"name,omitempty"` + NamespaceName string `protobuf:"bytes,2,opt,name=namespace_name,json=namespaceName" json:"namespace_name,omitempty"` + Description string `protobuf:"bytes,3,opt,name=description" json:"description,omitempty"` + Link string `protobuf:"bytes,4,opt,name=link" json:"link,omitempty"` + Severity string `protobuf:"bytes,5,opt,name=severity" json:"severity,omitempty"` + Metadata string `protobuf:"bytes,6,opt,name=metadata" json:"metadata,omitempty"` + FixedBy string `protobuf:"bytes,7,opt,name=fixed_by,json=fixedBy" json:"fixed_by,omitempty"` + FixedInFeatures []*Feature `protobuf:"bytes,8,rep,name=fixed_in_features,json=fixedInFeatures" json:"fixed_in_features,omitempty"` +} + +func (m *Vulnerability) Reset() { *m = Vulnerability{} } +func (m *Vulnerability) String() string { return proto.CompactTextString(m) } +func (*Vulnerability) ProtoMessage() {} +func (*Vulnerability) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{0} } + +func (m *Vulnerability) GetName() string { + if m != nil { + return m.Name + } + return "" +} + +func (m *Vulnerability) GetNamespaceName() string { + if m != nil { + return m.NamespaceName + } + return "" +} + +func (m *Vulnerability) GetDescription() string { + if m != nil { + return m.Description + } + return "" +} + +func (m *Vulnerability) GetLink() string { + if m != nil { + return m.Link + } + return "" +} + +func (m *Vulnerability) GetSeverity() string { + if m != nil { + return m.Severity + } + return "" +} + +func (m *Vulnerability) GetMetadata() string { + if m != nil { + return m.Metadata + } + return "" +} + +func (m *Vulnerability) GetFixedBy() string { + if m != nil { + return m.FixedBy + } + return "" +} + +func (m *Vulnerability) GetFixedInFeatures() []*Feature { + if m != nil { + return m.FixedInFeatures + } + return nil +} + +type Feature struct { + Name string `protobuf:"bytes,1,opt,name=name" json:"name,omitempty"` + NamespaceName string `protobuf:"bytes,2,opt,name=namespace_name,json=namespaceName" json:"namespace_name,omitempty"` + Version string `protobuf:"bytes,3,opt,name=version" json:"version,omitempty"` + VersionFormat string `protobuf:"bytes,4,opt,name=version_format,json=versionFormat" json:"version_format,omitempty"` + AddedBy string `protobuf:"bytes,5,opt,name=added_by,json=addedBy" json:"added_by,omitempty"` + Vulnerabilities []*Vulnerability `protobuf:"bytes,6,rep,name=vulnerabilities" json:"vulnerabilities,omitempty"` +} + +func (m *Feature) Reset() { *m = Feature{} } +func (m *Feature) String() string { return proto.CompactTextString(m) } +func (*Feature) ProtoMessage() {} +func (*Feature) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{1} } + +func (m *Feature) GetName() string { + if m != nil { + return m.Name + } + return "" +} + +func (m *Feature) GetNamespaceName() string { + if m != nil { + return m.NamespaceName + } + return "" +} + +func (m *Feature) GetVersion() string { + if m != nil { + return m.Version + } + return "" +} + +func (m *Feature) GetVersionFormat() string { + if m != nil { + return m.VersionFormat + } + return "" +} + +func (m *Feature) GetAddedBy() string { + if m != nil { + return m.AddedBy + } + return "" +} + +func (m *Feature) GetVulnerabilities() []*Vulnerability { + if m != nil { + return m.Vulnerabilities + } + return nil +} + +type Ancestry struct { + Name string `protobuf:"bytes,1,opt,name=name" json:"name,omitempty"` + EngineVersion int32 `protobuf:"varint,2,opt,name=engine_version,json=engineVersion" json:"engine_version,omitempty"` + Layers []*Layer `protobuf:"bytes,3,rep,name=layers" json:"layers,omitempty"` +} + +func (m *Ancestry) Reset() { *m = Ancestry{} } +func (m *Ancestry) String() string { return proto.CompactTextString(m) } +func (*Ancestry) ProtoMessage() {} +func (*Ancestry) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{2} } + +func (m *Ancestry) GetName() string { + if m != nil { + return m.Name + } + return "" +} + +func (m *Ancestry) GetEngineVersion() int32 { + if m != nil { + return m.EngineVersion + } + return 0 +} + +func (m *Ancestry) GetLayers() []*Layer { + if m != nil { + return m.Layers + } + return nil +} + +type LayersIntroducingVulnerabilty struct { + Vulnerability *Vulnerability `protobuf:"bytes,1,opt,name=vulnerability" json:"vulnerability,omitempty"` + Layers []*OrderedLayerName `protobuf:"bytes,2,rep,name=layers" json:"layers,omitempty"` +} + +func (m *LayersIntroducingVulnerabilty) Reset() { *m = LayersIntroducingVulnerabilty{} } +func (m *LayersIntroducingVulnerabilty) String() string { return proto.CompactTextString(m) } +func (*LayersIntroducingVulnerabilty) ProtoMessage() {} +func (*LayersIntroducingVulnerabilty) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{3} } + +func (m *LayersIntroducingVulnerabilty) GetVulnerability() *Vulnerability { + if m != nil { + return m.Vulnerability + } + return nil +} + +func (m *LayersIntroducingVulnerabilty) GetLayers() []*OrderedLayerName { + if m != nil { + return m.Layers + } + return nil +} + +type OrderedLayerName struct { + Index int32 `protobuf:"varint,1,opt,name=index" json:"index,omitempty"` + LayerName string `protobuf:"bytes,2,opt,name=layer_name,json=layerName" json:"layer_name,omitempty"` +} + +func (m *OrderedLayerName) Reset() { *m = OrderedLayerName{} } +func (m *OrderedLayerName) String() string { return proto.CompactTextString(m) } +func (*OrderedLayerName) ProtoMessage() {} +func (*OrderedLayerName) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{4} } + +func (m *OrderedLayerName) GetIndex() int32 { + if m != nil { + return m.Index + } + return 0 +} + +func (m *OrderedLayerName) GetLayerName() string { + if m != nil { + return m.LayerName + } + return "" +} + +type Layer struct { + Name string `protobuf:"bytes,1,opt,name=name" json:"name,omitempty"` + NamespaceNames []string `protobuf:"bytes,2,rep,name=namespace_names,json=namespaceNames" json:"namespace_names,omitempty"` +} + +func (m *Layer) Reset() { *m = Layer{} } +func (m *Layer) String() string { return proto.CompactTextString(m) } +func (*Layer) ProtoMessage() {} +func (*Layer) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{5} } + +func (m *Layer) GetName() string { + if m != nil { + return m.Name + } + return "" +} + +func (m *Layer) GetNamespaceNames() []string { + if m != nil { + return m.NamespaceNames + } + return nil +} + +type Notification struct { + Name string `protobuf:"bytes,1,opt,name=name" json:"name,omitempty"` + Created string `protobuf:"bytes,2,opt,name=created" json:"created,omitempty"` + Notified string `protobuf:"bytes,3,opt,name=notified" json:"notified,omitempty"` + Deleted string `protobuf:"bytes,4,opt,name=deleted" json:"deleted,omitempty"` + Limit int32 `protobuf:"varint,5,opt,name=limit" json:"limit,omitempty"` + Page *Page `protobuf:"bytes,6,opt,name=page" json:"page,omitempty"` +} + +func (m *Notification) Reset() { *m = Notification{} } +func (m *Notification) String() string { return proto.CompactTextString(m) } +func (*Notification) ProtoMessage() {} +func (*Notification) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{6} } + +func (m *Notification) GetName() string { + if m != nil { + return m.Name + } + return "" +} + +func (m *Notification) GetCreated() string { + if m != nil { + return m.Created + } + return "" +} + +func (m *Notification) GetNotified() string { + if m != nil { + return m.Notified + } + return "" +} + +func (m *Notification) GetDeleted() string { + if m != nil { + return m.Deleted + } + return "" +} + +func (m *Notification) GetLimit() int32 { + if m != nil { + return m.Limit + } + return 0 +} + +func (m *Notification) GetPage() *Page { + if m != nil { + return m.Page + } + return nil +} + +type Page struct { + ThisToken string `protobuf:"bytes,1,opt,name=this_token,json=thisToken" json:"this_token,omitempty"` + NextToken string `protobuf:"bytes,2,opt,name=next_token,json=nextToken" json:"next_token,omitempty"` + Old *LayersIntroducingVulnerabilty `protobuf:"bytes,3,opt,name=old" json:"old,omitempty"` + New *LayersIntroducingVulnerabilty `protobuf:"bytes,4,opt,name=new" json:"new,omitempty"` +} + +func (m *Page) Reset() { *m = Page{} } +func (m *Page) String() string { return proto.CompactTextString(m) } +func (*Page) ProtoMessage() {} +func (*Page) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{7} } + +func (m *Page) GetThisToken() string { + if m != nil { + return m.ThisToken + } + return "" +} + +func (m *Page) GetNextToken() string { + if m != nil { + return m.NextToken + } + return "" +} + +func (m *Page) GetOld() *LayersIntroducingVulnerabilty { + if m != nil { + return m.Old + } + return nil +} + +func (m *Page) GetNew() *LayersIntroducingVulnerabilty { + if m != nil { + return m.New + } + return nil +} + +type PostAncestryRequest struct { + AncestryName string `protobuf:"bytes,1,opt,name=ancestry_name,json=ancestryName" json:"ancestry_name,omitempty"` + Format string `protobuf:"bytes,2,opt,name=format" json:"format,omitempty"` + Layers []*PostAncestryRequest_PostLayer `protobuf:"bytes,3,rep,name=layers" json:"layers,omitempty"` +} + +func (m *PostAncestryRequest) Reset() { *m = PostAncestryRequest{} } +func (m *PostAncestryRequest) String() string { return proto.CompactTextString(m) } +func (*PostAncestryRequest) ProtoMessage() {} +func (*PostAncestryRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{8} } + +func (m *PostAncestryRequest) GetAncestryName() string { + if m != nil { + return m.AncestryName + } + return "" +} + +func (m *PostAncestryRequest) GetFormat() string { + if m != nil { + return m.Format + } + return "" +} + +func (m *PostAncestryRequest) GetLayers() []*PostAncestryRequest_PostLayer { + if m != nil { + return m.Layers + } + return nil +} + +type PostAncestryRequest_PostLayer struct { + Name string `protobuf:"bytes,1,opt,name=name" json:"name,omitempty"` + Path string `protobuf:"bytes,2,opt,name=path" json:"path,omitempty"` + Headers map[string]string `protobuf:"bytes,3,rep,name=headers" json:"headers,omitempty" protobuf_key:"bytes,1,opt,name=key" protobuf_val:"bytes,2,opt,name=value"` +} + +func (m *PostAncestryRequest_PostLayer) Reset() { *m = PostAncestryRequest_PostLayer{} } +func (m *PostAncestryRequest_PostLayer) String() string { return proto.CompactTextString(m) } +func (*PostAncestryRequest_PostLayer) ProtoMessage() {} +func (*PostAncestryRequest_PostLayer) Descriptor() ([]byte, []int) { + return fileDescriptor0, []int{8, 0} +} + +func (m *PostAncestryRequest_PostLayer) GetName() string { + if m != nil { + return m.Name + } + return "" +} + +func (m *PostAncestryRequest_PostLayer) GetPath() string { + if m != nil { + return m.Path + } + return "" +} + +func (m *PostAncestryRequest_PostLayer) GetHeaders() map[string]string { + if m != nil { + return m.Headers + } + return nil +} + +type PostAncestryResponse struct { + EngineVersion int32 `protobuf:"varint,1,opt,name=engine_version,json=engineVersion" json:"engine_version,omitempty"` +} + +func (m *PostAncestryResponse) Reset() { *m = PostAncestryResponse{} } +func (m *PostAncestryResponse) String() string { return proto.CompactTextString(m) } +func (*PostAncestryResponse) ProtoMessage() {} +func (*PostAncestryResponse) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{9} } + +func (m *PostAncestryResponse) GetEngineVersion() int32 { + if m != nil { + return m.EngineVersion + } + return 0 +} + +type GetAncestryRequest struct { + AncestryName string `protobuf:"bytes,1,opt,name=ancestry_name,json=ancestryName" json:"ancestry_name,omitempty"` + WithVulnerabilities bool `protobuf:"varint,2,opt,name=with_vulnerabilities,json=withVulnerabilities" json:"with_vulnerabilities,omitempty"` + WithFeatures bool `protobuf:"varint,3,opt,name=with_features,json=withFeatures" json:"with_features,omitempty"` +} + +func (m *GetAncestryRequest) Reset() { *m = GetAncestryRequest{} } +func (m *GetAncestryRequest) String() string { return proto.CompactTextString(m) } +func (*GetAncestryRequest) ProtoMessage() {} +func (*GetAncestryRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{10} } + +func (m *GetAncestryRequest) GetAncestryName() string { + if m != nil { + return m.AncestryName + } + return "" +} + +func (m *GetAncestryRequest) GetWithVulnerabilities() bool { + if m != nil { + return m.WithVulnerabilities + } + return false +} + +func (m *GetAncestryRequest) GetWithFeatures() bool { + if m != nil { + return m.WithFeatures + } + return false +} + +type GetAncestryResponse struct { + Ancestry *Ancestry `protobuf:"bytes,1,opt,name=ancestry" json:"ancestry,omitempty"` + Features []*Feature `protobuf:"bytes,2,rep,name=features" json:"features,omitempty"` +} + +func (m *GetAncestryResponse) Reset() { *m = GetAncestryResponse{} } +func (m *GetAncestryResponse) String() string { return proto.CompactTextString(m) } +func (*GetAncestryResponse) ProtoMessage() {} +func (*GetAncestryResponse) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{11} } + +func (m *GetAncestryResponse) GetAncestry() *Ancestry { + if m != nil { + return m.Ancestry + } + return nil +} + +func (m *GetAncestryResponse) GetFeatures() []*Feature { + if m != nil { + return m.Features + } + return nil +} + +type GetNotificationRequest struct { + Page string `protobuf:"bytes,1,opt,name=page" json:"page,omitempty"` + Limit int32 `protobuf:"varint,2,opt,name=limit" json:"limit,omitempty"` + Name string `protobuf:"bytes,3,opt,name=name" json:"name,omitempty"` +} + +func (m *GetNotificationRequest) Reset() { *m = GetNotificationRequest{} } +func (m *GetNotificationRequest) String() string { return proto.CompactTextString(m) } +func (*GetNotificationRequest) ProtoMessage() {} +func (*GetNotificationRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{12} } + +func (m *GetNotificationRequest) GetPage() string { + if m != nil { + return m.Page + } + return "" +} + +func (m *GetNotificationRequest) GetLimit() int32 { + if m != nil { + return m.Limit + } + return 0 +} + +func (m *GetNotificationRequest) GetName() string { + if m != nil { + return m.Name + } + return "" +} + +type GetNotificationResponse struct { + Notification *Notification `protobuf:"bytes,1,opt,name=notification" json:"notification,omitempty"` +} + +func (m *GetNotificationResponse) Reset() { *m = GetNotificationResponse{} } +func (m *GetNotificationResponse) String() string { return proto.CompactTextString(m) } +func (*GetNotificationResponse) ProtoMessage() {} +func (*GetNotificationResponse) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{13} } + +func (m *GetNotificationResponse) GetNotification() *Notification { + if m != nil { + return m.Notification + } + return nil +} + +type DeleteNotificationRequest struct { + Name string `protobuf:"bytes,1,opt,name=name" json:"name,omitempty"` +} + +func (m *DeleteNotificationRequest) Reset() { *m = DeleteNotificationRequest{} } +func (m *DeleteNotificationRequest) String() string { return proto.CompactTextString(m) } +func (*DeleteNotificationRequest) ProtoMessage() {} +func (*DeleteNotificationRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{14} } + +func (m *DeleteNotificationRequest) GetName() string { + if m != nil { + return m.Name + } + return "" +} + +func init() { + proto.RegisterType((*Vulnerability)(nil), "clairpb.Vulnerability") + proto.RegisterType((*Feature)(nil), "clairpb.Feature") + proto.RegisterType((*Ancestry)(nil), "clairpb.Ancestry") + proto.RegisterType((*LayersIntroducingVulnerabilty)(nil), "clairpb.LayersIntroducingVulnerabilty") + proto.RegisterType((*OrderedLayerName)(nil), "clairpb.OrderedLayerName") + proto.RegisterType((*Layer)(nil), "clairpb.Layer") + proto.RegisterType((*Notification)(nil), "clairpb.Notification") + proto.RegisterType((*Page)(nil), "clairpb.Page") + proto.RegisterType((*PostAncestryRequest)(nil), "clairpb.PostAncestryRequest") + proto.RegisterType((*PostAncestryRequest_PostLayer)(nil), "clairpb.PostAncestryRequest.PostLayer") + proto.RegisterType((*PostAncestryResponse)(nil), "clairpb.PostAncestryResponse") + proto.RegisterType((*GetAncestryRequest)(nil), "clairpb.GetAncestryRequest") + proto.RegisterType((*GetAncestryResponse)(nil), "clairpb.GetAncestryResponse") + proto.RegisterType((*GetNotificationRequest)(nil), "clairpb.GetNotificationRequest") + proto.RegisterType((*GetNotificationResponse)(nil), "clairpb.GetNotificationResponse") + proto.RegisterType((*DeleteNotificationRequest)(nil), "clairpb.DeleteNotificationRequest") +} + +// Reference imports to suppress errors if they are not otherwise used. +var _ context.Context +var _ grpc.ClientConn + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the grpc package it is being compiled against. +const _ = grpc.SupportPackageIsVersion4 + +// Client API for AncestryService service + +type AncestryServiceClient interface { + PostAncestry(ctx context.Context, in *PostAncestryRequest, opts ...grpc.CallOption) (*PostAncestryResponse, error) + GetAncestry(ctx context.Context, in *GetAncestryRequest, opts ...grpc.CallOption) (*GetAncestryResponse, error) +} + +type ancestryServiceClient struct { + cc *grpc.ClientConn +} + +func NewAncestryServiceClient(cc *grpc.ClientConn) AncestryServiceClient { + return &ancestryServiceClient{cc} +} + +func (c *ancestryServiceClient) PostAncestry(ctx context.Context, in *PostAncestryRequest, opts ...grpc.CallOption) (*PostAncestryResponse, error) { + out := new(PostAncestryResponse) + err := grpc.Invoke(ctx, "/clairpb.AncestryService/PostAncestry", in, out, c.cc, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *ancestryServiceClient) GetAncestry(ctx context.Context, in *GetAncestryRequest, opts ...grpc.CallOption) (*GetAncestryResponse, error) { + out := new(GetAncestryResponse) + err := grpc.Invoke(ctx, "/clairpb.AncestryService/GetAncestry", in, out, c.cc, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +// Server API for AncestryService service + +type AncestryServiceServer interface { + PostAncestry(context.Context, *PostAncestryRequest) (*PostAncestryResponse, error) + GetAncestry(context.Context, *GetAncestryRequest) (*GetAncestryResponse, error) +} + +func RegisterAncestryServiceServer(s *grpc.Server, srv AncestryServiceServer) { + s.RegisterService(&_AncestryService_serviceDesc, srv) +} + +func _AncestryService_PostAncestry_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(PostAncestryRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(AncestryServiceServer).PostAncestry(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/clairpb.AncestryService/PostAncestry", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(AncestryServiceServer).PostAncestry(ctx, req.(*PostAncestryRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _AncestryService_GetAncestry_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(GetAncestryRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(AncestryServiceServer).GetAncestry(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/clairpb.AncestryService/GetAncestry", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(AncestryServiceServer).GetAncestry(ctx, req.(*GetAncestryRequest)) + } + return interceptor(ctx, in, info, handler) +} + +var _AncestryService_serviceDesc = grpc.ServiceDesc{ + ServiceName: "clairpb.AncestryService", + HandlerType: (*AncestryServiceServer)(nil), + Methods: []grpc.MethodDesc{ + { + MethodName: "PostAncestry", + Handler: _AncestryService_PostAncestry_Handler, + }, + { + MethodName: "GetAncestry", + Handler: _AncestryService_GetAncestry_Handler, + }, + }, + Streams: []grpc.StreamDesc{}, + Metadata: "clair.proto", +} + +// Client API for NotificationService service + +type NotificationServiceClient interface { + GetNotification(ctx context.Context, in *GetNotificationRequest, opts ...grpc.CallOption) (*GetNotificationResponse, error) + DeleteNotification(ctx context.Context, in *DeleteNotificationRequest, opts ...grpc.CallOption) (*google_protobuf1.Empty, error) +} + +type notificationServiceClient struct { + cc *grpc.ClientConn +} + +func NewNotificationServiceClient(cc *grpc.ClientConn) NotificationServiceClient { + return ¬ificationServiceClient{cc} +} + +func (c *notificationServiceClient) GetNotification(ctx context.Context, in *GetNotificationRequest, opts ...grpc.CallOption) (*GetNotificationResponse, error) { + out := new(GetNotificationResponse) + err := grpc.Invoke(ctx, "/clairpb.NotificationService/GetNotification", in, out, c.cc, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *notificationServiceClient) DeleteNotification(ctx context.Context, in *DeleteNotificationRequest, opts ...grpc.CallOption) (*google_protobuf1.Empty, error) { + out := new(google_protobuf1.Empty) + err := grpc.Invoke(ctx, "/clairpb.NotificationService/DeleteNotification", in, out, c.cc, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +// Server API for NotificationService service + +type NotificationServiceServer interface { + GetNotification(context.Context, *GetNotificationRequest) (*GetNotificationResponse, error) + DeleteNotification(context.Context, *DeleteNotificationRequest) (*google_protobuf1.Empty, error) +} + +func RegisterNotificationServiceServer(s *grpc.Server, srv NotificationServiceServer) { + s.RegisterService(&_NotificationService_serviceDesc, srv) +} + +func _NotificationService_GetNotification_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(GetNotificationRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(NotificationServiceServer).GetNotification(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/clairpb.NotificationService/GetNotification", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(NotificationServiceServer).GetNotification(ctx, req.(*GetNotificationRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _NotificationService_DeleteNotification_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(DeleteNotificationRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(NotificationServiceServer).DeleteNotification(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/clairpb.NotificationService/DeleteNotification", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(NotificationServiceServer).DeleteNotification(ctx, req.(*DeleteNotificationRequest)) + } + return interceptor(ctx, in, info, handler) +} + +var _NotificationService_serviceDesc = grpc.ServiceDesc{ + ServiceName: "clairpb.NotificationService", + HandlerType: (*NotificationServiceServer)(nil), + Methods: []grpc.MethodDesc{ + { + MethodName: "GetNotification", + Handler: _NotificationService_GetNotification_Handler, + }, + { + MethodName: "DeleteNotification", + Handler: _NotificationService_DeleteNotification_Handler, + }, + }, + Streams: []grpc.StreamDesc{}, + Metadata: "clair.proto", +} + +func init() { proto.RegisterFile("clair.proto", fileDescriptor0) } + +var fileDescriptor0 = []byte{ + // 1042 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xa4, 0x56, 0xdd, 0x6e, 0x1b, 0x45, + 0x14, 0xd6, 0xda, 0x71, 0x6c, 0x1f, 0xdb, 0x49, 0x3a, 0x49, 0xd3, 0x8d, 0x93, 0x88, 0x74, 0x11, + 0xa5, 0xaa, 0xc0, 0x56, 0xd3, 0x9b, 0x12, 0x01, 0x82, 0xa8, 0x6d, 0xa8, 0x04, 0xa5, 0x5a, 0xaa, + 0x5c, 0x70, 0x63, 0x4d, 0xbc, 0x27, 0xce, 0x28, 0xeb, 0x59, 0xb3, 0x3b, 0x76, 0x62, 0x55, 0xdc, + 0xf0, 0x04, 0x54, 0x3c, 0x06, 0x2f, 0xc0, 0x15, 0x2f, 0xd1, 0x27, 0x00, 0xf1, 0x16, 0xdc, 0xa0, + 0xf9, 0xf5, 0xae, 0x63, 0x23, 0x7e, 0xae, 0x3c, 0xe7, 0x7c, 0xe7, 0xe7, 0x3b, 0x3f, 0x33, 0x5e, + 0x68, 0xf4, 0x63, 0xca, 0xd2, 0xce, 0x28, 0x4d, 0x44, 0x42, 0xaa, 0x4a, 0x18, 0x9d, 0xb5, 0xf7, + 0x06, 0x49, 0x32, 0x88, 0xb1, 0x4b, 0x47, 0xac, 0x4b, 0x39, 0x4f, 0x04, 0x15, 0x2c, 0xe1, 0x99, + 0x36, 0x6b, 0xef, 0x1a, 0x54, 0x49, 0x67, 0xe3, 0xf3, 0x2e, 0x0e, 0x47, 0x62, 0xaa, 0xc1, 0xe0, + 0x4d, 0x09, 0x5a, 0xa7, 0xe3, 0x98, 0x63, 0x4a, 0xcf, 0x58, 0xcc, 0xc4, 0x94, 0x10, 0x58, 0xe1, + 0x74, 0x88, 0xbe, 0x77, 0xe0, 0xdd, 0xaf, 0x87, 0xea, 0x4c, 0xde, 0x83, 0x35, 0xf9, 0x9b, 0x8d, + 0x68, 0x1f, 0x7b, 0x0a, 0x2d, 0x29, 0xb4, 0xe5, 0xb4, 0x2f, 0xa4, 0xd9, 0x01, 0x34, 0x22, 0xcc, + 0xfa, 0x29, 0x1b, 0xc9, 0xfc, 0x7e, 0x59, 0xd9, 0xe4, 0x55, 0x32, 0x78, 0xcc, 0xf8, 0xa5, 0xbf, + 0xa2, 0x83, 0xcb, 0x33, 0x69, 0x43, 0x2d, 0xc3, 0x09, 0xa6, 0x4c, 0x4c, 0xfd, 0x8a, 0xd2, 0x3b, + 0x59, 0x62, 0x43, 0x14, 0x34, 0xa2, 0x82, 0xfa, 0xab, 0x1a, 0xb3, 0x32, 0xd9, 0x81, 0xda, 0x39, + 0xbb, 0xc6, 0xa8, 0x77, 0x36, 0xf5, 0xab, 0x0a, 0xab, 0x2a, 0xf9, 0x78, 0x4a, 0x3e, 0x86, 0x5b, + 0x1a, 0x62, 0xbc, 0x77, 0x8e, 0x54, 0x8c, 0x53, 0xcc, 0xfc, 0xda, 0x41, 0xf9, 0x7e, 0xe3, 0x70, + 0xa3, 0x63, 0xba, 0xd6, 0x79, 0xa6, 0x81, 0x70, 0x5d, 0x99, 0x3e, 0xe7, 0x46, 0xce, 0x82, 0xdf, + 0x3d, 0xa8, 0x1a, 0xe1, 0xff, 0x74, 0xc3, 0x87, 0xea, 0x04, 0xd3, 0x6c, 0xd6, 0x09, 0x2b, 0xca, + 0x00, 0xe6, 0xd8, 0x3b, 0x4f, 0xd2, 0x21, 0x15, 0xa6, 0x1f, 0x2d, 0xa3, 0x7d, 0xa6, 0x94, 0xb2, + 0x40, 0x1a, 0x45, 0xba, 0x40, 0xdd, 0x98, 0xaa, 0x92, 0x8f, 0xa7, 0xe4, 0x33, 0x58, 0x9f, 0xe4, + 0xa6, 0xc6, 0x30, 0xf3, 0x57, 0x55, 0x79, 0xdb, 0xae, 0xbc, 0xc2, 0x54, 0xc3, 0x79, 0xf3, 0x60, + 0x08, 0xb5, 0xcf, 0x79, 0x1f, 0x33, 0x91, 0x2e, 0x1d, 0x39, 0xf2, 0x01, 0xe3, 0xd8, 0xb3, 0x45, + 0xc8, 0x22, 0x2b, 0x61, 0x4b, 0x6b, 0x4f, 0x4d, 0x29, 0xf7, 0x60, 0x35, 0xa6, 0x53, 0x4c, 0x33, + 0xbf, 0xac, 0xf2, 0xaf, 0xb9, 0xfc, 0x5f, 0x4a, 0x75, 0x68, 0xd0, 0xe0, 0x47, 0x0f, 0xf6, 0x95, + 0x26, 0x7b, 0xce, 0x45, 0x9a, 0x44, 0xe3, 0x3e, 0xe3, 0x83, 0x19, 0x45, 0x21, 0x67, 0xd6, 0xca, + 0x73, 0x9c, 0x2a, 0x36, 0xcb, 0x0b, 0x2a, 0x1a, 0x93, 0x87, 0x8e, 0x47, 0x49, 0xf1, 0xd8, 0x71, + 0x6e, 0x5f, 0xa7, 0x11, 0xa6, 0x18, 0xa9, 0xe4, 0x72, 0x2e, 0x8e, 0xd2, 0x09, 0x6c, 0xcc, 0x63, + 0x64, 0x0b, 0x2a, 0x8c, 0x47, 0x78, 0xad, 0x92, 0x57, 0x42, 0x2d, 0x90, 0x7d, 0x00, 0xe5, 0x93, + 0x1f, 0x76, 0x3d, 0xb6, 0x4e, 0xc1, 0x13, 0xa8, 0xa8, 0x08, 0x0b, 0xfb, 0xf8, 0x3e, 0xac, 0x17, + 0x97, 0x45, 0x33, 0xac, 0x87, 0x6b, 0x85, 0x6d, 0xc9, 0x82, 0x9f, 0x3d, 0x68, 0xbe, 0x48, 0x04, + 0x3b, 0x67, 0x7d, 0x6a, 0xef, 0xca, 0x8d, 0x68, 0x3e, 0x54, 0xfb, 0x29, 0x52, 0x81, 0x91, 0xa1, + 0x61, 0x45, 0x79, 0x53, 0xb8, 0xf2, 0xc6, 0xc8, 0xac, 0x9b, 0x93, 0xa5, 0x57, 0x84, 0x31, 0x4a, + 0x2f, 0xbd, 0x68, 0x56, 0x94, 0xf5, 0xc6, 0x6c, 0xc8, 0x84, 0xda, 0xaf, 0x4a, 0xa8, 0x05, 0x72, + 0x17, 0x56, 0x46, 0x74, 0x80, 0xea, 0xc6, 0x35, 0x0e, 0x5b, 0xae, 0x95, 0x2f, 0xe9, 0x00, 0x43, + 0x05, 0x05, 0xbf, 0x78, 0xb0, 0x22, 0x45, 0xd9, 0x1b, 0x71, 0xc1, 0xb2, 0x9e, 0x48, 0x2e, 0x91, + 0x1b, 0xae, 0x75, 0xa9, 0x79, 0x25, 0x15, 0x12, 0xe6, 0x78, 0x2d, 0x0c, 0x6c, 0x5a, 0x27, 0x35, + 0x1a, 0x7e, 0x0c, 0xe5, 0x24, 0xd6, 0x84, 0x1b, 0x87, 0xf7, 0x8a, 0xbb, 0xb3, 0x6c, 0x53, 0x42, + 0xe9, 0x22, 0x3d, 0x39, 0x5e, 0xa9, 0x7a, 0xfe, 0x85, 0x27, 0xc7, 0xab, 0xe0, 0x6d, 0x09, 0x36, + 0x5f, 0x26, 0x99, 0xb0, 0xeb, 0x1f, 0xe2, 0x77, 0x63, 0xcc, 0x04, 0x79, 0x17, 0x5a, 0xd4, 0xa8, + 0x7a, 0xb9, 0xc6, 0x37, 0xad, 0x52, 0x2d, 0xc8, 0x36, 0xac, 0x9a, 0x2b, 0xab, 0x6b, 0x31, 0x12, + 0xf9, 0x74, 0xee, 0x1e, 0xcc, 0x18, 0x2d, 0x48, 0xa5, 0x74, 0x85, 0xfb, 0xd1, 0xfe, 0xd5, 0x83, + 0xba, 0xd3, 0x2e, 0x1c, 0x3d, 0x91, 0x43, 0x11, 0x17, 0x26, 0xaf, 0x3a, 0x93, 0xaf, 0xa0, 0x7a, + 0x81, 0x34, 0x9a, 0xa5, 0x7d, 0xf4, 0xcf, 0xd2, 0x76, 0xbe, 0xd0, 0x5e, 0x4f, 0xb9, 0x44, 0x6d, + 0x8c, 0xf6, 0x11, 0x34, 0xf3, 0x00, 0xd9, 0x80, 0xf2, 0x25, 0x4e, 0x0d, 0x0b, 0x79, 0x94, 0xfb, + 0x32, 0xa1, 0xf1, 0xd8, 0x5e, 0x02, 0x2d, 0x1c, 0x95, 0x1e, 0x7b, 0xc1, 0x27, 0xb0, 0x55, 0x4c, + 0x99, 0x8d, 0x12, 0x9e, 0x2d, 0x7a, 0x47, 0xbc, 0x05, 0xef, 0x48, 0xf0, 0xc6, 0x03, 0x72, 0x82, + 0xff, 0x6d, 0x26, 0x0f, 0x61, 0xeb, 0x8a, 0x89, 0x8b, 0xde, 0xfc, 0x8b, 0x28, 0x39, 0xd6, 0xc2, + 0x4d, 0x89, 0x9d, 0x16, 0x21, 0x19, 0x57, 0xb9, 0xb8, 0x3f, 0x87, 0xb2, 0xb2, 0x6d, 0x4a, 0xa5, + 0xfb, 0x1f, 0x48, 0x61, 0xb3, 0x40, 0xc9, 0x54, 0xf4, 0x21, 0xd4, 0x6c, 0x7a, 0xf3, 0x46, 0xdd, + 0x72, 0x5d, 0x77, 0xc6, 0xce, 0x84, 0x7c, 0x00, 0x35, 0x97, 0xa5, 0xb4, 0xe4, 0x2f, 0xc8, 0x59, + 0x04, 0xa7, 0xb0, 0x7d, 0x82, 0x22, 0xff, 0x0e, 0xd8, 0x56, 0x10, 0x73, 0x29, 0x3d, 0x3b, 0xff, + 0x01, 0xce, 0xae, 0x6f, 0x29, 0x7f, 0x7d, 0xed, 0xf6, 0x94, 0x67, 0xdb, 0x13, 0xbc, 0x82, 0x3b, + 0x37, 0xe2, 0x9a, 0x7a, 0x3e, 0x82, 0x26, 0xcf, 0xe9, 0x4d, 0x4d, 0xb7, 0x1d, 0xc9, 0x82, 0x53, + 0xc1, 0x34, 0xe8, 0xc2, 0xce, 0x13, 0xf5, 0x92, 0x2c, 0x21, 0x3c, 0xbf, 0xc4, 0x87, 0xbf, 0x79, + 0xb0, 0x6e, 0x7b, 0xf4, 0x0d, 0xa6, 0x13, 0xd6, 0x47, 0x42, 0xa1, 0x99, 0xdf, 0x1c, 0xb2, 0xf7, + 0x77, 0x3b, 0xdc, 0xde, 0x5f, 0x82, 0xea, 0x62, 0x82, 0xad, 0x1f, 0xde, 0xfe, 0xf1, 0x53, 0x69, + 0x2d, 0xa8, 0x77, 0xed, 0x00, 0x8e, 0xbc, 0x07, 0xe4, 0x12, 0x1a, 0xb9, 0x49, 0x92, 0x5d, 0x17, + 0xe3, 0xe6, 0xca, 0xb5, 0xf7, 0x16, 0x83, 0x26, 0xfe, 0x5d, 0x15, 0x7f, 0x97, 0xec, 0xb8, 0xf8, + 0xdd, 0xd7, 0x85, 0x0d, 0xfd, 0xfe, 0xf0, 0x4f, 0x0f, 0x36, 0xf3, 0xfd, 0xb0, 0x75, 0x66, 0xb0, + 0x3e, 0x37, 0x02, 0xf2, 0x4e, 0x3e, 0xd7, 0x82, 0x1e, 0xb6, 0x0f, 0x96, 0x1b, 0x18, 0x42, 0xfb, + 0x8a, 0xd0, 0x1d, 0x72, 0xbb, 0x9b, 0x9f, 0x4c, 0xd6, 0x7d, 0xad, 0xc8, 0x90, 0x04, 0xc8, 0xcd, + 0x09, 0x91, 0xc0, 0x85, 0x5d, 0x3a, 0xbe, 0xf6, 0x76, 0x47, 0x7f, 0x37, 0x76, 0xec, 0x77, 0x63, + 0xe7, 0xa9, 0xfc, 0x6e, 0xb4, 0x09, 0x1f, 0x2c, 0x4e, 0x78, 0x5c, 0xff, 0xd6, 0x7e, 0x96, 0x9e, + 0xad, 0x2a, 0xcf, 0x47, 0x7f, 0x05, 0x00, 0x00, 0xff, 0xff, 0x5b, 0x9c, 0x1d, 0xc4, 0xb5, 0x0a, + 0x00, 0x00, +} diff --git a/api/v2/clairpb/clair.pb.gw.go b/api/v2/clairpb/clair.pb.gw.go new file mode 100644 index 00000000..f45c4f86 --- /dev/null +++ b/api/v2/clairpb/clair.pb.gw.go @@ -0,0 +1,346 @@ +// Code generated by protoc-gen-grpc-gateway +// source: clair.proto +// DO NOT EDIT! + +/* +Package clairpb is a reverse proxy. + +It translates gRPC into RESTful JSON APIs. +*/ +package clairpb + +import ( + "io" + "net/http" + + "github.com/golang/protobuf/proto" + "github.com/grpc-ecosystem/grpc-gateway/runtime" + "github.com/grpc-ecosystem/grpc-gateway/utilities" + "golang.org/x/net/context" + "google.golang.org/grpc" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/grpclog" + "google.golang.org/grpc/status" +) + +var _ codes.Code +var _ io.Reader +var _ status.Status +var _ = runtime.String +var _ = utilities.NewDoubleArray + +func request_AncestryService_PostAncestry_0(ctx context.Context, marshaler runtime.Marshaler, client AncestryServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq PostAncestryRequest + var metadata runtime.ServerMetadata + + if err := marshaler.NewDecoder(req.Body).Decode(&protoReq); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + + msg, err := client.PostAncestry(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + return msg, metadata, err + +} + +var ( + filter_AncestryService_GetAncestry_0 = &utilities.DoubleArray{Encoding: map[string]int{"ancestry_name": 0}, Base: []int{1, 1, 0}, Check: []int{0, 1, 2}} +) + +func request_AncestryService_GetAncestry_0(ctx context.Context, marshaler runtime.Marshaler, client AncestryServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq GetAncestryRequest + var metadata runtime.ServerMetadata + + var ( + val string + ok bool + err error + _ = err + ) + + val, ok = pathParams["ancestry_name"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "ancestry_name") + } + + protoReq.AncestryName, err = runtime.String(val) + + if err != nil { + return nil, metadata, err + } + + if err := runtime.PopulateQueryParameters(&protoReq, req.URL.Query(), filter_AncestryService_GetAncestry_0); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + + msg, err := client.GetAncestry(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + return msg, metadata, err + +} + +var ( + filter_NotificationService_GetNotification_0 = &utilities.DoubleArray{Encoding: map[string]int{"name": 0}, Base: []int{1, 1, 0}, Check: []int{0, 1, 2}} +) + +func request_NotificationService_GetNotification_0(ctx context.Context, marshaler runtime.Marshaler, client NotificationServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq GetNotificationRequest + var metadata runtime.ServerMetadata + + var ( + val string + ok bool + err error + _ = err + ) + + val, ok = pathParams["name"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "name") + } + + protoReq.Name, err = runtime.String(val) + + if err != nil { + return nil, metadata, err + } + + if err := runtime.PopulateQueryParameters(&protoReq, req.URL.Query(), filter_NotificationService_GetNotification_0); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + + msg, err := client.GetNotification(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + return msg, metadata, err + +} + +func request_NotificationService_DeleteNotification_0(ctx context.Context, marshaler runtime.Marshaler, client NotificationServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq DeleteNotificationRequest + var metadata runtime.ServerMetadata + + var ( + val string + ok bool + err error + _ = err + ) + + val, ok = pathParams["name"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "name") + } + + protoReq.Name, err = runtime.String(val) + + if err != nil { + return nil, metadata, err + } + + msg, err := client.DeleteNotification(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + return msg, metadata, err + +} + +// RegisterAncestryServiceHandlerFromEndpoint is same as RegisterAncestryServiceHandler but +// automatically dials to "endpoint" and closes the connection when "ctx" gets done. +func RegisterAncestryServiceHandlerFromEndpoint(ctx context.Context, mux *runtime.ServeMux, endpoint string, opts []grpc.DialOption) (err error) { + conn, err := grpc.Dial(endpoint, opts...) + if err != nil { + return err + } + defer func() { + if err != nil { + if cerr := conn.Close(); cerr != nil { + grpclog.Printf("Failed to close conn to %s: %v", endpoint, cerr) + } + return + } + go func() { + <-ctx.Done() + if cerr := conn.Close(); cerr != nil { + grpclog.Printf("Failed to close conn to %s: %v", endpoint, cerr) + } + }() + }() + + return RegisterAncestryServiceHandler(ctx, mux, conn) +} + +// RegisterAncestryServiceHandler registers the http handlers for service AncestryService to "mux". +// The handlers forward requests to the grpc endpoint over "conn". +func RegisterAncestryServiceHandler(ctx context.Context, mux *runtime.ServeMux, conn *grpc.ClientConn) error { + client := NewAncestryServiceClient(conn) + + mux.Handle("POST", pattern_AncestryService_PostAncestry_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(ctx) + defer cancel() + if cn, ok := w.(http.CloseNotifier); ok { + go func(done <-chan struct{}, closed <-chan bool) { + select { + case <-done: + case <-closed: + cancel() + } + }(ctx.Done(), cn.CloseNotify()) + } + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + rctx, err := runtime.AnnotateContext(ctx, mux, req) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := request_AncestryService_PostAncestry_0(rctx, inboundMarshaler, client, req, pathParams) + ctx = runtime.NewServerMetadataContext(ctx, md) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + + forward_AncestryService_PostAncestry_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("GET", pattern_AncestryService_GetAncestry_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(ctx) + defer cancel() + if cn, ok := w.(http.CloseNotifier); ok { + go func(done <-chan struct{}, closed <-chan bool) { + select { + case <-done: + case <-closed: + cancel() + } + }(ctx.Done(), cn.CloseNotify()) + } + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + rctx, err := runtime.AnnotateContext(ctx, mux, req) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := request_AncestryService_GetAncestry_0(rctx, inboundMarshaler, client, req, pathParams) + ctx = runtime.NewServerMetadataContext(ctx, md) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + + forward_AncestryService_GetAncestry_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + return nil +} + +var ( + pattern_AncestryService_PostAncestry_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0}, []string{"ancestry"}, "")) + + pattern_AncestryService_GetAncestry_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 1, 0, 4, 1, 5, 1}, []string{"ancestry", "ancestry_name"}, "")) +) + +var ( + forward_AncestryService_PostAncestry_0 = runtime.ForwardResponseMessage + + forward_AncestryService_GetAncestry_0 = runtime.ForwardResponseMessage +) + +// RegisterNotificationServiceHandlerFromEndpoint is same as RegisterNotificationServiceHandler but +// automatically dials to "endpoint" and closes the connection when "ctx" gets done. +func RegisterNotificationServiceHandlerFromEndpoint(ctx context.Context, mux *runtime.ServeMux, endpoint string, opts []grpc.DialOption) (err error) { + conn, err := grpc.Dial(endpoint, opts...) + if err != nil { + return err + } + defer func() { + if err != nil { + if cerr := conn.Close(); cerr != nil { + grpclog.Printf("Failed to close conn to %s: %v", endpoint, cerr) + } + return + } + go func() { + <-ctx.Done() + if cerr := conn.Close(); cerr != nil { + grpclog.Printf("Failed to close conn to %s: %v", endpoint, cerr) + } + }() + }() + + return RegisterNotificationServiceHandler(ctx, mux, conn) +} + +// RegisterNotificationServiceHandler registers the http handlers for service NotificationService to "mux". +// The handlers forward requests to the grpc endpoint over "conn". +func RegisterNotificationServiceHandler(ctx context.Context, mux *runtime.ServeMux, conn *grpc.ClientConn) error { + client := NewNotificationServiceClient(conn) + + mux.Handle("GET", pattern_NotificationService_GetNotification_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(ctx) + defer cancel() + if cn, ok := w.(http.CloseNotifier); ok { + go func(done <-chan struct{}, closed <-chan bool) { + select { + case <-done: + case <-closed: + cancel() + } + }(ctx.Done(), cn.CloseNotify()) + } + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + rctx, err := runtime.AnnotateContext(ctx, mux, req) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := request_NotificationService_GetNotification_0(rctx, inboundMarshaler, client, req, pathParams) + ctx = runtime.NewServerMetadataContext(ctx, md) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + + forward_NotificationService_GetNotification_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("DELETE", pattern_NotificationService_DeleteNotification_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(ctx) + defer cancel() + if cn, ok := w.(http.CloseNotifier); ok { + go func(done <-chan struct{}, closed <-chan bool) { + select { + case <-done: + case <-closed: + cancel() + } + }(ctx.Done(), cn.CloseNotify()) + } + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + rctx, err := runtime.AnnotateContext(ctx, mux, req) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := request_NotificationService_DeleteNotification_0(rctx, inboundMarshaler, client, req, pathParams) + ctx = runtime.NewServerMetadataContext(ctx, md) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + + forward_NotificationService_DeleteNotification_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + return nil +} + +var ( + pattern_NotificationService_GetNotification_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 1, 0, 4, 1, 5, 1}, []string{"notifications", "name"}, "")) + + pattern_NotificationService_DeleteNotification_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 1, 0, 4, 1, 5, 1}, []string{"notifications", "name"}, "")) +) + +var ( + forward_NotificationService_GetNotification_0 = runtime.ForwardResponseMessage + + forward_NotificationService_DeleteNotification_0 = runtime.ForwardResponseMessage +) diff --git a/api/v2/clairpb/clair.proto b/api/v2/clairpb/clair.proto new file mode 100644 index 00000000..c2e8fb06 --- /dev/null +++ b/api/v2/clairpb/clair.proto @@ -0,0 +1,149 @@ +// Copyright 2017 clair 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. + +syntax = "proto3"; +option go_package = "clairpb"; + +package clairpb; +import "google/api/annotations.proto"; +import "google/protobuf/empty.proto"; + +message Vulnerability { + string name = 1; + string namespace_name = 2; + string description = 3; + string link = 4; + string severity = 5; + string metadata = 6; + string fixed_by = 7; + repeated Feature fixed_in_features = 8; +} + +message Feature { + string name = 1; + string namespace_name = 2; + string version = 3; + string version_format = 4; + string added_by = 5; + repeated Vulnerability vulnerabilities = 6; +} + +message Ancestry { + string name = 1; + int32 engine_version = 2; + repeated Layer layers = 3; +} + +message LayersIntroducingVulnerabilty { + Vulnerability vulnerability = 1; + repeated OrderedLayerName layers = 2; +} + +message OrderedLayerName { + int32 index = 1; + string layer_name = 2; +} + +message Layer { + string name = 1; + repeated string namespace_names = 2; +} + +message Notification { + string name = 1; + string created = 2; + string notified = 3; + string deleted = 4; + int32 limit = 5; + Page page = 6; +} + +message Page { + string this_token = 1; + string next_token = 2; + LayersIntroducingVulnerabilty old = 3; + LayersIntroducingVulnerabilty new = 4; +} + + +message PostAncestryRequest { + message PostLayer { + string name = 1; + string path = 2; + map headers = 3; + } + string ancestry_name = 1; + string format = 2; + repeated PostLayer layers = 3; +} + +message PostAncestryResponse { + int32 engine_version = 1; +} + +message GetAncestryRequest { + string ancestry_name = 1; + bool with_vulnerabilities = 2; + bool with_features = 3; +} + +message GetAncestryResponse { + Ancestry ancestry = 1; + repeated Feature features = 2; +} + +message GetNotificationRequest { + string page = 1; + int32 limit = 2; + string name = 3; +} + +message GetNotificationResponse { + Notification notification = 1; +} + +message DeleteNotificationRequest { + string name = 1; +} + + + +service AncestryService{ + rpc PostAncestry(PostAncestryRequest) returns (PostAncestryResponse) { + option (google.api.http) = { + post: "/ancestry" + body: "*" + }; + } + + rpc GetAncestry(GetAncestryRequest) returns (GetAncestryResponse) { + option (google.api.http) = { + get: "/ancestry/{ancestry_name}" + }; + } +} + +service NotificationService{ + rpc GetNotification(GetNotificationRequest) returns (GetNotificationResponse) { + option (google.api.http) = { + get: "/notifications/{name}" + }; + } + + rpc DeleteNotification(DeleteNotificationRequest) returns (google.protobuf.Empty) { + option (google.api.http) = { + delete: "/notifications/{name}" + }; + } +} \ No newline at end of file diff --git a/api/v2/clairpb/clair.swagger.json b/api/v2/clairpb/clair.swagger.json new file mode 100644 index 00000000..99623387 --- /dev/null +++ b/api/v2/clairpb/clair.swagger.json @@ -0,0 +1,371 @@ +{ + "swagger": "2.0", + "info": { + "title": "clair.proto", + "version": "version not set" + }, + "schemes": [ + "http", + "https" + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "paths": { + "/ancestry": { + "post": { + "operationId": "PostAncestry", + "responses": { + "200": { + "description": "", + "schema": { + "$ref": "#/definitions/clairpbPostAncestryResponse" + } + } + }, + "parameters": [ + { + "name": "body", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/clairpbPostAncestryRequest" + } + } + ], + "tags": [ + "AncestryService" + ] + } + }, + "/ancestry/{ancestry_name}": { + "get": { + "operationId": "GetAncestry", + "responses": { + "200": { + "description": "", + "schema": { + "$ref": "#/definitions/clairpbGetAncestryResponse" + } + } + }, + "parameters": [ + { + "name": "ancestry_name", + "in": "path", + "required": true, + "type": "string" + }, + { + "name": "with_vulnerabilities", + "in": "query", + "required": false, + "type": "boolean", + "format": "boolean" + }, + { + "name": "with_features", + "in": "query", + "required": false, + "type": "boolean", + "format": "boolean" + } + ], + "tags": [ + "AncestryService" + ] + } + }, + "/notifications/{name}": { + "get": { + "operationId": "GetNotification", + "responses": { + "200": { + "description": "", + "schema": { + "$ref": "#/definitions/clairpbGetNotificationResponse" + } + } + }, + "parameters": [ + { + "name": "name", + "in": "path", + "required": true, + "type": "string" + }, + { + "name": "page", + "in": "query", + "required": false, + "type": "string" + }, + { + "name": "limit", + "in": "query", + "required": false, + "type": "integer", + "format": "int32" + } + ], + "tags": [ + "NotificationService" + ] + }, + "delete": { + "operationId": "DeleteNotification", + "responses": { + "200": { + "description": "", + "schema": { + "$ref": "#/definitions/protobufEmpty" + } + } + }, + "parameters": [ + { + "name": "name", + "in": "path", + "required": true, + "type": "string" + } + ], + "tags": [ + "NotificationService" + ] + } + } + }, + "definitions": { + "PostAncestryRequestPostLayer": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "path": { + "type": "string" + }, + "headers": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + } + }, + "clairpbAncestry": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "engine_version": { + "type": "integer", + "format": "int32" + }, + "layers": { + "type": "array", + "items": { + "$ref": "#/definitions/clairpbLayer" + } + } + } + }, + "clairpbFeature": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "namespace_name": { + "type": "string" + }, + "version": { + "type": "string" + }, + "version_format": { + "type": "string" + }, + "added_by": { + "type": "string" + }, + "vulnerabilities": { + "type": "array", + "items": { + "$ref": "#/definitions/clairpbVulnerability" + } + } + } + }, + "clairpbGetAncestryResponse": { + "type": "object", + "properties": { + "ancestry": { + "$ref": "#/definitions/clairpbAncestry" + }, + "features": { + "type": "array", + "items": { + "$ref": "#/definitions/clairpbFeature" + } + } + } + }, + "clairpbGetNotificationResponse": { + "type": "object", + "properties": { + "notification": { + "$ref": "#/definitions/clairpbNotification" + } + } + }, + "clairpbLayer": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "namespace_names": { + "type": "array", + "items": { + "type": "string" + } + } + } + }, + "clairpbLayersIntroducingVulnerabilty": { + "type": "object", + "properties": { + "vulnerability": { + "$ref": "#/definitions/clairpbVulnerability" + }, + "layers": { + "type": "array", + "items": { + "$ref": "#/definitions/clairpbOrderedLayerName" + } + } + } + }, + "clairpbNotification": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "created": { + "type": "string" + }, + "notified": { + "type": "string" + }, + "deleted": { + "type": "string" + }, + "limit": { + "type": "integer", + "format": "int32" + }, + "page": { + "$ref": "#/definitions/clairpbPage" + } + } + }, + "clairpbOrderedLayerName": { + "type": "object", + "properties": { + "index": { + "type": "integer", + "format": "int32" + }, + "layer_name": { + "type": "string" + } + } + }, + "clairpbPage": { + "type": "object", + "properties": { + "this_token": { + "type": "string" + }, + "next_token": { + "type": "string" + }, + "old": { + "$ref": "#/definitions/clairpbLayersIntroducingVulnerabilty" + }, + "new": { + "$ref": "#/definitions/clairpbLayersIntroducingVulnerabilty" + } + } + }, + "clairpbPostAncestryRequest": { + "type": "object", + "properties": { + "ancestry_name": { + "type": "string" + }, + "format": { + "type": "string" + }, + "layers": { + "type": "array", + "items": { + "$ref": "#/definitions/PostAncestryRequestPostLayer" + } + } + } + }, + "clairpbPostAncestryResponse": { + "type": "object", + "properties": { + "engine_version": { + "type": "integer", + "format": "int32" + } + } + }, + "clairpbVulnerability": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "namespace_name": { + "type": "string" + }, + "description": { + "type": "string" + }, + "link": { + "type": "string" + }, + "severity": { + "type": "string" + }, + "metadata": { + "type": "string" + }, + "fixed_by": { + "type": "string" + }, + "fixed_in_features": { + "type": "array", + "items": { + "$ref": "#/definitions/clairpbFeature" + } + } + } + }, + "protobufEmpty": { + "type": "object", + "description": "service Foo {\n rpc Bar(google.protobuf.Empty) returns (google.protobuf.Empty);\n }\n\nThe JSON representation for `Empty` is empty JSON object `{}`.", + "title": "A generic empty message that you can re-use to avoid defining duplicated\nempty messages in your APIs. A typical example is to use it as the request\nor the response type of an API method. For instance:" + } + } +} diff --git a/api/v2/clairpb/convert.go b/api/v2/clairpb/convert.go new file mode 100644 index 00000000..34e7eb6e --- /dev/null +++ b/api/v2/clairpb/convert.go @@ -0,0 +1,165 @@ +// Copyright 2017 clair 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 clairpb + +import ( + "encoding/json" + "fmt" + + "github.com/coreos/clair/api/token" + "github.com/coreos/clair/database" + "github.com/coreos/clair/ext/versionfmt" +) + +func NotificationFromDatabaseModel(dbNotification database.VulnerabilityNotification, limit int, pageToken string, nextPage database.VulnerabilityNotificationPageNumber, key string) (*Notification, error) { + var oldVuln *LayersIntroducingVulnerabilty + if dbNotification.OldVulnerability != nil { + v, err := LayersIntroducingVulnerabiltyFromDatabaseModel(*dbNotification.OldVulnerability) + if err != nil { + return nil, err + } + oldVuln = v + } + + var newVuln *LayersIntroducingVulnerabilty + if dbNotification.NewVulnerability != nil { + v, err := LayersIntroducingVulnerabiltyFromDatabaseModel(*dbNotification.NewVulnerability) + if err != nil { + return nil, err + } + newVuln = v + } + + var nextPageStr string + if nextPage != database.NoVulnerabilityNotificationPage { + nextPageBytes, _ := token.Marshal(nextPage, key) + nextPageStr = string(nextPageBytes) + } + + var created, notified, deleted string + if !dbNotification.Created.IsZero() { + created = fmt.Sprintf("%d", dbNotification.Created.Unix()) + } + if !dbNotification.Notified.IsZero() { + notified = fmt.Sprintf("%d", dbNotification.Notified.Unix()) + } + if !dbNotification.Deleted.IsZero() { + deleted = fmt.Sprintf("%d", dbNotification.Deleted.Unix()) + } + + return &Notification{ + Name: dbNotification.Name, + Created: created, + Notified: notified, + Deleted: deleted, + Limit: int32(limit), + Page: &Page{ + ThisToken: pageToken, + NextToken: nextPageStr, + Old: oldVuln, + New: newVuln, + }, + }, nil +} + +func LayersIntroducingVulnerabiltyFromDatabaseModel(dbVuln database.Vulnerability) (*LayersIntroducingVulnerabilty, error) { + vuln, err := VulnerabilityFromDatabaseModel(dbVuln, true) + if err != nil { + return nil, err + } + var orderedLayers []*OrderedLayerName + + return &LayersIntroducingVulnerabilty{ + Vulnerability: vuln, + Layers: orderedLayers, + }, nil +} + +func VulnerabilityFromDatabaseModel(dbVuln database.Vulnerability, withFixedIn bool) (*Vulnerability, error) { + metaString := "" + if dbVuln.Metadata != nil { + metadataByte, err := json.Marshal(dbVuln.Metadata) + if err != nil { + return nil, err + } + metaString = string(metadataByte) + } + + vuln := Vulnerability{ + Name: dbVuln.Name, + NamespaceName: dbVuln.Namespace.Name, + Description: dbVuln.Description, + Link: dbVuln.Link, + Severity: string(dbVuln.Severity), + Metadata: metaString, + } + + if dbVuln.FixedBy != versionfmt.MaxVersion { + vuln.FixedBy = dbVuln.FixedBy + } + + if withFixedIn { + for _, dbFeatureVersion := range dbVuln.FixedIn { + f, err := FeatureFromDatabaseModel(dbFeatureVersion, false) + if err != nil { + return nil, err + } + + vuln.FixedInFeatures = append(vuln.FixedInFeatures, f) + } + } + + return &vuln, nil +} + +func LayerFromDatabaseModel(dbLayer database.Layer) *Layer { + layer := Layer{ + Name: dbLayer.Name, + } + for _, ns := range dbLayer.Namespaces { + layer.NamespaceNames = append(layer.NamespaceNames, ns.Name) + } + + return &layer +} + +func FeatureFromDatabaseModel(fv database.FeatureVersion, withVulnerabilities bool) (*Feature, error) { + version := fv.Version + if version == versionfmt.MaxVersion { + version = "None" + } + f := &Feature{ + Name: fv.Feature.Name, + NamespaceName: fv.Feature.Namespace.Name, + VersionFormat: fv.Feature.Namespace.VersionFormat, + Version: version, + AddedBy: fv.AddedBy.Name, + } + + if withVulnerabilities { + for _, dbVuln := range fv.AffectedBy { + // VulnerabilityFromDatabaseModel should be called without FixedIn, + // Otherwise it might cause infinite loop + vul, err := VulnerabilityFromDatabaseModel(dbVuln, false) + if err != nil { + return nil, err + } + + f.Vulnerabilities = append(f.Vulnerabilities, vul) + } + } + + return f, nil +} diff --git a/api/v2/rpc.go b/api/v2/rpc.go new file mode 100644 index 00000000..9ff840cb --- /dev/null +++ b/api/v2/rpc.go @@ -0,0 +1,256 @@ +// Copyright 2017 clair 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 v2 + +import ( + "fmt" + + google_protobuf1 "github.com/golang/protobuf/ptypes/empty" + log "github.com/sirupsen/logrus" + "golang.org/x/net/context" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" + + "github.com/coreos/clair" + "github.com/coreos/clair/api/token" + pb "github.com/coreos/clair/api/v2/clairpb" + "github.com/coreos/clair/database" + "github.com/coreos/clair/pkg/commonerr" + "github.com/coreos/clair/pkg/tarutil" +) + +// NotificationServer implements NotificationService interface for serving RPC. +type NotificationServer struct { + Store database.Datastore + PaginationKey string +} + +// AncestryServer implements AncestryService interface for serving RPC. +type AncestryServer struct { + Store database.Datastore +} + +// PostAncestry implements posting an ancestry via the Clair gRPC service. +func (s *AncestryServer) PostAncestry(ctx context.Context, req *pb.PostAncestryRequest) (*pb.PostAncestryResponse, error) { + ancestryName := req.GetAncestryName() + if ancestryName == "" { + return nil, status.Error(codes.InvalidArgument, "Failed to provide proper ancestry name") + } + + layers := req.GetLayers() + if len(layers) == 0 { + return nil, status.Error(codes.InvalidArgument, "At least one layer should be provided for an ancestry") + } + + var currentName, parentName, rootName string + for i, layer := range layers { + if layer == nil { + err := status.Error(codes.InvalidArgument, "Failed to provide layer") + return nil, s.rollBackOnError(err, currentName, rootName) + } + + // TODO(keyboardnerd): after altering the database to support ancestry, + // we should use the ancestry name and index as key instead of + // the amalgamation of ancestry name of index + // Hack: layer name is [ancestryName]-[index] except the tail layer, + // tail layer name is [ancestryName] + if i == len(layers)-1 { + currentName = ancestryName + } else { + currentName = fmt.Sprintf("%s-%d", ancestryName, i) + } + + // if rootName is unset, this is the first iteration over the layers and + // the current layer is the root of the ancestry + if rootName == "" { + rootName = currentName + } + + err := clair.ProcessLayer(s.Store, req.GetFormat(), currentName, parentName, layer.GetPath(), layer.GetHeaders()) + if err != nil { + return nil, s.rollBackOnError(err, currentName, rootName) + } + + // Now that the current layer is processed, set the parentName for the + // next iteration. + parentName = currentName + } + + return &pb.PostAncestryResponse{ + EngineVersion: clair.Version, + }, nil +} + +// GetAncestry implements retrieving an ancestry via the Clair gRPC service. +func (s *AncestryServer) GetAncestry(ctx context.Context, req *pb.GetAncestryRequest) (*pb.GetAncestryResponse, error) { + if req.GetAncestryName() == "" { + return nil, status.Errorf(codes.InvalidArgument, "invalid get ancestry request") + } + + // TODO(keyboardnerd): after altering the database to support ancestry, this + // function is iteratively querying for for r.GetIndex() th parent of the + // requested layer until the indexed layer is found or index is out of bound + // this is a hack and will be replaced with one query + ancestry, features, err := s.getAncestry(req.GetAncestryName(), req.GetWithFeatures(), req.GetWithVulnerabilities()) + if err == commonerr.ErrNotFound { + return nil, status.Error(codes.NotFound, err.Error()) + } else if err != nil { + return nil, status.Error(codes.Internal, err.Error()) + } + + return &pb.GetAncestryResponse{ + Ancestry: ancestry, + Features: features, + }, nil +} + +// GetNotification implements retrieving a notification via the Clair gRPC +// service. +func (s *NotificationServer) GetNotification(ctx context.Context, req *pb.GetNotificationRequest) (*pb.GetNotificationResponse, error) { + if req.GetName() == "" { + return nil, status.Error(codes.InvalidArgument, "Failed to provide notification name") + } + + if req.GetLimit() <= 0 { + return nil, status.Error(codes.InvalidArgument, "Failed to provide page limit") + } + + page := database.VulnerabilityNotificationFirstPage + pageToken := req.GetPage() + if pageToken != "" { + err := token.Unmarshal(pageToken, s.PaginationKey, &page) + if err != nil { + return nil, status.Errorf(codes.InvalidArgument, "Invalid page format %s", err.Error()) + } + } else { + pageTokenBytes, err := token.Marshal(page, s.PaginationKey) + if err != nil { + return nil, status.Errorf(codes.InvalidArgument, "Failed to marshal token: %s", err.Error()) + } + pageToken = string(pageTokenBytes) + } + + dbNotification, nextPage, err := s.Store.GetNotification(req.GetName(), int(req.GetLimit()), page) + if err == commonerr.ErrNotFound { + return nil, status.Error(codes.NotFound, err.Error()) + } else if err != nil { + return nil, status.Error(codes.Internal, err.Error()) + } + + notification, err := pb.NotificationFromDatabaseModel(dbNotification, int(req.GetLimit()), pageToken, nextPage, s.PaginationKey) + if err != nil { + return nil, status.Error(codes.Internal, err.Error()) + } + + return &pb.GetNotificationResponse{Notification: notification}, nil +} + +// DeleteNotification implements deleting a notification via the Clair gRPC +// service. +func (s *NotificationServer) DeleteNotification(ctx context.Context, req *pb.DeleteNotificationRequest) (*google_protobuf1.Empty, error) { + if req.GetName() == "" { + return nil, status.Error(codes.InvalidArgument, "Failed to provide notification name") + } + + err := s.Store.DeleteNotification(req.GetName()) + if err == commonerr.ErrNotFound { + return nil, status.Error(codes.NotFound, err.Error()) + } else if err != nil { + return nil, status.Error(codes.Internal, err.Error()) + } + + return &google_protobuf1.Empty{}, nil +} + +// rollBackOnError handles server error and rollback whole ancestry insertion if +// any layer failed to be inserted. +func (s *AncestryServer) rollBackOnError(err error, currentLayerName, rootLayerName string) error { + // if the current layer failed to be inserted and it's the root layer, + // then the ancestry is not yet in the database. + if currentLayerName != rootLayerName { + errrb := s.Store.DeleteLayer(rootLayerName) + if errrb != nil { + return status.Errorf(codes.Internal, errrb.Error()) + } + log.WithField("layer name", currentLayerName).Warnf("Can't process %s: roll back the ancestry", currentLayerName) + } + + if err == tarutil.ErrCouldNotExtract || + err == tarutil.ErrExtractedFileTooBig || + err == clair.ErrUnsupported { + return status.Errorf(codes.InvalidArgument, "unprocessable entity %s", err.Error()) + } + + if _, badreq := err.(*commonerr.ErrBadRequest); badreq { + return status.Error(codes.InvalidArgument, err.Error()) + } + + return status.Error(codes.Internal, err.Error()) +} + +// TODO(keyboardnerd): Remove this Legacy compability code once the database is +// revised. +// getAncestry returns an ancestry from database by getting all parents of a +// layer given the layer name, and the layer's feature list if +// withFeature/withVulnerability is turned on. +func (s *AncestryServer) getAncestry(name string, withFeature bool, withVulnerability bool) (ancestry *pb.Ancestry, features []*pb.Feature, err error) { + var ( + layers = []*pb.Layer{} + layer database.Layer + ) + ancestry = &pb.Ancestry{} + + layer, err = s.Store.FindLayer(name, withFeature, withVulnerability) + if err != nil { + return + } + + if withFeature { + for _, fv := range layer.Features { + f, e := pb.FeatureFromDatabaseModel(fv, withVulnerability) + if e != nil { + err = e + return + } + + features = append(features, f) + } + } + + ancestry.Name = name + ancestry.EngineVersion = int32(layer.EngineVersion) + for name != "" { + layer, err = s.Store.FindLayer(name, false, false) + if err != nil { + return + } + + if layer.Parent != nil { + name = layer.Parent.Name + } else { + name = "" + } + + layers = append(layers, pb.LayerFromDatabaseModel(layer)) + } + + // reverse layers to make the root layer at the top + for i, j := 0, len(layers)-1; i < j; i, j = i+1, j-1 { + layers[i], layers[j] = layers[j], layers[i] + } + + ancestry.Layers = layers + return +} diff --git a/api/v2/server.go b/api/v2/server.go new file mode 100644 index 00000000..8b153680 --- /dev/null +++ b/api/v2/server.go @@ -0,0 +1,223 @@ +// Copyright 2017 clair 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 v2 + +import ( + "context" + "crypto/tls" + "fmt" + "net" + "net/http" + "strconv" + "strings" + "time" + + "github.com/cockroachdb/cmux" + "github.com/grpc-ecosystem/go-grpc-prometheus" + "github.com/grpc-ecosystem/grpc-gateway/runtime" + "github.com/prometheus/client_golang/prometheus" + log "github.com/sirupsen/logrus" + "google.golang.org/grpc" + "google.golang.org/grpc/credentials" + + pb "github.com/coreos/clair/api/v2/clairpb" + "github.com/coreos/clair/database" +) + +// handleShutdown handles the server shut down error. +func handleShutdown(err error) { + if err != nil { + if opErr, ok := err.(*net.OpError); !ok || (ok && opErr.Op != "accept") { + log.Fatal(err) + } + } +} + +var ( + promResponseDurationMilliseconds = prometheus.NewHistogramVec(prometheus.HistogramOpts{ + Name: "clair_v2_api_response_duration_milliseconds", + Help: "The duration of time it takes to receieve and write a response to an V2 API request", + Buckets: prometheus.ExponentialBuckets(9.375, 2, 10), + }, []string{"route", "code"}) +) + +func init() { + prometheus.MustRegister(promResponseDurationMilliseconds) +} + +func newGrpcServer(paginationKey string, store database.Datastore, tlsConfig *tls.Config) *grpc.Server { + grpcOpts := []grpc.ServerOption{ + grpc.UnaryInterceptor(grpc_prometheus.UnaryServerInterceptor), + grpc.StreamInterceptor(grpc_prometheus.StreamServerInterceptor), + } + + if tlsConfig != nil { + grpcOpts = append(grpcOpts, grpc.Creds(credentials.NewTLS(tlsConfig))) + } + + grpcServer := grpc.NewServer(grpcOpts...) + pb.RegisterAncestryServiceServer(grpcServer, &AncestryServer{Store: store}) + pb.RegisterNotificationServiceServer(grpcServer, &NotificationServer{PaginationKey: paginationKey, Store: store}) + return grpcServer +} + +type httpStatusWritter struct { + http.ResponseWriter + + StatusCode int +} + +func (w *httpStatusWritter) WriteHeader(code int) { + w.StatusCode = code + w.ResponseWriter.WriteHeader(code) +} + +// logHandler adds request logging to an http handler. +func logHandler(handler http.Handler) http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + start := time.Now() + lrw := &httpStatusWritter{ResponseWriter: w, StatusCode: http.StatusOK} + + handler.ServeHTTP(lrw, r) + + statusStr := strconv.Itoa(lrw.StatusCode) + if lrw.StatusCode == 0 { + statusStr = "???" + } + + log.WithFields(log.Fields{ + "remote addr": r.RemoteAddr, + "method": r.Method, + "request uri": r.RequestURI, + "status": statusStr, + "elapsed time": time.Since(start), + }).Info("Handled HTTP request") + }) +} + +func newGrpcGatewayServer(ctx context.Context, listenerAddr string, tlsConfig *tls.Config) http.Handler { + var ( + gwTLSConfig *tls.Config + gwOpts []grpc.DialOption + ) + + if tlsConfig != nil { + gwTLSConfig = tlsConfig.Clone() + gwTLSConfig.InsecureSkipVerify = true + gwOpts = append(gwOpts, grpc.WithTransportCredentials(credentials.NewTLS(gwTLSConfig))) + } else { + gwOpts = append(gwOpts, grpc.WithInsecure()) + } + + // changes json serializer to include empty fields with default values + jsonOpt := runtime.WithMarshalerOption(runtime.MIMEWildcard, &runtime.JSONPb{EmitDefaults: true}) + gwmux := runtime.NewServeMux(jsonOpt) + + conn, err := grpc.DialContext(ctx, listenerAddr, gwOpts...) + if err != nil { + log.WithError(err).Fatal("could not initialize grpc gateway connection") + } + + err = pb.RegisterAncestryServiceHandler(ctx, gwmux, conn) + if err != nil { + log.WithError(err).Fatal("could not initialize ancestry grpc gateway") + } + + err = pb.RegisterNotificationServiceHandler(ctx, gwmux, conn) + if err != nil { + log.WithError(err).Fatal("could not initialize notification grpc gateway") + } + + return logHandler(gwmux) +} + +func servePrometheus(mux *http.ServeMux) { + mux.Handle("/metrics", prometheus.Handler()) +} + +// Run initializes grpc and grpc gateway api services on the same port +func Run(GrpcPort int, tlsConfig *tls.Config, PaginationKey, CertFile, KeyFile string, store database.Datastore) { + l, err := net.Listen("tcp", fmt.Sprintf("localhost:%d", GrpcPort)) + if err != nil { + log.WithError(err).Fatalf("could not bind to port %d", GrpcPort) + } + log.WithField("addr", l.Addr().String()).Info("starting grpc server") + + var ( + apiHandler http.Handler + apiListener net.Listener + srv *http.Server + ctx = context.Background() + httpMux = http.NewServeMux() + tcpMux = cmux.New(l) + ) + + if tlsConfig != nil { + cert, err := tls.LoadX509KeyPair(CertFile, KeyFile) + if err != nil { + log.WithError(err).Fatal("Failed to load certificate files") + } + tlsConfig.Certificates = []tls.Certificate{cert} + tlsConfig.NextProtos = []string{"h2"} + + apiListener = tls.NewListener(tcpMux.Match(cmux.Any()), tlsConfig) + go func() { handleShutdown(tcpMux.Serve()) }() + + grpcServer := newGrpcServer(PaginationKey, store, tlsConfig) + gwmux := newGrpcGatewayServer(ctx, apiListener.Addr().String(), tlsConfig) + + httpMux.Handle("/", gwmux) + servePrometheus(httpMux) + apiHandler = grpcHandlerFunc(grpcServer, httpMux) + + log.Info("grpc server is configured with client certificate authentication") + } else { + grpcL := tcpMux.Match(cmux.HTTP2HeaderField("content-type", "application/grpc")) + apiListener = tcpMux.Match(cmux.Any()) + go func() { handleShutdown(tcpMux.Serve()) }() + + grpcServer := newGrpcServer(PaginationKey, store, nil) + go func() { handleShutdown(grpcServer.Serve(grpcL)) }() + + gwmux := newGrpcGatewayServer(ctx, apiListener.Addr().String(), nil) + + httpMux.Handle("/", gwmux) + servePrometheus(httpMux) + apiHandler = httpMux + + log.Warn("grpc server is configured without client certificate authentication") + } + + srv = &http.Server{ + Handler: apiHandler, + TLSConfig: tlsConfig, + } + + // blocking call + handleShutdown(srv.Serve(apiListener)) + log.Info("Grpc API stopped") +} + +// grpcHandlerFunc returns an http.Handler that delegates to grpcServer on incoming gRPC +// connections or otherHandler otherwise. Copied from cockroachdb. +func grpcHandlerFunc(grpcServer *grpc.Server, otherHandler http.Handler) http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + if r.ProtoMajor == 2 && strings.Contains(r.Header.Get("Content-Type"), "application/grpc") { + grpcServer.ServeHTTP(w, r) + } else { + otherHandler.ServeHTTP(w, r) + } + }) +} diff --git a/cmd/clair/config.go b/cmd/clair/config.go index 3e57a979..cccbebfb 100644 --- a/cmd/clair/config.go +++ b/cmd/clair/config.go @@ -59,6 +59,7 @@ func DefaultConfig() Config { API: &api.Config{ Port: 6060, HealthPort: 6061, + GrpcPort: 6070, Timeout: 900 * time.Second, }, Notifier: ¬ification.Config{ diff --git a/cmd/clair/main.go b/cmd/clair/main.go index efed24ba..0408a732 100644 --- a/cmd/clair/main.go +++ b/cmd/clair/main.go @@ -104,6 +104,7 @@ func Boot(config *Config) { // Start API st.Begin() go api.Run(config.API, db, st) + go api.RunV2(config.API, db) st.Begin() go api.RunHealth(config.API, db, st) diff --git a/config.example.yaml b/config.example.yaml index 423068a3..ab47886c 100644 --- a/config.example.yaml +++ b/config.example.yaml @@ -29,7 +29,7 @@ clair: api: # API server port port: 6060 - + grpcPort: 6070 # Health server port # This is an unencrypted endpoint useful for load balancers to check to healthiness of the clair server. healthport: 6061 diff --git a/glide.lock b/glide.lock index 236fcf37..1c864a32 100644 --- a/glide.lock +++ b/glide.lock @@ -1,10 +1,12 @@ -hash: 74b5b23a00a6db4941521bf3adc5ca49a1ab15c857534177654bacc9fc74cdfe -updated: 2017-05-05T11:40:41.533307281-04:00 +hash: b5b9ebebad30becd361736196a015af23b1d9a616a375c7fc13823121fd17226 +updated: 2017-06-05T16:11:29.019891941-04:00 imports: - name: github.com/beorn7/perks version: 4c0e84591b9aa9e6dcfdf3e020114cd81f89d5f9 subpackages: - quantile +- name: github.com/cockroachdb/cmux + version: 30d10be492927e2dcae0089c374c455d42414fcb - name: github.com/coreos/pkg version: 3ac0863d7acf3bc44daf49afef8919af12f704ef subpackages: @@ -16,9 +18,22 @@ imports: - name: github.com/fernet/fernet-go version: 1b2437bc582b3cfbb341ee5a29f8ef5b42912ff2 - name: github.com/golang/protobuf - version: 18c9bb3261723cd5401db4d0c9fbc5c3b6c70fe8 + version: 5a0f697c9ed9d68fef0116532c6e05cfeae00e55 subpackages: + - jsonpb - proto + - protoc-gen-go/descriptor + - ptypes/any + - ptypes/empty + - ptypes/struct +- name: github.com/grpc-ecosystem/go-grpc-prometheus + version: 2500245aa6110c562d17020fb31a2c133d737799 +- name: github.com/grpc-ecosystem/grpc-gateway + version: 2a40dd79571b760642c30f62ada35c65ac2b779c + subpackages: + - runtime + - runtime/internal + - utilities - name: github.com/guregu/null version: 41961cea0328defc5f95c1c473f89ebf0d1813f6 subpackages: @@ -30,7 +45,7 @@ imports: - name: github.com/julienschmidt/httprouter version: 8c199fb6259ffc1af525cc3ad52ee60ba8359669 - name: github.com/lib/pq - version: 2704adc878c21e1329f46f6e56a1c387d788ff94 + version: 8837942c3e09574accbc5f150e2c5e057189cace subpackages: - oid - name: github.com/matttproud/golang_protobuf_extensions @@ -58,7 +73,7 @@ imports: - internal/bitbucket.org/ww/goautoneg - model - name: github.com/prometheus/procfs - version: d098ca18df8bc825079013daf7bacefbb1ee877e + version: 65c1f6f8f0fc1e2185eb9863a3bc751496404259 subpackages: - xfs - name: github.com/remind101/migrate @@ -71,10 +86,48 @@ imports: - assert - name: github.com/tylerb/graceful version: 4654dfbb6ad53cb5e27f37d99b02e16c1872fbbb +- name: golang.org/x/net + version: 59a0b19b5533c7977ddeb86b017bf507ed407b12 + subpackages: + - context + - http2 + - http2/hpack + - idna + - internal/timeseries + - lex/httplex + - trace - name: golang.org/x/sys - version: 9ccfe848b9db8435a24c424abbc07a921adf1df5 + version: b90f89a1e7a9c1f6b918820b3daa7f08488c8594 subpackages: - unix +- name: golang.org/x/text + version: ccbd3f7822129ff389f8ca4858a9b9d4d910531c + subpackages: + - secure/bidirule + - transform + - unicode/bidi + - unicode/norm +- name: google.golang.org/genproto + version: aa2eb687b4d3e17154372564ad8d6bf11c3cf21f + subpackages: + - googleapis/api/annotations + - googleapis/rpc/status +- name: google.golang.org/grpc + version: 8de2dff78c3b968a51c99ec526d934f686537437 + subpackages: + - codes + - credentials + - grpclb/grpc_lb_v1 + - grpclog + - internal + - keepalive + - metadata + - naming + - peer + - stats + - status + - tap + - transport - name: gopkg.in/yaml.v2 version: cd8b52f8269e0feb286dfeef29f8fe4d5b397e0b testImports: [] diff --git a/glide.yaml b/glide.yaml index 5e7b7359..d67189b3 100644 --- a/glide.yaml +++ b/glide.yaml @@ -29,3 +29,4 @@ import: - package: github.com/tylerb/graceful version: ^1.2.15 - package: gopkg.in/yaml.v2 +- package: github.com/cockroachdb/cmux