mirror of
https://github.com/goharbor/harbor.git
synced 2024-12-22 16:48:30 +01:00
Merge pull request #9134 from steven-zou/feature/pluggable_scanners
support pluggable scanner
This commit is contained in:
commit
4c4897aef1
34
make/migrations/postgresql/0011_1.10.0_schema.up.sql
Normal file
34
make/migrations/postgresql/0011_1.10.0_schema.up.sql
Normal 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)
|
||||
)
|
@ -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)
|
||||
|
348
src/core/api/plug_scanners.go
Normal file
348
src/core/api/plug_scanners.go
Normal 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
|
||||
}
|
444
src/core/api/plug_scanners_test.go
Normal file
444
src/core/api/plug_scanners_test.go
Normal 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)
|
||||
}
|
@ -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{})
|
||||
|
||||
|
@ -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
|
||||
|
@ -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
25
src/pkg/q/query.go
Normal 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
|
||||
}
|
157
src/pkg/scan/scanner/api/controller.go
Normal file
157
src/pkg/scan/scanner/api/controller.go
Normal 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)
|
||||
}
|
287
src/pkg/scan/scanner/api/controller_test.go
Normal file
287
src/pkg/scan/scanner/api/controller_test.go
Normal 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)
|
||||
}
|
194
src/pkg/scan/scanner/api/registration.go
Normal file
194
src/pkg/scan/scanner/api/registration.go
Normal 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
|
||||
}
|
35
src/pkg/scan/scanner/api/scan.go
Normal file
35
src/pkg/scan/scanner/api/scan.go
Normal 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
|
||||
}
|
43
src/pkg/scan/scanner/dao/scan/report.go
Normal file
43
src/pkg/scan/scanner/dao/scan/report.go
Normal 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"},
|
||||
}
|
||||
}
|
120
src/pkg/scan/scanner/dao/scanner/model.go
Normal file
120
src/pkg/scan/scanner/dao/scanner/model.go
Normal 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
|
||||
}
|
87
src/pkg/scan/scanner/dao/scanner/model_test.go
Normal file
87
src/pkg/scan/scanner/dao/scanner/model_test.go
Normal 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)
|
||||
}
|
147
src/pkg/scan/scanner/dao/scanner/registration.go
Normal file
147
src/pkg/scan/scanner/dao/scanner/registration.go
Normal 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
|
||||
}
|
144
src/pkg/scan/scanner/dao/scanner/registration_test.go
Normal file
144
src/pkg/scan/scanner/dao/scanner/registration_test.go
Normal 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)
|
||||
}
|
131
src/pkg/scan/scanner/manager.go
Normal file
131
src/pkg/scan/scanner/manager.go
Normal 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()
|
||||
}
|
115
src/pkg/scan/scanner/manager_test.go
Normal file
115
src/pkg/scan/scanner/manager_test.go
Normal 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)
|
||||
}
|
48
src/pkg/scan/scanner/scan/controller.go
Normal file
48
src/pkg/scan/scanner/scan/controller.go
Normal 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)
|
||||
}
|
46
src/pkg/scan/scanner/scan/models.go
Normal file
46
src/pkg/scan/scanner/scan/models.go
Normal 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
9
src/vendor/github.com/google/uuid/.travis.yml
generated
vendored
Normal 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
10
src/vendor/github.com/google/uuid/CONTRIBUTING.md
generated
vendored
Normal 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
9
src/vendor/github.com/google/uuid/CONTRIBUTORS
generated
vendored
Normal 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
27
src/vendor/github.com/google/uuid/LICENSE
generated
vendored
Normal 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
19
src/vendor/github.com/google/uuid/README.md
generated
vendored
Normal 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
80
src/vendor/github.com/google/uuid/dce.go
generated
vendored
Normal 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
12
src/vendor/github.com/google/uuid/doc.go
generated
vendored
Normal 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
1
src/vendor/github.com/google/uuid/go.mod
generated
vendored
Normal file
@ -0,0 +1 @@
|
||||
module github.com/google/uuid
|
53
src/vendor/github.com/google/uuid/hash.go
generated
vendored
Normal file
53
src/vendor/github.com/google/uuid/hash.go
generated
vendored
Normal 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
37
src/vendor/github.com/google/uuid/marshal.go
generated
vendored
Normal 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
90
src/vendor/github.com/google/uuid/node.go
generated
vendored
Normal 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
12
src/vendor/github.com/google/uuid/node_js.go
generated
vendored
Normal 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
33
src/vendor/github.com/google/uuid/node_net.go
generated
vendored
Normal 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
59
src/vendor/github.com/google/uuid/sql.go
generated
vendored
Normal 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
123
src/vendor/github.com/google/uuid/time.go
generated
vendored
Normal 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
43
src/vendor/github.com/google/uuid/util.go
generated
vendored
Normal 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
245
src/vendor/github.com/google/uuid/uuid.go
generated
vendored
Normal 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
44
src/vendor/github.com/google/uuid/version1.go
generated
vendored
Normal 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
38
src/vendor/github.com/google/uuid/version4.go
generated
vendored
Normal 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 × 10−11),
|
||||
// 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
|
||||
}
|
10
src/vendor/modules.txt
vendored
10
src/vendor/modules.txt
vendored
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user