mirror of
https://github.com/goharbor/harbor.git
synced 2024-12-28 03:27:41 +01:00
Add audit_log forward endpoint (#16914)
add config item to set log forward endpoint fallback the audit log to default log when endpoint in error Signed-off-by: stonezdj <stonezdj@gmail.com>
This commit is contained in:
parent
4d062c33d1
commit
e778ec2edf
@ -8378,6 +8378,12 @@ definitions:
|
|||||||
storage_per_project:
|
storage_per_project:
|
||||||
$ref: '#/definitions/IntegerConfigItem'
|
$ref: '#/definitions/IntegerConfigItem'
|
||||||
description: The storage quota per project
|
description: The storage quota per project
|
||||||
|
audit_log_forward_endpoint:
|
||||||
|
$ref: '#/definitions/StringConfigItem'
|
||||||
|
description: The endpoint of the audit log forwarder
|
||||||
|
skip_audit_log_database:
|
||||||
|
$ref: '#/definitions/BoolConfigItem'
|
||||||
|
description: Whether skip the audit log in database
|
||||||
scan_all_policy:
|
scan_all_policy:
|
||||||
type: object
|
type: object
|
||||||
properties:
|
properties:
|
||||||
@ -8669,6 +8675,16 @@ definitions:
|
|||||||
description: The storage quota per project
|
description: The storage quota per project
|
||||||
x-omitempty: true
|
x-omitempty: true
|
||||||
x-isnullable: true
|
x-isnullable: true
|
||||||
|
audit_log_forward_endpoint:
|
||||||
|
type: string
|
||||||
|
description: The audit log forward endpoint
|
||||||
|
x-omitempty: true
|
||||||
|
x-isnullable: true
|
||||||
|
skip_audit_log_database:
|
||||||
|
type: boolean
|
||||||
|
description: Skip audit log database
|
||||||
|
x-omitempty: true
|
||||||
|
x-isnullable: true
|
||||||
StringConfigItem:
|
StringConfigItem:
|
||||||
type: object
|
type: object
|
||||||
properties:
|
properties:
|
||||||
|
@ -209,4 +209,8 @@ const (
|
|||||||
PurgeAuditIncludeOperations = "include_operations"
|
PurgeAuditIncludeOperations = "include_operations"
|
||||||
PurgeAuditDryRun = "dry_run"
|
PurgeAuditDryRun = "dry_run"
|
||||||
PurgeAuditRetentionHour = "audit_retention_hour"
|
PurgeAuditRetentionHour = "audit_retention_hour"
|
||||||
|
// AuditLogForwardEndpoint indicate to forward the audit log to an endpoint
|
||||||
|
AuditLogForwardEndpoint = "audit_log_forward_endpoint"
|
||||||
|
// SkipAuditLogDatabase skip to log audit log in database
|
||||||
|
SkipAuditLogDatabase = "skip_audit_log_database"
|
||||||
)
|
)
|
||||||
|
@ -27,6 +27,7 @@ import (
|
|||||||
"github.com/goharbor/harbor/src/lib/errors"
|
"github.com/goharbor/harbor/src/lib/errors"
|
||||||
"github.com/goharbor/harbor/src/lib/log"
|
"github.com/goharbor/harbor/src/lib/log"
|
||||||
"github.com/goharbor/harbor/src/lib/q"
|
"github.com/goharbor/harbor/src/lib/q"
|
||||||
|
"github.com/goharbor/harbor/src/pkg/audit"
|
||||||
"github.com/goharbor/harbor/src/pkg/user"
|
"github.com/goharbor/harbor/src/pkg/user"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -92,6 +93,22 @@ func (c *controller) UpdateUserConfigs(ctx context.Context, conf map[string]inte
|
|||||||
log.Errorf("failed to upload configurations: %v", err)
|
log.Errorf("failed to upload configurations: %v", err)
|
||||||
return fmt.Errorf("failed to validate configuration")
|
return fmt.Errorf("failed to validate configuration")
|
||||||
}
|
}
|
||||||
|
// update the audit logger to point to the new endpoint
|
||||||
|
return c.updateLogEndpoint(ctx, conf)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *controller) updateLogEndpoint(ctx context.Context, cfgs map[string]interface{}) error {
|
||||||
|
// check if the audit log forward endpoint updated
|
||||||
|
if _, ok := cfgs[common.AuditLogForwardEndpoint]; ok {
|
||||||
|
auditEP := config.AuditLogForwardEndpoint(ctx)
|
||||||
|
if len(auditEP) == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
if !audit.CheckEndpointActive(auditEP) {
|
||||||
|
return errors.BadRequestError(fmt.Errorf("could not connect to the audit endpoint: %v", auditEP))
|
||||||
|
}
|
||||||
|
audit.LogMgr.Init(ctx, auditEP)
|
||||||
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -116,6 +133,21 @@ func (c *controller) validateCfg(ctx context.Context, cfgs map[string]interface{
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.BadRequestError(err)
|
return errors.BadRequestError(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return verifySkipAuditLogCfg(ctx, cfgs, mgr)
|
||||||
|
}
|
||||||
|
|
||||||
|
func verifySkipAuditLogCfg(ctx context.Context, cfgs map[string]interface{}, mgr config.Manager) error {
|
||||||
|
if skip, exist := cfgs[common.SkipAuditLogDatabase]; exist {
|
||||||
|
endPoint := mgr.Get(ctx, common.AuditLogForwardEndpoint).GetString()
|
||||||
|
if edp, found := cfgs[common.AuditLogForwardEndpoint]; found {
|
||||||
|
endPoint = edp.(string)
|
||||||
|
}
|
||||||
|
skipAuditDB := skip.(bool)
|
||||||
|
if len(endPoint) == 0 && skipAuditDB {
|
||||||
|
return errors.BadRequestError(errors.New("audit log forward endpoint should be configured before enable skip audit log in database"))
|
||||||
|
}
|
||||||
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
59
src/controller/config/controller_test.go
Normal file
59
src/controller/config/controller_test.go
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
// 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 config
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"github.com/goharbor/harbor/src/common"
|
||||||
|
"github.com/goharbor/harbor/src/lib/config"
|
||||||
|
"github.com/goharbor/harbor/src/lib/config/metadata"
|
||||||
|
testCfg "github.com/goharbor/harbor/src/testing/lib/config"
|
||||||
|
"github.com/goharbor/harbor/src/testing/mock"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func Test_verifySkipAuditLogCfg(t *testing.T) {
|
||||||
|
cfgManager := &testCfg.Manager{}
|
||||||
|
cfgManager.On("Get", mock.Anything, common.AuditLogForwardEndpoint).
|
||||||
|
Return(&metadata.ConfigureValue{Name: common.AuditLogForwardEndpoint, Value: ""})
|
||||||
|
type args struct {
|
||||||
|
ctx context.Context
|
||||||
|
cfgs map[string]interface{}
|
||||||
|
mgr config.Manager
|
||||||
|
}
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
args args
|
||||||
|
wantErr bool
|
||||||
|
}{
|
||||||
|
{name: "both configured", args: args{ctx: context.TODO(),
|
||||||
|
cfgs: map[string]interface{}{common.AuditLogForwardEndpoint: "harbor-log:15041",
|
||||||
|
common.SkipAuditLogDatabase: true},
|
||||||
|
mgr: cfgManager}, wantErr: false},
|
||||||
|
{name: "no forward endpoint config", args: args{ctx: context.TODO(),
|
||||||
|
cfgs: map[string]interface{}{common.SkipAuditLogDatabase: true},
|
||||||
|
mgr: cfgManager}, wantErr: true},
|
||||||
|
{name: "none configured", args: args{ctx: context.TODO(),
|
||||||
|
cfgs: map[string]interface{}{},
|
||||||
|
mgr: cfgManager}, wantErr: false},
|
||||||
|
}
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
if err := verifySkipAuditLogCfg(tt.args.ctx, tt.args.cfgs, tt.args.mgr); (err != nil) != tt.wantErr {
|
||||||
|
t.Errorf("verifySkipAuditLogCfg() error = %v, wantErr %v", err, tt.wantErr)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
@ -23,6 +23,7 @@ import (
|
|||||||
"github.com/goharbor/harbor/src/controller/event/metadata"
|
"github.com/goharbor/harbor/src/controller/event/metadata"
|
||||||
"github.com/goharbor/harbor/src/lib/q"
|
"github.com/goharbor/harbor/src/lib/q"
|
||||||
"github.com/goharbor/harbor/src/pkg/audit/model"
|
"github.com/goharbor/harbor/src/pkg/audit/model"
|
||||||
|
_ "github.com/goharbor/harbor/src/pkg/config/db"
|
||||||
"github.com/goharbor/harbor/src/pkg/notifier"
|
"github.com/goharbor/harbor/src/pkg/notifier"
|
||||||
ne "github.com/goharbor/harbor/src/pkg/notifier/event"
|
ne "github.com/goharbor/harbor/src/pkg/notifier/event"
|
||||||
"github.com/stretchr/testify/mock"
|
"github.com/stretchr/testify/mock"
|
||||||
|
@ -1,16 +1,16 @@
|
|||||||
// Copyright 2018 Project Harbor Authors
|
// Copyright Project Harbor Authors
|
||||||
//
|
//
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
// you may not use this file except in compliance with the License.
|
// you may not use this file except in compliance with the License.
|
||||||
// You may obtain a copy of the License at
|
// You may obtain a copy of the License at
|
||||||
//
|
//
|
||||||
// http://www.apache.org/licenses/LICENSE-2.0
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
//
|
//
|
||||||
// Unless required by applicable law or agreed to in writing, software
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
// See the License for the specific language governing permissions and
|
// See the License for the specific language governing permissions and
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
package main
|
package main
|
||||||
|
|
||||||
@ -53,6 +53,7 @@ import (
|
|||||||
"github.com/goharbor/harbor/src/lib/orm"
|
"github.com/goharbor/harbor/src/lib/orm"
|
||||||
tracelib "github.com/goharbor/harbor/src/lib/trace"
|
tracelib "github.com/goharbor/harbor/src/lib/trace"
|
||||||
"github.com/goharbor/harbor/src/migration"
|
"github.com/goharbor/harbor/src/migration"
|
||||||
|
"github.com/goharbor/harbor/src/pkg/audit"
|
||||||
dbCfg "github.com/goharbor/harbor/src/pkg/config/db"
|
dbCfg "github.com/goharbor/harbor/src/pkg/config/db"
|
||||||
_ "github.com/goharbor/harbor/src/pkg/config/inmemory"
|
_ "github.com/goharbor/harbor/src/pkg/config/inmemory"
|
||||||
"github.com/goharbor/harbor/src/pkg/notification"
|
"github.com/goharbor/harbor/src/pkg/notification"
|
||||||
@ -202,6 +203,9 @@ func main() {
|
|||||||
go gracefulShutdown(closing, done, shutdownTracerProvider)
|
go gracefulShutdown(closing, done, shutdownTracerProvider)
|
||||||
// Start health checker for registries
|
// Start health checker for registries
|
||||||
go registry.Ctl.StartRegularHealthCheck(orm.Context(), closing, done)
|
go registry.Ctl.StartRegularHealthCheck(orm.Context(), closing, done)
|
||||||
|
// Init audit log
|
||||||
|
auditEP := config.AuditLogForwardEndpoint(ctx)
|
||||||
|
audit.LogMgr.Init(ctx, auditEP)
|
||||||
|
|
||||||
log.Info("initializing notification...")
|
log.Info("initializing notification...")
|
||||||
notification.Init()
|
notification.Init()
|
||||||
|
@ -186,5 +186,8 @@ var (
|
|||||||
|
|
||||||
{Name: common.CacheEnabled, Scope: SystemScope, Group: BasicGroup, EnvKey: "CACHE_ENABLED", DefaultValue: "false", ItemType: &BoolType{}, Editable: false, Description: `Enable cache`},
|
{Name: common.CacheEnabled, Scope: SystemScope, Group: BasicGroup, EnvKey: "CACHE_ENABLED", DefaultValue: "false", ItemType: &BoolType{}, Editable: false, Description: `Enable cache`},
|
||||||
{Name: common.CacheExpireHours, Scope: SystemScope, Group: BasicGroup, EnvKey: "CACHE_EXPIRE_HOURS", DefaultValue: "24", ItemType: &IntType{}, Editable: false, Description: `The expire hours for cache`},
|
{Name: common.CacheExpireHours, Scope: SystemScope, Group: BasicGroup, EnvKey: "CACHE_EXPIRE_HOURS", DefaultValue: "24", ItemType: &IntType{}, Editable: false, Description: `The expire hours for cache`},
|
||||||
|
|
||||||
|
{Name: common.AuditLogForwardEndpoint, Scope: UserScope, Group: BasicGroup, EnvKey: "AUDIT_LOG_FORWARD_ENDPOINT", DefaultValue: "", ItemType: &StringType{}, Editable: false, Description: `The endpoint to forward the audit log.`},
|
||||||
|
{Name: common.SkipAuditLogDatabase, Scope: UserScope, Group: BasicGroup, EnvKey: "SKIP_LOG_AUDIT_DATABASE", DefaultValue: "false", ItemType: &BoolType{}, Editable: false, Description: `The option to skip audit log in database`},
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
@ -241,3 +241,13 @@ func PullTimeUpdateDisable(ctx context.Context) bool {
|
|||||||
func PullAuditLogDisable(ctx context.Context) bool {
|
func PullAuditLogDisable(ctx context.Context) bool {
|
||||||
return DefaultMgr().Get(ctx, common.PullAuditLogDisable).GetBool()
|
return DefaultMgr().Get(ctx, common.PullAuditLogDisable).GetBool()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// AuditLogForwardEndpoint returns the audit log forward endpoint
|
||||||
|
func AuditLogForwardEndpoint(ctx context.Context) string {
|
||||||
|
return DefaultMgr().Get(ctx, common.AuditLogForwardEndpoint).GetString()
|
||||||
|
}
|
||||||
|
|
||||||
|
// SkipAuditLogDatabase returns the audit log forward endpoint
|
||||||
|
func SkipAuditLogDatabase(ctx context.Context) bool {
|
||||||
|
return DefaultMgr().Get(ctx, common.SkipAuditLogDatabase).GetBool()
|
||||||
|
}
|
||||||
|
@ -1,16 +1,16 @@
|
|||||||
// Copyright Project Harbor Authors
|
// Copyright Project Harbor Authors
|
||||||
//
|
//
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
// you may not use this file except in compliance with the License.
|
// you may not use this file except in compliance with the License.
|
||||||
// You may obtain a copy of the License at
|
// You may obtain a copy of the License at
|
||||||
//
|
//
|
||||||
// http://www.apache.org/licenses/LICENSE-2.0
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
//
|
//
|
||||||
// Unless required by applicable law or agreed to in writing, software
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
// See the License for the specific language governing permissions and
|
// See the License for the specific language governing permissions and
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
package log
|
package log
|
||||||
|
|
||||||
@ -59,6 +59,7 @@ type Logger struct {
|
|||||||
fields map[string]interface{}
|
fields map[string]interface{}
|
||||||
fieldsStr string
|
fieldsStr string
|
||||||
mu *sync.Mutex // ptr here to share one sync.Mutex for clone method
|
mu *sync.Mutex // ptr here to share one sync.Mutex for clone method
|
||||||
|
fallback *Logger // fallback logger when current out fail
|
||||||
}
|
}
|
||||||
|
|
||||||
// New returns a customized Logger
|
// New returns a customized Logger
|
||||||
@ -89,6 +90,11 @@ func DefaultLogger() *Logger {
|
|||||||
return logger
|
return logger
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SetFallback enable fallback when error happen
|
||||||
|
func (l *Logger) SetFallback(logger *Logger) {
|
||||||
|
l.fallback = logger
|
||||||
|
}
|
||||||
|
|
||||||
func (l *Logger) clone() *Logger {
|
func (l *Logger) clone() *Logger {
|
||||||
return &Logger{
|
return &Logger{
|
||||||
out: l.out,
|
out: l.out,
|
||||||
@ -146,8 +152,8 @@ func (l *Logger) WithField(key string, value interface{}) *Logger {
|
|||||||
return l.WithFields(Fields{key: value})
|
return l.WithFields(Fields{key: value})
|
||||||
}
|
}
|
||||||
|
|
||||||
// setOutput sets the output of Logger l
|
// SetOutput sets the output of Logger l
|
||||||
func (l *Logger) setOutput(out io.Writer) {
|
func (l *Logger) SetOutput(out io.Writer) {
|
||||||
l.mu.Lock()
|
l.mu.Lock()
|
||||||
defer l.mu.Unlock()
|
defer l.mu.Unlock()
|
||||||
|
|
||||||
@ -175,12 +181,17 @@ func (l *Logger) output(record *Record) (err error) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
defer func() {
|
||||||
|
if err := recover(); err != nil && l.fallback != nil {
|
||||||
|
_ = l.fallback.output(record)
|
||||||
|
}
|
||||||
|
}()
|
||||||
l.mu.Lock()
|
l.mu.Lock()
|
||||||
defer l.mu.Unlock()
|
defer l.mu.Unlock()
|
||||||
|
|
||||||
_, err = l.out.Write(b)
|
_, err = l.out.Write(b)
|
||||||
|
if err != nil && l.fallback != nil {
|
||||||
|
_ = l.fallback.output(record)
|
||||||
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -32,7 +32,7 @@ func contains(t *testing.T, str string, lvl string, line, msg string) bool {
|
|||||||
|
|
||||||
func TestSetx(t *testing.T) {
|
func TestSetx(t *testing.T) {
|
||||||
logger := New(nil, nil, WarningLevel)
|
logger := New(nil, nil, WarningLevel)
|
||||||
logger.setOutput(os.Stdout)
|
logger.SetOutput(os.Stdout)
|
||||||
fmt := NewTextFormatter()
|
fmt := NewTextFormatter()
|
||||||
logger.setFormatter(fmt)
|
logger.setFormatter(fmt)
|
||||||
logger.setLevel(DebugLevel)
|
logger.setLevel(DebugLevel)
|
||||||
@ -223,11 +223,11 @@ func enter() *bytes.Buffer {
|
|||||||
b := make([]byte, 0, 32)
|
b := make([]byte, 0, 32)
|
||||||
buf := bytes.NewBuffer(b)
|
buf := bytes.NewBuffer(b)
|
||||||
|
|
||||||
logger.setOutput(buf)
|
logger.SetOutput(buf)
|
||||||
|
|
||||||
return buf
|
return buf
|
||||||
}
|
}
|
||||||
|
|
||||||
func exit() {
|
func exit() {
|
||||||
logger.setOutput(os.Stdout)
|
logger.SetOutput(os.Stdout)
|
||||||
}
|
}
|
||||||
|
74
src/pkg/audit/forward.go
Normal file
74
src/pkg/audit/forward.go
Normal file
@ -0,0 +1,74 @@
|
|||||||
|
// 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 audit
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"github.com/goharbor/harbor/src/lib/config"
|
||||||
|
"github.com/goharbor/harbor/src/lib/log"
|
||||||
|
"io"
|
||||||
|
"log/syslog"
|
||||||
|
"os"
|
||||||
|
)
|
||||||
|
|
||||||
|
// LogMgr manage the audit log forward operations
|
||||||
|
var LogMgr = &LoggerManager{}
|
||||||
|
|
||||||
|
// LoggerManager manage the operations related to the audit log
|
||||||
|
type LoggerManager struct {
|
||||||
|
endpoint string
|
||||||
|
initialized bool
|
||||||
|
remoteLogger *log.Logger
|
||||||
|
}
|
||||||
|
|
||||||
|
// Init redirect the audit log to the forward endpoint
|
||||||
|
func (a *LoggerManager) Init(ctx context.Context, logEndpoint string) {
|
||||||
|
var w io.Writer
|
||||||
|
w, err := syslog.Dial("tcp", logEndpoint,
|
||||||
|
syslog.LOG_INFO, "audit")
|
||||||
|
a.initialized = true
|
||||||
|
if err != nil {
|
||||||
|
log.Errorf("failed to create audit log, error %v", err)
|
||||||
|
w = os.Stdout
|
||||||
|
a.initialized = false
|
||||||
|
}
|
||||||
|
a.remoteLogger = log.New(w, log.NewTextFormatter(), log.InfoLevel, 3)
|
||||||
|
a.remoteLogger.SetFallback(log.DefaultLogger())
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// DefaultLogger ...
|
||||||
|
func (a *LoggerManager) DefaultLogger(ctx context.Context) *log.Logger {
|
||||||
|
endpoint := config.AuditLogForwardEndpoint(ctx)
|
||||||
|
if a.endpoint != endpoint {
|
||||||
|
a.Init(ctx, endpoint)
|
||||||
|
a.initialized = true
|
||||||
|
}
|
||||||
|
return a.remoteLogger
|
||||||
|
}
|
||||||
|
|
||||||
|
// CheckEndpointActive check the liveliness of the endpoint
|
||||||
|
func CheckEndpointActive(address string) bool {
|
||||||
|
al, err := syslog.Dial("tcp", address,
|
||||||
|
syslog.LOG_INFO, "audit")
|
||||||
|
if al != nil {
|
||||||
|
defer al.Close()
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
log.Errorf("failed to connect to audit log endpoint, error %v", err)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
@ -16,6 +16,7 @@ package audit
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"github.com/goharbor/harbor/src/lib/config"
|
||||||
"github.com/goharbor/harbor/src/lib/q"
|
"github.com/goharbor/harbor/src/lib/q"
|
||||||
"github.com/goharbor/harbor/src/pkg/audit/dao"
|
"github.com/goharbor/harbor/src/pkg/audit/dao"
|
||||||
"github.com/goharbor/harbor/src/pkg/audit/model"
|
"github.com/goharbor/harbor/src/pkg/audit/model"
|
||||||
@ -68,6 +69,14 @@ func (m *manager) Get(ctx context.Context, id int64) (*model.AuditLog, error) {
|
|||||||
|
|
||||||
// Create ...
|
// Create ...
|
||||||
func (m *manager) Create(ctx context.Context, audit *model.AuditLog) (int64, error) {
|
func (m *manager) Create(ctx context.Context, audit *model.AuditLog) (int64, error) {
|
||||||
|
if len(config.AuditLogForwardEndpoint(ctx)) > 0 {
|
||||||
|
LogMgr.DefaultLogger(ctx).WithField("operator", audit.Username).
|
||||||
|
WithField("time", audit.OpTime).
|
||||||
|
Infof("action:%s, resource:%s", audit.Operation, audit.Resource)
|
||||||
|
}
|
||||||
|
if config.SkipAuditLogDatabase(ctx) {
|
||||||
|
return 0, nil
|
||||||
|
}
|
||||||
return m.dao.Create(ctx, audit)
|
return m.dao.Create(ctx, audit)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -16,6 +16,7 @@ package audit
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/goharbor/harbor/src/pkg/audit/model"
|
"github.com/goharbor/harbor/src/pkg/audit/model"
|
||||||
|
_ "github.com/goharbor/harbor/src/pkg/config/db"
|
||||||
mockDAO "github.com/goharbor/harbor/src/testing/pkg/audit/dao"
|
mockDAO "github.com/goharbor/harbor/src/testing/pkg/audit/dao"
|
||||||
"github.com/stretchr/testify/mock"
|
"github.com/stretchr/testify/mock"
|
||||||
"github.com/stretchr/testify/suite"
|
"github.com/stretchr/testify/suite"
|
||||||
|
157
src/testing/lib/config/manager.go
Normal file
157
src/testing/lib/config/manager.go
Normal file
@ -0,0 +1,157 @@
|
|||||||
|
// Code generated by mockery v2.12.3. DO NOT EDIT.
|
||||||
|
|
||||||
|
package config
|
||||||
|
|
||||||
|
import (
|
||||||
|
context "context"
|
||||||
|
|
||||||
|
metadata "github.com/goharbor/harbor/src/lib/config/metadata"
|
||||||
|
mock "github.com/stretchr/testify/mock"
|
||||||
|
|
||||||
|
models "github.com/goharbor/harbor/src/common/models"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Manager is an autogenerated mock type for the Manager type
|
||||||
|
type Manager struct {
|
||||||
|
mock.Mock
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get provides a mock function with given fields: ctx, key
|
||||||
|
func (_m *Manager) Get(ctx context.Context, key string) *metadata.ConfigureValue {
|
||||||
|
ret := _m.Called(ctx, key)
|
||||||
|
|
||||||
|
var r0 *metadata.ConfigureValue
|
||||||
|
if rf, ok := ret.Get(0).(func(context.Context, string) *metadata.ConfigureValue); ok {
|
||||||
|
r0 = rf(ctx, key)
|
||||||
|
} else {
|
||||||
|
if ret.Get(0) != nil {
|
||||||
|
r0 = ret.Get(0).(*metadata.ConfigureValue)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return r0
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetAll provides a mock function with given fields: ctx
|
||||||
|
func (_m *Manager) GetAll(ctx context.Context) map[string]interface{} {
|
||||||
|
ret := _m.Called(ctx)
|
||||||
|
|
||||||
|
var r0 map[string]interface{}
|
||||||
|
if rf, ok := ret.Get(0).(func(context.Context) map[string]interface{}); ok {
|
||||||
|
r0 = rf(ctx)
|
||||||
|
} else {
|
||||||
|
if ret.Get(0) != nil {
|
||||||
|
r0 = ret.Get(0).(map[string]interface{})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return r0
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetDatabaseCfg provides a mock function with given fields:
|
||||||
|
func (_m *Manager) GetDatabaseCfg() *models.Database {
|
||||||
|
ret := _m.Called()
|
||||||
|
|
||||||
|
var r0 *models.Database
|
||||||
|
if rf, ok := ret.Get(0).(func() *models.Database); ok {
|
||||||
|
r0 = rf()
|
||||||
|
} else {
|
||||||
|
if ret.Get(0) != nil {
|
||||||
|
r0 = ret.Get(0).(*models.Database)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return r0
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetUserCfgs provides a mock function with given fields: ctx
|
||||||
|
func (_m *Manager) GetUserCfgs(ctx context.Context) map[string]interface{} {
|
||||||
|
ret := _m.Called(ctx)
|
||||||
|
|
||||||
|
var r0 map[string]interface{}
|
||||||
|
if rf, ok := ret.Get(0).(func(context.Context) map[string]interface{}); ok {
|
||||||
|
r0 = rf(ctx)
|
||||||
|
} else {
|
||||||
|
if ret.Get(0) != nil {
|
||||||
|
r0 = ret.Get(0).(map[string]interface{})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return r0
|
||||||
|
}
|
||||||
|
|
||||||
|
// Load provides a mock function with given fields: ctx
|
||||||
|
func (_m *Manager) Load(ctx context.Context) error {
|
||||||
|
ret := _m.Called(ctx)
|
||||||
|
|
||||||
|
var r0 error
|
||||||
|
if rf, ok := ret.Get(0).(func(context.Context) error); ok {
|
||||||
|
r0 = rf(ctx)
|
||||||
|
} else {
|
||||||
|
r0 = ret.Error(0)
|
||||||
|
}
|
||||||
|
|
||||||
|
return r0
|
||||||
|
}
|
||||||
|
|
||||||
|
// Save provides a mock function with given fields: ctx
|
||||||
|
func (_m *Manager) Save(ctx context.Context) error {
|
||||||
|
ret := _m.Called(ctx)
|
||||||
|
|
||||||
|
var r0 error
|
||||||
|
if rf, ok := ret.Get(0).(func(context.Context) error); ok {
|
||||||
|
r0 = rf(ctx)
|
||||||
|
} else {
|
||||||
|
r0 = ret.Error(0)
|
||||||
|
}
|
||||||
|
|
||||||
|
return r0
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set provides a mock function with given fields: ctx, key, value
|
||||||
|
func (_m *Manager) Set(ctx context.Context, key string, value interface{}) {
|
||||||
|
_m.Called(ctx, key, value)
|
||||||
|
}
|
||||||
|
|
||||||
|
// UpdateConfig provides a mock function with given fields: ctx, cfgs
|
||||||
|
func (_m *Manager) UpdateConfig(ctx context.Context, cfgs map[string]interface{}) error {
|
||||||
|
ret := _m.Called(ctx, cfgs)
|
||||||
|
|
||||||
|
var r0 error
|
||||||
|
if rf, ok := ret.Get(0).(func(context.Context, map[string]interface{}) error); ok {
|
||||||
|
r0 = rf(ctx, cfgs)
|
||||||
|
} else {
|
||||||
|
r0 = ret.Error(0)
|
||||||
|
}
|
||||||
|
|
||||||
|
return r0
|
||||||
|
}
|
||||||
|
|
||||||
|
// ValidateCfg provides a mock function with given fields: ctx, cfgs
|
||||||
|
func (_m *Manager) ValidateCfg(ctx context.Context, cfgs map[string]interface{}) error {
|
||||||
|
ret := _m.Called(ctx, cfgs)
|
||||||
|
|
||||||
|
var r0 error
|
||||||
|
if rf, ok := ret.Get(0).(func(context.Context, map[string]interface{}) error); ok {
|
||||||
|
r0 = rf(ctx, cfgs)
|
||||||
|
} else {
|
||||||
|
r0 = ret.Error(0)
|
||||||
|
}
|
||||||
|
|
||||||
|
return r0
|
||||||
|
}
|
||||||
|
|
||||||
|
type NewManagerT interface {
|
||||||
|
mock.TestingT
|
||||||
|
Cleanup(func())
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewManager creates a new instance of Manager. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
|
||||||
|
func NewManager(t NewManagerT) *Manager {
|
||||||
|
mock := &Manager{}
|
||||||
|
mock.Mock.Test(t)
|
||||||
|
|
||||||
|
t.Cleanup(func() { mock.AssertExpectations(t) })
|
||||||
|
|
||||||
|
return mock
|
||||||
|
}
|
@ -16,3 +16,4 @@ package lib
|
|||||||
|
|
||||||
//go:generate mockery --case snake --dir ../../lib/orm --name Creator --output ./orm --outpkg orm
|
//go:generate mockery --case snake --dir ../../lib/orm --name Creator --output ./orm --outpkg orm
|
||||||
//go:generate mockery --case snake --dir ../../lib/cache --name Cache --output ./cache --outpkg cache
|
//go:generate mockery --case snake --dir ../../lib/cache --name Cache --output ./cache --outpkg cache
|
||||||
|
//go:generate mockery --case snake --dir ../../lib/config --name Manager --output ./config --outpkg config
|
||||||
|
Loading…
Reference in New Issue
Block a user