Merge pull request #9134 from steven-zou/feature/pluggable_scanners

support pluggable scanner
This commit is contained in:
Steven Zou 2019-09-19 16:08:24 +08:00 committed by GitHub
commit 4c4897aef1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
41 changed files with 3372 additions and 4 deletions

View File

@ -0,0 +1,34 @@
/*Table for keeping the plug scanner registration*/
CREATE TABLE scanner_registration
(
id SERIAL PRIMARY KEY NOT NULL,
uuid VARCHAR(64) UNIQUE NOT NULL,
url VARCHAR(256) UNIQUE NOT NULL,
name VARCHAR(128) UNIQUE NOT NULL,
description VARCHAR(1024) NULL,
auth VARCHAR(16) NOT NULL,
access_cred VARCHAR(512) NULL,
adapter VARCHAR(128) NOT NULL,
vendor VARCHAR(128) NOT NULL,
version VARCHAR(32) NOT NULL,
disabled BOOLEAN NOT NULL DEFAULT FALSE,
is_default BOOLEAN NOT NULL DEFAULT FALSE,
skip_cert_verify BOOLEAN NOT NULL DEFAULT FALSE,
create_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
update_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
/*Table for keeping the scanner report. The report details are stored as JSONB*/
CREATE TABLE scanner_report
(
id SERIAL PRIMARY KEY NOT NULL,
digest VARCHAR(256) NOT NULL,
registration_id VARCHAR(64) NOT NULL,
job_id VARCHAR(32),
status VARCHAR(16) NOT NULL,
status_code INTEGER DEFAULT 0,
report JSON,
start_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
end_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
UNIQUE(digest, registration_id)
)

View File

@ -206,6 +206,13 @@ func init() {
beego.Router("/api/internal/switchquota", &InternalAPI{}, "put:SwitchQuota")
beego.Router("/api/internal/syncquota", &InternalAPI{}, "post:SyncQuota")
// Add routes for plugin scanner management
scannerAPI := &ScannerAPI{}
beego.Router("/api/scanners", scannerAPI, "post:Create;get:List")
beego.Router("/api/scanners/:uid", scannerAPI, "get:Get;delete:Delete;put:Update;patch:SetAsDefault")
// Add routes for project level scanner
beego.Router("/api/projects/:pid([0-9]+)/scanner", scannerAPI, "get:GetProjectScanner;put:SetProjectScanner")
// syncRegistry
if err := SyncRegistry(config.GlobalProjectMgr); err != nil {
log.Fatalf("failed to sync repositories from registry: %v", err)

View File

@ -0,0 +1,348 @@
// Copyright Project Harbor 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 api
import (
"fmt"
"net/http"
"github.com/goharbor/harbor/src/pkg/q"
"github.com/goharbor/harbor/src/pkg/scan/scanner/api"
"github.com/goharbor/harbor/src/pkg/scan/scanner/dao/scanner"
"github.com/pkg/errors"
)
// ScannerAPI provides the API for managing the plugin scanners
type ScannerAPI struct {
// The base controller to provide common utilities
BaseController
// Controller for the plug scanners
c api.Controller
}
// Prepare sth. for the subsequent actions
func (sa *ScannerAPI) Prepare() {
// Call super prepare method
sa.BaseController.Prepare()
// Check access permissions
if !sa.SecurityCtx.IsAuthenticated() {
sa.SendUnAuthorizedError(errors.New("UnAuthorized"))
return
}
if !sa.SecurityCtx.IsSysAdmin() {
sa.SendForbiddenError(errors.New(sa.SecurityCtx.GetUsername()))
return
}
// Use the default controller
sa.c = api.DefaultController
}
// Get the specified scanner
func (sa *ScannerAPI) Get() {
if r := sa.get(); r != nil {
// Response to the client
sa.Data["json"] = r
sa.ServeJSON()
}
}
// List all the scanners
func (sa *ScannerAPI) List() {
p, pz, err := sa.GetPaginationParams()
if err != nil {
sa.SendBadRequestError(errors.Wrap(err, "scanner API: list all"))
return
}
query := &q.Query{
PageSize: pz,
PageNumber: p,
}
// Get query key words
kws := make(map[string]string)
properties := []string{"name", "description", "url"}
for _, k := range properties {
kw := sa.GetString(k)
if len(kw) > 0 {
kws[k] = kw
}
}
if len(kws) > 0 {
query.Keywords = kws
}
all, err := sa.c.ListRegistrations(query)
if err != nil {
sa.SendInternalServerError(errors.Wrap(err, "scanner API: list all"))
return
}
// Response to the client
sa.Data["json"] = all
sa.ServeJSON()
}
// Create a new scanner
func (sa *ScannerAPI) Create() {
r := &scanner.Registration{}
if err := sa.DecodeJSONReq(r); err != nil {
sa.SendBadRequestError(errors.Wrap(err, "scanner API: create"))
return
}
if err := r.Validate(false); err != nil {
sa.SendBadRequestError(errors.Wrap(err, "scanner API: create"))
return
}
// Explicitly check if conflict
if !sa.checkDuplicated("name", r.Name) ||
!sa.checkDuplicated("url", r.URL) {
return
}
// All newly created should be non default one except the 1st one
r.IsDefault = false
uuid, err := sa.c.CreateRegistration(r)
if err != nil {
sa.SendInternalServerError(errors.Wrap(err, "scanner API: create"))
return
}
location := fmt.Sprintf("%s/%s", sa.Ctx.Request.RequestURI, uuid)
sa.Ctx.ResponseWriter.Header().Add("Location", location)
resp := make(map[string]string, 1)
resp["uuid"] = uuid
// Response to the client
sa.Ctx.ResponseWriter.WriteHeader(http.StatusCreated)
sa.Data["json"] = resp
sa.ServeJSON()
}
// Update a scanner
func (sa *ScannerAPI) Update() {
r := sa.get()
if r == nil {
// meet error
return
}
// full dose updated
rr := &scanner.Registration{}
if err := sa.DecodeJSONReq(rr); err != nil {
sa.SendBadRequestError(errors.Wrap(err, "scanner API: update"))
return
}
if err := r.Validate(true); err != nil {
sa.SendBadRequestError(errors.Wrap(err, "scanner API: update"))
return
}
// Name changed?
if r.Name != rr.Name {
if !sa.checkDuplicated("name", rr.Name) {
return
}
}
// URL changed?
if r.URL != rr.URL {
if !sa.checkDuplicated("url", rr.URL) {
return
}
}
getChanges(r, rr)
if err := sa.c.UpdateRegistration(r); err != nil {
sa.SendInternalServerError(errors.Wrap(err, "scanner API: update"))
return
}
location := fmt.Sprintf("%s/%s", sa.Ctx.Request.RequestURI, r.UUID)
sa.Ctx.ResponseWriter.Header().Add("Location", location)
// Response to the client
sa.Data["json"] = r
sa.ServeJSON()
}
// Delete the scanner
func (sa *ScannerAPI) Delete() {
uid := sa.GetStringFromPath(":uid")
if len(uid) == 0 {
sa.SendBadRequestError(errors.New("missing uid"))
return
}
deleted, err := sa.c.DeleteRegistration(uid)
if err != nil {
sa.SendInternalServerError(errors.Wrap(err, "scanner API: delete"))
return
}
if deleted == nil {
// Not found
sa.SendNotFoundError(errors.Errorf("scanner registration: %s", uid))
return
}
sa.Data["json"] = deleted
sa.ServeJSON()
}
// SetAsDefault sets the given registration as default one
func (sa *ScannerAPI) SetAsDefault() {
uid := sa.GetStringFromPath(":uid")
if len(uid) == 0 {
sa.SendBadRequestError(errors.New("missing uid"))
return
}
m := make(map[string]interface{})
if err := sa.DecodeJSONReq(&m); err != nil {
sa.SendBadRequestError(errors.Wrap(err, "scanner API: set as default"))
return
}
if v, ok := m["is_default"]; ok {
if isDefault, y := v.(bool); y && isDefault {
if err := sa.c.SetDefaultRegistration(uid); err != nil {
sa.SendInternalServerError(errors.Wrap(err, "scanner API: set as default"))
}
return
}
}
// Not supported
sa.SendForbiddenError(errors.Errorf("not supported: %#v", m))
}
// GetProjectScanner gets the project level scanner
func (sa *ScannerAPI) GetProjectScanner() {
pid, err := sa.GetInt64FromPath(":pid")
if err != nil {
sa.SendBadRequestError(errors.Wrap(err, "scanner API: get project scanners"))
return
}
r, err := sa.c.GetRegistrationByProject(pid)
if err != nil {
sa.SendInternalServerError(errors.Wrap(err, "scanner API: get project scanners"))
return
}
if r != nil {
sa.Data["json"] = r
} else {
sa.Data["json"] = make(map[string]interface{})
}
sa.ServeJSON()
}
// SetProjectScanner sets the project level scanner
func (sa *ScannerAPI) SetProjectScanner() {
pid, err := sa.GetInt64FromPath(":pid")
if err != nil {
sa.SendBadRequestError(errors.Wrap(err, "scanner API: set project scanners"))
return
}
body := make(map[string]string)
if err := sa.DecodeJSONReq(&body); err != nil {
sa.SendBadRequestError(errors.Wrap(err, "scanner API: set project scanners"))
return
}
uuid, ok := body["uuid"]
if !ok || len(uuid) == 0 {
sa.SendBadRequestError(errors.New("missing scanner uuid when setting project scanner"))
return
}
if err := sa.c.SetRegistrationByProject(pid, uuid); err != nil {
sa.SendInternalServerError(errors.Wrap(err, "scanner API: set project scanners"))
return
}
}
// get the specified scanner
func (sa *ScannerAPI) get() *scanner.Registration {
uid := sa.GetStringFromPath(":uid")
if len(uid) == 0 {
sa.SendBadRequestError(errors.New("missing uid"))
return nil
}
r, err := sa.c.GetRegistration(uid)
if err != nil {
sa.SendInternalServerError(errors.Wrap(err, "scanner API: get"))
return nil
}
if r == nil {
// NOT found
sa.SendNotFoundError(errors.Errorf("scanner: %s", uid))
return nil
}
return r
}
func (sa *ScannerAPI) checkDuplicated(property, value string) bool {
// Explicitly check if conflict
kw := make(map[string]string)
kw[property] = value
query := &q.Query{
Keywords: kw,
}
l, err := sa.c.ListRegistrations(query)
if err != nil {
sa.SendInternalServerError(errors.Wrap(err, "scanner API: check existence"))
return false
}
if len(l) > 0 {
sa.SendConflictError(errors.Errorf("duplicated entries: %s:%s", property, value))
return false
}
return true
}
func getChanges(e *scanner.Registration, eChange *scanner.Registration) {
e.Name = eChange.Name
e.Description = eChange.Description
e.URL = eChange.URL
e.Auth = eChange.Auth
e.AccessCredential = eChange.AccessCredential
e.Disabled = eChange.Disabled
e.SkipCertVerify = eChange.SkipCertVerify
}

View File

@ -0,0 +1,444 @@
// Copyright Project Harbor 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 api
import (
"fmt"
"net/http"
"testing"
"github.com/goharbor/harbor/src/pkg/q"
"github.com/goharbor/harbor/src/pkg/scan/scanner/api"
dscan "github.com/goharbor/harbor/src/pkg/scan/scanner/dao/scan"
"github.com/goharbor/harbor/src/pkg/scan/scanner/dao/scanner"
"github.com/goharbor/harbor/src/pkg/scan/scanner/scan"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/mock"
"github.com/stretchr/testify/require"
"github.com/stretchr/testify/suite"
)
const (
rootRoute = "/api/scanners"
)
// ScannerAPITestSuite is test suite for testing the scanner API
type ScannerAPITestSuite struct {
suite.Suite
originC api.Controller
mockC *MockScannerAPIController
}
// TestScannerAPI is the entry of ScannerAPITestSuite
func TestScannerAPI(t *testing.T) {
suite.Run(t, new(ScannerAPITestSuite))
}
// SetupSuite prepares testing env
func (suite *ScannerAPITestSuite) SetupTest() {
suite.originC = api.DefaultController
m := &MockScannerAPIController{}
api.DefaultController = m
suite.mockC = m
}
// TearDownTest clears test case env
func (suite *ScannerAPITestSuite) TearDownTest() {
// Restore
api.DefaultController = suite.originC
}
// TestScannerAPICreate tests the post request to create new one
func (suite *ScannerAPITestSuite) TestScannerAPIBase() {
// Including general cases
cases := []*codeCheckingCase{
// 401
{
request: &testingRequest{
url: rootRoute,
method: http.MethodPost,
},
code: http.StatusUnauthorized,
},
// 403
{
request: &testingRequest{
url: rootRoute,
method: http.MethodPost,
credential: nonSysAdmin,
},
code: http.StatusForbidden,
},
// 400
{
request: &testingRequest{
url: rootRoute,
method: http.MethodPost,
credential: sysAdmin,
bodyJSON: &scanner.Registration{
URL: "http://a.b.c",
},
},
code: http.StatusBadRequest,
},
}
runCodeCheckingCases(suite.T(), cases...)
}
// TestScannerAPIGet tests api get
func (suite *ScannerAPITestSuite) TestScannerAPIGet() {
res := &scanner.Registration{
ID: 1000,
UUID: "uuid",
Name: "TestScannerAPIGet",
Description: "JUST FOR TEST",
URL: "https://a.b.c",
Adapter: "Clair",
Vendor: "Harbor",
Version: "0.1.0",
}
suite.mockC.On("GetRegistration", "uuid").Return(res, nil)
// Get
rr := &scanner.Registration{}
err := handleAndParse(&testingRequest{
url: fmt.Sprintf("%s/%s", rootRoute, "uuid"),
method: http.MethodGet,
credential: sysAdmin,
}, rr)
require.NoError(suite.T(), err)
require.NotNil(suite.T(), rr)
assert.Equal(suite.T(), res.Name, rr.Name)
assert.Equal(suite.T(), res.UUID, rr.UUID)
}
// TestScannerAPICreate tests create.
func (suite *ScannerAPITestSuite) TestScannerAPICreate() {
r := &scanner.Registration{
Name: "TestScannerAPICreate",
Description: "JUST FOR TEST",
URL: "https://a.b.c",
Adapter: "Clair",
Vendor: "Harbor",
Version: "0.1.0",
}
suite.mockQuery(r)
suite.mockC.On("CreateRegistration", r).Return("uuid", nil)
// Create
res := make(map[string]string, 1)
err := handleAndParse(
&testingRequest{
url: rootRoute,
method: http.MethodPost,
credential: sysAdmin,
bodyJSON: r,
}, &res)
require.NoError(suite.T(), err)
require.Condition(suite.T(), func() (success bool) {
success = res["uuid"] == "uuid"
return
})
}
// TestScannerAPIList tests list
func (suite *ScannerAPITestSuite) TestScannerAPIList() {
query := &q.Query{
PageNumber: 1,
PageSize: 500,
}
ll := []*scanner.Registration{
{
ID: 1001,
UUID: "uuid",
Name: "TestScannerAPIList",
Description: "JUST FOR TEST",
URL: "https://a.b.c",
Adapter: "Clair",
Vendor: "Harbor",
Version: "0.1.0",
}}
suite.mockC.On("ListRegistrations", query).Return(ll, nil)
// List
l := make([]*scanner.Registration, 0)
err := handleAndParse(&testingRequest{
url: rootRoute,
method: http.MethodGet,
credential: sysAdmin,
}, &l)
require.NoError(suite.T(), err)
assert.Condition(suite.T(), func() (success bool) {
success = len(l) > 0 && l[0].Name == ll[0].Name
return
})
}
// TestScannerAPIUpdate tests the update API
func (suite *ScannerAPITestSuite) TestScannerAPIUpdate() {
before := &scanner.Registration{
ID: 1002,
UUID: "uuid",
Name: "TestScannerAPIUpdate_before",
Description: "JUST FOR TEST",
URL: "https://a.b.c",
Adapter: "Clair",
Vendor: "Harbor",
Version: "0.1.0",
}
updated := &scanner.Registration{
ID: 1002,
UUID: "uuid",
Name: "TestScannerAPIUpdate",
Description: "JUST FOR TEST",
URL: "https://a.b.c",
Adapter: "Clair",
Vendor: "Harbor",
Version: "0.1.0",
}
suite.mockQuery(updated)
suite.mockC.On("UpdateRegistration", updated).Return(nil)
suite.mockC.On("GetRegistration", "uuid").Return(before, nil)
rr := &scanner.Registration{}
err := handleAndParse(&testingRequest{
url: fmt.Sprintf("%s/%s", rootRoute, "uuid"),
method: http.MethodPut,
credential: sysAdmin,
bodyJSON: updated,
}, rr)
require.NoError(suite.T(), err)
require.NotNil(suite.T(), rr)
assert.Equal(suite.T(), updated.Name, rr.Name)
assert.Equal(suite.T(), updated.UUID, rr.UUID)
}
//
func (suite *ScannerAPITestSuite) TestScannerAPIDelete() {
r := &scanner.Registration{
ID: 1003,
UUID: "uuid",
Name: "TestScannerAPIDelete",
Description: "JUST FOR TEST",
URL: "https://a.b.c",
Adapter: "Clair",
Vendor: "Harbor",
Version: "0.1.0",
}
suite.mockC.On("DeleteRegistration", "uuid").Return(r, nil)
deleted := &scanner.Registration{}
err := handleAndParse(&testingRequest{
url: fmt.Sprintf("%s/%s", rootRoute, "uuid"),
method: http.MethodDelete,
credential: sysAdmin,
}, deleted)
require.NoError(suite.T(), err)
assert.Equal(suite.T(), r.UUID, deleted.UUID)
assert.Equal(suite.T(), r.Name, deleted.Name)
}
// TestScannerAPISetDefault tests the set default
func (suite *ScannerAPITestSuite) TestScannerAPISetDefault() {
suite.mockC.On("SetDefaultRegistration", "uuid").Return(nil)
body := make(map[string]interface{}, 1)
body["is_default"] = true
runCodeCheckingCases(suite.T(), &codeCheckingCase{
request: &testingRequest{
url: fmt.Sprintf("%s/%s", rootRoute, "uuid"),
method: http.MethodPatch,
credential: sysAdmin,
bodyJSON: body,
},
code: http.StatusOK,
})
}
// TestScannerAPIProjectScanner tests the API of getting/setting project level scanner
func (suite *ScannerAPITestSuite) TestScannerAPIProjectScanner() {
suite.mockC.On("SetRegistrationByProject", int64(1), "uuid").Return(nil)
// Set
body := make(map[string]interface{}, 1)
body["uuid"] = "uuid"
runCodeCheckingCases(suite.T(), &codeCheckingCase{
request: &testingRequest{
url: fmt.Sprintf("/api/projects/%d/scanner", 1),
method: http.MethodPut,
credential: sysAdmin,
bodyJSON: body,
},
code: http.StatusOK,
})
r := &scanner.Registration{
ID: 1004,
UUID: "uuid",
Name: "TestScannerAPIProjectScanner",
Description: "JUST FOR TEST",
URL: "https://a.b.c",
Adapter: "Clair",
Vendor: "Harbor",
Version: "0.1.0",
}
suite.mockC.On("GetRegistrationByProject", int64(1)).Return(r, nil)
// Get
rr := &scanner.Registration{}
err := handleAndParse(&testingRequest{
url: fmt.Sprintf("/api/projects/%d/scanner", 1),
method: http.MethodGet,
credential: sysAdmin,
}, rr)
require.NoError(suite.T(), err)
assert.Equal(suite.T(), r.Name, rr.Name)
assert.Equal(suite.T(), r.UUID, rr.UUID)
}
func (suite *ScannerAPITestSuite) mockQuery(r *scanner.Registration) {
kw := make(map[string]string, 1)
kw["name"] = r.Name
query := &q.Query{
Keywords: kw,
}
emptyL := make([]*scanner.Registration, 0)
suite.mockC.On("ListRegistrations", query).Return(emptyL, nil)
kw2 := make(map[string]string, 1)
kw2["url"] = r.URL
query2 := &q.Query{
Keywords: kw2,
}
suite.mockC.On("ListRegistrations", query2).Return(emptyL, nil)
}
// MockScannerAPIController is mock of scanner API controller
type MockScannerAPIController struct {
mock.Mock
}
// ListRegistrations ...
func (m *MockScannerAPIController) ListRegistrations(query *q.Query) ([]*scanner.Registration, error) {
args := m.Called(query)
return args.Get(0).([]*scanner.Registration), args.Error(1)
}
// CreateRegistration ...
func (m *MockScannerAPIController) CreateRegistration(registration *scanner.Registration) (string, error) {
args := m.Called(registration)
return args.String(0), args.Error(1)
}
// GetRegistration ...
func (m *MockScannerAPIController) GetRegistration(registrationUUID string) (*scanner.Registration, error) {
args := m.Called(registrationUUID)
s := args.Get(0)
if s == nil {
return nil, args.Error(1)
}
return s.(*scanner.Registration), args.Error(1)
}
// RegistrationExists ...
func (m *MockScannerAPIController) RegistrationExists(registrationUUID string) bool {
args := m.Called(registrationUUID)
return args.Bool(0)
}
// UpdateRegistration ...
func (m *MockScannerAPIController) UpdateRegistration(registration *scanner.Registration) error {
args := m.Called(registration)
return args.Error(0)
}
// DeleteRegistration ...
func (m *MockScannerAPIController) DeleteRegistration(registrationUUID string) (*scanner.Registration, error) {
args := m.Called(registrationUUID)
s := args.Get(0)
if s == nil {
return nil, args.Error(1)
}
return s.(*scanner.Registration), args.Error(1)
}
// SetDefaultRegistration ...
func (m *MockScannerAPIController) SetDefaultRegistration(registrationUUID string) error {
args := m.Called(registrationUUID)
return args.Error(0)
}
// SetRegistrationByProject ...
func (m *MockScannerAPIController) SetRegistrationByProject(projectID int64, scannerID string) error {
args := m.Called(projectID, scannerID)
return args.Error(0)
}
// GetRegistrationByProject ...
func (m *MockScannerAPIController) GetRegistrationByProject(projectID int64) (*scanner.Registration, error) {
args := m.Called(projectID)
s := args.Get(0)
if s == nil {
return nil, args.Error(1)
}
return s.(*scanner.Registration), args.Error(1)
}
// Ping ...
func (m *MockScannerAPIController) Ping(registration *scanner.Registration) error {
args := m.Called(registration)
return args.Error(0)
}
// Scan ...
func (m *MockScannerAPIController) Scan(artifact *scan.Artifact) error {
args := m.Called(artifact)
return args.Error(0)
}
// GetReport ...
func (m *MockScannerAPIController) GetReport(artifact *scan.Artifact) ([]*dscan.Report, error) {
args := m.Called(artifact)
r := args.Get(0)
if r == nil {
return nil, args.Error(1)
}
return r.([]*dscan.Report), args.Error(1)
}
// GetScanLog ...
func (m *MockScannerAPIController) GetScanLog(digest string) ([]byte, error) {
args := m.Called(digest)
l := args.Get(0)
if l == nil {
return nil, args.Error(1)
}
return l.([]byte), args.Error(1)
}

View File

@ -192,6 +192,13 @@ func initRouters() {
beego.Router("/api/chartrepo/:repo/charts/:name/:version/labels/:id([0-9]+)", chartLabelAPIType, "delete:RemoveLabel")
}
// Add routes for plugin scanner management
scannerAPI := &api.ScannerAPI{}
beego.Router("/api/scanners", scannerAPI, "post:Create;get:List")
beego.Router("/api/scanners/:uid", scannerAPI, "get:Get;delete:Delete;put:Update;patch:SetAsDefault")
// Add routes for project level scanner
beego.Router("/api/projects/:pid([0-9]+)/scanner", scannerAPI, "get:GetProjectScanner;put:SetProjectScanner")
// Error pages
beego.ErrorController(&controllers.ErrorController{})

View File

@ -45,6 +45,7 @@ require (
github.com/google/certificate-transparency-go v1.0.21 // indirect
github.com/google/go-querystring v0.0.0-20170111101155-53e6ce116135 // indirect
github.com/google/gofuzz v0.0.0-20170612174753-24818f796faf // indirect
github.com/google/uuid v1.1.1
github.com/gorilla/handlers v1.3.0
github.com/gorilla/mux v1.6.2
github.com/graph-gophers/dataloader v5.0.0+incompatible

View File

@ -140,6 +140,8 @@ github.com/google/gofuzz v0.0.0-20170612174753-24818f796faf h1:+RRA9JqSOZFfKrOeq
github.com/google/gofuzz v0.0.0-20170612174753-24818f796faf/go.mod h1:HP5RmnzzSNb993RKQDq4+1A4ia9nllfqcQFTQJedwGI=
github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
github.com/google/uuid v1.1.1 h1:Gkbcsh/GbpXz7lPftLA3P6TYMwjCLYm83jiFQZF/3gY=
github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 h1:EGx4pi6eqNxGaHF6qqu48+N2wcFQ5qg5FXgOdqsJ5d8=
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=

25
src/pkg/q/query.go Normal file
View File

@ -0,0 +1,25 @@
// Copyright Project Harbor 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 q
// Query parameters
type Query struct {
// Page number
PageNumber int64
// Page size
PageSize int64
// List of key words
Keywords map[string]string
}

View File

@ -0,0 +1,157 @@
// Copyright Project Harbor 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 api
import (
"github.com/goharbor/harbor/src/pkg/q"
dscan "github.com/goharbor/harbor/src/pkg/scan/scanner/dao/scan"
"github.com/goharbor/harbor/src/pkg/scan/scanner/dao/scanner"
"github.com/goharbor/harbor/src/pkg/scan/scanner/scan"
)
// Controller provides the related operations of scanner for the upper API.
// All the capabilities of the scanner are defined here.
type Controller interface {
// ListRegistrations returns a list of currently configured scanner registrations.
// Query parameters are optional
//
// Arguments:
// query *q.Query : query parameters
//
// Returns:
// []*scanner.Registration : scanner list of all the matched ones
// error : non nil error if any errors occurred
ListRegistrations(query *q.Query) ([]*scanner.Registration, error)
// CreateRegistration creates a new scanner registration with the given data.
// Returns the scanner registration identifier.
//
// Arguments:
// registration *scanner.Registration : scanner registration to create
//
// Returns:
// string : the generated UUID of the new scanner
// error : non nil error if any errors occurred
CreateRegistration(registration *scanner.Registration) (string, error)
// GetRegistration returns the details of the specified scanner registration.
//
// Arguments:
// registrationUUID string : the UUID of the given scanner
//
// Returns:
// *scanner.Registration : the required scanner
// error : non nil error if any errors occurred
GetRegistration(registrationUUID string) (*scanner.Registration, error)
// RegistrationExists checks if the provided registration is there.
//
// Arguments:
// registrationUUID string : the UUID of the given scanner
//
// Returns:
// true for existing or false for not existing
RegistrationExists(registrationUUID string) bool
// UpdateRegistration updates the specified scanner registration.
//
// Arguments:
// registration *scanner.Registration : scanner registration to update
//
// Returns:
// error : non nil error if any errors occurred
UpdateRegistration(registration *scanner.Registration) error
// DeleteRegistration deletes the specified scanner registration.
//
// Arguments:
// registrationUUID string : the UUID of the given scanner which is going to be deleted
//
// Returns:
// *scanner.Registration : the deleted scanner
// error : non nil error if any errors occurred
DeleteRegistration(registrationUUID string) (*scanner.Registration, error)
// SetDefaultRegistration marks the specified scanner registration as default.
// The implementation is supposed to unset any registration previously set as default.
//
// Arguments:
// registrationUUID string : the UUID of the given scanner which is marked as default
//
// Returns:
// error : non nil error if any errors occurred
SetDefaultRegistration(registrationUUID string) error
// SetRegistrationByProject sets scanner for the given project.
//
// Arguments:
// projectID int64 : the ID of the given project
// scannerID string : the UUID of the the scanner
//
// Returns:
// error : non nil error if any errors occurred
SetRegistrationByProject(projectID int64, scannerID string) error
// GetRegistrationByProject returns the configured scanner registration of the given project or
// the system default registration if exists or `nil` if no system registrations set.
//
// Arguments:
// projectID int64 : the ID of the given project
//
// Returns:
// *scanner.Registration : the default scanner registration
// error : non nil error if any errors occurred
GetRegistrationByProject(projectID int64) (*scanner.Registration, error)
// Ping pings Scanner Adapter to test EndpointURL and Authorization settings.
// The implementation is supposed to call the GetMetadata method on scanner.Client.
// Returns `nil` if connection succeeded, a non `nil` error otherwise.
//
// Arguments:
// registration *scanner.Registration : scanner registration to ping
//
// Returns:
// error : non nil error if any errors occurred
Ping(registration *scanner.Registration) error
// Scan the given artifact
//
// Arguments:
// artifact *res.Artifact : artifact to be scanned
//
// Returns:
// error : non nil error if any errors occurred
Scan(artifact *scan.Artifact) error
// GetReport gets the reports for the given artifact identified by the digest
//
// Arguments:
// artifact *res.Artifact : the scanned artifact
//
// Returns:
// []*scan.Report : scan results by different scanner vendors
// error : non nil error if any errors occurred
GetReport(artifact *scan.Artifact) ([]*dscan.Report, error)
// Get the scan log for the specified artifact with the given digest
//
// Arguments:
// digest string : the digest of the artifact
//
// Returns:
// []byte : the log text stream
// error : non nil error if any errors occurred
GetScanLog(digest string) ([]byte, error)
}

View File

@ -0,0 +1,287 @@
// Copyright Project Harbor 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 api
import (
"testing"
"github.com/goharbor/harbor/src/common/models"
"github.com/goharbor/harbor/src/pkg/q"
"github.com/goharbor/harbor/src/pkg/scan/scanner/dao/scanner"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/mock"
"github.com/stretchr/testify/require"
"github.com/stretchr/testify/suite"
)
// ControllerTestSuite is test suite to test the basic api controller.
type ControllerTestSuite struct {
suite.Suite
c *basicController
mMgr *MockScannerManager
mMeta *MockProMetaManager
sample *scanner.Registration
}
// TestController is the entry of controller test suite
func TestController(t *testing.T) {
suite.Run(t, new(ControllerTestSuite))
}
// SetupSuite prepares env for the controller test suite
func (suite *ControllerTestSuite) SetupSuite() {
suite.mMgr = new(MockScannerManager)
suite.mMeta = new(MockProMetaManager)
suite.c = &basicController{
manager: suite.mMgr,
proMetaMgr: suite.mMeta,
}
suite.sample = &scanner.Registration{
Name: "forUT",
Description: "sample registration",
URL: "https://sample.scanner.com",
Adapter: "Clair",
Version: "0.1.0",
Vendor: "Harbor",
}
}
// Clear test case
func (suite *ControllerTestSuite) TearDownTest() {
suite.sample.UUID = ""
}
// TestListRegistrations tests ListRegistrations
func (suite *ControllerTestSuite) TestListRegistrations() {
query := &q.Query{
PageSize: 10,
PageNumber: 1,
}
suite.sample.UUID = "uuid"
l := []*scanner.Registration{suite.sample}
suite.mMgr.On("List", query).Return(l, nil)
rl, err := suite.c.ListRegistrations(query)
require.NoError(suite.T(), err)
assert.Equal(suite.T(), 1, len(rl))
}
// TestCreateRegistration tests CreateRegistration
func (suite *ControllerTestSuite) TestCreateRegistration() {
suite.mMgr.On("Create", suite.sample).Return("uuid", nil)
uid, err := suite.mMgr.Create(suite.sample)
require.NoError(suite.T(), err)
assert.Equal(suite.T(), uid, "uuid")
}
// TestGetRegistration tests GetRegistration
func (suite *ControllerTestSuite) TestGetRegistration() {
suite.sample.UUID = "uuid"
suite.mMgr.On("Get", "uuid").Return(suite.sample, nil)
rr, err := suite.c.GetRegistration("uuid")
require.NoError(suite.T(), err)
assert.NotNil(suite.T(), rr)
assert.Equal(suite.T(), "forUT", rr.Name)
}
// TestRegistrationExists tests RegistrationExists
func (suite *ControllerTestSuite) TestRegistrationExists() {
suite.sample.UUID = "uuid"
suite.mMgr.On("Get", "uuid").Return(suite.sample, nil)
exists := suite.c.RegistrationExists("uuid")
assert.Equal(suite.T(), true, exists)
suite.mMgr.On("Get", "uuid2").Return(nil, nil)
exists = suite.c.RegistrationExists("uuid2")
assert.Equal(suite.T(), false, exists)
}
// TestUpdateRegistration tests UpdateRegistration
func (suite *ControllerTestSuite) TestUpdateRegistration() {
suite.sample.UUID = "uuid"
suite.mMgr.On("Update", suite.sample).Return(nil)
err := suite.c.UpdateRegistration(suite.sample)
require.NoError(suite.T(), err)
}
// TestDeleteRegistration tests DeleteRegistration
func (suite *ControllerTestSuite) TestDeleteRegistration() {
suite.sample.UUID = "uuid"
suite.mMgr.On("Get", "uuid").Return(suite.sample, nil)
suite.mMgr.On("Delete", "uuid").Return(nil)
r, err := suite.c.DeleteRegistration("uuid")
require.NoError(suite.T(), err)
require.NotNil(suite.T(), r)
assert.Equal(suite.T(), "forUT", r.Name)
}
// TestSetDefaultRegistration tests SetDefaultRegistration
func (suite *ControllerTestSuite) TestSetDefaultRegistration() {
suite.mMgr.On("SetAsDefault", "uuid").Return(nil)
err := suite.c.SetDefaultRegistration("uuid")
require.NoError(suite.T(), err)
}
// TestSetRegistrationByProject tests SetRegistrationByProject
func (suite *ControllerTestSuite) TestSetRegistrationByProject() {
m := make(map[string]string, 1)
mm := make(map[string]string, 1)
mmm := make(map[string]string, 1)
mm[proScannerMetaKey] = "uuid"
mmm[proScannerMetaKey] = "uuid2"
var pid, pid2 int64 = 1, 2
// not set before
suite.mMeta.On("Get", pid, []string{proScannerMetaKey}).Return(m, nil)
suite.mMeta.On("Add", pid, mm).Return(nil)
err := suite.c.SetRegistrationByProject(pid, "uuid")
require.NoError(suite.T(), err)
// Set before
suite.mMeta.On("Get", pid2, []string{proScannerMetaKey}).Return(mm, nil)
suite.mMeta.On("Update", pid2, mmm).Return(nil)
err = suite.c.SetRegistrationByProject(pid2, "uuid2")
require.NoError(suite.T(), err)
}
// TestGetRegistrationByProject tests GetRegistrationByProject
func (suite *ControllerTestSuite) TestGetRegistrationByProject() {
m := make(map[string]string, 1)
m[proScannerMetaKey] = "uuid"
// Configured at project level
var pid int64 = 1
suite.sample.UUID = "uuid"
suite.mMeta.On("Get", pid, []string{proScannerMetaKey}).Return(m, nil)
suite.mMgr.On("Get", "uuid").Return(suite.sample, nil)
r, err := suite.c.GetRegistrationByProject(pid)
require.NoError(suite.T(), err)
require.Equal(suite.T(), "forUT", r.Name)
// Not configured at project level, return system default
suite.mMeta.On("Get", pid, []string{proScannerMetaKey}).Return(nil, nil)
suite.mMgr.On("GetDefault").Return(suite.sample, nil)
r, err = suite.c.GetRegistrationByProject(pid)
require.NoError(suite.T(), err)
require.NotNil(suite.T(), r)
assert.Equal(suite.T(), "forUT", r.Name)
}
// MockScannerManager is mock of the scanner manager
type MockScannerManager struct {
mock.Mock
}
// List ...
func (m *MockScannerManager) List(query *q.Query) ([]*scanner.Registration, error) {
args := m.Called(query)
return args.Get(0).([]*scanner.Registration), args.Error(1)
}
// Create ...
func (m *MockScannerManager) Create(registration *scanner.Registration) (string, error) {
args := m.Called(registration)
return args.String(0), args.Error(1)
}
// Get ...
func (m *MockScannerManager) Get(registrationUUID string) (*scanner.Registration, error) {
args := m.Called(registrationUUID)
r := args.Get(0)
if r == nil {
return nil, args.Error(1)
}
return r.(*scanner.Registration), args.Error(1)
}
// Update ...
func (m *MockScannerManager) Update(registration *scanner.Registration) error {
args := m.Called(registration)
return args.Error(0)
}
// Delete ...
func (m *MockScannerManager) Delete(registrationUUID string) error {
args := m.Called(registrationUUID)
return args.Error(0)
}
// SetAsDefault ...
func (m *MockScannerManager) SetAsDefault(registrationUUID string) error {
args := m.Called(registrationUUID)
return args.Error(0)
}
// GetDefault ...
func (m *MockScannerManager) GetDefault() (*scanner.Registration, error) {
args := m.Called()
return args.Get(0).(*scanner.Registration), args.Error(1)
}
// MockProMetaManager is the mock of the ProjectMetadataManager
type MockProMetaManager struct {
mock.Mock
}
// Add ...
func (m *MockProMetaManager) Add(projectID int64, meta map[string]string) error {
args := m.Called(projectID, meta)
return args.Error(0)
}
// Delete ...
func (m *MockProMetaManager) Delete(projecdtID int64, meta ...string) error {
args := m.Called(projecdtID, meta)
return args.Error(0)
}
// Update ...
func (m *MockProMetaManager) Update(projectID int64, meta map[string]string) error {
args := m.Called(projectID, meta)
return args.Error(0)
}
// Get ...
func (m *MockProMetaManager) Get(projectID int64, meta ...string) (map[string]string, error) {
args := m.Called(projectID, meta)
return args.Get(0).(map[string]string), args.Error(1)
}
// List ...
func (m *MockProMetaManager) List(name, value string) ([]*models.ProjectMetadata, error) {
args := m.Called(name, value)
return args.Get(0).([]*models.ProjectMetadata), args.Error(1)
}

View File

@ -0,0 +1,194 @@
// Copyright Project Harbor 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 api
import (
"github.com/goharbor/harbor/src/core/promgr/metamgr"
"github.com/goharbor/harbor/src/jobservice/logger"
"github.com/goharbor/harbor/src/pkg/q"
rscanner "github.com/goharbor/harbor/src/pkg/scan/scanner"
"github.com/goharbor/harbor/src/pkg/scan/scanner/dao/scanner"
"github.com/goharbor/harbor/src/pkg/scan/scanner/scan"
"github.com/pkg/errors"
)
const (
proScannerMetaKey = "projectScanner"
)
// DefaultController is a singleton api controller for plug scanners
var DefaultController = New()
// New a basic controller
func New() Controller {
return &basicController{
manager: rscanner.New(),
proMetaMgr: metamgr.NewDefaultProjectMetadataManager(),
}
}
// basicController is default implementation of api.Controller interface
type basicController struct {
// managers for managing the scanner registrations
manager rscanner.Manager
// for operating the project level configured scanner
proMetaMgr metamgr.ProjectMetadataManager
// controller for scan actions
c scan.Controller
// Client
}
// ListRegistrations ...
func (bc *basicController) ListRegistrations(query *q.Query) ([]*scanner.Registration, error) {
return bc.manager.List(query)
}
// CreateRegistration ...
func (bc *basicController) CreateRegistration(registration *scanner.Registration) (string, error) {
// TODO: Get metadata from the adapter service first
l, err := bc.manager.List(nil)
if err != nil {
return "", err
}
if len(l) == 0 && !registration.IsDefault {
// Mark the 1st as default automatically
registration.IsDefault = true
}
return bc.manager.Create(registration)
}
// GetRegistration ...
func (bc *basicController) GetRegistration(registrationUUID string) (*scanner.Registration, error) {
return bc.manager.Get(registrationUUID)
}
// RegistrationExists ...
func (bc *basicController) RegistrationExists(registrationUUID string) bool {
registration, err := bc.manager.Get(registrationUUID)
// Just logged when an error occurred
if err != nil {
logger.Errorf("Check existence of registration error: %s", err)
}
return !(err == nil && registration == nil)
}
// UpdateRegistration ...
func (bc *basicController) UpdateRegistration(registration *scanner.Registration) error {
return bc.manager.Update(registration)
}
// SetDefaultRegistration ...
func (bc *basicController) DeleteRegistration(registrationUUID string) (*scanner.Registration, error) {
registration, err := bc.manager.Get(registrationUUID)
if registration == nil && err == nil {
// Not found
return nil, nil
}
if err := bc.manager.Delete(registrationUUID); err != nil {
return nil, errors.Wrap(err, "delete registration")
}
return registration, nil
}
// SetDefaultRegistration ...
func (bc *basicController) SetDefaultRegistration(registrationUUID string) error {
return bc.manager.SetAsDefault(registrationUUID)
}
// SetRegistrationByProject ...
func (bc *basicController) SetRegistrationByProject(projectID int64, registrationID string) error {
if projectID == 0 {
return errors.New("invalid project ID")
}
if len(registrationID) == 0 {
return errors.New("missing scanner UUID")
}
// Only keep the UUID in the metadata of the given project
// Scanner metadata existing?
m, err := bc.proMetaMgr.Get(projectID, proScannerMetaKey)
if err != nil {
return errors.Wrap(err, "set project scanner")
}
// Update if exists
if len(m) > 0 {
// Compare and set new
if registrationID != m[proScannerMetaKey] {
m[proScannerMetaKey] = registrationID
if err := bc.proMetaMgr.Update(projectID, m); err != nil {
return errors.Wrap(err, "set project scanner")
}
}
} else {
meta := make(map[string]string, 1)
meta[proScannerMetaKey] = registrationID
if err := bc.proMetaMgr.Add(projectID, meta); err != nil {
return errors.Wrap(err, "set project scanner")
}
}
return nil
}
// GetRegistrationByProject ...
func (bc *basicController) GetRegistrationByProject(projectID int64) (*scanner.Registration, error) {
if projectID == 0 {
return nil, errors.New("invalid project ID")
}
// First, get it from the project metadata
m, err := bc.proMetaMgr.Get(projectID, proScannerMetaKey)
if err != nil {
return nil, errors.Wrap(err, "get project scanner")
}
if len(m) > 0 {
if registrationID, ok := m[proScannerMetaKey]; ok && len(registrationID) > 0 {
registration, err := bc.manager.Get(registrationID)
if err != nil {
return nil, errors.Wrap(err, "get project scanner")
}
if registration == nil {
// Not found
// Might be deleted by the admin, the project scanner ID reference should be cleared
if err := bc.proMetaMgr.Delete(projectID, proScannerMetaKey); err != nil {
return nil, errors.Wrap(err, "get project scanner")
}
} else {
return registration, nil
}
}
}
// Second, get the default one
registration, err := bc.manager.GetDefault()
// TODO: Check status by the client later
return registration, err
}
// Ping ...
func (bc *basicController) Ping(registration *scanner.Registration) error {
return nil
}

View File

@ -0,0 +1,35 @@
// Copyright Project Harbor 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 api
import (
dscan "github.com/goharbor/harbor/src/pkg/scan/scanner/dao/scan"
"github.com/goharbor/harbor/src/pkg/scan/scanner/scan"
)
// Scan ...
func (bc *basicController) Scan(artifact *scan.Artifact) error {
return nil
}
// GetReport ...
func (bc *basicController) GetReport(artifact *scan.Artifact) ([]*dscan.Report, error) {
return nil, nil
}
// GetScanLog ...
func (bc *basicController) GetScanLog(digest string) ([]byte, error) {
return nil, nil
}

View File

@ -0,0 +1,43 @@
// Copyright Project Harbor 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 scan
import "time"
// Report of the scan
// Identified by the `digest` and `endpoint_id`
type Report struct {
ID int64 `orm:"pk;auto;column(id)"`
Digest string `orm:"column(digest)"`
ReregistrationID string `orm:"column(registration_id)"`
JobID string `orm:"column(job_id)"`
Status string `orm:"column(status)"`
StatusCode int `orm:"column(status_code)"`
Report string `orm:"column(report);type(json)"`
StartTime time.Time `orm:"column(start_time);auto_now_add;type(datetime)"`
EndTime time.Time `orm:"column(end_time);type(datetime)"`
}
// TableName for Report
func (r *Report) TableName() string {
return "scanner_report"
}
// TableUnique for Report
func (r *Report) TableUnique() [][]string {
return [][]string{
{"digest", "registration_id"},
}
}

View File

@ -0,0 +1,120 @@
// Copyright Project Harbor 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 scanner
import (
"encoding/json"
"net/url"
"strings"
"time"
"github.com/pkg/errors"
)
// Registration represents a named configuration for invoking a scanner via its adapter.
// UUID will be used to track the scanner.Endpoint as unique ID
type Registration struct {
// Basic information
// int64 ID is kept for being aligned with previous DB schema
ID int64 `orm:"pk;auto;column(id)" json:"-"`
UUID string `orm:"unique;column(uuid)" json:"uuid"`
Name string `orm:"unique;column(name);size(128)" json:"name"`
Description string `orm:"column(description);null;size(1024)" json:"description"`
URL string `orm:"column(url);unique;size(512)" json:"url"`
Disabled bool `orm:"column(disabled);default(true)" json:"disabled"`
IsDefault bool `orm:"column(is_default);default(false)" json:"is_default"`
Health bool `orm:"-" json:"health"`
// Authentication settings
// "None","Basic" and "Bearer" can be supported
Auth string `orm:"column(auth);size(16)" json:"auth"`
AccessCredential string `orm:"column(access_cred);null;size(512)" json:"access_credential,omitempty"`
// Http connection settings
SkipCertVerify bool `orm:"column(skip_cert_verify);default(false)" json:"skip_certVerify"`
// Adapter settings
Adapter string `orm:"column(adapter);size(128)" json:"adapter"`
Vendor string `orm:"column(vendor);size(128)" json:"vendor"`
Version string `orm:"column(version);size(32)" json:"version"`
// Timestamps
CreateTime time.Time `orm:"column(create_time);auto_now_add;type(datetime)" json:"create_time"`
UpdateTime time.Time `orm:"column(update_time);auto_now;type(datetime)" json:"update_time"`
}
// TableName for Endpoint
func (r *Registration) TableName() string {
return "scanner_registration"
}
// FromJSON parses json data
func (r *Registration) FromJSON(jsonData string) error {
if len(jsonData) == 0 {
return errors.New("empty json data to parse")
}
return json.Unmarshal([]byte(jsonData), r)
}
// ToJSON marshals endpoint to JSON data
func (r *Registration) ToJSON() (string, error) {
data, err := json.Marshal(r)
if err != nil {
return "", err
}
return string(data), nil
}
// Validate endpoint
func (r *Registration) Validate(checkUUID bool) error {
if checkUUID && len(r.UUID) == 0 {
return errors.New("malformed endpoint")
}
if len(r.Name) == 0 {
return errors.New("missing registration name")
}
err := checkURL(r.URL)
if err != nil {
return errors.Wrap(err, "scanner registration validate")
}
if len(r.Adapter) == 0 ||
len(r.Vendor) == 0 ||
len(r.Version) == 0 {
return errors.Errorf("missing adapter settings in registration %s:%s", r.Name, r.URL)
}
return nil
}
// Check the registration URL with url package
func checkURL(u string) error {
if len(strings.TrimSpace(u)) == 0 {
return errors.New("empty url")
}
uri, err := url.Parse(u)
if err == nil {
if uri.Scheme != "http" && uri.Scheme != "https" {
err = errors.New("invalid scheme")
}
}
return err
}

View File

@ -0,0 +1,87 @@
// Copyright Project Harbor 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 scanner
import (
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/stretchr/testify/suite"
)
// ModelTestSuite tests the utility functions of the model
type ModelTestSuite struct {
suite.Suite
}
// TestModel is the entry of the model test suite
func TestModel(t *testing.T) {
suite.Run(t, new(ModelTestSuite))
}
// TestJSON tests the marshal and unmarshal functions
func (suite *ModelTestSuite) TestJSON() {
r := &Registration{
Name: "forUT",
Description: "sample registration",
URL: "https://sample.scanner.com",
Adapter: "Clair",
Version: "0.1.0",
Vendor: "Harbor",
}
json, err := r.ToJSON()
require.NoError(suite.T(), err)
assert.Condition(suite.T(), func() (success bool) {
success = len(json) > 0
return
})
r2 := &Registration{}
err = r2.FromJSON(json)
require.NoError(suite.T(), err)
assert.Equal(suite.T(), "forUT", r2.Name)
}
// TestValidate tests the validate function
func (suite *ModelTestSuite) TestValidate() {
r := &Registration{}
err := r.Validate(true)
require.Error(suite.T(), err)
r.UUID = "uuid"
err = r.Validate(true)
require.Error(suite.T(), err)
r.Name = "forUT"
err = r.Validate(true)
require.Error(suite.T(), err)
r.URL = "a.b.c"
err = r.Validate(true)
require.Error(suite.T(), err)
r.URL = "http://a.b.c"
err = r.Validate(true)
require.Error(suite.T(), err)
r.Adapter = "Clair"
r.Vendor = "Harbor"
r.Version = "0.1.0"
err = r.Validate(true)
require.NoError(suite.T(), err)
}

View File

@ -0,0 +1,147 @@
// Copyright Project Harbor 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 scanner
import (
"fmt"
"github.com/astaxie/beego/orm"
"github.com/goharbor/harbor/src/common/dao"
"github.com/goharbor/harbor/src/pkg/q"
"github.com/pkg/errors"
)
func init() {
orm.RegisterModel(new(Registration))
}
// AddRegistration adds a new registration
func AddRegistration(r *Registration) (int64, error) {
o := dao.GetOrmer()
return o.Insert(r)
}
// GetRegistration gets the specified registration
func GetRegistration(UUID string) (*Registration, error) {
e := &Registration{}
o := dao.GetOrmer()
qs := o.QueryTable(new(Registration))
if err := qs.Filter("uuid", UUID).One(e); err != nil {
if err == orm.ErrNoRows {
// Not existing case
return nil, nil
}
return nil, err
}
return e, nil
}
// UpdateRegistration update the specified registration
func UpdateRegistration(r *Registration, cols ...string) error {
o := dao.GetOrmer()
count, err := o.Update(r, cols...)
if err != nil {
return err
}
if count == 0 {
return errors.Errorf("no item with UUID %s is updated", r.UUID)
}
return nil
}
// DeleteRegistration deletes the registration with the specified UUID
func DeleteRegistration(UUID string) error {
o := dao.GetOrmer()
qt := o.QueryTable(new(Registration))
// delete with query way
count, err := qt.Filter("uuid", UUID).Delete()
if err != nil {
return err
}
if count == 0 {
return errors.Errorf("no item with UUID %s is deleted", UUID)
}
return nil
}
// ListRegistrations lists all the existing registrations
func ListRegistrations(query *q.Query) ([]*Registration, error) {
o := dao.GetOrmer()
qt := o.QueryTable(new(Registration))
if query != nil {
if len(query.Keywords) > 0 {
for k, v := range query.Keywords {
qt = qt.Filter(fmt.Sprintf("%s__icontains", k), v)
}
}
if query.PageNumber > 0 && query.PageSize > 0 {
qt = qt.Limit(query.PageSize, (query.PageNumber-1)*query.PageSize)
}
}
l := make([]*Registration, 0)
_, err := qt.All(&l)
return l, err
}
// SetDefaultRegistration sets the specified registration as default one
func SetDefaultRegistration(UUID string) error {
o := dao.GetOrmer()
qt := o.QueryTable(new(Registration))
_, err := qt.Filter("is_default", true).Update(orm.Params{
"is_default": false,
})
if err != nil {
return err
}
qt2 := o.QueryTable(new(Registration))
_, err = qt2.Filter("uuid", UUID).Update(orm.Params{
"is_default": true,
})
return err
}
// GetDefaultRegistration gets the default registration
func GetDefaultRegistration() (*Registration, error) {
o := dao.GetOrmer()
qt := o.QueryTable(new(Registration))
e := &Registration{}
if err := qt.Filter("is_default", true).One(e); err != nil {
if err == orm.ErrNoRows {
return nil, nil
}
return nil, err
}
return e, nil
}

View File

@ -0,0 +1,144 @@
// Copyright Project Harbor 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 scanner
import (
"testing"
"github.com/goharbor/harbor/src/common/dao"
"github.com/goharbor/harbor/src/pkg/q"
"github.com/google/uuid"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/stretchr/testify/suite"
)
// RegistrationDAOTestSuite is test suite of testing registration DAO
type RegistrationDAOTestSuite struct {
suite.Suite
registrationID string
}
// TestRegistrationDAO is entry of test cases
func TestRegistrationDAO(t *testing.T) {
suite.Run(t, new(RegistrationDAOTestSuite))
}
// SetupSuite prepare testing env for the suite
func (suite *RegistrationDAOTestSuite) SetupSuite() {
dao.PrepareTestForPostgresSQL()
}
// SetupTest prepare stuff for test cases
func (suite *RegistrationDAOTestSuite) SetupTest() {
suite.registrationID = uuid.New().String()
r := &Registration{
UUID: suite.registrationID,
Name: "forUT",
Description: "sample registration",
URL: "https://sample.scanner.com",
Adapter: "Clair",
Version: "0.1.0",
Vendor: "Harbor",
}
_, err := AddRegistration(r)
require.NoError(suite.T(), err, "add new registration")
}
// TearDownTest clears all the stuff of test cases
func (suite *RegistrationDAOTestSuite) TearDownTest() {
err := DeleteRegistration(suite.registrationID)
require.NoError(suite.T(), err, "clear registration")
}
// TestGet tests get registration
func (suite *RegistrationDAOTestSuite) TestGet() {
// Found
r, err := GetRegistration(suite.registrationID)
require.NoError(suite.T(), err)
require.NotNil(suite.T(), r)
assert.Equal(suite.T(), r.Name, "forUT")
// Not found
re, err := GetRegistration("not_found")
require.NoError(suite.T(), err)
require.Nil(suite.T(), re)
}
// TestUpdate tests update registration
func (suite *RegistrationDAOTestSuite) TestUpdate() {
r, err := GetRegistration(suite.registrationID)
require.NoError(suite.T(), err)
require.NotNil(suite.T(), r)
r.Disabled = true
r.IsDefault = true
r.URL = "http://updated.registration.com"
err = UpdateRegistration(r)
require.NoError(suite.T(), err, "update registration")
r, err = GetRegistration(suite.registrationID)
require.NoError(suite.T(), err)
require.NotNil(suite.T(), r)
assert.Equal(suite.T(), true, r.Disabled)
assert.Equal(suite.T(), true, r.IsDefault)
assert.Equal(suite.T(), "http://updated.registration.com", r.URL)
}
// TestList tests list registrations
func (suite *RegistrationDAOTestSuite) TestList() {
// no query
l, err := ListRegistrations(nil)
require.NoError(suite.T(), err)
require.Equal(suite.T(), 1, len(l))
// with query and found items
keywords := make(map[string]string)
keywords["adapter"] = "Clair"
l, err = ListRegistrations(&q.Query{
PageSize: 5,
PageNumber: 1,
Keywords: keywords,
})
require.NoError(suite.T(), err)
require.Equal(suite.T(), 1, len(l))
// With query and not found items
keywords["adapter"] = "Micro scanner"
l, err = ListRegistrations(&q.Query{
Keywords: keywords,
})
require.NoError(suite.T(), err)
require.Equal(suite.T(), 0, len(l))
}
// TestDefault tests set/get default
func (suite *RegistrationDAOTestSuite) TestDefault() {
dr, err := GetDefaultRegistration()
require.NoError(suite.T(), err, "not found")
require.Nil(suite.T(), dr)
err = SetDefaultRegistration(suite.registrationID)
require.NoError(suite.T(), err)
dr, err = GetDefaultRegistration()
require.NoError(suite.T(), err)
require.NotNil(suite.T(), dr)
}

View File

@ -0,0 +1,131 @@
// Copyright Project Harbor 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 scanner
import (
"github.com/goharbor/harbor/src/pkg/q"
"github.com/goharbor/harbor/src/pkg/scan/scanner/dao/scanner"
"github.com/google/uuid"
"github.com/pkg/errors"
)
// Manager defines the related scanner API endpoints
type Manager interface {
// List returns a list of currently configured scanner registrations.
// Query parameters are optional
List(query *q.Query) ([]*scanner.Registration, error)
// Create creates a new scanner registration with the given data.
// Returns the scanner registration identifier.
Create(registration *scanner.Registration) (string, error)
// Get returns the details of the specified scanner registration.
Get(registrationUUID string) (*scanner.Registration, error)
// Update updates the specified scanner registration.
Update(registration *scanner.Registration) error
// Delete deletes the specified scanner registration.
Delete(registrationUUID string) error
// SetAsDefault marks the specified scanner registration as default.
// The implementation is supposed to unset any registration previously set as default.
SetAsDefault(registrationUUID string) error
// GetDefault returns the default scanner registration or `nil` if there are no registrations configured.
GetDefault() (*scanner.Registration, error)
}
// basicManager is the default implementation of Manager
type basicManager struct{}
// New a basic manager
func New() Manager {
return &basicManager{}
}
// Create ...
func (bm *basicManager) Create(registration *scanner.Registration) (string, error) {
if registration == nil {
return "", errors.New("nil endpoint to create")
}
// Inject new UUID
uid, err := uuid.NewUUID()
if err != nil {
return "", errors.Wrap(err, "new UUID: create registration")
}
registration.UUID = uid.String()
if err := registration.Validate(true); err != nil {
return "", errors.Wrap(err, "create registration")
}
if _, err := scanner.AddRegistration(registration); err != nil {
return "", errors.Wrap(err, "dao: create registration")
}
return uid.String(), nil
}
// Get ...
func (bm *basicManager) Get(registrationUUID string) (*scanner.Registration, error) {
if len(registrationUUID) == 0 {
return nil, errors.New("empty uuid of registration")
}
return scanner.GetRegistration(registrationUUID)
}
// Update ...
func (bm *basicManager) Update(registration *scanner.Registration) error {
if registration == nil {
return errors.New("nil endpoint to update")
}
if err := registration.Validate(true); err != nil {
return errors.Wrap(err, "update endpoint")
}
return scanner.UpdateRegistration(registration)
}
// Delete ...
func (bm *basicManager) Delete(registrationUUID string) error {
if len(registrationUUID) == 0 {
return errors.New("empty UUID to delete")
}
return scanner.DeleteRegistration(registrationUUID)
}
// List ...
func (bm *basicManager) List(query *q.Query) ([]*scanner.Registration, error) {
return scanner.ListRegistrations(query)
}
// SetAsDefault ...
func (bm *basicManager) SetAsDefault(registrationUUID string) error {
if len(registrationUUID) == 0 {
return errors.New("empty UUID to set default")
}
return scanner.SetDefaultRegistration(registrationUUID)
}
// GetDefault ...
func (bm *basicManager) GetDefault() (*scanner.Registration, error) {
return scanner.GetDefaultRegistration()
}

View File

@ -0,0 +1,115 @@
// Copyright Project Harbor 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 scanner
import (
"testing"
"github.com/goharbor/harbor/src/common/dao"
"github.com/goharbor/harbor/src/pkg/q"
"github.com/goharbor/harbor/src/pkg/scan/scanner/dao/scanner"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/stretchr/testify/suite"
)
// BasicManagerTestSuite tests the basic manager
type BasicManagerTestSuite struct {
suite.Suite
mgr Manager
sampleUUID string
}
// TestBasicManager is the entry of BasicManagerTestSuite
func TestBasicManager(t *testing.T) {
suite.Run(t, new(BasicManagerTestSuite))
}
// SetupSuite prepares env for test suite
func (suite *BasicManagerTestSuite) SetupSuite() {
dao.PrepareTestForPostgresSQL()
suite.mgr = New()
r := &scanner.Registration{
Name: "forUT",
Description: "sample registration",
URL: "https://sample.scanner.com",
Adapter: "Clair",
Version: "0.1.0",
Vendor: "Harbor",
}
uid, err := suite.mgr.Create(r)
require.NoError(suite.T(), err)
suite.sampleUUID = uid
}
// TearDownSuite clears env for test suite
func (suite *BasicManagerTestSuite) TearDownSuite() {
err := suite.mgr.Delete(suite.sampleUUID)
require.NoError(suite.T(), err, "delete registration")
}
// TestList tests list registrations
func (suite *BasicManagerTestSuite) TestList() {
m := make(map[string]string, 1)
m["name"] = "forUT"
l, err := suite.mgr.List(&q.Query{
PageNumber: 1,
PageSize: 10,
Keywords: m,
})
require.NoError(suite.T(), err)
require.Equal(suite.T(), 1, len(l))
}
// TestGet tests get registration
func (suite *BasicManagerTestSuite) TestGet() {
r, err := suite.mgr.Get(suite.sampleUUID)
require.NoError(suite.T(), err)
require.NotNil(suite.T(), r)
assert.Equal(suite.T(), "forUT", r.Name)
}
// TestUpdate tests update registration
func (suite *BasicManagerTestSuite) TestUpdate() {
r, err := suite.mgr.Get(suite.sampleUUID)
require.NoError(suite.T(), err)
require.NotNil(suite.T(), r)
r.URL = "https://updated.com"
err = suite.mgr.Update(r)
require.NoError(suite.T(), err)
r, err = suite.mgr.Get(suite.sampleUUID)
require.NoError(suite.T(), err)
require.NotNil(suite.T(), r)
assert.Equal(suite.T(), "https://updated.com", r.URL)
}
// TestDefault tests get/set default registration
func (suite *BasicManagerTestSuite) TestDefault() {
err := suite.mgr.SetAsDefault(suite.sampleUUID)
require.NoError(suite.T(), err)
dr, err := suite.mgr.GetDefault()
require.NoError(suite.T(), err)
require.NotNil(suite.T(), dr)
assert.Equal(suite.T(), true, dr.IsDefault)
}

View File

@ -0,0 +1,48 @@
// Copyright Project Harbor 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 scan
import "github.com/goharbor/harbor/src/pkg/scan/scanner/dao/scan"
// Options object for the scan action
type Options struct{}
// Option for scan action
type Option interface {
// Apply option to the passing in options
Apply(options *Options) error
}
// Controller defines operations for scan controlling
type Controller interface {
// Scan the given artifact
//
// Arguments:
// artifact *res.Artifact : artifact to be scanned
//
// Returns:
// error : non nil error if any errors occurred
Scan(artifact *Artifact, options ...Option) error
// GetReport gets the reports for the given artifact identified by the digest
//
// Arguments:
// artifact *res.Artifact : the scanned artifact
//
// Returns:
// []*scan.Report : scan results by different scanner vendors
// error : non nil error if any errors occurred
GetReport(artifact *Artifact) ([]*scan.Report, error)
}

View File

@ -0,0 +1,46 @@
// Copyright Project Harbor 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 scan
// Artifact represents an artifact stored in Registry.
type Artifact struct {
// The full name of a Harbor repository containing the artifact, including the namespace.
// For example, `library/oracle/nosql`.
Repository string
// The artifact's digest, consisting of an algorithm and hex portion.
// For example, `sha256:6c3c624b58dbbcd3c0dd82b4c53f04194d1247c6eebdaab7c610cf7d66709b3b`,
// represents sha256 based digest.
Digest string
// The mime type of the scanned artifact
MimeType string
}
// Registry represents Registry connection settings.
type Registry struct {
// A base URL of the Docker Registry v2 API exposed by Harbor.
URL string
// An optional value of the HTTP Authorization header sent with each request to the Docker Registry v2 API.
// For example, `Bearer: JWTTOKENGOESHERE`.
Authorization string
}
// Request represents a structure that is sent to a Scanner Adapter to initiate artifact scanning.
// Conducts all the details required to pull the artifact from a Harbor registry.
type Request struct {
// Connection settings for the Docker Registry v2 API exposed by Harbor.
Registry *Registry
// Artifact to be scanned.
Artifact *Artifact
}

9
src/vendor/github.com/google/uuid/.travis.yml generated vendored Normal file
View File

@ -0,0 +1,9 @@
language: go
go:
- 1.4.3
- 1.5.3
- tip
script:
- go test -v ./...

10
src/vendor/github.com/google/uuid/CONTRIBUTING.md generated vendored Normal file
View File

@ -0,0 +1,10 @@
# How to contribute
We definitely welcome patches and contribution to this project!
### Legal requirements
In order to protect both you and ourselves, you will need to sign the
[Contributor License Agreement](https://cla.developers.google.com/clas).
You may have already signed it for other Google projects.

9
src/vendor/github.com/google/uuid/CONTRIBUTORS generated vendored Normal file
View File

@ -0,0 +1,9 @@
Paul Borman <borman@google.com>
bmatsuo
shawnps
theory
jboverfelt
dsymonds
cd1
wallclockbuilder
dansouza

27
src/vendor/github.com/google/uuid/LICENSE generated vendored Normal file
View File

@ -0,0 +1,27 @@
Copyright (c) 2009,2014 Google Inc. All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above
copyright notice, this list of conditions and the following disclaimer
in the documentation and/or other materials provided with the
distribution.
* Neither the name of Google Inc. nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

19
src/vendor/github.com/google/uuid/README.md generated vendored Normal file
View File

@ -0,0 +1,19 @@
# uuid ![build status](https://travis-ci.org/google/uuid.svg?branch=master)
The uuid package generates and inspects UUIDs based on
[RFC 4122](http://tools.ietf.org/html/rfc4122)
and DCE 1.1: Authentication and Security Services.
This package is based on the github.com/pborman/uuid package (previously named
code.google.com/p/go-uuid). It differs from these earlier packages in that
a UUID is a 16 byte array rather than a byte slice. One loss due to this
change is the ability to represent an invalid UUID (vs a NIL UUID).
###### Install
`go get github.com/google/uuid`
###### Documentation
[![GoDoc](https://godoc.org/github.com/google/uuid?status.svg)](http://godoc.org/github.com/google/uuid)
Full `go doc` style documentation for the package can be viewed online without
installing this package by using the GoDoc site here:
http://godoc.org/github.com/google/uuid

80
src/vendor/github.com/google/uuid/dce.go generated vendored Normal file
View File

@ -0,0 +1,80 @@
// Copyright 2016 Google Inc. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package uuid
import (
"encoding/binary"
"fmt"
"os"
)
// A Domain represents a Version 2 domain
type Domain byte
// Domain constants for DCE Security (Version 2) UUIDs.
const (
Person = Domain(0)
Group = Domain(1)
Org = Domain(2)
)
// NewDCESecurity returns a DCE Security (Version 2) UUID.
//
// The domain should be one of Person, Group or Org.
// On a POSIX system the id should be the users UID for the Person
// domain and the users GID for the Group. The meaning of id for
// the domain Org or on non-POSIX systems is site defined.
//
// For a given domain/id pair the same token may be returned for up to
// 7 minutes and 10 seconds.
func NewDCESecurity(domain Domain, id uint32) (UUID, error) {
uuid, err := NewUUID()
if err == nil {
uuid[6] = (uuid[6] & 0x0f) | 0x20 // Version 2
uuid[9] = byte(domain)
binary.BigEndian.PutUint32(uuid[0:], id)
}
return uuid, err
}
// NewDCEPerson returns a DCE Security (Version 2) UUID in the person
// domain with the id returned by os.Getuid.
//
// NewDCESecurity(Person, uint32(os.Getuid()))
func NewDCEPerson() (UUID, error) {
return NewDCESecurity(Person, uint32(os.Getuid()))
}
// NewDCEGroup returns a DCE Security (Version 2) UUID in the group
// domain with the id returned by os.Getgid.
//
// NewDCESecurity(Group, uint32(os.Getgid()))
func NewDCEGroup() (UUID, error) {
return NewDCESecurity(Group, uint32(os.Getgid()))
}
// Domain returns the domain for a Version 2 UUID. Domains are only defined
// for Version 2 UUIDs.
func (uuid UUID) Domain() Domain {
return Domain(uuid[9])
}
// ID returns the id for a Version 2 UUID. IDs are only defined for Version 2
// UUIDs.
func (uuid UUID) ID() uint32 {
return binary.BigEndian.Uint32(uuid[0:4])
}
func (d Domain) String() string {
switch d {
case Person:
return "Person"
case Group:
return "Group"
case Org:
return "Org"
}
return fmt.Sprintf("Domain%d", int(d))
}

12
src/vendor/github.com/google/uuid/doc.go generated vendored Normal file
View File

@ -0,0 +1,12 @@
// Copyright 2016 Google Inc. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Package uuid generates and inspects UUIDs.
//
// UUIDs are based on RFC 4122 and DCE 1.1: Authentication and Security
// Services.
//
// A UUID is a 16 byte (128 bit) array. UUIDs may be used as keys to
// maps or compared directly.
package uuid

1
src/vendor/github.com/google/uuid/go.mod generated vendored Normal file
View File

@ -0,0 +1 @@
module github.com/google/uuid

53
src/vendor/github.com/google/uuid/hash.go generated vendored Normal file
View File

@ -0,0 +1,53 @@
// Copyright 2016 Google Inc. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package uuid
import (
"crypto/md5"
"crypto/sha1"
"hash"
)
// Well known namespace IDs and UUIDs
var (
NameSpaceDNS = Must(Parse("6ba7b810-9dad-11d1-80b4-00c04fd430c8"))
NameSpaceURL = Must(Parse("6ba7b811-9dad-11d1-80b4-00c04fd430c8"))
NameSpaceOID = Must(Parse("6ba7b812-9dad-11d1-80b4-00c04fd430c8"))
NameSpaceX500 = Must(Parse("6ba7b814-9dad-11d1-80b4-00c04fd430c8"))
Nil UUID // empty UUID, all zeros
)
// NewHash returns a new UUID derived from the hash of space concatenated with
// data generated by h. The hash should be at least 16 byte in length. The
// first 16 bytes of the hash are used to form the UUID. The version of the
// UUID will be the lower 4 bits of version. NewHash is used to implement
// NewMD5 and NewSHA1.
func NewHash(h hash.Hash, space UUID, data []byte, version int) UUID {
h.Reset()
h.Write(space[:])
h.Write(data)
s := h.Sum(nil)
var uuid UUID
copy(uuid[:], s)
uuid[6] = (uuid[6] & 0x0f) | uint8((version&0xf)<<4)
uuid[8] = (uuid[8] & 0x3f) | 0x80 // RFC 4122 variant
return uuid
}
// NewMD5 returns a new MD5 (Version 3) UUID based on the
// supplied name space and data. It is the same as calling:
//
// NewHash(md5.New(), space, data, 3)
func NewMD5(space UUID, data []byte) UUID {
return NewHash(md5.New(), space, data, 3)
}
// NewSHA1 returns a new SHA1 (Version 5) UUID based on the
// supplied name space and data. It is the same as calling:
//
// NewHash(sha1.New(), space, data, 5)
func NewSHA1(space UUID, data []byte) UUID {
return NewHash(sha1.New(), space, data, 5)
}

37
src/vendor/github.com/google/uuid/marshal.go generated vendored Normal file
View File

@ -0,0 +1,37 @@
// Copyright 2016 Google Inc. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package uuid
import "fmt"
// MarshalText implements encoding.TextMarshaler.
func (uuid UUID) MarshalText() ([]byte, error) {
var js [36]byte
encodeHex(js[:], uuid)
return js[:], nil
}
// UnmarshalText implements encoding.TextUnmarshaler.
func (uuid *UUID) UnmarshalText(data []byte) error {
id, err := ParseBytes(data)
if err == nil {
*uuid = id
}
return err
}
// MarshalBinary implements encoding.BinaryMarshaler.
func (uuid UUID) MarshalBinary() ([]byte, error) {
return uuid[:], nil
}
// UnmarshalBinary implements encoding.BinaryUnmarshaler.
func (uuid *UUID) UnmarshalBinary(data []byte) error {
if len(data) != 16 {
return fmt.Errorf("invalid UUID (got %d bytes)", len(data))
}
copy(uuid[:], data)
return nil
}

90
src/vendor/github.com/google/uuid/node.go generated vendored Normal file
View File

@ -0,0 +1,90 @@
// Copyright 2016 Google Inc. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package uuid
import (
"sync"
)
var (
nodeMu sync.Mutex
ifname string // name of interface being used
nodeID [6]byte // hardware for version 1 UUIDs
zeroID [6]byte // nodeID with only 0's
)
// NodeInterface returns the name of the interface from which the NodeID was
// derived. The interface "user" is returned if the NodeID was set by
// SetNodeID.
func NodeInterface() string {
defer nodeMu.Unlock()
nodeMu.Lock()
return ifname
}
// SetNodeInterface selects the hardware address to be used for Version 1 UUIDs.
// If name is "" then the first usable interface found will be used or a random
// Node ID will be generated. If a named interface cannot be found then false
// is returned.
//
// SetNodeInterface never fails when name is "".
func SetNodeInterface(name string) bool {
defer nodeMu.Unlock()
nodeMu.Lock()
return setNodeInterface(name)
}
func setNodeInterface(name string) bool {
iname, addr := getHardwareInterface(name) // null implementation for js
if iname != "" && addr != nil {
ifname = iname
copy(nodeID[:], addr)
return true
}
// We found no interfaces with a valid hardware address. If name
// does not specify a specific interface generate a random Node ID
// (section 4.1.6)
if name == "" {
ifname = "random"
randomBits(nodeID[:])
return true
}
return false
}
// NodeID returns a slice of a copy of the current Node ID, setting the Node ID
// if not already set.
func NodeID() []byte {
defer nodeMu.Unlock()
nodeMu.Lock()
if nodeID == zeroID {
setNodeInterface("")
}
nid := nodeID
return nid[:]
}
// SetNodeID sets the Node ID to be used for Version 1 UUIDs. The first 6 bytes
// of id are used. If id is less than 6 bytes then false is returned and the
// Node ID is not set.
func SetNodeID(id []byte) bool {
if len(id) < 6 {
return false
}
defer nodeMu.Unlock()
nodeMu.Lock()
copy(nodeID[:], id)
ifname = "user"
return true
}
// NodeID returns the 6 byte node id encoded in uuid. It returns nil if uuid is
// not valid. The NodeID is only well defined for version 1 and 2 UUIDs.
func (uuid UUID) NodeID() []byte {
var node [6]byte
copy(node[:], uuid[10:])
return node[:]
}

12
src/vendor/github.com/google/uuid/node_js.go generated vendored Normal file
View File

@ -0,0 +1,12 @@
// Copyright 2017 Google Inc. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// +build js
package uuid
// getHardwareInterface returns nil values for the JS version of the code.
// This remvoves the "net" dependency, because it is not used in the browser.
// Using the "net" library inflates the size of the transpiled JS code by 673k bytes.
func getHardwareInterface(name string) (string, []byte) { return "", nil }

33
src/vendor/github.com/google/uuid/node_net.go generated vendored Normal file
View File

@ -0,0 +1,33 @@
// Copyright 2017 Google Inc. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// +build !js
package uuid
import "net"
var interfaces []net.Interface // cached list of interfaces
// getHardwareInterface returns the name and hardware address of interface name.
// If name is "" then the name and hardware address of one of the system's
// interfaces is returned. If no interfaces are found (name does not exist or
// there are no interfaces) then "", nil is returned.
//
// Only addresses of at least 6 bytes are returned.
func getHardwareInterface(name string) (string, []byte) {
if interfaces == nil {
var err error
interfaces, err = net.Interfaces()
if err != nil {
return "", nil
}
}
for _, ifs := range interfaces {
if len(ifs.HardwareAddr) >= 6 && (name == "" || name == ifs.Name) {
return ifs.Name, ifs.HardwareAddr
}
}
return "", nil
}

59
src/vendor/github.com/google/uuid/sql.go generated vendored Normal file
View File

@ -0,0 +1,59 @@
// Copyright 2016 Google Inc. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package uuid
import (
"database/sql/driver"
"fmt"
)
// Scan implements sql.Scanner so UUIDs can be read from databases transparently
// Currently, database types that map to string and []byte are supported. Please
// consult database-specific driver documentation for matching types.
func (uuid *UUID) Scan(src interface{}) error {
switch src := src.(type) {
case nil:
return nil
case string:
// if an empty UUID comes from a table, we return a null UUID
if src == "" {
return nil
}
// see Parse for required string format
u, err := Parse(src)
if err != nil {
return fmt.Errorf("Scan: %v", err)
}
*uuid = u
case []byte:
// if an empty UUID comes from a table, we return a null UUID
if len(src) == 0 {
return nil
}
// assumes a simple slice of bytes if 16 bytes
// otherwise attempts to parse
if len(src) != 16 {
return uuid.Scan(string(src))
}
copy((*uuid)[:], src)
default:
return fmt.Errorf("Scan: unable to scan type %T into UUID", src)
}
return nil
}
// Value implements sql.Valuer so that UUIDs can be written to databases
// transparently. Currently, UUIDs map to strings. Please consult
// database-specific driver documentation for matching types.
func (uuid UUID) Value() (driver.Value, error) {
return uuid.String(), nil
}

123
src/vendor/github.com/google/uuid/time.go generated vendored Normal file
View File

@ -0,0 +1,123 @@
// Copyright 2016 Google Inc. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package uuid
import (
"encoding/binary"
"sync"
"time"
)
// A Time represents a time as the number of 100's of nanoseconds since 15 Oct
// 1582.
type Time int64
const (
lillian = 2299160 // Julian day of 15 Oct 1582
unix = 2440587 // Julian day of 1 Jan 1970
epoch = unix - lillian // Days between epochs
g1582 = epoch * 86400 // seconds between epochs
g1582ns100 = g1582 * 10000000 // 100s of a nanoseconds between epochs
)
var (
timeMu sync.Mutex
lasttime uint64 // last time we returned
clockSeq uint16 // clock sequence for this run
timeNow = time.Now // for testing
)
// UnixTime converts t the number of seconds and nanoseconds using the Unix
// epoch of 1 Jan 1970.
func (t Time) UnixTime() (sec, nsec int64) {
sec = int64(t - g1582ns100)
nsec = (sec % 10000000) * 100
sec /= 10000000
return sec, nsec
}
// GetTime returns the current Time (100s of nanoseconds since 15 Oct 1582) and
// clock sequence as well as adjusting the clock sequence as needed. An error
// is returned if the current time cannot be determined.
func GetTime() (Time, uint16, error) {
defer timeMu.Unlock()
timeMu.Lock()
return getTime()
}
func getTime() (Time, uint16, error) {
t := timeNow()
// If we don't have a clock sequence already, set one.
if clockSeq == 0 {
setClockSequence(-1)
}
now := uint64(t.UnixNano()/100) + g1582ns100
// If time has gone backwards with this clock sequence then we
// increment the clock sequence
if now <= lasttime {
clockSeq = ((clockSeq + 1) & 0x3fff) | 0x8000
}
lasttime = now
return Time(now), clockSeq, nil
}
// ClockSequence returns the current clock sequence, generating one if not
// already set. The clock sequence is only used for Version 1 UUIDs.
//
// The uuid package does not use global static storage for the clock sequence or
// the last time a UUID was generated. Unless SetClockSequence is used, a new
// random clock sequence is generated the first time a clock sequence is
// requested by ClockSequence, GetTime, or NewUUID. (section 4.2.1.1)
func ClockSequence() int {
defer timeMu.Unlock()
timeMu.Lock()
return clockSequence()
}
func clockSequence() int {
if clockSeq == 0 {
setClockSequence(-1)
}
return int(clockSeq & 0x3fff)
}
// SetClockSequence sets the clock sequence to the lower 14 bits of seq. Setting to
// -1 causes a new sequence to be generated.
func SetClockSequence(seq int) {
defer timeMu.Unlock()
timeMu.Lock()
setClockSequence(seq)
}
func setClockSequence(seq int) {
if seq == -1 {
var b [2]byte
randomBits(b[:]) // clock sequence
seq = int(b[0])<<8 | int(b[1])
}
oldSeq := clockSeq
clockSeq = uint16(seq&0x3fff) | 0x8000 // Set our variant
if oldSeq != clockSeq {
lasttime = 0
}
}
// Time returns the time in 100s of nanoseconds since 15 Oct 1582 encoded in
// uuid. The time is only defined for version 1 and 2 UUIDs.
func (uuid UUID) Time() Time {
time := int64(binary.BigEndian.Uint32(uuid[0:4]))
time |= int64(binary.BigEndian.Uint16(uuid[4:6])) << 32
time |= int64(binary.BigEndian.Uint16(uuid[6:8])&0xfff) << 48
return Time(time)
}
// ClockSequence returns the clock sequence encoded in uuid.
// The clock sequence is only well defined for version 1 and 2 UUIDs.
func (uuid UUID) ClockSequence() int {
return int(binary.BigEndian.Uint16(uuid[8:10])) & 0x3fff
}

43
src/vendor/github.com/google/uuid/util.go generated vendored Normal file
View File

@ -0,0 +1,43 @@
// Copyright 2016 Google Inc. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package uuid
import (
"io"
)
// randomBits completely fills slice b with random data.
func randomBits(b []byte) {
if _, err := io.ReadFull(rander, b); err != nil {
panic(err.Error()) // rand should never fail
}
}
// xvalues returns the value of a byte as a hexadecimal digit or 255.
var xvalues = [256]byte{
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 255, 255, 255, 255, 255, 255,
255, 10, 11, 12, 13, 14, 15, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 10, 11, 12, 13, 14, 15, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
}
// xtob converts hex characters x1 and x2 into a byte.
func xtob(x1, x2 byte) (byte, bool) {
b1 := xvalues[x1]
b2 := xvalues[x2]
return (b1 << 4) | b2, b1 != 255 && b2 != 255
}

245
src/vendor/github.com/google/uuid/uuid.go generated vendored Normal file
View File

@ -0,0 +1,245 @@
// Copyright 2018 Google Inc. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package uuid
import (
"bytes"
"crypto/rand"
"encoding/hex"
"errors"
"fmt"
"io"
"strings"
)
// A UUID is a 128 bit (16 byte) Universal Unique IDentifier as defined in RFC
// 4122.
type UUID [16]byte
// A Version represents a UUID's version.
type Version byte
// A Variant represents a UUID's variant.
type Variant byte
// Constants returned by Variant.
const (
Invalid = Variant(iota) // Invalid UUID
RFC4122 // The variant specified in RFC4122
Reserved // Reserved, NCS backward compatibility.
Microsoft // Reserved, Microsoft Corporation backward compatibility.
Future // Reserved for future definition.
)
var rander = rand.Reader // random function
// Parse decodes s into a UUID or returns an error. Both the standard UUID
// forms of xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx and
// urn:uuid:xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx are decoded as well as the
// Microsoft encoding {xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx} and the raw hex
// encoding: xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx.
func Parse(s string) (UUID, error) {
var uuid UUID
switch len(s) {
// xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
case 36:
// urn:uuid:xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
case 36 + 9:
if strings.ToLower(s[:9]) != "urn:uuid:" {
return uuid, fmt.Errorf("invalid urn prefix: %q", s[:9])
}
s = s[9:]
// {xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx}
case 36 + 2:
s = s[1:]
// xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
case 32:
var ok bool
for i := range uuid {
uuid[i], ok = xtob(s[i*2], s[i*2+1])
if !ok {
return uuid, errors.New("invalid UUID format")
}
}
return uuid, nil
default:
return uuid, fmt.Errorf("invalid UUID length: %d", len(s))
}
// s is now at least 36 bytes long
// it must be of the form xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
if s[8] != '-' || s[13] != '-' || s[18] != '-' || s[23] != '-' {
return uuid, errors.New("invalid UUID format")
}
for i, x := range [16]int{
0, 2, 4, 6,
9, 11,
14, 16,
19, 21,
24, 26, 28, 30, 32, 34} {
v, ok := xtob(s[x], s[x+1])
if !ok {
return uuid, errors.New("invalid UUID format")
}
uuid[i] = v
}
return uuid, nil
}
// ParseBytes is like Parse, except it parses a byte slice instead of a string.
func ParseBytes(b []byte) (UUID, error) {
var uuid UUID
switch len(b) {
case 36: // xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
case 36 + 9: // urn:uuid:xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
if !bytes.Equal(bytes.ToLower(b[:9]), []byte("urn:uuid:")) {
return uuid, fmt.Errorf("invalid urn prefix: %q", b[:9])
}
b = b[9:]
case 36 + 2: // {xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx}
b = b[1:]
case 32: // xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
var ok bool
for i := 0; i < 32; i += 2 {
uuid[i/2], ok = xtob(b[i], b[i+1])
if !ok {
return uuid, errors.New("invalid UUID format")
}
}
return uuid, nil
default:
return uuid, fmt.Errorf("invalid UUID length: %d", len(b))
}
// s is now at least 36 bytes long
// it must be of the form xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
if b[8] != '-' || b[13] != '-' || b[18] != '-' || b[23] != '-' {
return uuid, errors.New("invalid UUID format")
}
for i, x := range [16]int{
0, 2, 4, 6,
9, 11,
14, 16,
19, 21,
24, 26, 28, 30, 32, 34} {
v, ok := xtob(b[x], b[x+1])
if !ok {
return uuid, errors.New("invalid UUID format")
}
uuid[i] = v
}
return uuid, nil
}
// MustParse is like Parse but panics if the string cannot be parsed.
// It simplifies safe initialization of global variables holding compiled UUIDs.
func MustParse(s string) UUID {
uuid, err := Parse(s)
if err != nil {
panic(`uuid: Parse(` + s + `): ` + err.Error())
}
return uuid
}
// FromBytes creates a new UUID from a byte slice. Returns an error if the slice
// does not have a length of 16. The bytes are copied from the slice.
func FromBytes(b []byte) (uuid UUID, err error) {
err = uuid.UnmarshalBinary(b)
return uuid, err
}
// Must returns uuid if err is nil and panics otherwise.
func Must(uuid UUID, err error) UUID {
if err != nil {
panic(err)
}
return uuid
}
// String returns the string form of uuid, xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
// , or "" if uuid is invalid.
func (uuid UUID) String() string {
var buf [36]byte
encodeHex(buf[:], uuid)
return string(buf[:])
}
// URN returns the RFC 2141 URN form of uuid,
// urn:uuid:xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx, or "" if uuid is invalid.
func (uuid UUID) URN() string {
var buf [36 + 9]byte
copy(buf[:], "urn:uuid:")
encodeHex(buf[9:], uuid)
return string(buf[:])
}
func encodeHex(dst []byte, uuid UUID) {
hex.Encode(dst, uuid[:4])
dst[8] = '-'
hex.Encode(dst[9:13], uuid[4:6])
dst[13] = '-'
hex.Encode(dst[14:18], uuid[6:8])
dst[18] = '-'
hex.Encode(dst[19:23], uuid[8:10])
dst[23] = '-'
hex.Encode(dst[24:], uuid[10:])
}
// Variant returns the variant encoded in uuid.
func (uuid UUID) Variant() Variant {
switch {
case (uuid[8] & 0xc0) == 0x80:
return RFC4122
case (uuid[8] & 0xe0) == 0xc0:
return Microsoft
case (uuid[8] & 0xe0) == 0xe0:
return Future
default:
return Reserved
}
}
// Version returns the version of uuid.
func (uuid UUID) Version() Version {
return Version(uuid[6] >> 4)
}
func (v Version) String() string {
if v > 15 {
return fmt.Sprintf("BAD_VERSION_%d", v)
}
return fmt.Sprintf("VERSION_%d", v)
}
func (v Variant) String() string {
switch v {
case RFC4122:
return "RFC4122"
case Reserved:
return "Reserved"
case Microsoft:
return "Microsoft"
case Future:
return "Future"
case Invalid:
return "Invalid"
}
return fmt.Sprintf("BadVariant%d", int(v))
}
// SetRand sets the random number generator to r, which implements io.Reader.
// If r.Read returns an error when the package requests random data then
// a panic will be issued.
//
// Calling SetRand with nil sets the random number generator to the default
// generator.
func SetRand(r io.Reader) {
if r == nil {
rander = rand.Reader
return
}
rander = r
}

44
src/vendor/github.com/google/uuid/version1.go generated vendored Normal file
View File

@ -0,0 +1,44 @@
// Copyright 2016 Google Inc. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package uuid
import (
"encoding/binary"
)
// NewUUID returns a Version 1 UUID based on the current NodeID and clock
// sequence, and the current time. If the NodeID has not been set by SetNodeID
// or SetNodeInterface then it will be set automatically. If the NodeID cannot
// be set NewUUID returns nil. If clock sequence has not been set by
// SetClockSequence then it will be set automatically. If GetTime fails to
// return the current NewUUID returns nil and an error.
//
// In most cases, New should be used.
func NewUUID() (UUID, error) {
nodeMu.Lock()
if nodeID == zeroID {
setNodeInterface("")
}
nodeMu.Unlock()
var uuid UUID
now, seq, err := GetTime()
if err != nil {
return uuid, err
}
timeLow := uint32(now & 0xffffffff)
timeMid := uint16((now >> 32) & 0xffff)
timeHi := uint16((now >> 48) & 0x0fff)
timeHi |= 0x1000 // Version 1
binary.BigEndian.PutUint32(uuid[0:], timeLow)
binary.BigEndian.PutUint16(uuid[4:], timeMid)
binary.BigEndian.PutUint16(uuid[6:], timeHi)
binary.BigEndian.PutUint16(uuid[8:], seq)
copy(uuid[10:], nodeID[:])
return uuid, nil
}

38
src/vendor/github.com/google/uuid/version4.go generated vendored Normal file
View File

@ -0,0 +1,38 @@
// Copyright 2016 Google Inc. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package uuid
import "io"
// New creates a new random UUID or panics. New is equivalent to
// the expression
//
// uuid.Must(uuid.NewRandom())
func New() UUID {
return Must(NewRandom())
}
// NewRandom returns a Random (Version 4) UUID.
//
// The strength of the UUIDs is based on the strength of the crypto/rand
// package.
//
// A note about uniqueness derived from the UUID Wikipedia entry:
//
// Randomly generated UUIDs have 122 random bits. One's annual risk of being
// hit by a meteorite is estimated to be one chance in 17 billion, that
// means the probability is about 0.00000000006 (6 × 1011),
// equivalent to the odds of creating a few tens of trillions of UUIDs in a
// year and having one duplicate.
func NewRandom() (UUID, error) {
var uuid UUID
_, err := io.ReadFull(rander, uuid[:])
if err != nil {
return Nil, err
}
uuid[6] = (uuid[6] & 0x0f) | 0x40 // Version 4
uuid[8] = (uuid[8] & 0x3f) | 0x80 // Variant is 10
return uuid, nil
}

View File

@ -93,17 +93,17 @@ github.com/dghubble/sling
# github.com/dgrijalva/jwt-go v3.2.0+incompatible
github.com/dgrijalva/jwt-go
# github.com/docker/distribution v2.7.1+incompatible
github.com/docker/distribution/registry/auth/token
github.com/docker/distribution
github.com/docker/distribution/manifest/schema1
github.com/docker/distribution/manifest/schema2
github.com/docker/distribution/registry/auth/token
github.com/docker/distribution/manifest/schema1
github.com/docker/distribution/reference
github.com/docker/distribution/registry/client/auth/challenge
github.com/docker/distribution/health
github.com/docker/distribution/manifest/manifestlist
github.com/docker/distribution/manifest
github.com/docker/distribution/context
github.com/docker/distribution/registry/auth
github.com/docker/distribution/manifest
github.com/docker/distribution/digestset
github.com/docker/distribution/registry/api/errcode
github.com/docker/distribution/uuid
@ -151,6 +151,8 @@ github.com/gomodule/redigo/internal
github.com/google/go-querystring/query
# github.com/google/gofuzz v0.0.0-20170612174753-24818f796faf
github.com/google/gofuzz
# github.com/google/uuid v1.1.1
github.com/google/uuid
# github.com/gorilla/context v1.1.1
github.com/gorilla/context
# github.com/gorilla/handlers v1.3.0
@ -206,9 +208,9 @@ github.com/spf13/pflag
github.com/stretchr/objx
# github.com/stretchr/testify v1.3.0
github.com/stretchr/testify/mock
github.com/stretchr/testify/suite
github.com/stretchr/testify/assert
github.com/stretchr/testify/require
github.com/stretchr/testify/suite
# github.com/theupdateframework/notary v0.6.1
github.com/theupdateframework/notary/tuf/data
github.com/theupdateframework/notary