From 0246ca7aa4eaad423383455c8696099817312270 Mon Sep 17 00:00:00 2001 From: He Weiwei Date: Fri, 15 Nov 2019 09:43:16 +0000 Subject: [PATCH] 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 --- src/core/main.go | 6 ++- src/pkg/scan/dao/scanner/registration.go | 12 ++++- src/pkg/scan/init.go | 68 ++++++++++++++++++++---- src/pkg/scan/init_test.go | 39 +++++++++++++- src/pkg/types/error.go | 24 +++++++++ 5 files changed, 136 insertions(+), 13 deletions(-) create mode 100644 src/pkg/types/error.go diff --git a/src/core/main.go b/src/core/main.go index 98448e051..524af6f0e 100755 --- a/src/core/main.go +++ b/src/core/main.go @@ -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{}) diff --git a/src/pkg/scan/dao/scanner/registration.go b/src/pkg/scan/dao/scanner/registration.go index 8bcdc13ae..ad9858489 100644 --- a/src/pkg/scan/dao/scanner/registration.go +++ b/src/pkg/scan/dao/scanner/registration.go @@ -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 diff --git a/src/pkg/scan/init.go b/src/pkg/scan/init.go index fdf22848b..a0288ac88 100644 --- a/src/pkg/scan/init.go +++ b/src/pkg/scan/init.go @@ -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 diff --git a/src/pkg/scan/init_test.go b/src/pkg/scan/init_test.go index c2a0a0869..b95bd1369 100644 --- a/src/pkg/scan/init_test.go +++ b/src/pkg/scan/init_test.go @@ -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)) +} diff --git a/src/pkg/types/error.go b/src/pkg/types/error.go new file mode 100644 index 000000000..c7af6d1d7 --- /dev/null +++ b/src/pkg/types/error.go @@ -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") +)