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
pull/408/head
Sida Chen 7 years ago
parent abd7d2e013
commit a4edf38566

@ -1,4 +1,8 @@
language: go
go:
- 1.8
sudo: required
install:

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

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

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

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

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

@ -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 &notificationServiceClient{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,
}

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

@ -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<string, string> 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}"
};
}
}

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

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

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

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

@ -59,6 +59,7 @@ func DefaultConfig() Config {
API: &api.Config{
Port: 6060,
HealthPort: 6061,
GrpcPort: 6070,
Timeout: 900 * time.Second,
},
Notifier: &notification.Config{

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

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

65
glide.lock generated

@ -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: []

@ -29,3 +29,4 @@ import:
- package: github.com/tylerb/graceful
version: ^1.2.15
- package: gopkg.in/yaml.v2
- package: github.com/cockroachdb/cmux

Loading…
Cancel
Save