fix(scanner): process scenario reinstall without clair flag

1. Fix name conflict when install internal clair adapter.
2. Remove all internal adapters when reinstall harbor without --with-clair flag

Signed-off-by: He Weiwei <hweiwei@vmware.com>
This commit is contained in:
He Weiwei 2019-11-15 09:43:16 +00:00
parent 7b12ed14a1
commit 0246ca7aa4
5 changed files with 136 additions and 13 deletions

View File

@ -226,9 +226,13 @@ func main() {
Immutable: true,
}
if err := scan.EnsureScanner(reg); err != nil {
if err := scan.EnsureScanner(reg, true); err != nil {
log.Fatalf("failed to initialize clair scanner: %v", err)
}
} else {
if err := scan.RemoveImmutableScanners(); err != nil {
log.Warningf("failed to remove immutable scanners: %v", err)
}
}
closing := make(chan struct{})

View File

@ -21,6 +21,7 @@ import (
"github.com/astaxie/beego/orm"
"github.com/goharbor/harbor/src/common/dao"
"github.com/goharbor/harbor/src/pkg/q"
"github.com/goharbor/harbor/src/pkg/types"
"github.com/pkg/errors"
)
@ -31,7 +32,16 @@ func init() {
// AddRegistration adds a new registration
func AddRegistration(r *Registration) (int64, error) {
o := dao.GetOrmer()
return o.Insert(r)
id, err := o.Insert(r)
if err != nil {
if strings.Contains(err.Error(), "duplicate key value violates unique constraint") {
return 0, types.ErrDupRows
}
return 0, err
}
return id, nil
}
// GetRegistration gets the specified registration

View File

@ -21,14 +21,17 @@ import (
"github.com/goharbor/harbor/src/pkg/q"
"github.com/goharbor/harbor/src/pkg/scan/dao/scanner"
sc "github.com/goharbor/harbor/src/pkg/scan/scanner"
"github.com/goharbor/harbor/src/pkg/types"
"github.com/google/uuid"
"github.com/pkg/errors"
)
var (
scannerManager = sc.New()
)
// EnsureScanner ensure the scanner which specially name exists in the system
func EnsureScanner(registration *scanner.Registration) error {
// EnsureScanner ensure the scanner which specially endpoint exists in the system
func EnsureScanner(registration *scanner.Registration, resolveConflicts ...bool) error {
q := &q.Query{
Keywords: map[string]interface{}{"url": registration.URL},
}
@ -39,20 +42,65 @@ func EnsureScanner(registration *scanner.Registration) error {
return err
}
if len(registrations) == 0 {
defaultReg, err := scannerManager.GetDefault()
if len(registrations) > 0 {
return nil
}
var resolveConflict bool
if len(resolveConflicts) > 0 {
resolveConflict = resolveConflicts[0]
}
var defaultReg *scanner.Registration
defaultReg, err = scannerManager.GetDefault()
if err != nil {
return fmt.Errorf("failed to get the default scanner, error: %v", err)
}
// Set the registration to be default one when no default registration exist in the system
registration.IsDefault = defaultReg == nil
for {
_, err = scannerManager.Create(registration)
if err != nil {
return fmt.Errorf("failed to get the default scanner, error: %v", err)
if resolveConflict && errors.Cause(err) == types.ErrDupRows {
var id uuid.UUID
id, err = uuid.NewUUID()
if err != nil {
break
}
registration.Name = registration.Name + "-" + id.String()
resolveConflict = false
continue
}
}
// Set the registration to be default one when no default registration exist in the system
registration.IsDefault = defaultReg == nil
break
}
if _, err := scannerManager.Create(registration); err != nil {
if err == nil {
log.Infof("initialized scanner named %s", registration.Name)
}
return err
}
// RemoveImmutableScanners remove all immutable scanners in the system
func RemoveImmutableScanners() error {
q := &q.Query{
Keywords: map[string]interface{}{"immutable": true},
}
registrations, err := scannerManager.List(q)
if err != nil {
return err
}
for _, reg := range registrations {
if err := scannerManager.Delete(reg.UUID); err != nil {
return err
}
log.Infof("initialized scanner named %s", registration.Name)
}
return nil

View File

@ -21,6 +21,7 @@ import (
"github.com/goharbor/harbor/src/pkg/scan/dao/scanner"
sc "github.com/goharbor/harbor/src/pkg/scan/scanner"
"github.com/goharbor/harbor/src/pkg/scan/scanner/mocks"
"github.com/goharbor/harbor/src/pkg/types"
"github.com/pkg/errors"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/mock"
@ -32,6 +33,7 @@ type managerOptions struct {
getError error
getDefaultError error
createError error
createErrorFn func(*scanner.Registration) error
}
func newManager(opts *managerOptions) sc.Manager {
@ -95,10 +97,18 @@ func newManager(opts *managerOptions) sc.Manager {
return reg.URL
}
createError := func(reg *scanner.Registration) error {
if opts.createErrorFn != nil {
return opts.createErrorFn(reg)
}
return opts.createError
}
mgr.On("List", mock.AnythingOfType("*q.Query")).Return(listFn, opts.listError)
mgr.On("Get", mock.AnythingOfType("string")).Return(getFn, opts.getError)
mgr.On("GetDefault").Return(getDefaultFn, opts.getDefaultError)
mgr.On("Create", mock.AnythingOfType("*scanner.Registration")).Return(createFn, opts.createError)
mgr.On("Create", mock.AnythingOfType("*scanner.Registration")).Return(createFn, createError)
return mgr
}
@ -176,3 +186,30 @@ func TestEnsureScanner(t *testing.T) {
assert.NotNil(reg3)
assert.False(reg3.IsDefault)
}
func TestEnsureScannerWithResolveConflict(t *testing.T) {
assert := assert.New(t)
registrations := []*scanner.Registration{
{URL: "reg1"},
}
// create registration got ErrDupRows when its name is Clair
scannerManager = newManager(
&managerOptions{
registrations: registrations,
createErrorFn: func(reg *scanner.Registration) error {
if reg.Name == "Clair" {
return errors.Wrap(types.ErrDupRows, "failed to create reg")
}
return nil
},
},
)
assert.Nil(EnsureScanner(&scanner.Registration{Name: "Clair", URL: "reg1"}))
assert.Error(EnsureScanner(&scanner.Registration{Name: "Clair", URL: "reg2"}))
assert.Nil(EnsureScanner(&scanner.Registration{Name: "Clair", URL: "reg2"}, true))
}

24
src/pkg/types/error.go Normal file
View File

@ -0,0 +1,24 @@
// 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 types
import (
"errors"
)
var (
// ErrDupRows is returned by DAO when inserting failed with error "duplicate key value violates unique constraint"
ErrDupRows = errors.New("sql: duplicate row in DB")
)