diff --git a/Makefile b/Makefile index af94edd59..58d4936d1 100644 --- a/Makefile +++ b/Makefile @@ -523,7 +523,7 @@ GOLANGCI_LINT := $(shell go env GOPATH)/bin/golangci-lint lint: @echo checking lint @echo $(GOLANGCI_LINT) - @cd ./src/; $(GOLANGCI_LINT) -v run ./...; + @cd ./src/; $(GOLANGCI_LINT) -v run ./... --timeout=10m; # go install golang.org/x/vuln/cmd/govulncheck@latest GOVULNCHECK := $(shell go env GOPATH)/bin/govulncheck diff --git a/src/chartserver/cache.go b/src/chartserver/cache.go index 4cffef5b8..db27c2f7f 100644 --- a/src/chartserver/cache.go +++ b/src/chartserver/cache.go @@ -1,14 +1,15 @@ package chartserver import ( + "context" "encoding/json" "errors" "math" "time" - beego_cache "github.com/beego/beego/cache" + beego_cache "github.com/beego/beego/v2/client/cache" // Enable redis cache adaptor - _ "github.com/beego/beego/cache/redis" + _ "github.com/beego/beego/v2/client/cache/redis" hlog "github.com/goharbor/harbor/src/lib/log" ) @@ -94,6 +95,7 @@ func (chc *ChartCache) IsEnabled() bool { // PutChart caches the detailed data of chart version func (chc *ChartCache) PutChart(chart *ChartVersionDetails) { + ctx := context.Background() // If cache is not enabled, do nothing if !chc.IsEnabled() { return @@ -107,12 +109,12 @@ func (chc *ChartCache) PutChart(chart *ChartVersionDetails) { switch chc.driverType { case cacheDriverMem: // Directly put object in - err = chc.cache.Put(chart.Metadata.Digest, chart, standardExpireTime) + err = chc.cache.Put(ctx, chart.Metadata.Digest, chart, standardExpireTime) case cacheDriverRedis, cacheDriverRedisSentinel: // Marshal to json data before saving var jsonData []byte if jsonData, err = json.Marshal(chart); err == nil { - err = chc.cache.Put(chart.Metadata.Digest, jsonData, standardExpireTime) + err = chc.cache.Put(ctx, chart.Metadata.Digest, jsonData, standardExpireTime) } default: // Should not reach here, but still put guard code here @@ -132,11 +134,15 @@ func (chc *ChartCache) PutChart(chart *ChartVersionDetails) { // otherwise, nil object is returned func (chc *ChartCache) GetChart(chartDigest string) *ChartVersionDetails { // If cache is not enabled, do nothing + ctx := context.Background() if !chc.IsEnabled() { return nil } - object := chc.cache.Get(chartDigest) + object, err := chc.cache.Get(ctx, chartDigest) + if err != nil { + hlog.Warningf("Failed to get cache value by key with error: %s", err) + } if object != nil { // Try to convert data // First try the normal way diff --git a/src/chartserver/redis_sentinel.go b/src/chartserver/redis_sentinel.go index 34a08fd10..4e2b984b7 100644 --- a/src/chartserver/redis_sentinel.go +++ b/src/chartserver/redis_sentinel.go @@ -1,6 +1,7 @@ package chartserver import ( + "context" "encoding/json" "errors" "fmt" @@ -9,7 +10,7 @@ import ( "time" "github.com/FZambia/sentinel" - "github.com/beego/beego/cache" + "github.com/beego/beego/v2/client/cache" "github.com/gomodule/redigo/redis" ) @@ -52,15 +53,16 @@ func (rc *Cache) associate(originKey interface{}) string { } // Get cache from redis. -func (rc *Cache) Get(key string) interface{} { - if v, err := rc.do("GET", key); err == nil { - return v +func (rc *Cache) Get(ctx context.Context, key string) (interface{}, error) { + v, err := rc.do("GET", key) + if err != nil { + return nil, err } - return nil + return v, err } // GetMulti get cache from redis. -func (rc *Cache) GetMulti(keys []string) []interface{} { +func (rc *Cache) GetMulti(ctx context.Context, keys []string) ([]interface{}, error) { c := rc.p.Get() defer c.Close() var args []interface{} @@ -69,46 +71,46 @@ func (rc *Cache) GetMulti(keys []string) []interface{} { } values, err := redis.Values(c.Do("MGET", args...)) if err != nil { - return nil + return nil, err } - return values + return values, nil } // Put put cache to redis. -func (rc *Cache) Put(key string, val interface{}, timeout time.Duration) error { +func (rc *Cache) Put(ctx context.Context, key string, val interface{}, timeout time.Duration) error { _, err := rc.do("SETEX", key, int64(timeout/time.Second), val) return err } // Delete delete cache in redis. -func (rc *Cache) Delete(key string) error { +func (rc *Cache) Delete(ctx context.Context, key string) error { _, err := rc.do("DEL", key) return err } // IsExist check cache's existence in redis. -func (rc *Cache) IsExist(key string) bool { +func (rc *Cache) IsExist(ctx context.Context, key string) (bool, error) { v, err := redis.Bool(rc.do("EXISTS", key)) if err != nil { - return false + return false, err } - return v + return v, nil } // Incr increase counter in redis. -func (rc *Cache) Incr(key string) error { +func (rc *Cache) Incr(ctx context.Context, key string) error { _, err := redis.Bool(rc.do("INCRBY", key, 1)) return err } // Decr decrease counter in redis. -func (rc *Cache) Decr(key string) error { +func (rc *Cache) Decr(ctx context.Context, key string) error { _, err := redis.Bool(rc.do("INCRBY", key, -1)) return err } // ClearAll clean all cache in redis. delete this redis collection. -func (rc *Cache) ClearAll() error { +func (rc *Cache) ClearAll(ctx context.Context) error { c := rc.p.Get() defer c.Close() cachedKeys, err := redis.Strings(c.Do("KEYS", rc.key+":*")) diff --git a/src/common/api/base.go b/src/common/api/base.go index 84d6ea637..116883ae7 100644 --- a/src/common/api/base.go +++ b/src/common/api/base.go @@ -22,8 +22,8 @@ import ( "net/http" "strconv" - "github.com/beego/beego" - "github.com/beego/beego/validation" + "github.com/beego/beego/v2/core/validation" + "github.com/beego/beego/v2/server/web" commonhttp "github.com/goharbor/harbor/src/common/http" lib_http "github.com/goharbor/harbor/src/lib/http" @@ -40,7 +40,7 @@ const ( // BaseAPI wraps common methods for controllers to host API type BaseAPI struct { - beego.Controller + web.Controller } // Context returns the context.Context from http.Request @@ -79,10 +79,10 @@ func (b *BaseAPI) RenderError(code int, text string) { // DecodeJSONReq decodes a json request func (b *BaseAPI) DecodeJSONReq(v interface{}) error { - err := json.Unmarshal(b.Ctx.Input.CopyBody(1<<32), v) + err := json.Unmarshal(b.Ctx.Input.CopyBody(1<<35), v) if err != nil { log.Errorf("Error while decoding the json request, error: %v, %v", - err, string(b.Ctx.Input.CopyBody(1 << 32)[:])) + err, string(b.Ctx.Input.CopyBody(1 << 35)[:])) return errors.New("invalid json request") } return nil diff --git a/src/common/dao/base.go b/src/common/dao/base.go index eeccb2525..cf9bba282 100644 --- a/src/common/dao/base.go +++ b/src/common/dao/base.go @@ -20,7 +20,7 @@ import ( "strconv" "sync" - "github.com/beego/beego/orm" + "github.com/beego/beego/v2/client/orm" "github.com/goharbor/harbor/src/common/models" "github.com/goharbor/harbor/src/lib/log" diff --git a/src/common/dao/dao_test.go b/src/common/dao/dao_test.go index a1248dbf8..6efa0f86f 100644 --- a/src/common/dao/dao_test.go +++ b/src/common/dao/dao_test.go @@ -19,8 +19,8 @@ import ( "os" "testing" - "github.com/beego/beego/orm" - + "github.com/beego/beego/v2/client/orm" + "github.com/goharbor/harbor/src/common/models" "github.com/goharbor/harbor/src/lib/log" libOrm "github.com/goharbor/harbor/src/lib/orm" @@ -29,7 +29,7 @@ import ( var testCtx context.Context -func execUpdate(o orm.Ormer, sql string, params ...interface{}) error { +func execUpdate(o orm.TxOrmer, sql string, params ...interface{}) error { p, err := o.Raw(sql).Prepare() if err != nil { return err @@ -46,9 +46,9 @@ func cleanByUser(username string) { var err error o := GetOrmer() - o.Begin() + txOrm, err := o.Begin() - err = execUpdate(o, `delete + err = execUpdate(txOrm, `delete from project_member where entity_id = ( select user_id @@ -56,11 +56,11 @@ func cleanByUser(username string) { where username = ? ) `, username) if err != nil { - o.Rollback() + txOrm.Rollback() log.Error(err) } - err = execUpdate(o, `delete + err = execUpdate(txOrm, `delete from project_member where project_id = ( select project_id @@ -68,30 +68,30 @@ func cleanByUser(username string) { where name = ? )`, projectName) if err != nil { - o.Rollback() + txOrm.Rollback() log.Error(err) } - err = execUpdate(o, `delete from project where name = ?`, projectName) + err = execUpdate(txOrm, `delete from project where name = ?`, projectName) if err != nil { - o.Rollback() + txOrm.Rollback() log.Error(err) } - err = execUpdate(o, `delete from harbor_user where username = ?`, username) + err = execUpdate(txOrm, `delete from harbor_user where username = ?`, username) if err != nil { - o.Rollback() + txOrm.Rollback() log.Error(err) } - err = execUpdate(o, `delete from replication_policy where id < 99`) + err = execUpdate(txOrm, `delete from replication_policy where id < 99`) if err != nil { log.Error(err) } - err = execUpdate(o, `delete from registry where id < 99`) + err = execUpdate(txOrm, `delete from registry where id < 99`) if err != nil { log.Error(err) } - o.Commit() + txOrm.Commit() } const username string = "Tester01" diff --git a/src/common/dao/mysql.go b/src/common/dao/mysql.go index 5222272cb..9c78755fd 100644 --- a/src/common/dao/mysql.go +++ b/src/common/dao/mysql.go @@ -18,7 +18,7 @@ import ( "fmt" "time" - "github.com/beego/beego/orm" + "github.com/beego/beego/v2/client/orm" _ "github.com/go-sql-driver/mysql" // register mysql driver "github.com/goharbor/harbor/src/common/utils" diff --git a/src/common/dao/pgsql.go b/src/common/dao/pgsql.go index 592357e08..665e8c2e9 100644 --- a/src/common/dao/pgsql.go +++ b/src/common/dao/pgsql.go @@ -22,7 +22,7 @@ import ( "strconv" "time" - "github.com/beego/beego/orm" + "github.com/beego/beego/v2/client/orm" migrate "github.com/golang-migrate/migrate/v4" _ "github.com/golang-migrate/migrate/v4/database/pgx" // import pgx driver for migrator _ "github.com/golang-migrate/migrate/v4/source/file" // import local file driver for migrator @@ -91,17 +91,11 @@ func (p *pgsql) Register(alias ...string) error { info := fmt.Sprintf("host=%s port=%s user=%s password=%s dbname=%s sslmode=%s timezone=UTC", p.host, p.port, p.usr, p.pwd, p.database, p.sslmode) - if err := orm.RegisterDataBase(an, "pgx", info, p.maxIdleConns, p.maxOpenConns); err != nil { + if err := orm.RegisterDataBase(an, "pgx", info, orm.MaxIdleConnections(p.maxIdleConns), + orm.MaxOpenConnections(p.maxOpenConns), orm.ConnMaxLifetime(5*time.Minute)); err != nil { return err } - // Due to the issues of beego v1.12.1 and v1.12.2, we set the max open conns ourselves. - // See https://github.com/goharbor/harbor/issues/12403 - // and https://github.com/beego/beego/issues/4059 for more info. - db, _ := orm.GetDB(an) - db.SetMaxOpenConns(p.maxOpenConns) - db.SetConnMaxLifetime(5 * time.Minute) - return nil } diff --git a/src/common/dao/pgsql_test.go b/src/common/dao/pgsql_test.go index 2a2aa635c..2e0461151 100644 --- a/src/common/dao/pgsql_test.go +++ b/src/common/dao/pgsql_test.go @@ -19,7 +19,7 @@ import ( "sync" "testing" - "github.com/beego/beego/orm" + "github.com/beego/beego/v2/client/orm" ) func TestMaxOpenConns(t *testing.T) { diff --git a/src/common/dao/resource_label.go b/src/common/dao/resource_label.go index 6f3f68acb..721b7b46b 100644 --- a/src/common/dao/resource_label.go +++ b/src/common/dao/resource_label.go @@ -17,7 +17,7 @@ package dao import ( "time" - "github.com/beego/beego/orm" + "github.com/beego/beego/v2/client/orm" "github.com/goharbor/harbor/src/common/models" "github.com/goharbor/harbor/src/pkg/label/model" diff --git a/src/common/dao/sqlite.go b/src/common/dao/sqlite.go index 15ee7bb02..36702d2ad 100644 --- a/src/common/dao/sqlite.go +++ b/src/common/dao/sqlite.go @@ -17,7 +17,7 @@ package dao import ( "fmt" - "github.com/beego/beego/orm" + "github.com/beego/beego/v2/client/orm" // _ "github.com/mattn/go-sqlite3" // register sqlite driver ) diff --git a/src/common/dao/testutils.go b/src/common/dao/testutils.go index a0d7101b0..0e7f4f5d2 100644 --- a/src/common/dao/testutils.go +++ b/src/common/dao/testutils.go @@ -19,11 +19,14 @@ import ( "os" "strconv" + "github.com/beego/beego/v2/client/orm" + "github.com/goharbor/harbor/src/common/models" "github.com/goharbor/harbor/src/lib/log" ) var defaultRegistered = false +var o orm.Ormer // PrepareTestForMySQL is for test only. func PrepareTestForMySQL() { @@ -72,13 +75,14 @@ func PrepareTestForPostgresSQL() { } log.Infof("POSTGRES_HOST: %s, POSTGRES_USR: %s, POSTGRES_PORT: %d, POSTGRES_PWD: %s\n", dbHost, dbUser, dbPort, dbPassword) - initDatabaseForTest(database) + o = initDatabaseForTest(database) } -func initDatabaseForTest(db *models.Database) { +func initDatabaseForTest(db *models.Database) orm.Ormer { database, err := getDatabase(db) if err != nil { - panic(err) + log.Fatal(err) + return nil } log.Infof("initializing database: %s", database.String()) @@ -89,17 +93,18 @@ func initDatabaseForTest(db *models.Database) { alias = "default" } if err := database.Register(alias); err != nil { - panic(err) + log.Fatal(err) + return nil } if err := database.UpgradeSchema(); err != nil { - panic(err) + log.Fatal(err) + return nil } if alias != "default" { - if err = GetOrmer().Using(alias); err != nil { - log.Fatalf("failed to create new orm: %v", err) - } + return orm.NewOrmUsingDB(alias) } + return GetOrmer() } // PrepareTestData -- Clean and Create data diff --git a/src/common/models/base.go b/src/common/models/base.go index 45720443f..4380c78e2 100644 --- a/src/common/models/base.go +++ b/src/common/models/base.go @@ -15,7 +15,7 @@ package models import ( - "github.com/beego/beego/orm" + "github.com/beego/beego/v2/client/orm" ) func init() { diff --git a/src/controller/event/handler/internal/artifact_test.go b/src/controller/event/handler/internal/artifact_test.go index eb6fa7925..07887e22b 100644 --- a/src/controller/event/handler/internal/artifact_test.go +++ b/src/controller/event/handler/internal/artifact_test.go @@ -19,7 +19,7 @@ import ( "testing" "time" - beegoorm "github.com/beego/beego/orm" + beegoorm "github.com/beego/beego/v2/client/orm" "github.com/stretchr/testify/suite" common_dao "github.com/goharbor/harbor/src/common/dao" diff --git a/src/controller/event/handler/webhook/scan/scan.go b/src/controller/event/handler/webhook/scan/scan.go index 64f666094..ee3c56ad7 100644 --- a/src/controller/event/handler/webhook/scan/scan.go +++ b/src/controller/event/handler/webhook/scan/scan.go @@ -18,7 +18,7 @@ import ( "context" "time" - o "github.com/beego/beego/orm" + o "github.com/beego/beego/v2/client/orm" "github.com/goharbor/harbor/src/controller/artifact" "github.com/goharbor/harbor/src/controller/event" diff --git a/src/controller/health/checker.go b/src/controller/health/checker.go index 45f53d554..0843cf4f0 100644 --- a/src/controller/health/checker.go +++ b/src/controller/health/checker.go @@ -23,7 +23,7 @@ import ( "sync" "time" - "github.com/beego/beego/orm" + "github.com/beego/beego/v2/client/orm" "github.com/docker/distribution/health" httputil "github.com/goharbor/harbor/src/common/http" diff --git a/src/core/api/base.go b/src/core/api/base.go index 273d06cb8..ce8b5430c 100644 --- a/src/core/api/base.go +++ b/src/core/api/base.go @@ -137,7 +137,11 @@ func (b *BaseController) SendPermissionError() { // WriteJSONData writes the JSON data to the client. func (b *BaseController) WriteJSONData(object interface{}) { b.Data["json"] = object - b.ServeJSON() + if err := b.ServeJSON(); err != nil { + log.Errorf("failed to serve json, %v", err) + b.SendInternalServerError(err) + return + } } // WriteYamlData writes the yaml data to the client. @@ -162,7 +166,11 @@ func (b *BaseController) PopulateUserSession(u models.User) { b.SendError(err) return } - b.SetSession(userSessionKey, u) + if err := b.SetSession(userSessionKey, u); err != nil { + log.Errorf("failed to set user into session, error: %v", err) + b.SendError(err) + return + } } // Init related objects/configurations for the API controllers diff --git a/src/core/api/chart_repository_test.go b/src/core/api/chart_repository_test.go index 48fe9b9e4..fc3a66862 100644 --- a/src/core/api/chart_repository_test.go +++ b/src/core/api/chart_repository_test.go @@ -7,7 +7,7 @@ import ( "net/http/httptest" "testing" - bcontext "github.com/beego/beego/context" + bcontext "github.com/beego/beego/v2/server/web/context" "github.com/goharbor/harbor/src/chartserver" proModels "github.com/goharbor/harbor/src/pkg/project/models" diff --git a/src/core/api/harborapi_test.go b/src/core/api/harborapi_test.go index 7c9cd08d9..fe35bc898 100644 --- a/src/core/api/harborapi_test.go +++ b/src/core/api/harborapi_test.go @@ -22,7 +22,7 @@ import ( "path/filepath" "runtime" - "github.com/beego/beego" + "github.com/beego/beego/v2/server/web" "github.com/dghubble/sling" "github.com/goharbor/harbor/src/common/api" @@ -84,31 +84,31 @@ func init() { dir := filepath.Dir(file) dir = filepath.Join(dir, "..") apppath, _ := filepath.Abs(dir) - beego.BConfig.WebConfig.Session.SessionOn = true - beego.TestBeegoInit(apppath) + web.BConfig.WebConfig.Session.SessionOn = true + web.TestBeegoInit(apppath) // Charts are controlled under projects chartRepositoryAPIType := &ChartRepositoryAPI{} - beego.Router("/api/chartrepo/health", chartRepositoryAPIType, "get:GetHealthStatus") - beego.Router("/api/chartrepo/:repo/charts", chartRepositoryAPIType, "get:ListCharts") - beego.Router("/api/chartrepo/:repo/charts/:name", chartRepositoryAPIType, "get:ListChartVersions") - beego.Router("/api/chartrepo/:repo/charts/:name", chartRepositoryAPIType, "delete:DeleteChart") - beego.Router("/api/chartrepo/:repo/charts/:name/:version", chartRepositoryAPIType, "get:GetChartVersion") - beego.Router("/api/chartrepo/:repo/charts/:name/:version", chartRepositoryAPIType, "delete:DeleteChartVersion") - beego.Router("/api/chartrepo/:repo/charts", chartRepositoryAPIType, "post:UploadChartVersion") - beego.Router("/api/chartrepo/:repo/prov", chartRepositoryAPIType, "post:UploadChartProvFile") - beego.Router("/api/chartrepo/charts", chartRepositoryAPIType, "post:UploadChartVersion") + web.Router("/api/chartrepo/health", chartRepositoryAPIType, "get:GetHealthStatus") + web.Router("/api/chartrepo/:repo/charts", chartRepositoryAPIType, "get:ListCharts") + web.Router("/api/chartrepo/:repo/charts/:name", chartRepositoryAPIType, "get:ListChartVersions") + web.Router("/api/chartrepo/:repo/charts/:name", chartRepositoryAPIType, "delete:DeleteChart") + web.Router("/api/chartrepo/:repo/charts/:name/:version", chartRepositoryAPIType, "get:GetChartVersion") + web.Router("/api/chartrepo/:repo/charts/:name/:version", chartRepositoryAPIType, "delete:DeleteChartVersion") + web.Router("/api/chartrepo/:repo/charts", chartRepositoryAPIType, "post:UploadChartVersion") + web.Router("/api/chartrepo/:repo/prov", chartRepositoryAPIType, "post:UploadChartProvFile") + web.Router("/api/chartrepo/charts", chartRepositoryAPIType, "post:UploadChartVersion") // Repository services - beego.Router("/chartrepo/:repo/index.yaml", chartRepositoryAPIType, "get:GetIndexByRepo") - beego.Router("/chartrepo/index.yaml", chartRepositoryAPIType, "get:GetIndex") - beego.Router("/chartrepo/:repo/charts/:filename", chartRepositoryAPIType, "get:DownloadChart") + web.Router("/chartrepo/:repo/index.yaml", chartRepositoryAPIType, "get:GetIndexByRepo") + web.Router("/chartrepo/index.yaml", chartRepositoryAPIType, "get:GetIndex") + web.Router("/chartrepo/:repo/charts/:filename", chartRepositoryAPIType, "get:DownloadChart") // Labels for chart chartLabelAPIType := &ChartLabelAPI{} - beego.Router("/api/"+api.APIVersion+"/chartrepo/:repo/charts/:name/:version/labels", chartLabelAPIType, "get:GetLabels;post:MarkLabel") - beego.Router("/api/"+api.APIVersion+"/chartrepo/:repo/charts/:name/:version/labels/:id([0-9]+)", chartLabelAPIType, "delete:RemoveLabel") + web.Router("/api/"+api.APIVersion+"/chartrepo/:repo/charts/:name/:version/labels", chartLabelAPIType, "get:GetLabels;post:MarkLabel") + web.Router("/api/"+api.APIVersion+"/chartrepo/:repo/charts/:name/:version/labels/:id([0-9]+)", chartLabelAPIType, "delete:RemoveLabel") - beego.Router("/api/internal/syncquota", &InternalAPI{}, "post:SyncQuota") + web.Router("/api/internal/syncquota", &InternalAPI{}, "post:SyncQuota") // Init user Info admin = &usrInfo{adminName, adminPwd} @@ -120,7 +120,7 @@ func init() { defer mockServer.Close() chain := middleware.Chain(orm.Middleware(), security.Middleware(), security.UnauthorizedMiddleware()) - handler = chain(beego.BeeApp.Handlers) + handler = chain(web.BeeApp.Handlers) } func request0(_sling *sling.Sling, acceptHeader string, authInfo ...usrInfo) (int, http.Header, []byte, error) { diff --git a/src/core/api/internal.go b/src/core/api/internal.go index e804e6452..db0ea6b0b 100644 --- a/src/core/api/internal.go +++ b/src/core/api/internal.go @@ -17,7 +17,7 @@ package api import ( "context" - o "github.com/beego/beego/orm" + o "github.com/beego/beego/v2/client/orm" "github.com/goharbor/harbor/src/common" "github.com/goharbor/harbor/src/common/models" @@ -66,7 +66,10 @@ func (ia *InternalAPI) RenameAdmin() { return } log.Debugf("The super user has been renamed to: %s", newName) - ia.DestroySession() + if err := ia.DestroySession(); err != nil { + log.Errorf("failed to destroy session for admin user, error: %v", err) + return + } } // SyncQuota ... diff --git a/src/core/api/label_resource.go b/src/core/api/label_resource.go index 01eec065b..86e985327 100644 --- a/src/core/api/label_resource.go +++ b/src/core/api/label_resource.go @@ -6,6 +6,7 @@ import ( "github.com/goharbor/harbor/src/common/models" "github.com/goharbor/harbor/src/core/label" + "github.com/goharbor/harbor/src/lib/log" pkg_label "github.com/goharbor/harbor/src/pkg/label" "github.com/goharbor/harbor/src/pkg/label/model" ) @@ -34,7 +35,11 @@ func (lra *LabelResourceAPI) getLabelsOfResource(rType string, rIDOrName interfa } lra.Data["json"] = labels - lra.ServeJSON() + if err := lra.ServeJSON(); err != nil { + log.Errorf("failed to serve json, %v", err) + lra.handleErrors(err) + return + } } func (lra *LabelResourceAPI) markLabelToResource(rl *models.ResourceLabel) { diff --git a/src/core/controllers/base.go b/src/core/controllers/base.go index 3dd09fe46..800122192 100644 --- a/src/core/controllers/base.go +++ b/src/core/controllers/base.go @@ -20,7 +20,7 @@ import ( "os" "strings" - "github.com/beego/beego" + "github.com/beego/beego/v2/server/web" "github.com/beego/i18n" "github.com/goharbor/harbor/src/common" @@ -110,7 +110,10 @@ func (cc *CommonController) Login() { // LogOut Habor UI func (cc *CommonController) LogOut() { - cc.DestroySession() + if err := cc.DestroySession(); err != nil { + log.Errorf("Error occurred in LogOut: %v", err) + cc.CustomAbort(http.StatusInternalServerError, "Internal error.") + } } // UserExists checks if user exists when user input value in sign in form. @@ -143,7 +146,10 @@ func (cc *CommonController) UserExists() { cc.CustomAbort(http.StatusInternalServerError, "Internal error.") } cc.Data["json"] = n > 0 - cc.ServeJSON() + if err := cc.ServeJSON(); err != nil { + log.Errorf("failed to serve json: %v", err) + cc.CustomAbort(http.StatusInternalServerError, "Internal error.") + } } func init() { @@ -151,7 +157,7 @@ func init() { configPath := os.Getenv("CONFIG_PATH") if len(configPath) != 0 { log.Infof("Config path: %s", configPath) - if err := beego.LoadAppConfig("ini", configPath); err != nil { + if err := web.LoadAppConfig("ini", configPath); err != nil { log.Errorf("failed to load app config: %v", err) } } diff --git a/src/core/controllers/controllers_test.go b/src/core/controllers/controllers_test.go index 1467399b5..f59439a41 100644 --- a/src/core/controllers/controllers_test.go +++ b/src/core/controllers/controllers_test.go @@ -23,7 +23,7 @@ import ( "strings" "testing" - "github.com/beego/beego" + "github.com/beego/beego/v2/server/web" "github.com/stretchr/testify/assert" "github.com/goharbor/harbor/src/common" @@ -41,13 +41,13 @@ func init() { dir := filepath.Dir(file) dir = filepath.Join(dir, "..") apppath, _ := filepath.Abs(dir) - beego.BConfig.WebConfig.Session.SessionOn = true - beego.TestBeegoInit(apppath) - beego.AddTemplateExt("htm") + web.BConfig.WebConfig.Session.SessionOn = true + web.TestBeegoInit(apppath) + web.AddTemplateExt("htm") - beego.Router("/c/login", &CommonController{}, "post:Login") - beego.Router("/c/log_out", &CommonController{}, "get:LogOut") - beego.Router("/c/userExists", &CommonController{}, "post:UserExists") + web.Router("/c/login", &CommonController{}, "post:Login") + web.Router("/c/log_out", &CommonController{}, "get:LogOut") + web.Router("/c/userExists", &CommonController{}, "post:UserExists") } func TestMain(m *testing.M) { @@ -71,7 +71,7 @@ func TestRedirectForOIDC(t *testing.T) { func TestAll(t *testing.T) { config.InitWithSettings(utilstest.GetUnitTestConfig()) assert := assert.New(t) - handler := http.Handler(beego.BeeApp.Handlers) + handler := http.Handler(web.BeeApp.Handlers) mws := middlewares.MiddleWares() for i := len(mws) - 1; i >= 0; i-- { if mws[i] == nil { diff --git a/src/core/controllers/error.go b/src/core/controllers/error.go index a5aa37056..644977423 100644 --- a/src/core/controllers/error.go +++ b/src/core/controllers/error.go @@ -15,12 +15,12 @@ package controllers import ( - "github.com/beego/beego" + "github.com/beego/beego/v2/server/web" ) // ErrorController handles beego error pages type ErrorController struct { - beego.Controller + web.Controller } // Error404 renders the 404 page diff --git a/src/core/controllers/oidc.go b/src/core/controllers/oidc.go index 14d0dc34e..1f4f040b3 100644 --- a/src/core/controllers/oidc.go +++ b/src/core/controllers/oidc.go @@ -63,8 +63,16 @@ func (oc *OIDCController) RedirectLogin() { oc.SendInternalServerError(err) return } - oc.SetSession(redirectURLKey, oc.Ctx.Request.URL.Query().Get("redirect_url")) - oc.SetSession(stateKey, state) + if err := oc.SetSession(redirectURLKey, oc.Ctx.Request.URL.Query().Get("redirect_url")); err != nil { + log.Errorf("failed to set session for key: %s, error: %v", redirectURLKey, err) + oc.SendInternalServerError(err) + return + } + if err := oc.SetSession(stateKey, state); err != nil { + log.Errorf("failed to set session for key: %s, error: %v", stateKey, err) + oc.SendInternalServerError(err) + return + } log.Debugf("State dumped to session: %s", state) // Force to use the func 'Redirect' of beego.Controller oc.Controller.Redirect(url, http.StatusFound) @@ -91,7 +99,11 @@ func (oc *OIDCController) Callback() { redirectURL := oc.GetSession(redirectURLKey) if redirectURL != nil { redirectURLStr = redirectURL.(string) - oc.DelSession(redirectURLKey) + if err := oc.DelSession(redirectURLKey); err != nil { + log.Errorf("failed to delete session for key:%s, error: %v", redirectURLKey, err) + oc.SendInternalServerError(err) + return + } } code := oc.Ctx.Request.URL.Query().Get("code") ctx := oc.Ctx.Request.Context() @@ -122,7 +134,11 @@ func (oc *OIDCController) Callback() { oc.SendInternalServerError(err) return } - oc.SetSession(tokenKey, tokenBytes) + if err := oc.SetSession(tokenKey, tokenBytes); err != nil { + log.Errorf("failed to set session for key: %s, error: %v", tokenKey, err) + oc.SendInternalServerError(err) + return + } u, err := ctluser.Ctl.GetBySubIss(ctx, info.Subject, info.Issuer) if errors.IsNotFoundErr(err) { // User is not onboarded, kickoff the onboard flow // Recover the username from d.Username by default @@ -150,7 +166,11 @@ func (oc *OIDCController) Callback() { log.Debug("User automatically onboarded\n") u = userRec } else { - oc.SetSession(userInfoKey, string(ouDataStr)) + if err := oc.SetSession(userInfoKey, string(ouDataStr)); err != nil { + log.Errorf("failed to set session for key: %s, error: %v", userInfoKey, err) + oc.SendInternalServerError(err) + return + } oc.Controller.Redirect(fmt.Sprintf("/oidc-onboard?username=%s&redirect_url=%s", username, redirectURLStr), http.StatusFound) // Once redirected, no further actions are done return @@ -253,7 +273,11 @@ func (oc *OIDCController) Onboard() { ctx := oc.Ctx.Request.Context() if user, onboarded := userOnboard(ctx, oc, d, username, tb); onboarded { user.OIDCUserMeta = nil - oc.DelSession(userInfoKey) + if err := oc.DelSession(userInfoKey); err != nil { + log.Errorf("failed to delete session for key:%s, error: %v", userInfoKey, err) + oc.SendInternalServerError(err) + return + } oc.PopulateUserSession(*user) } } diff --git a/src/core/main.go b/src/core/main.go index bcb59c02c..2f97034d4 100755 --- a/src/core/main.go +++ b/src/core/main.go @@ -26,7 +26,7 @@ import ( "syscall" "time" - "github.com/beego/beego" + "github.com/beego/beego/v2/server/web" "github.com/goharbor/harbor/src/common/dao" common_http "github.com/goharbor/harbor/src/common/http" @@ -120,8 +120,10 @@ func main() { runMode := flag.String("mode", "normal", "The harbor-core container run mode, it could be normal, migrate or skip-migrate, default is normal") flag.Parse() - beego.BConfig.WebConfig.Session.SessionOn = true - beego.BConfig.WebConfig.Session.SessionName = config.SessionCookieName + web.BConfig.WebConfig.Session.SessionOn = true + web.BConfig.WebConfig.Session.SessionName = config.SessionCookieName + web.BConfig.MaxMemory = 1 << 35 // (32GB) + web.BConfig.MaxUploadSize = 1 << 35 // (32GB) redisURL := os.Getenv("_REDIS_URL_CORE") if len(redisURL) > 0 { @@ -130,8 +132,8 @@ func main() { panic("bad _REDIS_URL") } - beego.BConfig.WebConfig.Session.SessionProvider = session.HarborProviderName - beego.BConfig.WebConfig.Session.SessionProviderConfig = redisURL + web.BConfig.WebConfig.Session.SessionProvider = session.HarborProviderName + web.BConfig.WebConfig.Session.SessionProviderConfig = redisURL log.Info("initializing cache ...") if err := cache.Initialize(u.Scheme, redisURL); err != nil { @@ -141,7 +143,7 @@ func main() { // enable config cache explicitly when the cache is ready dbCfg.EnableConfigCache() } - beego.AddTemplateExt("htm") + web.AddTemplateExt("htm") log.Info("initializing configurations...") config.Init() @@ -223,12 +225,12 @@ func main() { iTLSCertPath := os.Getenv("INTERNAL_TLS_CERT_PATH") log.Infof("load client key: %s client cert: %s", iTLSKeyPath, iTLSCertPath) - beego.BConfig.Listen.EnableHTTP = false - beego.BConfig.Listen.EnableHTTPS = true - beego.BConfig.Listen.HTTPSPort = 8443 - beego.BConfig.Listen.HTTPSKeyFile = iTLSKeyPath - beego.BConfig.Listen.HTTPSCertFile = iTLSCertPath - beego.BeeApp.Server.TLSConfig = common_http.NewServerTLSConfig() + web.BConfig.Listen.EnableHTTP = false + web.BConfig.Listen.EnableHTTPS = true + web.BConfig.Listen.HTTPSPort = 8443 + web.BConfig.Listen.HTTPSKeyFile = iTLSKeyPath + web.BConfig.Listen.HTTPSCertFile = iTLSCertPath + web.BeeApp.Server.TLSConfig = common_http.NewServerTLSConfig() } log.Infof("Version: %s, Git commit: %s", version.ReleaseVersion, version.GitCommit) @@ -257,7 +259,7 @@ func main() { } systemartifact.ScheduleCleanupTask(ctx) }() - beego.RunWithMiddleWares("", middlewares.MiddleWares()...) + web.RunWithMiddleWares("", middlewares.MiddleWares()...) } const ( diff --git a/src/core/middlewares/middlewares.go b/src/core/middlewares/middlewares.go index 2e85b2a4d..62a1c13d2 100644 --- a/src/core/middlewares/middlewares.go +++ b/src/core/middlewares/middlewares.go @@ -18,7 +18,7 @@ import ( "net/http" "regexp" - "github.com/beego/beego" + "github.com/beego/beego/v2/server/web" "github.com/goharbor/harbor/src/pkg/distribution" "github.com/goharbor/harbor/src/server/middleware" @@ -79,8 +79,8 @@ var ( ) // MiddleWares returns global middlewares -func MiddleWares() []beego.MiddleWare { - return []beego.MiddleWare{ +func MiddleWares() []web.MiddleWare { + return []web.MiddleWare{ url.Middleware(), mergeslash.Middleware(), trace.Middleware(), diff --git a/src/core/service/token/token.go b/src/core/service/token/token.go index f0f817d26..b2cd40ae5 100644 --- a/src/core/service/token/token.go +++ b/src/core/service/token/token.go @@ -19,14 +19,14 @@ import ( "html/template" "net/http" - "github.com/beego/beego" + "github.com/beego/beego/v2/server/web" "github.com/goharbor/harbor/src/lib/log" ) // Handler handles request on /service/token, which is the auth provider for registry. type Handler struct { - beego.Controller + web.Controller } // Get handles GET request, it checks the http header for user credentials @@ -51,5 +51,8 @@ func (h *Handler) Get() { h.CustomAbort(http.StatusInternalServerError, "") } h.Data["json"] = token - h.ServeJSON() + if err := h.ServeJSON(); err != nil { + log.Errorf("failed to serve json on /service/token, %v", err) + h.CustomAbort(http.StatusInternalServerError, "") + } } diff --git a/src/core/session/codec.go b/src/core/session/codec.go index fac99fd99..4fc10c044 100644 --- a/src/core/session/codec.go +++ b/src/core/session/codec.go @@ -17,7 +17,7 @@ package session import ( "encoding/gob" - "github.com/beego/beego/session" + "github.com/beego/beego/v2/server/web/session" commonmodels "github.com/goharbor/harbor/src/common/models" "github.com/goharbor/harbor/src/lib/cache" diff --git a/src/core/session/session.go b/src/core/session/session.go index bca0c2f72..80d4b389c 100644 --- a/src/core/session/session.go +++ b/src/core/session/session.go @@ -21,7 +21,7 @@ import ( "sync" "time" - "github.com/beego/beego/session" + "github.com/beego/beego/v2/server/web/session" goredis "github.com/go-redis/redis/v8" "github.com/goharbor/harbor/src/lib/cache" @@ -47,7 +47,7 @@ type Store struct { } // Set value in redis session -func (rs *Store) Set(key, value interface{}) error { +func (rs *Store) Set(ctx context.Context, key, value interface{}) error { rs.lock.Lock() defer rs.lock.Unlock() rs.values[key] = value @@ -55,7 +55,7 @@ func (rs *Store) Set(key, value interface{}) error { } // Get value in redis session -func (rs *Store) Get(key interface{}) interface{} { +func (rs *Store) Get(ctx context.Context, key interface{}) interface{} { rs.lock.RLock() defer rs.lock.RUnlock() if v, ok := rs.values[key]; ok { @@ -65,7 +65,7 @@ func (rs *Store) Get(key interface{}) interface{} { } // Delete value in redis session -func (rs *Store) Delete(key interface{}) error { +func (rs *Store) Delete(ctx context.Context, key interface{}) error { rs.lock.Lock() defer rs.lock.Unlock() delete(rs.values, key) @@ -73,7 +73,7 @@ func (rs *Store) Delete(key interface{}) error { } // Flush clear all values in redis session -func (rs *Store) Flush() error { +func (rs *Store) Flush(ctx context.Context) error { rs.lock.Lock() defer rs.lock.Unlock() rs.values = make(map[interface{}]interface{}) @@ -81,18 +81,20 @@ func (rs *Store) Flush() error { } // SessionID get redis session id -func (rs *Store) SessionID() string { +func (rs *Store) SessionID(ctx context.Context) string { return rs.sid } // SessionRelease save session values to redis -func (rs *Store) SessionRelease(w http.ResponseWriter) { +func (rs *Store) SessionRelease(ctx context.Context, w http.ResponseWriter) { b, err := session.EncodeGob(rs.values) if err != nil { return } - ctx := context.TODO() + if ctx == nil { + ctx = context.TODO() + } maxlifetime := time.Duration(systemSessionTimeout(ctx, rs.maxlifetime)) if rdb, ok := rs.c.(*redis.Cache); ok { cmd := rdb.Client.Set(ctx, rs.sid, string(b), maxlifetime) @@ -109,20 +111,26 @@ type Provider struct { } // SessionInit init redis session -func (rp *Provider) SessionInit(maxlifetime int64, url string) (err error) { +func (rp *Provider) SessionInit(ctx context.Context, maxlifetime int64, url string) (err error) { rp.maxlifetime = maxlifetime * int64(time.Second) rp.c, err = redis.New(cache.Options{Address: url, Codec: codec}) if err != nil { return err } - return rp.c.Ping(context.TODO()) + if ctx == nil { + ctx = context.TODO() + } + return rp.c.Ping(ctx) } // SessionRead read redis session by sid -func (rp *Provider) SessionRead(sid string) (session.Store, error) { +func (rp *Provider) SessionRead(ctx context.Context, sid string) (session.Store, error) { kv := make(map[interface{}]interface{}) - err := rp.c.Fetch(context.TODO(), sid, &kv) + if ctx == nil { + ctx = context.TODO() + } + err := rp.c.Fetch(ctx, sid, &kv) if err != nil && !strings.Contains(err.Error(), goredis.Nil.Error()) { return nil, err } @@ -132,16 +140,21 @@ func (rp *Provider) SessionRead(sid string) (session.Store, error) { } // SessionExist check redis session exist by sid -func (rp *Provider) SessionExist(sid string) bool { - return rp.c.Contains(context.TODO(), sid) +func (rp *Provider) SessionExist(ctx context.Context, sid string) (bool, error) { + if ctx == nil { + ctx = context.TODO() + } + return rp.c.Contains(ctx, sid), nil } // SessionRegenerate generate new sid for redis session -func (rp *Provider) SessionRegenerate(oldsid, sid string) (session.Store, error) { - ctx := context.TODO() +func (rp *Provider) SessionRegenerate(ctx context.Context, oldsid, sid string) (session.Store, error) { + if ctx == nil { + ctx = context.TODO() + } maxlifetime := time.Duration(systemSessionTimeout(ctx, rp.maxlifetime)) - if !rp.SessionExist(oldsid) { - err := rp.c.Save(ctx, sid, "", maxlifetime) + if isExist, _ := rp.SessionExist(ctx, oldsid); !isExist { + err := rp.c.Save(ctx, sid, "", time.Duration(rp.maxlifetime)) if err != nil { log.Debugf("failed to save sid=%s, where oldsid=%s, error: %s", sid, oldsid, err) } @@ -168,20 +181,23 @@ func (rp *Provider) SessionRegenerate(oldsid, sid string) (session.Store, error) } } - return rp.SessionRead(sid) + return rp.SessionRead(ctx, sid) } // SessionDestroy delete redis session by id -func (rp *Provider) SessionDestroy(sid string) error { - return rp.c.Delete(context.TODO(), sid) +func (rp *Provider) SessionDestroy(ctx context.Context, sid string) error { + if ctx == nil { + ctx = context.TODO() + } + return rp.c.Delete(ctx, sid) } // SessionGC Implement method, no used. -func (rp *Provider) SessionGC() { +func (rp *Provider) SessionGC(ctx context.Context) { } // SessionAll return all activeSession -func (rp *Provider) SessionAll() int { +func (rp *Provider) SessionAll(ctx context.Context) int { return 0 } diff --git a/src/core/session/session_test.go b/src/core/session/session_test.go index 85b0c2e81..41bc0d41a 100644 --- a/src/core/session/session_test.go +++ b/src/core/session/session_test.go @@ -15,9 +15,10 @@ package session import ( + "context" "testing" - "github.com/beego/beego/session" + "github.com/beego/beego/v2/server/web/session" "github.com/stretchr/testify/suite" "github.com/goharbor/harbor/src/lib/config" @@ -39,78 +40,83 @@ func (s *sessionTestSuite) SetupSuite() { s.NoError(err, "should get harbor provider") s.NotNil(s.provider, "provider should not nil") - err = s.provider.SessionInit(3600, "redis://127.0.0.1:6379/0") + err = s.provider.SessionInit(context.Background(), 3600, "redis://127.0.0.1:6379/0") s.NoError(err, "session init should not error") } func (s *sessionTestSuite) TestSessionRead() { - store, err := s.provider.SessionRead("session-001") + store, err := s.provider.SessionRead(context.Background(), "session-001") s.NoError(err, "session read should not error") s.NotNil(store) } func (s *sessionTestSuite) TestSessionExist() { // prepare session - store, err := s.provider.SessionRead("session-001") + ctx := context.Background() + store, err := s.provider.SessionRead(ctx, "session-001") s.NoError(err, "session read should not error") s.NotNil(store) - store.SessionRelease(nil) + store.SessionRelease(context.Background(), nil) defer func() { // clean session - err = s.provider.SessionDestroy("session-001") + err = s.provider.SessionDestroy(ctx, "session-001") s.NoError(err) }() - exist := s.provider.SessionExist("session-001") + exist, _ := s.provider.SessionExist(ctx, "session-001") s.True(exist, "session-001 should exist") - exist = s.provider.SessionExist("session-002") + exist, _ = s.provider.SessionExist(ctx, "session-002") s.False(exist, "session-002 should not exist") } func (s *sessionTestSuite) TestSessionRegenerate() { // prepare session - store, err := s.provider.SessionRead("session-001") + ctx := context.Background() + store, err := s.provider.SessionRead(ctx, "session-001") s.NoError(err, "session read should not error") s.NotNil(store) - store.SessionRelease(nil) + store.SessionRelease(ctx, nil) defer func() { // clean session - err = s.provider.SessionDestroy("session-001") + err = s.provider.SessionDestroy(ctx, "session-001") s.NoError(err) - err = s.provider.SessionDestroy("session-003") + err = s.provider.SessionDestroy(ctx, "session-003") s.NoError(err) }() - _, err = s.provider.SessionRegenerate("session-001", "session-003") + _, err = s.provider.SessionRegenerate(ctx, "session-001", "session-003") s.NoError(err, "session regenerate should not error") - s.True(s.provider.SessionExist("session-003")) - s.False(s.provider.SessionExist("session-001")) + s.True(s.provider.SessionExist(ctx, "session-003")) + s.False(s.provider.SessionExist(ctx, "session-001")) } func (s *sessionTestSuite) TestSessionDestroy() { // prepare session - store, err := s.provider.SessionRead("session-004") + ctx := context.Background() + store, err := s.provider.SessionRead(ctx, "session-004") s.NoError(err, "session read should not error") s.NotNil(store) - store.SessionRelease(nil) - s.True(s.provider.SessionExist("session-004"), "session-004 should exist") + store.SessionRelease(ctx, nil) + isExist, _ := s.provider.SessionExist(ctx, "session-004") + s.True(isExist, "session-004 should exist") - err = s.provider.SessionDestroy("session-004") + err = s.provider.SessionDestroy(ctx, "session-004") s.NoError(err, "session destroy should not error") - s.False(s.provider.SessionExist("session-004"), "session-004 should not exist") + isExist, _ = s.provider.SessionExist(ctx, "session-004") + s.False(isExist, "session-004 should not exist") } func (s *sessionTestSuite) TestSessionGC() { - s.provider.SessionGC() + s.provider.SessionGC(context.Background()) } func (s *sessionTestSuite) TestSessionAll() { - c := s.provider.SessionAll() + c := s.provider.SessionAll(context.Background()) s.Equal(0, c) } diff --git a/src/go.mod b/src/go.mod index f9ff97699..245baf000 100644 --- a/src/go.mod +++ b/src/go.mod @@ -10,7 +10,7 @@ require ( github.com/aws/aws-sdk-go v1.34.28 github.com/beego/i18n v0.0.0-20140604031826-e87155e8f0c0 github.com/bmatcuk/doublestar v1.1.1 - github.com/casbin/casbin v1.7.0 + github.com/casbin/casbin v1.9.1 github.com/cenkalti/backoff/v4 v4.1.2 github.com/coreos/go-oidc/v3 v3.0.0 github.com/dghubble/sling v1.1.0 @@ -27,7 +27,7 @@ require ( github.com/go-openapi/swag v0.19.14 github.com/go-openapi/validate v0.19.10 github.com/go-redis/redis/v8 v8.11.4 - github.com/go-sql-driver/mysql v1.5.0 + github.com/go-sql-driver/mysql v1.6.0 github.com/gocarina/gocsv v0.0.0-20210516172204-ca9e8a8ddea8 github.com/gocraft/work v0.5.1 github.com/golang-jwt/jwt/v4 v4.2.0 @@ -59,11 +59,11 @@ require ( github.com/vmihailenco/msgpack/v5 v5.0.0-rc.2 go.opentelemetry.io/contrib/instrumentation/github.com/gorilla/mux/otelmux v0.22.0 go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.22.0 - go.opentelemetry.io/otel v1.3.0 + go.opentelemetry.io/otel v1.8.0 go.opentelemetry.io/otel/exporters/jaeger v1.0.0 go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.3.0 - go.opentelemetry.io/otel/sdk v1.3.0 - go.opentelemetry.io/otel/trace v1.3.0 + go.opentelemetry.io/otel/sdk v1.8.0 + go.opentelemetry.io/otel/trace v1.8.0 go.uber.org/ratelimit v0.2.0 golang.org/x/crypto v0.1.0 golang.org/x/net v0.1.0 @@ -78,7 +78,7 @@ require ( ) require ( - github.com/beego/beego v1.12.11 + github.com/beego/beego/v2 v2.0.6 golang.org/x/text v0.4.0 ) @@ -165,7 +165,7 @@ require ( github.com/mattn/go-runewidth v0.0.9 // indirect github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369 // indirect github.com/mitchellh/copystructure v1.2.0 // indirect - github.com/mitchellh/mapstructure v1.4.1 // indirect + github.com/mitchellh/mapstructure v1.4.3 // indirect github.com/mitchellh/reflectwalk v1.0.2 // indirect github.com/moby/locker v1.0.1 // indirect github.com/moby/term v0.0.0-20210619224110-3f7ff695adc6 // indirect @@ -182,7 +182,7 @@ require ( github.com/prometheus/common v0.37.0 // indirect github.com/prometheus/procfs v0.8.0 // indirect github.com/satori/go.uuid v1.2.0 // indirect - github.com/shiena/ansicolor v0.0.0-20151119151921-a422bbe96644 // indirect + github.com/shiena/ansicolor v0.0.0-20200904210342-c7312218db18 // indirect github.com/sirupsen/logrus v1.8.1 // indirect github.com/spf13/afero v1.6.0 // indirect github.com/spf13/cast v1.4.1 // indirect diff --git a/src/go.sum b/src/go.sum index 160085e33..d5fcd1593 100644 --- a/src/go.sum +++ b/src/go.sum @@ -83,7 +83,6 @@ github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym github.com/ClickHouse/clickhouse-go v1.4.3/go.mod h1:EaI/sW7Azgz9UATzd5ZdZHRUhHgv5+JMS9NSr2smCJI= github.com/FZambia/sentinel v1.1.0 h1:qrCBfxc8SvJihYNjBWgwUI93ZCvFe/PJIPTHKmlp8a8= github.com/FZambia/sentinel v1.1.0/go.mod h1:ytL1Am/RLlAoAXG6Kj5LNuw/TRRQrv2rt2FT26vP5gI= -github.com/Knetic/govaluate v3.0.0+incompatible/go.mod h1:r7JcOSlj0wfOMncg0iLm8Leh48TZaKVeNIfJntJ2wa0= github.com/Knetic/govaluate v3.0.1-0.20171022003610-9aa49832a739+incompatible h1:1G1pk05UrOh0NlF1oeaaix1x8XzrfjIDK47TY0Zehcw= github.com/Knetic/govaluate v3.0.1-0.20171022003610-9aa49832a739+incompatible/go.mod h1:r7JcOSlj0wfOMncg0iLm8Leh48TZaKVeNIfJntJ2wa0= github.com/Masterminds/semver v1.5.0 h1:H65muMkzWKEuNDnfl9d70GUjFniHKHRbFPGBuZ3QEww= @@ -134,8 +133,6 @@ github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRF github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho= github.com/alexflint/go-filemutex v0.0.0-20171022225611-72bdc8eae2ae/go.mod h1:CgnQgUtFrFz9mxFNtED3jI5tLDjKlOM+oUF/sTk6ps0= -github.com/alicebob/gopher-json v0.0.0-20180125190556-5a6b3ba71ee6/go.mod h1:SGnFV6hVsYE877CKEZ6tDNTjaSXYUk6QqoIK6PrAtcc= -github.com/alicebob/miniredis v2.5.0+incompatible/go.mod h1:8HZjEj4yU0dwhYHky+DxYx+6BMjkBbe5ONFIF1MXffk= github.com/aliyun/alibaba-cloud-sdk-go v0.0.0-20190726115642-cd293c93fd97 h1:bNE5ID4C3YOkROfvBjXJUG53gyb+8az3TQN02LqnGBk= github.com/aliyun/alibaba-cloud-sdk-go v0.0.0-20190726115642-cd293c93fd97/go.mod h1:myCDvQSzCW+wB1WAlocEru4wMGJxy+vlxHdhegi1CDQ= github.com/aliyun/aliyun-oss-go-sdk v0.0.0-20190307165228-86c17b95fcd5/go.mod h1:T/Aws4fEfogEE9v+HPhhw+CntffsBHJ8nXQCwKr0/g8= @@ -190,12 +187,10 @@ github.com/aws/aws-sdk-go-v2/service/sts v1.7.2/go.mod h1:8EzeIqfWt2wWT4rJVu3f21 github.com/aws/smithy-go v1.7.0/go.mod h1:SObp3lf9smib00L/v3U2eAKG8FyQ7iLrJnQiAmR5n+E= github.com/aws/smithy-go v1.8.0/go.mod h1:SObp3lf9smib00L/v3U2eAKG8FyQ7iLrJnQiAmR5n+E= github.com/baiyubin/aliyun-sts-go-sdk v0.0.0-20180326062324-cfa1a18b161f/go.mod h1:AuiFmCCPBSrqvVMvuqFuk0qogytodnVFVSN5CeJB8Gc= -github.com/beego/beego v1.12.11 h1:MWKcnpavb7iAIS0m6uuEq6pHKkYvGNw/5umIUKqL7jM= -github.com/beego/beego v1.12.11/go.mod h1:QURFL1HldOcCZAxnc1cZ7wrplsYR5dKPHFjmk6WkLAs= -github.com/beego/goyaml2 v0.0.0-20130207012346-5545475820dd/go.mod h1:1b+Y/CofkYwXMUU0OhQqGvsY2Bvgr4j6jfT699wyZKQ= +github.com/beego/beego/v2 v2.0.6 h1:21Aqz3+RzUE1yP9a5xdU6LK54n9Z7NLEJtR4PE7NrPQ= +github.com/beego/beego/v2 v2.0.6/go.mod h1:CH2/JIaB4ceGYVQlYqTAFft4pVk/ol1ZkakUrUvAyns= github.com/beego/i18n v0.0.0-20140604031826-e87155e8f0c0 h1:fQaDnUQvBXHHQdGBu9hz8nPznB4BeiPQokvmQVjmNEw= github.com/beego/i18n v0.0.0-20140604031826-e87155e8f0c0/go.mod h1:KLeFCpAMq2+50NkXC8iiJxLLiiTfTqrGtKEVm+2fk7s= -github.com/beego/x2j v0.0.0-20131220205130-a0352aadc542/go.mod h1:kSeGC/p1AbBiEp5kat81+DSQrZenVBZXklMLaELspWU= github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= @@ -211,15 +206,14 @@ github.com/bmatcuk/doublestar v1.1.1 h1:YroD6BJCZBYx06yYFEWvUuKVWQn3vLLQAVmDmvTS github.com/bmatcuk/doublestar v1.1.1/go.mod h1:UD6OnuiIn0yFxxA2le/rnRU1G4RaI4UvFv1sNto9p6w= github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869/go.mod h1:Ekp36dRnpXw/yCqJaO+ZrUyxD+3VXMFFr56k5XYrpB4= github.com/boombuler/barcode v1.0.0/go.mod h1:paBWMcWSl3LHKBqUq+rly7CNSldXjb2rDl3JlRe0mD8= -github.com/bradfitz/gomemcache v0.0.0-20180710155616-bc664df96737/go.mod h1:PmM6Mmwb0LSuEubjR8N7PtNe1KxZLtOUHtbeikc5h60= github.com/bshuster-repo/logrus-logstash-hook v1.0.0 h1:e+C0SB5R1pu//O4MQ3f9cFuPGoOVeF2fE4Og9otCc70= github.com/buger/jsonparser v0.0.0-20180808090653-f4dd9f5a6b44/go.mod h1:bbYlZJ7hK1yFx9hf58LP0zeX7UjIGs20ufpu3evjr+s= github.com/bugsnag/bugsnag-go v1.5.2 h1:fdaGJJEReigPzSE6HajOhpJwE2IEP/TdHDHXKGeOJtc= github.com/bugsnag/bugsnag-go v1.5.2/go.mod h1:2oa8nejYd4cQ/b0hMIopN0lCRxU0bueqREvZLWFrtK8= github.com/bugsnag/panicwrap v1.2.0 h1:OzrKrRvXis8qEvOkfcxNcYbOd2O7xXS2nnKMEMABFQA= github.com/bugsnag/panicwrap v1.2.0/go.mod h1:D/8v3kj0zr8ZAKg1AQ6crr+5VwKN5eIywRkfhyM/+dE= -github.com/casbin/casbin v1.7.0 h1:PuzlE8w0JBg/DhIqnkF1Dewf3z+qmUZMVN07PonvVUQ= -github.com/casbin/casbin v1.7.0/go.mod h1:c67qKN6Oum3UF5Q1+BByfFxkwKvhwW57ITjqwtzR1KE= +github.com/casbin/casbin v1.9.1 h1:ucjbS5zTrmSLtH4XogqOG920Poe6QatdXtz1FEbApeM= +github.com/casbin/casbin v1.9.1/go.mod h1:z8uPsfBJGUsnkagrt3G8QvjgTKFMBJ32UP8HpZllfog= github.com/casbin/casbin/v2 v2.1.2/go.mod h1:YcPU1XXisHhLzuxH9coDNf2FbKpjGlbCg3n9yuLkIJQ= github.com/cenkalti/backoff v2.2.1+incompatible h1:tNowT99t7UNflLxfYYSlKYsBpXdEet03Pg2g16Swow4= github.com/cenkalti/backoff v2.2.1+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM= @@ -362,9 +356,6 @@ github.com/coreos/go-systemd/v22 v22.1.0/go.mod h1:xO0FLkIi5MaZafQlIrOotqXZ90ih+ github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= github.com/coreos/pkg v0.0.0-20160727233714-3ac0863d7acf/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= -github.com/couchbase/go-couchbase v0.0.0-20201216133707-c04035124b17/go.mod h1:+/bddYDxXsf9qt0xpDUtRR47A2GjaXmGGAqQ/k3GJ8A= -github.com/couchbase/gomemcached v0.1.2-0.20201224031647-c432ccf49f32/go.mod h1:mxliKQxOv84gQ0bJWbI+w9Wxdpt9HjDvgW9MjCym5Vo= -github.com/couchbase/goutils v0.0.0-20210118111533-e33d3ffb5401/go.mod h1:BQwMFlJzDjFDG3DJUdU0KORxn88UlsOULuxLExMh3Hs= github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= @@ -372,7 +363,6 @@ github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7Do github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/creack/pty v1.1.11 h1:07n33Z8lZxZ2qwegKbObQohDhXDQxiMMz1NOUGYlesw= github.com/creack/pty v1.1.11/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= -github.com/cupcake/rdb v0.0.0-20161107195141-43ba34106c76/go.mod h1:vYwsqCOLxGiisLwp9rITslkFNpZD5rz43tf41QFkTWY= github.com/cyphar/filepath-securejoin v0.2.2/go.mod h1:FpkQEhXnPnOthhzymB7CGsFk2G9VLXONKD9G7QGMM+4= github.com/cyphar/filepath-securejoin v0.2.3 h1:YX6ebbZCZP7VkM3scTTokDgBL2TY741X51MTk3ycuNI= github.com/cyphar/filepath-securejoin v0.2.3/go.mod h1:aPGpWjXOXUn2NCNjFvBE6aRxGGx79pTxQpKOJNYHHl4= @@ -432,9 +422,7 @@ github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21/go.mod h1 github.com/eapache/queue v1.1.0/go.mod h1:6eCeP0CKFpHLu8blIFXhExK/dRa7WDZfr6jVFPTqq+I= github.com/edsrzf/mmap-go v0.0.0-20170320065105-0bce6a688712/go.mod h1:YO35OhQPt3KJa3ryjFM5Bs14WD66h8eGKpfaBNrHW5M= github.com/edsrzf/mmap-go v1.0.0/go.mod h1:YO35OhQPt3KJa3ryjFM5Bs14WD66h8eGKpfaBNrHW5M= -github.com/elastic/go-elasticsearch/v6 v6.8.5/go.mod h1:UwaDJsD3rWLM5rKNFzv9hgox93HoX8utj1kxD9aFUcI= -github.com/elazarl/go-bindata-assetfs v1.0.0 h1:G/bYguwHIzWq9ZoyUQqrjTmJbbYn3j3CKKpKinvZLFk= -github.com/elazarl/go-bindata-assetfs v1.0.0/go.mod h1:v+YaWX3bdea5J/mo8dSETolEo7R71Vk1u8bnjau5yw4= +github.com/elazarl/go-bindata-assetfs v1.0.1 h1:m0kkaHRKEu7tUIUFVwhGGGYClXvyl4RE03qmvRTNfbw= github.com/elazarl/goproxy v0.0.0-20180725130230-947c36da3153/go.mod h1:/Zj4wYkgs4iZTTu3o/KG3Itv/qCCa8VVMlb3i9OVuzc= github.com/emicklei/go-restful v0.0.0-20170410110728-ff4f55a20633/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs= github.com/emicklei/go-restful v2.9.5+incompatible/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs= @@ -475,7 +463,6 @@ github.com/gabriel-vasile/mimetype v1.4.0/go.mod h1:fA8fi6KUiG7MgQQ+mEWotXoEOvmx github.com/ghodss/yaml v0.0.0-20150909031657-73d445a93680/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= -github.com/glendc/gopher-json v0.0.0-20170414221815-dc4743023d0c/go.mod h1:Gja1A+xZ9BoviGJNA2E9vFkPjjsl+CoJxSXiQM1UXtw= github.com/globalsign/mgo v0.0.0-20180905125535-1ca0a4f7cbcb/go.mod h1:xkRDCp4j0OGD1HRkm4kmhM+pmpv3AKq5SU7GMg4oO/Q= github.com/globalsign/mgo v0.0.0-20181015135952-eeefdecb41b8/go.mod h1:xkRDCp4j0OGD1HRkm4kmhM+pmpv3AKq5SU7GMg4oO/Q= github.com/go-asn1-ber/asn1-ber v1.5.1 h1:pDbRAunXzIUXfx4CB2QJFv5IuPiuoW+sWvr/Us009o8= @@ -578,13 +565,13 @@ github.com/go-openapi/validate v0.19.2/go.mod h1:1tRCw7m3jtI8eNWEEliiAqUIcBztB2K github.com/go-openapi/validate v0.19.3/go.mod h1:90Vh6jjkTn+OT1Eefm0ZixWNFjhtOH7vS9k0lo6zwJo= github.com/go-openapi/validate v0.19.10 h1:tG3SZ5DC5KF4cyt7nqLVcQXGj5A7mpaYkAcNPlDK+Yk= github.com/go-openapi/validate v0.19.10/go.mod h1:RKEZTUWDkxKQxN2jDT7ZnZi2bhZlbNMAuKvKB+IaGx8= -github.com/go-redis/redis v6.14.2+incompatible/go.mod h1:NAIEuMOZ/fxfXJIrKDQDz8wamY7mA7PouImQ2Jvg6kA= github.com/go-redis/redis/v8 v8.11.4 h1:kHoYkfZP6+pe04aFTnhDH6GDROa5yJdHJVNxV3F46Tg= github.com/go-redis/redis/v8 v8.11.4/go.mod h1:2Z2wHZXdQpCDXEGzqMockDpNyYvi2l4Pxt6RJr792+w= github.com/go-sql-driver/mysql v1.4.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w= github.com/go-sql-driver/mysql v1.4.1/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w= -github.com/go-sql-driver/mysql v1.5.0 h1:ozyZYNQW3x3HtqT1jira07DN2PArx2v7/mN66gGcHOs= github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= +github.com/go-sql-driver/mysql v1.6.0 h1:BCTh4TKNUYmOmMUcQ3IipzF5prigylS7XXjEkfCHuOE= +github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= github.com/go-stack/stack v1.8.0 h1:5SgMzNM5HxrEjV0ww2lTmX6E2Izsfxas4+YHWRs3Lsk= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE= @@ -949,7 +936,6 @@ github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/ktrysmt/go-bitbucket v0.6.4/go.mod h1:9u0v3hsd2rqCHRIpbir1oP7F58uo5dq19sBYvuMoyQ4= -github.com/ledisdb/ledisdb v0.0.0-20200510135210-d35789ec47e6/go.mod h1:n931TsDuKuq+uX4v1fulaMbA/7ZLLhjc85h7chZGBCQ= github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= github.com/lib/pq v1.1.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= @@ -996,8 +982,7 @@ github.com/mattn/go-shellwords v1.0.3/go.mod h1:3xCvwCdWdlDJUrvuMn7Wuy9eWs4pE8vq github.com/mattn/go-sqlite3 v1.9.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc= github.com/mattn/go-sqlite3 v1.10.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc= github.com/mattn/go-sqlite3 v1.14.6/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU= -github.com/mattn/go-sqlite3 v2.0.3+incompatible h1:gXHsfypPkaMZrKbD5209QV9jbUTJKjyR5WD3HYQSd+U= -github.com/mattn/go-sqlite3 v2.0.3+incompatible/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc= +github.com/mattn/go-sqlite3 v1.14.7 h1:fxWBnXkxfM6sRiuH3bqJ4CfzZojMOLVc0UTsTglEghA= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369 h1:I0XW9+e1XWDxdcEniV4rQAIOPUGDq67JSCiRCgGCZLI= github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= @@ -1018,8 +1003,9 @@ github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:F github.com/mitchellh/mapstructure v0.0.0-20180220230111-00c29f56e238/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= github.com/mitchellh/mapstructure v1.3.2/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= -github.com/mitchellh/mapstructure v1.4.1 h1:CpVNEelQCZBooIPDn+AR3NpivK/TIKU8bDxdASFVQag= github.com/mitchellh/mapstructure v1.4.1/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= +github.com/mitchellh/mapstructure v1.4.3 h1:OVowDSCllw/YjdLkam3/sm7wEtOy59d8ndGgCcyj8cs= +github.com/mitchellh/mapstructure v1.4.3/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= github.com/mitchellh/reflectwalk v1.0.2 h1:G2LzWKi524PWgd3mLHV8Y5k7s6XUvT0Gef6zxSIeXaQ= github.com/mitchellh/reflectwalk v1.0.2/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= github.com/moby/locker v1.0.1 h1:fOXqR41zeveg4fFODix+1Ch4mj/gT0NE1XJbp/epuBg= @@ -1136,7 +1122,6 @@ github.com/openzipkin/zipkin-go v0.2.2/go.mod h1:NaW6tEwdmWMaCDZzg8sh+IBNOxHMPnh github.com/pact-foundation/pact-go v1.0.4/go.mod h1:uExwJY4kCzNPcHRj+hCR/HBbOOIwwtUjcrb0b5/5kLM= github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= github.com/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k= -github.com/pelletier/go-toml v1.0.1/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= github.com/pelletier/go-toml v1.4.0/go.mod h1:PN7xzY2wHTK0K9p34ErDQMlFxa51Fk0OUruD3k1mMwo= github.com/pelletier/go-toml v1.7.0/go.mod h1:vwGMzjaWMwyfHwgIBhI2YUM4fB6nL6lVAvS1LBMMhTE= @@ -1146,7 +1131,6 @@ github.com/pelletier/go-toml v1.9.3/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCko github.com/performancecopilot/speed v3.0.0+incompatible/go.mod h1:/CLtqpZ5gBg1M9iaPbIdPPGyKcA8hKdoy6hAWba7Yac= github.com/peterbourgon/diskv v2.0.1+incompatible h1:UBdAOUP5p4RWqPBg048CAvpKN+vxiaj6gdUUzhl4XmI= github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU= -github.com/peterh/liner v1.0.1-0.20171122030339-3681c2a91233/go.mod h1:xIteQHvHuaLYG9IFj6mSxM0fCKrs34IrEQUhOYuGPHc= github.com/phayes/freeport v0.0.0-20220201140144-74d24b5ae9f5 h1:Ii+DKncOVM8Cu1Hc+ETb5K+23HdAMvESYE3ZJ5b5cMI= github.com/phpdave11/gofpdf v1.4.2/go.mod h1:zpO6xFn9yxo3YLyMvW8HcKWVdbNqgIfOOp2dXMnm1mY= github.com/phpdave11/gofpdi v1.0.12/go.mod h1:vBmVV0Do6hSBHC8uKUQ71JGW+ZGQq74llk/7bXwjDoI= @@ -1172,7 +1156,6 @@ github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDf github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= github.com/prometheus/client_golang v1.1.0/go.mod h1:I1FGZT9+L76gKKOs5djB6ezCbFQP1xR9D75/vuwEF3g= github.com/prometheus/client_golang v1.3.0/go.mod h1:hJaj2vgQTGQmVCsAACORcieXFeDPbaTKGT+JTgUa3og= -github.com/prometheus/client_golang v1.7.0/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M= github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M= github.com/prometheus/client_golang v1.11.0/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0= github.com/prometheus/client_golang v1.12.1/go.mod h1:3Z9XVyYiZYEO+YQWt3RD2R3jrbd179Rt297l4aS6nDY= @@ -1238,16 +1221,13 @@ github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdh github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= github.com/seccomp/libseccomp-golang v0.9.1/go.mod h1:GbW5+tmTXfcxTToHLXlScSlAvWlF4P2Ca7zGrPiEpWo= github.com/sergi/go-diff v1.1.0 h1:we8PVUC3FE2uYfodKH/nBHMSetSfHDR6scGdBi+erh0= -github.com/shiena/ansicolor v0.0.0-20151119151921-a422bbe96644 h1:X+yvsM2yrEktyI+b2qND5gpH8YhURn0k8OCaeRnkINo= -github.com/shiena/ansicolor v0.0.0-20151119151921-a422bbe96644/go.mod h1:nkxAfR/5quYxwPZhyDxgasBMnRtBZd0FCEpawpjMUFg= +github.com/shiena/ansicolor v0.0.0-20200904210342-c7312218db18 h1:DAYUYH5869yV94zvCES9F51oYtN5oGlwjxJJz7ZCnik= +github.com/shiena/ansicolor v0.0.0-20200904210342-c7312218db18/go.mod h1:nkxAfR/5quYxwPZhyDxgasBMnRtBZd0FCEpawpjMUFg= github.com/shopspring/decimal v0.0.0-20180709203117-cd690d0c9e24/go.mod h1:M+9NzErvs504Cn4c5DxATwIqPbtswREoFCre64PpcG4= github.com/shopspring/decimal v0.0.0-20200227202807-02e2044944cc/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o= github.com/shopspring/decimal v1.2.0 h1:abSATXmQEYyShuxI4/vyW3tV1MrKAJzCZ/0zLUXYbsQ= github.com/shopspring/decimal v1.2.0/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o= github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= -github.com/siddontang/go v0.0.0-20170517070808-cb568a3e5cc0/go.mod h1:3yhqj7WBBfRhbBlzyOC3gUxftwsU0u8gqevxwIHQpMw= -github.com/siddontang/goredis v0.0.0-20150324035039-760763f78400/go.mod h1:DDcKzU3qCuvj/tPnimWSsZZzvk9qvkvrIL5naVBPh5s= -github.com/siddontang/rdb v0.0.0-20150307021120-fc89ed2e418d/go.mod h1:AMEsy7v5z92TR1JKMkLLoaOQk++LVnOKL3ScbJ8GNGA= github.com/sirupsen/logrus v1.0.4-0.20170822132746-89742aefa4b2/go.mod h1:pMByvHTf9Beacp5x1UXfOR9xyW/9antXMhjMPG0dEzc= github.com/sirupsen/logrus v1.0.6/go.mod h1:pMByvHTf9Beacp5x1UXfOR9xyW/9antXMhjMPG0dEzc= github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= @@ -1292,7 +1272,6 @@ github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An github.com/spf13/viper v1.4.0/go.mod h1:PTJ7Z/lr49W6bUbkmS1V3by4uWynFiR9p7+dSq/yZzE= github.com/spf13/viper v1.8.1 h1:Kq1fyeebqsBfbjZj4EL7gj2IO0mMaiyjYUWcUsl2O44= github.com/spf13/viper v1.8.1/go.mod h1:o0Pch8wJ9BVSWGQMbra6iw0oQ5oktSIBaujf1rJH9Ns= -github.com/ssdb/gossdb v0.0.0-20180723034631-88f6b59b84ec/go.mod h1:QBvMkMya+gXctz3kmljlUCu/yB3GZ6oee+dUozsezQE= github.com/stefanberger/go-pkcs11uri v0.0.0-20201008174630-78d3cae3a980/go.mod h1:AO3tvPzVZ/ayst6UlUKUv6rcPQInYe3IknH3jYhAKu8= github.com/stoewer/go-strcase v1.2.0/go.mod h1:IBiWB2sKIp3wVVQ3Y035++gc+knqhUQag1KpM8ahLw8= github.com/streadway/amqp v0.0.0-20190404075320-75d898a42a94/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw= @@ -1319,7 +1298,6 @@ github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69 github.com/syndtr/gocapability v0.0.0-20170704070218-db04d3cc01c8/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww= github.com/syndtr/gocapability v0.0.0-20180916011248-d98352740cb2/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww= github.com/syndtr/gocapability v0.0.0-20200815063812-42c35b437635/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww= -github.com/syndtr/goleveldb v0.0.0-20160425020131-cfa635847112/go.mod h1:Z4AUp2Km+PwemOoO/VB5AOx9XSsIItzFjoJlOSiYmn0= github.com/tchap/go-patricia v2.2.6+incompatible/go.mod h1:bmLyhP68RS6kStMGxByiQ23RP/odRBOTVjwp2cDyi6I= github.com/tencentcloud/tencentcloud-sdk-go v1.0.62 h1:Vnr3IqaafEuQUciG6D6EaeLJm26Mg8sjAfbI4OoeauM= github.com/tencentcloud/tencentcloud-sdk-go v1.0.62/go.mod h1:asUz5BPXxgoPGaRgZaVm1iGcUAuHyYUo1nXqKa83cvI= @@ -1329,7 +1307,6 @@ github.com/tidwall/pretty v1.0.0 h1:HsD+QiTn7sK6flMKIvNmpqz1qrpP3Ps6jOKIKMooyg4= github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk= github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= -github.com/ugorji/go v0.0.0-20171122102828-84cb69a8af83/go.mod h1:hnLbHMwcvSihnDhEfx2/BzKp2xb0Y+ErdfYcrs9tkJQ= github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc= github.com/urfave/cli v0.0.0-20171014202726-7bc6a0acffa5/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA= github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA= @@ -1345,7 +1322,6 @@ github.com/vmihailenco/msgpack/v5 v5.0.0-rc.2 h1:ognci8XPlosGhIHK1OLYSpSpnlhSFeB github.com/vmihailenco/msgpack/v5 v5.0.0-rc.2/go.mod h1:HVxBVPUK/+fZMonk4bi1islLa8V3cfnBug0+4dykPzo= github.com/vmihailenco/tagparser v0.1.2 h1:gnjoVuB/kljJ5wICEEOpx98oXMWPLj22G67Vbd1qPqc= github.com/vmihailenco/tagparser v0.1.2/go.mod h1:OeAg3pn3UbLjkWt+rN9oFYB6u/cQgqMEUPoW2WPyhdI= -github.com/wendal/errors v0.0.0-20181209125328-7f31f4b264ec/go.mod h1:Q12BUT7DqIlHRmgv3RskH+UCM/4eqVMgI0EMmlSpAXc= github.com/willf/bitset v1.1.11-0.20200630133818-d5bec3311243/go.mod h1:RjeCKbqT1RxIR/KWY6phxZiaY1IyutSBfGjNPySAYV4= github.com/willf/bitset v1.1.11/go.mod h1:83CECat5yLh5zVOf4P1ErAgKA5UDvKtgyUABdr3+MjI= github.com/xanzy/go-gitlab v0.15.0/go.mod h1:8zdQa/ri1dfn8eS3Ir1SyfvOKlw7WBJ8DVThkpGiXrs= @@ -1371,7 +1347,6 @@ github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9de github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= -github.com/yuin/gopher-lua v0.0.0-20171031051903-609c9cd26973/go.mod h1:aEV29XrmTYFr3CiRxZeGHpkvbwq+prZduBqMaascyCU= github.com/yvasiyarov/go-metrics v0.0.0-20140926110328-57bccd1ccd43 h1:+lm10QQTNSBd8DVTNGHx7o/IKu9HYDvLMffDhbyLccI= github.com/yvasiyarov/gorelic v0.0.0-20141212073537-a9bba5b9ab50 h1:hlE8//ciYMztlGpl/VA+Zm1AcTPHYkHJPbHqE6WJUXE= github.com/yvasiyarov/newrelic_platform_go v0.0.0-20140908184405-b21fdbd4370f h1:ERexzlUfuTvpE74urLSbIQW0Z/6hF9t8U4NsJLaioAY= @@ -1411,8 +1386,9 @@ go.opentelemetry.io/contrib/propagators v0.22.0/go.mod h1:xGOuXr6lLIF9BXipA4pm6U go.opentelemetry.io/otel v1.0.0-RC1/go.mod h1:x9tRa9HK4hSSq7jf2TKbqFbtt58/TGk0f9XiEYISI1I= go.opentelemetry.io/otel v1.0.0-RC2/go.mod h1:w1thVQ7qbAy8MHb0IFj8a5Q2QU0l2ksf8u/CN8m3NOM= go.opentelemetry.io/otel v1.0.0/go.mod h1:AjRVh9A5/5DE7S+mZtTR6t8vpKKryam+0lREnfmS4cg= -go.opentelemetry.io/otel v1.3.0 h1:APxLf0eiBwLl+SOXiJJCVYzA1OOJNyAoV8C5RNRyy7Y= go.opentelemetry.io/otel v1.3.0/go.mod h1:PWIKzi6JCp7sM0k9yZ43VX+T345uNbAkDKwHVjb2PTs= +go.opentelemetry.io/otel v1.8.0 h1:zcvBFizPbpa1q7FehvFiHbQwGzmPILebO0tyqIR5Djg= +go.opentelemetry.io/otel v1.8.0/go.mod h1:2pkj+iMj0o03Y+cW6/m8Y4WkRdYN3AvCXCnzRMp9yvM= go.opentelemetry.io/otel/exporters/jaeger v1.0.0 h1:cLhx8llHw02h5JTqGqaRbYn+QVKHmrzD9vEbKnSPk5U= go.opentelemetry.io/otel/exporters/jaeger v1.0.0/go.mod h1:q10N1AolE1JjqKrFJK2tYw0iZpmX+HBaXBtuCzRnBGQ= go.opentelemetry.io/otel/exporters/otlp/internal/retry v1.3.0 h1:R/OBkMoGgfy2fLhs2QhkCI1w4HLEQX92GCcJB6SSdNk= @@ -1429,13 +1405,15 @@ go.opentelemetry.io/otel/oteltest v1.0.0-RC1/go.mod h1:+eoIG0gdEOaPNftuy1YScLr1G go.opentelemetry.io/otel/oteltest v1.0.0-RC2 h1:xNKqMhlZYkASSyvF4JwObZFMq0jhFN3c3SP+2rCzVPk= go.opentelemetry.io/otel/oteltest v1.0.0-RC2/go.mod h1:kiQ4tw5tAL4JLTbcOYwK1CWI1HkT5aiLzHovgOVnz/A= go.opentelemetry.io/otel/sdk v1.0.0/go.mod h1:PCrDHlSy5x1kjezSdL37PhbFUMjrsLRshJ2zCzeXwbM= -go.opentelemetry.io/otel/sdk v1.3.0 h1:3278edCoH89MEJ0Ky8WQXVmDQv3FX4ZJ3Pp+9fJreAI= go.opentelemetry.io/otel/sdk v1.3.0/go.mod h1:rIo4suHNhQwBIPg9axF8V9CA72Wz2mKF1teNrup8yzs= +go.opentelemetry.io/otel/sdk v1.8.0 h1:xwu69/fNuwbSHWe/0PGS888RmjWY181OmcXDQKu7ZQk= +go.opentelemetry.io/otel/sdk v1.8.0/go.mod h1:uPSfc+yfDH2StDM/Rm35WE8gXSNdvCg023J6HeGNO0c= go.opentelemetry.io/otel/trace v1.0.0-RC1/go.mod h1:86UHmyHWFEtWjfWPSbu0+d0Pf9Q6e1U+3ViBOc+NXAg= go.opentelemetry.io/otel/trace v1.0.0-RC2/go.mod h1:JPQ+z6nNw9mqEGT8o3eoPTdnNI+Aj5JcxEsVGREIAy4= go.opentelemetry.io/otel/trace v1.0.0/go.mod h1:PXTWqayeFUlJV1YDNhsJYB184+IvAH814St6o6ajzIs= -go.opentelemetry.io/otel/trace v1.3.0 h1:doy8Hzb1RJ+I3yFhtDmwNc7tIyw1tNMOIsyPzp1NOGY= go.opentelemetry.io/otel/trace v1.3.0/go.mod h1:c/VDhno8888bvQYmbYLqe41/Ldmr/KKunbvWM4/fEjk= +go.opentelemetry.io/otel/trace v1.8.0 h1:cSy0DF9eGI5WIfNwZ1q2iUyGj00tGzP24dE1lOlHrfY= +go.opentelemetry.io/otel/trace v1.8.0/go.mod h1:0Bt3PXY8w+3pheS3hQUt+wow8b1ojPaTBoTCh2zIFI4= go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI= go.opentelemetry.io/proto/otlp v0.11.0 h1:cLDgIBTf4lLOlztkhzAEdQsJ4Lj+i5Wc9k6Nn0K1VyU= go.opentelemetry.io/proto/otlp v0.11.0/go.mod h1:QpEjXPrNQzrFDZgoTo49dgHR9RYRSrg3NAKnUGl9YpQ= @@ -1483,7 +1461,6 @@ golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPh golang.org/x/crypto v0.0.0-20200728195943-123391ffb6de/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20201002170205-7f63de1d35b0/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20201203163018-be400aefbc4c/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= -golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= golang.org/x/crypto v0.0.0-20210616213533-5ff15b29337e/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20210817164053-32db794688a5/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= @@ -1934,14 +1911,18 @@ gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8 gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= gopkg.in/cheggaaa/pb.v1 v1.0.25/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qStrOgw= +gopkg.in/dancannon/gorethink.v3 v3.0.5 h1:/g7PWP7zUS6vSNmHSDbjCHQh1Rqn8Jy6zSMQxAsBSMQ= gopkg.in/dancannon/gorethink.v3 v3.0.5/go.mod h1:GXsi1e3N2OcKhcP6nsYABTiUejbWMFO4GY5a4pEaeEc= gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= +gopkg.in/fatih/pool.v2 v2.0.0 h1:xIFeWtxifuQJGk/IEPKsTduEKcKvPmhoiVDGpC40nKg= gopkg.in/fatih/pool.v2 v2.0.0/go.mod h1:8xVGeu1/2jr2wm5V9SPuMht2H5AEmf5aFMGSQixtjTY= gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= gopkg.in/gcfg.v1 v1.2.3/go.mod h1:yesOnuUOFQAhST5vPY4nbZsb/huCgGGXlipJsBn0b3o= gopkg.in/gemnasium/logrus-airbrake-hook.v2 v2.1.2/go.mod h1:Xk6kEKp8OKb+X14hQBKWaSkCsqBpgog8nAV2xsGOxlo= +gopkg.in/gorethink/gorethink.v3 v3.0.5 h1:e2Uc/Xe+hpcVQFsj6MuHlYog3r0JYpnTzwDj/y2O4MU= gopkg.in/gorethink/gorethink.v3 v3.0.5/go.mod h1:+3yIIHJUGMBK+wyPH+iN5TP+88ikFDfZdqTlK3Y9q8I= gopkg.in/h2non/gentleman.v1 v1.0.4/go.mod h1:JYuHVdFzS4MKOXe0o+chKJ4hCe6tqKKw9XH9YP6WFrg= gopkg.in/h2non/gock.v1 v1.0.16 h1:F11k+OafeuFENsjei5t2vMTSTs9L62AdyTe4E1cgdG8= @@ -1952,13 +1933,13 @@ gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= gopkg.in/ini.v1 v1.42.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/ini.v1 v1.62.0 h1:duBzk771uxoUuOlyRLkHsygud9+5lrlGjdFBb4mSKDU= gopkg.in/ini.v1 v1.62.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= -gopkg.in/mgo.v2 v2.0.0-20190816093944-a6b53ec6cb22/go.mod h1:yeKp02qBN3iKW1OzL3MGk2IdtZzaj7SFntXj72NppTA= gopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k= gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo= gopkg.in/square/go-jose.v2 v2.2.2/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI= gopkg.in/square/go-jose.v2 v2.3.1/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI= gopkg.in/square/go-jose.v2 v2.5.1 h1:7odma5RETjNHWJnR32wx8t+Io4djHE1PqxCFx3iiZ2w= gopkg.in/square/go-jose.v2 v2.5.1/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI= +gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI= gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74= @@ -1981,8 +1962,10 @@ gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gorm.io/driver/postgres v1.0.8/go.mod h1:4eOzrI1MUfm6ObJU/UcmbXyiHSs8jSwH95G5P5dxcAg= gorm.io/gorm v1.20.12/go.mod h1:0HFTzE/SqkGTzK6TlDPPQbAYCluiVvhzoA1+aVyzenw= gorm.io/gorm v1.21.4/go.mod h1:0HFTzE/SqkGTzK6TlDPPQbAYCluiVvhzoA1+aVyzenw= +gotest.tools v2.2.0+incompatible h1:VsBPFP1AI068pPrMxtb/S8Zkgf9xEmTLJjfM+P5UIEo= gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw= gotest.tools/v3 v3.0.2/go.mod h1:3SzNCllyD9/Y+b5r9JIKQ474KzkZyqLqEfYqMsX94Bk= +gotest.tools/v3 v3.0.3 h1:4AuOwCGf4lLR9u3YOe2awrHygurzhO/HeQ6laiA6Sx0= gotest.tools/v3 v3.0.3/go.mod h1:Z7Lb0S5l+klDB31fvDQX8ss/FlKDxtlFlw3Oa8Ymbl8= helm.sh/helm/v3 v3.10.1 h1:uTnNlYx8QcTSNA4ZJ50Llwife4CSohUY4ehumyVf2QE= helm.sh/helm/v3 v3.10.1/go.mod h1:CXOcs02AYvrlPMWARNYNRgf2rNP7gLJQsi/Ubd4EDrI= diff --git a/src/jobservice/job/impl/context.go b/src/jobservice/job/impl/context.go index 0a4bf9219..ed4a0beb6 100644 --- a/src/jobservice/job/impl/context.go +++ b/src/jobservice/job/impl/context.go @@ -22,7 +22,7 @@ import ( "sync" "time" - o "github.com/beego/beego/orm" + o "github.com/beego/beego/v2/client/orm" "github.com/goharbor/harbor/src/common/dao" "github.com/goharbor/harbor/src/jobservice/config" diff --git a/src/jobservice/job/impl/default_context.go b/src/jobservice/job/impl/default_context.go index 34824df40..9b8da36c7 100644 --- a/src/jobservice/job/impl/default_context.go +++ b/src/jobservice/job/impl/default_context.go @@ -18,7 +18,7 @@ import ( "context" "errors" - o "github.com/beego/beego/orm" + o "github.com/beego/beego/v2/client/orm" "github.com/goharbor/harbor/src/jobservice/job" "github.com/goharbor/harbor/src/jobservice/logger" diff --git a/src/jobservice/sync/schedule.go b/src/jobservice/sync/schedule.go index 5801369c2..67ef3ede9 100644 --- a/src/jobservice/sync/schedule.go +++ b/src/jobservice/sync/schedule.go @@ -19,7 +19,7 @@ import ( "fmt" "time" - o "github.com/beego/beego/orm" + o "github.com/beego/beego/v2/client/orm" "github.com/goharbor/harbor/src/jobservice/config" "github.com/goharbor/harbor/src/jobservice/env" diff --git a/src/lib/config/models/model.go b/src/lib/config/models/model.go index 780c5577e..ad1d1c844 100644 --- a/src/lib/config/models/model.go +++ b/src/lib/config/models/model.go @@ -15,7 +15,7 @@ package models import ( - "github.com/beego/beego/orm" + "github.com/beego/beego/v2/client/orm" ) // HTTPAuthProxy wraps the settings for HTTP auth proxy diff --git a/src/lib/orm/creator.go b/src/lib/orm/creator.go index 5867ff17c..099298b4a 100644 --- a/src/lib/orm/creator.go +++ b/src/lib/orm/creator.go @@ -14,7 +14,7 @@ package orm -import "github.com/beego/beego/orm" +import "github.com/beego/beego/v2/client/orm" var ( // Crt is a global instance of ORM creator diff --git a/src/lib/orm/error.go b/src/lib/orm/error.go index 1c5cd8aa1..0d8ea034d 100644 --- a/src/lib/orm/error.go +++ b/src/lib/orm/error.go @@ -15,7 +15,7 @@ package orm import ( - "github.com/beego/beego/orm" + "github.com/beego/beego/v2/client/orm" "github.com/jackc/pgconn" "github.com/goharbor/harbor/src/lib/errors" diff --git a/src/lib/orm/error_test.go b/src/lib/orm/error_test.go index 67e772504..826678ccf 100644 --- a/src/lib/orm/error_test.go +++ b/src/lib/orm/error_test.go @@ -17,7 +17,7 @@ package orm import ( "testing" - "github.com/beego/beego/orm" + "github.com/beego/beego/v2/client/orm" "github.com/jackc/pgconn" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" diff --git a/src/lib/orm/metadata.go b/src/lib/orm/metadata.go index 433f38c8a..505aee5c0 100644 --- a/src/lib/orm/metadata.go +++ b/src/lib/orm/metadata.go @@ -21,7 +21,7 @@ import ( "sync" "unicode" - "github.com/beego/beego/orm" + "github.com/beego/beego/v2/client/orm" "github.com/goharbor/harbor/src/lib/q" ) diff --git a/src/lib/orm/metadata_test.go b/src/lib/orm/metadata_test.go index f00c2f207..87e918692 100644 --- a/src/lib/orm/metadata_test.go +++ b/src/lib/orm/metadata_test.go @@ -18,7 +18,7 @@ import ( "context" "testing" - "github.com/beego/beego/orm" + "github.com/beego/beego/v2/client/orm" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" diff --git a/src/lib/orm/orm.go b/src/lib/orm/orm.go index 26ac3bc74..a04d48319 100644 --- a/src/lib/orm/orm.go +++ b/src/lib/orm/orm.go @@ -16,15 +16,15 @@ package orm import ( "context" - "errors" "fmt" "os" "strconv" "strings" "time" - "github.com/beego/beego/orm" + "github.com/beego/beego/v2/client/orm" + "github.com/goharbor/harbor/src/lib/errors" "github.com/goharbor/harbor/src/lib/log" tracelib "github.com/goharbor/harbor/src/lib/trace" ) @@ -71,8 +71,8 @@ func init() { } // FromContext returns orm from context -func FromContext(ctx context.Context) (orm.Ormer, error) { - o, ok := ctx.Value(ormKey{}).(orm.Ormer) +func FromContext(ctx context.Context) (orm.QueryExecutor, error) { + o, ok := ctx.Value(ormKey{}).(orm.QueryExecutor) if !ok { return nil, errors.New("cannot get the ORM from context") } @@ -80,7 +80,7 @@ func FromContext(ctx context.Context) (orm.Ormer, error) { } // NewContext returns new context with orm -func NewContext(ctx context.Context, o orm.Ormer) context.Context { +func NewContext(ctx context.Context, o orm.QueryExecutor) context.Context { if ctx == nil { ctx = context.Background() } @@ -105,7 +105,7 @@ func Copy(ctx context.Context) context.Context { type operationNameKey struct{} -// SetTransactionOpName sets the transaction operation name +// SetTransactionOpNameToContext sets the transaction operation name func SetTransactionOpNameToContext(ctx context.Context, name string) context.Context { if ctx == nil { ctx = context.Background() @@ -136,13 +136,24 @@ func WithTransaction(f func(ctx context.Context) error) func(ctx context.Context return err } - tx := ormerTx{Ormer: o} + var tx ormerTx + if _, ok := o.(orm.Ormer); ok { + tx = ormerTx{Ormer: o.(orm.Ormer)} + } else if _, ok := o.(orm.TxOrmer); ok { + tx = ormerTx{TxOrmer: o.(orm.TxOrmer)} + } else { + return errors.New("no orm found in the context") + } + if err := tx.Begin(); err != nil { tracelib.RecordError(span, err, "begin transaction failed") log.Errorf("begin transaction failed: %v", err) return err } + // When set multiple times, context.WithValue returns only the last ormer. + // To ensure that the rollback works, set TxOrmer as the ormer in the transaction. + cx = NewContext(cx, tx.TxOrmer) if err := f(cx); err != nil { span.AddEvent("rollback transaction") if e := tx.Rollback(); e != nil { @@ -164,7 +175,7 @@ func WithTransaction(f func(ctx context.Context) error) func(ctx context.Context } } -// ReadOrCreate read or create instance to datebase, retry to read when met a duplicate key error after the creating +// ReadOrCreate read or create instance to database, retry to read when met a duplicate key error after the creating func ReadOrCreate(ctx context.Context, md interface{}, col1 string, cols ...string) (created bool, id int64, err error) { getter, ok := md.(interface { GetID() int64 diff --git a/src/lib/orm/query.go b/src/lib/orm/query.go index 815d3c582..6a2233d9c 100644 --- a/src/lib/orm/query.go +++ b/src/lib/orm/query.go @@ -20,7 +20,7 @@ import ( "reflect" "strings" - "github.com/beego/beego/orm" + "github.com/beego/beego/v2/client/orm" "github.com/goharbor/harbor/src/lib/q" ) diff --git a/src/lib/orm/test/orm_test.go b/src/lib/orm/test/orm_test.go index ce36d5ed3..b9046c931 100644 --- a/src/lib/orm/test/orm_test.go +++ b/src/lib/orm/test/orm_test.go @@ -20,7 +20,7 @@ import ( "sync" "testing" - "github.com/beego/beego/orm" + "github.com/beego/beego/v2/client/orm" "github.com/stretchr/testify/suite" "github.com/goharbor/harbor/src/common/dao" diff --git a/src/lib/orm/tx.go b/src/lib/orm/tx.go index 0bfb69ce6..546a66463 100644 --- a/src/lib/orm/tx.go +++ b/src/lib/orm/tx.go @@ -19,7 +19,7 @@ import ( "encoding/hex" "fmt" - "github.com/beego/beego/orm" + "github.com/beego/beego/v2/client/orm" "github.com/google/uuid" ) @@ -37,6 +37,7 @@ func HasCommittedKey(ctx context.Context) bool { // ormerTx transaction which support savepoint type ormerTx struct { orm.Ormer + orm.TxOrmer savepoint string } @@ -48,28 +49,30 @@ func (o *ormerTx) createSavepoint() error { val := uuid.New() o.savepoint = fmt.Sprintf("p%s", hex.EncodeToString(val[:])) - _, err := o.Raw(fmt.Sprintf("SAVEPOINT %s", o.savepoint)).Exec() + _, err := o.TxOrmer.Raw(fmt.Sprintf("SAVEPOINT %s", o.savepoint)).Exec() return err } func (o *ormerTx) releaseSavepoint() error { - _, err := o.Raw(fmt.Sprintf("RELEASE SAVEPOINT %s", o.savepoint)).Exec() + _, err := o.TxOrmer.Raw(fmt.Sprintf("RELEASE SAVEPOINT %s", o.savepoint)).Exec() return err } func (o *ormerTx) rollbackToSavepoint() error { - _, err := o.Raw(fmt.Sprintf("ROLLBACK TO SAVEPOINT %s", o.savepoint)).Exec() + _, err := o.TxOrmer.Raw(fmt.Sprintf("ROLLBACK TO SAVEPOINT %s", o.savepoint)).Exec() return err } func (o *ormerTx) Begin() error { - err := o.Ormer.Begin() - if err == orm.ErrTxHasBegan { - // transaction has began for the ormer, so begin nested transaction by savepoint + if o.TxOrmer != nil { return o.createSavepoint() } - - return err + txOrmer, err := o.Ormer.Begin() + if err != nil { + return err + } + o.TxOrmer = txOrmer + return nil } func (o *ormerTx) Commit() error { @@ -77,7 +80,7 @@ func (o *ormerTx) Commit() error { return o.releaseSavepoint() } - return o.Ormer.Commit() + return o.TxOrmer.Commit() } func (o *ormerTx) Rollback() error { @@ -85,5 +88,5 @@ func (o *ormerTx) Rollback() error { return o.rollbackToSavepoint() } - return o.Ormer.Rollback() + return o.TxOrmer.Rollback() } diff --git a/src/migration/migration.go b/src/migration/migration.go index 396dcf543..9f08c3236 100644 --- a/src/migration/migration.go +++ b/src/migration/migration.go @@ -19,7 +19,7 @@ import ( "fmt" "time" - beegorm "github.com/beego/beego/orm" + beegorm "github.com/beego/beego/v2/client/orm" "github.com/golang-migrate/migrate/v4" "github.com/goharbor/harbor/src/common/dao" diff --git a/src/pkg/accessory/dao/dao_test.go b/src/pkg/accessory/dao/dao_test.go index aa1b02750..660224f60 100644 --- a/src/pkg/accessory/dao/dao_test.go +++ b/src/pkg/accessory/dao/dao_test.go @@ -19,7 +19,7 @@ import ( "fmt" "testing" - beegoorm "github.com/beego/beego/orm" + beegoorm "github.com/beego/beego/v2/client/orm" "github.com/stretchr/testify/suite" common_dao "github.com/goharbor/harbor/src/common/dao" diff --git a/src/pkg/accessory/dao/model.go b/src/pkg/accessory/dao/model.go index 810de4643..1c0f64e4a 100644 --- a/src/pkg/accessory/dao/model.go +++ b/src/pkg/accessory/dao/model.go @@ -17,7 +17,7 @@ package dao import ( "time" - "github.com/beego/beego/orm" + "github.com/beego/beego/v2/client/orm" ) func init() { diff --git a/src/pkg/allowlist/dao/dao.go b/src/pkg/allowlist/dao/dao.go index c9d4a3916..404511fda 100644 --- a/src/pkg/allowlist/dao/dao.go +++ b/src/pkg/allowlist/dao/dao.go @@ -6,7 +6,7 @@ import ( "fmt" "time" - beegoorm "github.com/beego/beego/orm" + beegoorm "github.com/beego/beego/v2/client/orm" "github.com/goharbor/harbor/src/lib/log" "github.com/goharbor/harbor/src/lib/orm" diff --git a/src/pkg/artifact/dao/dao.go b/src/pkg/artifact/dao/dao.go index 7dac2a83f..aaa87c7a6 100644 --- a/src/pkg/artifact/dao/dao.go +++ b/src/pkg/artifact/dao/dao.go @@ -20,7 +20,7 @@ import ( "strings" "time" - beegoorm "github.com/beego/beego/orm" + beegoorm "github.com/beego/beego/v2/client/orm" "github.com/goharbor/harbor/src/lib/errors" "github.com/goharbor/harbor/src/lib/orm" diff --git a/src/pkg/artifact/dao/dao_test.go b/src/pkg/artifact/dao/dao_test.go index e92f5bd3a..adefcfff7 100644 --- a/src/pkg/artifact/dao/dao_test.go +++ b/src/pkg/artifact/dao/dao_test.go @@ -19,7 +19,7 @@ import ( "testing" "time" - beegoorm "github.com/beego/beego/orm" + beegoorm "github.com/beego/beego/v2/client/orm" v1 "github.com/opencontainers/image-spec/specs-go/v1" "github.com/stretchr/testify/suite" diff --git a/src/pkg/artifact/dao/model.go b/src/pkg/artifact/dao/model.go index 1de3a171e..4ce548619 100644 --- a/src/pkg/artifact/dao/model.go +++ b/src/pkg/artifact/dao/model.go @@ -17,7 +17,7 @@ package dao import ( "time" - "github.com/beego/beego/orm" + "github.com/beego/beego/v2/client/orm" "github.com/goharbor/harbor/src/lib/q" ) diff --git a/src/pkg/artifactrash/dao/dao_test.go b/src/pkg/artifactrash/dao/dao_test.go index 6e53ebb2d..e4099e8fe 100644 --- a/src/pkg/artifactrash/dao/dao_test.go +++ b/src/pkg/artifactrash/dao/dao_test.go @@ -5,7 +5,7 @@ import ( "testing" "time" - beegoorm "github.com/beego/beego/orm" + beegoorm "github.com/beego/beego/v2/client/orm" v1 "github.com/opencontainers/image-spec/specs-go/v1" "github.com/stretchr/testify/suite" diff --git a/src/pkg/artifactrash/model/model.go b/src/pkg/artifactrash/model/model.go index ad654d5c0..fc94dafff 100644 --- a/src/pkg/artifactrash/model/model.go +++ b/src/pkg/artifactrash/model/model.go @@ -18,7 +18,7 @@ import ( "fmt" "time" - "github.com/beego/beego/orm" + "github.com/beego/beego/v2/client/orm" ) func init() { diff --git a/src/pkg/audit/dao/dao.go b/src/pkg/audit/dao/dao.go index e771a1848..019ffe6d5 100644 --- a/src/pkg/audit/dao/dao.go +++ b/src/pkg/audit/dao/dao.go @@ -18,7 +18,7 @@ import ( "context" "strings" - beegorm "github.com/beego/beego/orm" + beegorm "github.com/beego/beego/v2/client/orm" "github.com/goharbor/harbor/src/common/rbac" "github.com/goharbor/harbor/src/lib/errors" @@ -90,7 +90,7 @@ func (*dao) Purge(ctx context.Context, retentionHour int, includeOperations []st return delRows, err } -func dryRunPurge(ormer beegorm.Ormer, retentionHour int, includeOperations []string) (int64, error) { +func dryRunPurge(ormer beegorm.QueryExecutor, retentionHour int, includeOperations []string) (int64, error) { sql := "SELECT count(1) cnt FROM audit_log WHERE op_time < NOW() - ? * interval '1 hour' " filterOps := permitOps(includeOperations) if len(filterOps) == 0 { diff --git a/src/pkg/audit/dao/dao_test.go b/src/pkg/audit/dao/dao_test.go index 95d8f3641..b1103adb4 100644 --- a/src/pkg/audit/dao/dao_test.go +++ b/src/pkg/audit/dao/dao_test.go @@ -20,7 +20,7 @@ import ( "testing" "time" - beegoorm "github.com/beego/beego/orm" + beegoorm "github.com/beego/beego/v2/client/orm" "github.com/stretchr/testify/suite" common_dao "github.com/goharbor/harbor/src/common/dao" diff --git a/src/pkg/audit/model/model.go b/src/pkg/audit/model/model.go index 6d624fe19..0e477276a 100644 --- a/src/pkg/audit/model/model.go +++ b/src/pkg/audit/model/model.go @@ -3,7 +3,7 @@ package model import ( "time" - beego_orm "github.com/beego/beego/orm" + beego_orm "github.com/beego/beego/v2/client/orm" ) func init() { diff --git a/src/pkg/blob/models/blob.go b/src/pkg/blob/models/blob.go index cdb93516b..24c55f2d2 100644 --- a/src/pkg/blob/models/blob.go +++ b/src/pkg/blob/models/blob.go @@ -20,7 +20,7 @@ import ( "strings" "time" - "github.com/beego/beego/orm" + "github.com/beego/beego/v2/client/orm" "github.com/docker/distribution/manifest/manifestlist" "github.com/docker/distribution/manifest/schema1" "github.com/docker/distribution/manifest/schema2" diff --git a/src/pkg/immutable/dao/model/rule.go b/src/pkg/immutable/dao/model/rule.go index 94d1db141..19bc80e19 100644 --- a/src/pkg/immutable/dao/model/rule.go +++ b/src/pkg/immutable/dao/model/rule.go @@ -1,7 +1,7 @@ package model import ( - "github.com/beego/beego/orm" + "github.com/beego/beego/v2/client/orm" ) func init() { diff --git a/src/pkg/immutable/model/rule.go b/src/pkg/immutable/model/rule.go index 8c3e6ed7e..65136507d 100644 --- a/src/pkg/immutable/model/rule.go +++ b/src/pkg/immutable/model/rule.go @@ -1,7 +1,7 @@ package model import ( - "github.com/beego/beego/validation" + "github.com/beego/beego/v2/core/validation" ) // Metadata of the immutable rule diff --git a/src/pkg/joblog/models/joblog.go b/src/pkg/joblog/models/joblog.go index 9576d6ee9..ba6c00cb1 100644 --- a/src/pkg/joblog/models/joblog.go +++ b/src/pkg/joblog/models/joblog.go @@ -3,7 +3,7 @@ package models import ( "time" - "github.com/beego/beego/orm" + "github.com/beego/beego/v2/client/orm" ) func init() { diff --git a/src/pkg/label/dao/dao_test.go b/src/pkg/label/dao/dao_test.go index 371aa43cc..7049f0db1 100644 --- a/src/pkg/label/dao/dao_test.go +++ b/src/pkg/label/dao/dao_test.go @@ -18,7 +18,7 @@ import ( "context" "testing" - beegoorm "github.com/beego/beego/orm" + beegoorm "github.com/beego/beego/v2/client/orm" v1 "github.com/opencontainers/image-spec/specs-go/v1" "github.com/stretchr/testify/suite" diff --git a/src/pkg/label/model/model.go b/src/pkg/label/model/model.go index 258298610..2d1128f6a 100644 --- a/src/pkg/label/model/model.go +++ b/src/pkg/label/model/model.go @@ -3,7 +3,7 @@ package model import ( "time" - "github.com/beego/beego/orm" + "github.com/beego/beego/v2/client/orm" "github.com/goharbor/harbor/src/common" "github.com/goharbor/harbor/src/lib/errors" diff --git a/src/pkg/notification/job/model/model.go b/src/pkg/notification/job/model/model.go index bf7830177..8d96c5a3a 100644 --- a/src/pkg/notification/job/model/model.go +++ b/src/pkg/notification/job/model/model.go @@ -3,7 +3,7 @@ package model import ( "time" - "github.com/beego/beego/orm" + "github.com/beego/beego/v2/client/orm" ) func init() { diff --git a/src/pkg/notification/policy/model/model.go b/src/pkg/notification/policy/model/model.go index 581772d2b..c7a531bb9 100644 --- a/src/pkg/notification/policy/model/model.go +++ b/src/pkg/notification/policy/model/model.go @@ -4,7 +4,7 @@ import ( "encoding/json" "time" - "github.com/beego/beego/orm" + "github.com/beego/beego/v2/client/orm" ) func init() { diff --git a/src/pkg/p2p/preheat/dao/instance/dao.go b/src/pkg/p2p/preheat/dao/instance/dao.go index 4acb211f4..32b8606da 100644 --- a/src/pkg/p2p/preheat/dao/instance/dao.go +++ b/src/pkg/p2p/preheat/dao/instance/dao.go @@ -4,8 +4,6 @@ import ( "context" "fmt" - beego_orm "github.com/beego/beego/orm" - "github.com/goharbor/harbor/src/lib/orm" "github.com/goharbor/harbor/src/lib/q" "github.com/goharbor/harbor/src/pkg/p2p/preheat/models/provider" @@ -40,8 +38,7 @@ var _ DAO = (*dao)(nil) // Create adds a new distribution instance. func (d *dao) Create(ctx context.Context, instance *provider.Instance) (id int64, err error) { - var o beego_orm.Ormer - o, err = orm.FromContext(ctx) + o, err := orm.FromContext(ctx) if err != nil { return } diff --git a/src/pkg/p2p/preheat/dao/instance/dao_test.go b/src/pkg/p2p/preheat/dao/instance/dao_test.go index 1ce525232..cc2b6e4ca 100644 --- a/src/pkg/p2p/preheat/dao/instance/dao_test.go +++ b/src/pkg/p2p/preheat/dao/instance/dao_test.go @@ -4,7 +4,7 @@ import ( "context" "testing" - beego_orm "github.com/beego/beego/orm" + beego_orm "github.com/beego/beego/v2/client/orm" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/suite" diff --git a/src/pkg/p2p/preheat/dao/policy/dao.go b/src/pkg/p2p/preheat/dao/policy/dao.go index f1e4da130..25b44b460 100644 --- a/src/pkg/p2p/preheat/dao/policy/dao.go +++ b/src/pkg/p2p/preheat/dao/policy/dao.go @@ -17,7 +17,7 @@ package policy import ( "context" - beego_orm "github.com/beego/beego/orm" + beego_orm "github.com/beego/beego/v2/client/orm" "github.com/goharbor/harbor/src/lib/errors" "github.com/goharbor/harbor/src/lib/orm" @@ -62,8 +62,7 @@ func (d *dao) Count(ctx context.Context, query *q.Query) (total int64, err error // Create a policy schema. func (d *dao) Create(ctx context.Context, schema *policy.Schema) (id int64, err error) { - var ormer beego_orm.Ormer - ormer, err = orm.FromContext(ctx) + ormer, err := orm.FromContext(ctx) if err != nil { return } @@ -81,8 +80,7 @@ func (d *dao) Create(ctx context.Context, schema *policy.Schema) (id int64, err // Update a policy schema. func (d *dao) Update(ctx context.Context, schema *policy.Schema, props ...string) (err error) { - var ormer beego_orm.Ormer - ormer, err = orm.FromContext(ctx) + ormer, err := orm.FromContext(ctx) if err != nil { return err } @@ -101,8 +99,7 @@ func (d *dao) Update(ctx context.Context, schema *policy.Schema, props ...string // Get a policy schema by id. func (d *dao) Get(ctx context.Context, id int64) (schema *policy.Schema, err error) { - var ormer beego_orm.Ormer - ormer, err = orm.FromContext(ctx) + ormer, err := orm.FromContext(ctx) if err != nil { return } @@ -120,8 +117,7 @@ func (d *dao) Get(ctx context.Context, id int64) (schema *policy.Schema, err err // GetByName gets a policy schema by name. func (d *dao) GetByName(ctx context.Context, projectID int64, name string) (schema *policy.Schema, err error) { - var ormer beego_orm.Ormer - ormer, err = orm.FromContext(ctx) + ormer, err := orm.FromContext(ctx) if err != nil { return } @@ -139,8 +135,7 @@ func (d *dao) GetByName(ctx context.Context, projectID int64, name string) (sche // Delete a policy schema by id. func (d *dao) Delete(ctx context.Context, id int64) (err error) { - var ormer beego_orm.Ormer - ormer, err = orm.FromContext(ctx) + ormer, err := orm.FromContext(ctx) if err != nil { return } diff --git a/src/pkg/p2p/preheat/dao/policy/dao_test.go b/src/pkg/p2p/preheat/dao/policy/dao_test.go index cf9bc542a..fb6161bc4 100644 --- a/src/pkg/p2p/preheat/dao/policy/dao_test.go +++ b/src/pkg/p2p/preheat/dao/policy/dao_test.go @@ -19,7 +19,7 @@ import ( "testing" "time" - beego_orm "github.com/beego/beego/orm" + beego_orm "github.com/beego/beego/v2/client/orm" "github.com/stretchr/testify/suite" common_dao "github.com/goharbor/harbor/src/common/dao" diff --git a/src/pkg/p2p/preheat/models/policy/policy.go b/src/pkg/p2p/preheat/models/policy/policy.go index 420dd9e7c..aab0e789d 100644 --- a/src/pkg/p2p/preheat/models/policy/policy.go +++ b/src/pkg/p2p/preheat/models/policy/policy.go @@ -20,8 +20,8 @@ import ( "strconv" "time" - beego_orm "github.com/beego/beego/orm" - "github.com/beego/beego/validation" + beego_orm "github.com/beego/beego/v2/client/orm" + "github.com/beego/beego/v2/core/validation" "github.com/goharbor/harbor/src/common/utils" "github.com/goharbor/harbor/src/lib/errors" diff --git a/src/pkg/p2p/preheat/models/policy/policy_test.go b/src/pkg/p2p/preheat/models/policy/policy_test.go index 985300423..6c39b42e1 100644 --- a/src/pkg/p2p/preheat/models/policy/policy_test.go +++ b/src/pkg/p2p/preheat/models/policy/policy_test.go @@ -17,7 +17,7 @@ package policy import ( "testing" - "github.com/beego/beego/validation" + "github.com/beego/beego/v2/core/validation" "github.com/stretchr/testify/require" "github.com/stretchr/testify/suite" ) diff --git a/src/pkg/p2p/preheat/models/provider/instance.go b/src/pkg/p2p/preheat/models/provider/instance.go index b42010f8c..407daf0ad 100644 --- a/src/pkg/p2p/preheat/models/provider/instance.go +++ b/src/pkg/p2p/preheat/models/provider/instance.go @@ -17,7 +17,7 @@ package provider import ( "encoding/json" - "github.com/beego/beego/orm" + "github.com/beego/beego/v2/client/orm" "github.com/goharbor/harbor/src/lib/errors" ) diff --git a/src/pkg/project/metadata/models/metadata.go b/src/pkg/project/metadata/models/metadata.go index 70c2b7c11..cf7142968 100644 --- a/src/pkg/project/metadata/models/metadata.go +++ b/src/pkg/project/metadata/models/metadata.go @@ -17,7 +17,7 @@ package models import ( "time" - "github.com/beego/beego/orm" + "github.com/beego/beego/v2/client/orm" ) func init() { diff --git a/src/pkg/quota/dao/model.go b/src/pkg/quota/dao/model.go index 73a43092c..6c05dbe4b 100644 --- a/src/pkg/quota/dao/model.go +++ b/src/pkg/quota/dao/model.go @@ -17,7 +17,7 @@ package dao import ( "time" - "github.com/beego/beego/orm" + "github.com/beego/beego/v2/client/orm" ) func init() { diff --git a/src/pkg/rbac/model/model.go b/src/pkg/rbac/model/model.go index db9a4157a..24ce458af 100644 --- a/src/pkg/rbac/model/model.go +++ b/src/pkg/rbac/model/model.go @@ -3,7 +3,7 @@ package model import ( "time" - "github.com/beego/beego/orm" + "github.com/beego/beego/v2/client/orm" ) func init() { diff --git a/src/pkg/reg/dao/dao_test.go b/src/pkg/reg/dao/dao_test.go index 890a4a26c..fa357e56a 100644 --- a/src/pkg/reg/dao/dao_test.go +++ b/src/pkg/reg/dao/dao_test.go @@ -18,7 +18,7 @@ import ( "context" "testing" - beegoorm "github.com/beego/beego/orm" + beegoorm "github.com/beego/beego/v2/client/orm" "github.com/stretchr/testify/suite" common_dao "github.com/goharbor/harbor/src/common/dao" diff --git a/src/pkg/reg/dao/model.go b/src/pkg/reg/dao/model.go index 20181590d..840f168eb 100644 --- a/src/pkg/reg/dao/model.go +++ b/src/pkg/reg/dao/model.go @@ -17,7 +17,7 @@ package dao import ( "time" - "github.com/beego/beego/orm" + "github.com/beego/beego/v2/client/orm" ) func init() { diff --git a/src/pkg/replication/dao/dao_test.go b/src/pkg/replication/dao/dao_test.go index cf6b87a39..e8c061d33 100644 --- a/src/pkg/replication/dao/dao_test.go +++ b/src/pkg/replication/dao/dao_test.go @@ -18,7 +18,7 @@ import ( "context" "testing" - beegoorm "github.com/beego/beego/orm" + beegoorm "github.com/beego/beego/v2/client/orm" "github.com/stretchr/testify/suite" common_dao "github.com/goharbor/harbor/src/common/dao" diff --git a/src/pkg/replication/model/model.go b/src/pkg/replication/model/model.go index 492371edd..04d17237c 100644 --- a/src/pkg/replication/model/model.go +++ b/src/pkg/replication/model/model.go @@ -17,7 +17,7 @@ package model import ( "time" - "github.com/beego/beego/orm" + "github.com/beego/beego/v2/client/orm" ) func init() { diff --git a/src/pkg/repository/dao/dao.go b/src/pkg/repository/dao/dao.go index dd629fae0..864761ef7 100644 --- a/src/pkg/repository/dao/dao.go +++ b/src/pkg/repository/dao/dao.go @@ -18,7 +18,7 @@ import ( "context" "time" - o "github.com/beego/beego/orm" + o "github.com/beego/beego/v2/client/orm" "github.com/goharbor/harbor/src/lib/errors" "github.com/goharbor/harbor/src/lib/orm" diff --git a/src/pkg/repository/dao/dao_test.go b/src/pkg/repository/dao/dao_test.go index f00a48c8d..ed630dcc6 100644 --- a/src/pkg/repository/dao/dao_test.go +++ b/src/pkg/repository/dao/dao_test.go @@ -20,7 +20,7 @@ import ( "testing" "time" - beegoorm "github.com/beego/beego/orm" + beegoorm "github.com/beego/beego/v2/client/orm" v1 "github.com/opencontainers/image-spec/specs-go/v1" "github.com/stretchr/testify/suite" diff --git a/src/pkg/retention/dao/models/retention.go b/src/pkg/retention/dao/models/retention.go index 7c25fec28..d573a0366 100644 --- a/src/pkg/retention/dao/models/retention.go +++ b/src/pkg/retention/dao/models/retention.go @@ -3,7 +3,7 @@ package models import ( "time" - "github.com/beego/beego/orm" + "github.com/beego/beego/v2/client/orm" ) // const definitions diff --git a/src/pkg/retention/manager.go b/src/pkg/retention/manager.go index 8006d747d..8b36d0a93 100644 --- a/src/pkg/retention/manager.go +++ b/src/pkg/retention/manager.go @@ -20,7 +20,7 @@ import ( "fmt" "time" - "github.com/beego/beego/orm" + "github.com/beego/beego/v2/client/orm" "github.com/goharbor/harbor/src/pkg/retention/dao" "github.com/goharbor/harbor/src/pkg/retention/dao/models" diff --git a/src/pkg/retention/policy/models.go b/src/pkg/retention/policy/models.go index e4147890b..c58daf63f 100644 --- a/src/pkg/retention/policy/models.go +++ b/src/pkg/retention/policy/models.go @@ -15,7 +15,7 @@ package policy import ( - "github.com/beego/beego/validation" + "github.com/beego/beego/v2/core/validation" "github.com/goharbor/harbor/src/lib/selector/selectors/doublestar" "github.com/goharbor/harbor/src/pkg/retention/policy/rule" diff --git a/src/pkg/retention/policy/models_test.go b/src/pkg/retention/policy/models_test.go index eb3037dd4..5ca55ab9e 100644 --- a/src/pkg/retention/policy/models_test.go +++ b/src/pkg/retention/policy/models_test.go @@ -4,7 +4,7 @@ import ( "fmt" "testing" - "github.com/beego/beego/validation" + "github.com/beego/beego/v2/core/validation" "github.com/stretchr/testify/require" "github.com/goharbor/harbor/src/pkg/retention/policy/rule" diff --git a/src/pkg/retention/policy/rule/models.go b/src/pkg/retention/policy/rule/models.go index 88294bdf4..0c55abe52 100644 --- a/src/pkg/retention/policy/rule/models.go +++ b/src/pkg/retention/policy/rule/models.go @@ -15,7 +15,7 @@ package rule import ( - "github.com/beego/beego/validation" + "github.com/beego/beego/v2/core/validation" ) // Metadata of the retention rule diff --git a/src/pkg/robot/model/model.go b/src/pkg/robot/model/model.go index 9064bc77d..4c2c52ef6 100644 --- a/src/pkg/robot/model/model.go +++ b/src/pkg/robot/model/model.go @@ -4,7 +4,7 @@ import ( "encoding/json" "time" - "github.com/beego/beego/orm" + "github.com/beego/beego/v2/client/orm" "github.com/goharbor/harbor/src/lib/errors" ) diff --git a/src/pkg/scan/export/manager.go b/src/pkg/scan/export/manager.go index 5b74abc13..61d531488 100644 --- a/src/pkg/scan/export/manager.go +++ b/src/pkg/scan/export/manager.go @@ -6,7 +6,7 @@ import ( "errors" "fmt" - beego_orm "github.com/beego/beego/orm" + beego_orm "github.com/beego/beego/v2/client/orm" "github.com/goharbor/harbor/src/lib/orm" q2 "github.com/goharbor/harbor/src/lib/q" diff --git a/src/pkg/scheduler/dao.go b/src/pkg/scheduler/dao.go index 14cbf806f..e061d7ed7 100644 --- a/src/pkg/scheduler/dao.go +++ b/src/pkg/scheduler/dao.go @@ -18,7 +18,7 @@ import ( "context" "time" - beegoorm "github.com/beego/beego/orm" + beegoorm "github.com/beego/beego/v2/client/orm" "github.com/goharbor/harbor/src/lib/errors" "github.com/goharbor/harbor/src/lib/orm" diff --git a/src/pkg/tag/dao/dao.go b/src/pkg/tag/dao/dao.go index bbe9e03c2..79acf00de 100644 --- a/src/pkg/tag/dao/dao.go +++ b/src/pkg/tag/dao/dao.go @@ -17,7 +17,7 @@ package dao import ( "context" - beego_orm "github.com/beego/beego/orm" + beego_orm "github.com/beego/beego/v2/client/orm" "github.com/goharbor/harbor/src/lib/errors" "github.com/goharbor/harbor/src/lib/orm" diff --git a/src/pkg/tag/dao/dao_test.go b/src/pkg/tag/dao/dao_test.go index 1ab0a03ff..7b5a20dcd 100644 --- a/src/pkg/tag/dao/dao_test.go +++ b/src/pkg/tag/dao/dao_test.go @@ -19,7 +19,7 @@ import ( "testing" "time" - beegoorm "github.com/beego/beego/orm" + beegoorm "github.com/beego/beego/v2/client/orm" "github.com/stretchr/testify/suite" common_dao "github.com/goharbor/harbor/src/common/dao" diff --git a/src/pkg/task/dao/model.go b/src/pkg/task/dao/model.go index 27ea36f32..e43cfd7e8 100644 --- a/src/pkg/task/dao/model.go +++ b/src/pkg/task/dao/model.go @@ -17,7 +17,7 @@ package dao import ( "time" - "github.com/beego/beego/orm" + "github.com/beego/beego/v2/client/orm" "github.com/goharbor/harbor/src/lib/q" ) diff --git a/src/server/middleware/blob/head_blob_test.go b/src/server/middleware/blob/head_blob_test.go index f24ccf785..e6c41a07c 100644 --- a/src/server/middleware/blob/head_blob_test.go +++ b/src/server/middleware/blob/head_blob_test.go @@ -6,7 +6,7 @@ import ( "net/http/httptest" "testing" - beego_orm "github.com/beego/beego/orm" + beego_orm "github.com/beego/beego/v2/client/orm" "github.com/stretchr/testify/suite" "github.com/goharbor/harbor/src/controller/blob" diff --git a/src/server/middleware/orm/orm.go b/src/server/middleware/orm/orm.go index 16463e5f2..29db17a7a 100644 --- a/src/server/middleware/orm/orm.go +++ b/src/server/middleware/orm/orm.go @@ -17,7 +17,7 @@ package orm import ( "net/http" - o "github.com/beego/beego/orm" + o "github.com/beego/beego/v2/client/orm" "github.com/goharbor/harbor/src/lib/orm" "github.com/goharbor/harbor/src/server/middleware" diff --git a/src/server/middleware/orm/orm_test.go b/src/server/middleware/orm/orm_test.go index fead4b93e..505716024 100644 --- a/src/server/middleware/orm/orm_test.go +++ b/src/server/middleware/orm/orm_test.go @@ -19,7 +19,7 @@ import ( "net/http/httptest" "testing" - o "github.com/beego/beego/orm" + o "github.com/beego/beego/v2/client/orm" "github.com/stretchr/testify/assert" "github.com/goharbor/harbor/src/lib/orm" diff --git a/src/server/middleware/security/session.go b/src/server/middleware/security/session.go index 6425fdf76..e8bc29591 100644 --- a/src/server/middleware/security/session.go +++ b/src/server/middleware/security/session.go @@ -18,7 +18,7 @@ import ( "net/http" "net/http/httptest" - "github.com/beego/beego" + "github.com/beego/beego/v2/server/web" "github.com/goharbor/harbor/src/common/models" "github.com/goharbor/harbor/src/common/security" @@ -30,12 +30,12 @@ type session struct{} func (s *session) Generate(req *http.Request) security.Context { log := log.G(req.Context()) - store, err := beego.GlobalSessions.SessionStart(httptest.NewRecorder(), req) + store, err := web.GlobalSessions.SessionStart(httptest.NewRecorder(), req) if err != nil { log.Errorf("failed to get the session store for request: %v", err) return nil } - userInterface := store.Get("user") + userInterface := store.Get(req.Context(), "user") if userInterface == nil { return nil } diff --git a/src/server/middleware/security/session_test.go b/src/server/middleware/security/session_test.go index 149618e6c..28ceae0d1 100644 --- a/src/server/middleware/security/session_test.go +++ b/src/server/middleware/security/session_test.go @@ -20,8 +20,8 @@ import ( "path/filepath" "testing" - "github.com/beego/beego" - beegosession "github.com/beego/beego/session" + "github.com/beego/beego/v2/server/web" + beegosession "github.com/beego/beego/v2/server/web/session" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -32,15 +32,15 @@ func TestSession(t *testing.T) { var err error // initialize beego session manager conf := &beegosession.ManagerConfig{ - CookieName: beego.BConfig.WebConfig.Session.SessionName, - Gclifetime: beego.BConfig.WebConfig.Session.SessionGCMaxLifetime, - ProviderConfig: filepath.ToSlash(beego.BConfig.WebConfig.Session.SessionProviderConfig), - Secure: beego.BConfig.Listen.EnableHTTPS, - EnableSetCookie: beego.BConfig.WebConfig.Session.SessionAutoSetCookie, - Domain: beego.BConfig.WebConfig.Session.SessionDomain, - CookieLifeTime: beego.BConfig.WebConfig.Session.SessionCookieLifeTime, + CookieName: web.BConfig.WebConfig.Session.SessionName, + Gclifetime: web.BConfig.WebConfig.Session.SessionGCMaxLifetime, + ProviderConfig: filepath.ToSlash(web.BConfig.WebConfig.Session.SessionProviderConfig), + Secure: web.BConfig.Listen.EnableHTTPS, + EnableSetCookie: web.BConfig.WebConfig.Session.SessionAutoSetCookie, + Domain: web.BConfig.WebConfig.Session.SessionDomain, + CookieLifeTime: web.BConfig.WebConfig.Session.SessionCookieLifeTime, } - beego.GlobalSessions, err = beegosession.NewManager("memory", conf) + web.GlobalSessions, err = beegosession.NewManager("memory", conf) require.Nil(t, err) user := models.User{ @@ -51,9 +51,9 @@ func TestSession(t *testing.T) { } req, err := http.NewRequest(http.MethodGet, "http://127.0.0.1/api/projects/", nil) require.Nil(t, err) - store, err := beego.GlobalSessions.SessionStart(httptest.NewRecorder(), req) + store, err := web.GlobalSessions.SessionStart(httptest.NewRecorder(), req) require.Nil(t, err) - err = store.Set("user", user) + err = store.Set(req.Context(), "user", user) require.Nil(t, err) session := &session{} diff --git a/src/server/middleware/session/session_test.go b/src/server/middleware/session/session_test.go index 72f1f56aa..771646e84 100644 --- a/src/server/middleware/session/session_test.go +++ b/src/server/middleware/session/session_test.go @@ -20,8 +20,8 @@ import ( "path/filepath" "testing" - "github.com/beego/beego" - beegosession "github.com/beego/beego/session" + "github.com/beego/beego/v2/server/web" + beegosession "github.com/beego/beego/v2/server/web/session" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -41,19 +41,19 @@ func TestSession(t *testing.T) { assert.False(t, carrySession) // contains session - beego.BConfig.WebConfig.Session.SessionName = config.SessionCookieName + web.BConfig.WebConfig.Session.SessionName = config.SessionCookieName conf := &beegosession.ManagerConfig{ - CookieName: beego.BConfig.WebConfig.Session.SessionName, - Gclifetime: beego.BConfig.WebConfig.Session.SessionGCMaxLifetime, - ProviderConfig: filepath.ToSlash(beego.BConfig.WebConfig.Session.SessionProviderConfig), - Secure: beego.BConfig.Listen.EnableHTTPS, - EnableSetCookie: beego.BConfig.WebConfig.Session.SessionAutoSetCookie, - Domain: beego.BConfig.WebConfig.Session.SessionDomain, - CookieLifeTime: beego.BConfig.WebConfig.Session.SessionCookieLifeTime, + CookieName: web.BConfig.WebConfig.Session.SessionName, + Gclifetime: web.BConfig.WebConfig.Session.SessionGCMaxLifetime, + ProviderConfig: filepath.ToSlash(web.BConfig.WebConfig.Session.SessionProviderConfig), + Secure: web.BConfig.Listen.EnableHTTPS, + EnableSetCookie: web.BConfig.WebConfig.Session.SessionAutoSetCookie, + Domain: web.BConfig.WebConfig.Session.SessionDomain, + CookieLifeTime: web.BConfig.WebConfig.Session.SessionCookieLifeTime, } - beego.GlobalSessions, err = beegosession.NewManager("memory", conf) + web.GlobalSessions, err = beegosession.NewManager("memory", conf) require.Nil(t, err) - _, err = beego.GlobalSessions.SessionStart(httptest.NewRecorder(), req) + _, err = web.GlobalSessions.SessionStart(httptest.NewRecorder(), req) require.Nil(t, err) Middleware()(handler).ServeHTTP(nil, req) assert.True(t, carrySession) diff --git a/src/server/middleware/transaction/transaction_test.go b/src/server/middleware/transaction/transaction_test.go index c925fc80a..994947a6c 100644 --- a/src/server/middleware/transaction/transaction_test.go +++ b/src/server/middleware/transaction/transaction_test.go @@ -16,61 +16,70 @@ package transaction import ( "context" - "errors" "io" "net/http" "net/http/httptest" "testing" - o "github.com/beego/beego/orm" + o "github.com/beego/beego/v2/client/orm" "github.com/stretchr/testify/assert" + "github.com/goharbor/harbor/src/lib/errors" "github.com/goharbor/harbor/src/lib/orm" "github.com/goharbor/harbor/src/server/middleware" ) type mockOrmer struct { o.Ormer - records []interface{} - beginErr error - commitErr error + tx mockTxOrmer + beginErr error } -func (m *mockOrmer) Insert(i interface{}) (int64, error) { - m.records = append(m.records, i) - - return int64(len(m.records)), nil -} - -func (m *mockOrmer) Begin() error { - return m.beginErr -} - -func (m *mockOrmer) Commit() error { - return m.commitErr -} - -func (m *mockOrmer) Rollback() error { - m.ResetRecords() - - return nil -} - -func (m *mockOrmer) ResetRecords() { - m.records = nil +func (m *mockOrmer) Begin() (o.TxOrmer, error) { + return &m.tx, m.beginErr } func (m *mockOrmer) Reset() { - m.ResetRecords() - + m.tx.Reset() m.beginErr = nil +} + +type mockTxOrmer struct { + o.TxOrmer + commitErr error + records []interface{} +} + +func (m *mockTxOrmer) Insert(i interface{}) (int64, error) { + m.records = append(m.records, i) + return int64(len(m.records)), nil +} + +func (m *mockTxOrmer) Commit() error { + return m.commitErr +} + +func (m *mockTxOrmer) Rollback() error { + m.ResetRecords() + return nil +} + +func (m *mockTxOrmer) ResetRecords() { + m.records = nil +} + +func (m *mockTxOrmer) Reset() { + m.ResetRecords() m.commitErr = nil } func TestTransaction(t *testing.T) { assert := assert.New(t) - mo := &mockOrmer{} + tx := mockTxOrmer{} + mo := &mockOrmer{ + tx: tx, + } newRequest := func(method, target string, body io.Reader) *http.Request { req := httptest.NewRequest(http.MethodGet, "/req1", nil) @@ -79,7 +88,7 @@ func TestTransaction(t *testing.T) { next := func(status int) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - mo.Insert("record1") + mo.tx.Insert("record1") w.WriteHeader(status) }) } @@ -89,17 +98,17 @@ func TestTransaction(t *testing.T) { rec1 := httptest.NewRecorder() Middleware()(next(http.StatusOK)).ServeHTTP(rec1, req1) assert.Equal(http.StatusOK, rec1.Code) - assert.NotEmpty(mo.records) + assert.NotEmpty(mo.tx.records) - mo.ResetRecords() - assert.Empty(mo.records) + mo.tx.ResetRecords() + assert.Empty(mo.tx.records) // test response status code not accepted req2 := newRequest(http.MethodGet, "/req", nil) rec2 := httptest.NewRecorder() Middleware()(next(http.StatusBadRequest)).ServeHTTP(rec2, req2) assert.Equal(http.StatusBadRequest, rec2.Code) - assert.Empty(mo.records) + assert.Empty(mo.tx.records) // test begin transaction failed mo.beginErr = errors.New("begin tx failed") @@ -107,11 +116,11 @@ func TestTransaction(t *testing.T) { rec3 := httptest.NewRecorder() Middleware()(next(http.StatusBadRequest)).ServeHTTP(rec3, req3) assert.Equal(http.StatusInternalServerError, rec3.Code) - assert.Empty(mo.records) + assert.Empty(mo.tx.records) // test commit transaction failed mo.beginErr = nil - mo.commitErr = errors.New("commit tx failed") + mo.tx.commitErr = errors.New("commit tx failed") req4 := newRequest(http.MethodGet, "/req", nil) rec4 := httptest.NewRecorder() Middleware()(next(http.StatusOK)).ServeHTTP(rec4, req4) @@ -119,12 +128,12 @@ func TestTransaction(t *testing.T) { // test MustCommit mo.Reset() - assert.Empty(mo.records) + assert.Empty(mo.tx.records) txMustCommit := func(status int) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { defer MustCommit(r) - mo.Insert("record1") + mo.tx.Insert("record1") w.WriteHeader(status) }) } @@ -139,7 +148,7 @@ func TestTransaction(t *testing.T) { Middleware()(m1((txMustCommit(http.StatusBadRequest)))).ServeHTTP(rec5, req5) assert.Equal(http.StatusBadRequest, rec2.Code) - assert.NotEmpty(mo.records) + assert.NotEmpty(mo.tx.records) } func TestMustCommit(t *testing.T) { diff --git a/src/server/registry/manifest_test.go b/src/server/registry/manifest_test.go index bdea36dac..39af2f660 100644 --- a/src/server/registry/manifest_test.go +++ b/src/server/registry/manifest_test.go @@ -20,7 +20,7 @@ import ( "net/http/httptest" "testing" - beegocontext "github.com/beego/beego/context" + beegocontext "github.com/beego/beego/v2/server/web/context" "github.com/stretchr/testify/suite" "github.com/goharbor/harbor/src/controller/artifact" diff --git a/src/server/route.go b/src/server/route.go index 6dce4ddf9..a57210ea8 100644 --- a/src/server/route.go +++ b/src/server/route.go @@ -17,7 +17,7 @@ package server import ( "net/http" - "github.com/beego/beego" + "github.com/beego/beego/v2/server/web" "github.com/goharbor/harbor/src/common" "github.com/goharbor/harbor/src/core/api" @@ -38,18 +38,18 @@ func registerRoutes() { router.NewRoute().Method(http.MethodGet).Path("/api/version").HandlerFunc(GetAPIVersion) // Controller API: - beego.Router("/c/login", &controllers.CommonController{}, "post:Login") - beego.Router("/c/log_out", &controllers.CommonController{}, "get:LogOut") - beego.Router("/c/userExists", &controllers.CommonController{}, "post:UserExists") - beego.Router(common.OIDCLoginPath, &controllers.OIDCController{}, "get:RedirectLogin") - beego.Router("/c/oidc/onboard", &controllers.OIDCController{}, "post:Onboard") - beego.Router(common.OIDCCallbackPath, &controllers.OIDCController{}, "get:Callback") - beego.Router(common.AuthProxyRediretPath, &controllers.AuthProxyController{}, "get:HandleRedirect") + web.Router("/c/login", &controllers.CommonController{}, "post:Login") + web.Router("/c/log_out", &controllers.CommonController{}, "get:LogOut") + web.Router("/c/userExists", &controllers.CommonController{}, "post:UserExists") + web.Router(common.OIDCLoginPath, &controllers.OIDCController{}, "get:RedirectLogin") + web.Router("/c/oidc/onboard", &controllers.OIDCController{}, "post:Onboard") + web.Router(common.OIDCCallbackPath, &controllers.OIDCController{}, "get:Callback") + web.Router(common.AuthProxyRediretPath, &controllers.AuthProxyController{}, "get:HandleRedirect") - beego.Router("/api/internal/renameadmin", &api.InternalAPI{}, "post:RenameAdmin") - beego.Router("/api/internal/syncquota", &api.InternalAPI{}, "post:SyncQuota") + web.Router("/api/internal/renameadmin", &api.InternalAPI{}, "post:RenameAdmin") + web.Router("/api/internal/syncquota", &api.InternalAPI{}, "post:SyncQuota") - beego.Router("/service/notifications/jobs/webhook/:id([0-9]+)", &jobs.Handler{}, "post:HandleNotificationJob") + web.Router("/service/notifications/jobs/webhook/:id([0-9]+)", &jobs.Handler{}, "post:HandleNotificationJob") router.NewRoute().Method(http.MethodPost).Path("/service/notifications/jobs/adminjob/:id([0-9]+)").Handler(handler.NewJobStatusHandler()) // legacy job status hook endpoint for adminjob router.NewRoute().Method(http.MethodPost).Path("/service/notifications/jobs/scan/:uuid").HandlerFunc(ignoreNotification) // ignore legacy scan job notifaction router.NewRoute().Method(http.MethodPost).Path("/service/notifications/schedules/:id([0-9]+)").Handler(handler.NewJobStatusHandler()) // legacy job status hook endpoint for scheduler @@ -58,25 +58,25 @@ func registerRoutes() { router.NewRoute().Method(http.MethodPost).Path("/service/notifications/jobs/retention/task/:id([0-9]+)").Handler(handler.NewJobStatusHandler()) router.NewRoute().Method(http.MethodPost).Path("/service/notifications/tasks/:id").Handler(handler.NewJobStatusHandler()) - beego.Router("/service/token", &token.Handler{}) + web.Router("/service/token", &token.Handler{}) // chart repository services if config.WithChartMuseum() { chartRepositoryAPIType := &api.ChartRepositoryAPI{} - beego.Router("/chartrepo/:repo/index.yaml", chartRepositoryAPIType, "get:GetIndexByRepo") - beego.Router("/chartrepo/index.yaml", chartRepositoryAPIType, "get:GetIndex") - beego.Router("/chartrepo/:repo/charts/:filename", chartRepositoryAPIType, "get:DownloadChart") - beego.Router("/api/chartrepo/health", chartRepositoryAPIType, "get:GetHealthStatus") - beego.Router("/api/chartrepo/:repo/charts", chartRepositoryAPIType, "get:ListCharts") - beego.Router("/api/chartrepo/:repo/charts/:name", chartRepositoryAPIType, "get:ListChartVersions") - beego.Router("/api/chartrepo/:repo/charts/:name", chartRepositoryAPIType, "delete:DeleteChart") - beego.Router("/api/chartrepo/:repo/charts/:name/:version", chartRepositoryAPIType, "get:GetChartVersion") - beego.Router("/api/chartrepo/:repo/charts/:name/:version", chartRepositoryAPIType, "delete:DeleteChartVersion") - beego.Router("/api/chartrepo/:repo/charts", chartRepositoryAPIType, "post:UploadChartVersion") - beego.Router("/api/chartrepo/:repo/prov", chartRepositoryAPIType, "post:UploadChartProvFile") - beego.Router("/api/chartrepo/charts", chartRepositoryAPIType, "post:UploadChartVersion") + web.Router("/chartrepo/:repo/index.yaml", chartRepositoryAPIType, "get:GetIndexByRepo") + web.Router("/chartrepo/index.yaml", chartRepositoryAPIType, "get:GetIndex") + web.Router("/chartrepo/:repo/charts/:filename", chartRepositoryAPIType, "get:DownloadChart") + web.Router("/api/chartrepo/health", chartRepositoryAPIType, "get:GetHealthStatus") + web.Router("/api/chartrepo/:repo/charts", chartRepositoryAPIType, "get:ListCharts") + web.Router("/api/chartrepo/:repo/charts/:name", chartRepositoryAPIType, "get:ListChartVersions") + web.Router("/api/chartrepo/:repo/charts/:name", chartRepositoryAPIType, "delete:DeleteChart") + web.Router("/api/chartrepo/:repo/charts/:name/:version", chartRepositoryAPIType, "get:GetChartVersion") + web.Router("/api/chartrepo/:repo/charts/:name/:version", chartRepositoryAPIType, "delete:DeleteChartVersion") + web.Router("/api/chartrepo/:repo/charts", chartRepositoryAPIType, "post:UploadChartVersion") + web.Router("/api/chartrepo/:repo/prov", chartRepositoryAPIType, "post:UploadChartProvFile") + web.Router("/api/chartrepo/charts", chartRepositoryAPIType, "post:UploadChartVersion") } // Error pages - beego.ErrorController(&controllers.ErrorController{}) + web.ErrorController(&controllers.ErrorController{}) } diff --git a/src/server/router/router.go b/src/server/router/router.go index 0b9611ea1..78b1ad3d7 100644 --- a/src/server/router/router.go +++ b/src/server/router/router.go @@ -19,8 +19,8 @@ import ( "net/http" "path/filepath" - "github.com/beego/beego" - beegocontext "github.com/beego/beego/context" + "github.com/beego/beego/v2/server/web" + beegocontext "github.com/beego/beego/v2/server/web/context" "github.com/goharbor/harbor/src/server/middleware" ) @@ -84,7 +84,7 @@ func (r *Route) Handler(handler http.Handler) { } middlewares = append(middlewares, r.middlewares...) - filterFunc := beego.FilterFunc(func(ctx *beegocontext.Context) { + filterFunc := web.FilterFunc(func(ctx *beegocontext.Context) { ctx.Request = ctx.Request.WithContext( context.WithValue(ctx.Request.Context(), ContextKeyInput{}, ctx.Input)) // TODO remove the WithMiddlewares? @@ -93,25 +93,25 @@ func (r *Route) Handler(handler http.Handler) { }) if len(methods) == 0 { - beego.Any(path, filterFunc) + web.Any(path, filterFunc) return } for _, method := range methods { switch method { case http.MethodGet: - beego.Get(path, filterFunc) + web.Get(path, filterFunc) case http.MethodHead: - beego.Head(path, filterFunc) + web.Head(path, filterFunc) case http.MethodPut: - beego.Put(path, filterFunc) + web.Put(path, filterFunc) case http.MethodPatch: - beego.Patch(path, filterFunc) + web.Patch(path, filterFunc) case http.MethodPost: - beego.Post(path, filterFunc) + web.Post(path, filterFunc) case http.MethodDelete: - beego.Delete(path, filterFunc) + web.Delete(path, filterFunc) case http.MethodOptions: - beego.Options(path, filterFunc) + web.Options(path, filterFunc) } } } diff --git a/src/server/router/router_test.go b/src/server/router/router_test.go index af65072b7..3eac69b58 100644 --- a/src/server/router/router_test.go +++ b/src/server/router/router_test.go @@ -19,7 +19,7 @@ import ( "net/http" "testing" - beegocontext "github.com/beego/beego/context" + beegocontext "github.com/beego/beego/v2/server/web/context" "github.com/stretchr/testify/suite" "github.com/goharbor/harbor/src/server/middleware" diff --git a/src/server/v2.0/handler/scanexport_test.go b/src/server/v2.0/handler/scanexport_test.go index da59da0ab..76008c20b 100644 --- a/src/server/v2.0/handler/scanexport_test.go +++ b/src/server/v2.0/handler/scanexport_test.go @@ -12,7 +12,7 @@ import ( "testing" "time" - beegoorm "github.com/beego/beego/orm" + beegoorm "github.com/beego/beego/v2/client/orm" "github.com/goharbor/harbor/src/lib/errors" testifymock "github.com/stretchr/testify/mock" "github.com/stretchr/testify/suite" diff --git a/src/server/v2.0/route/legacy.go b/src/server/v2.0/route/legacy.go index 696ee1a55..c31b02ec4 100755 --- a/src/server/v2.0/route/legacy.go +++ b/src/server/v2.0/route/legacy.go @@ -15,7 +15,7 @@ package route import ( - "github.com/beego/beego" + "github.com/beego/beego/v2/server/web" "github.com/goharbor/harbor/src/core/api" "github.com/goharbor/harbor/src/lib/config" @@ -29,7 +29,7 @@ func registerLegacyRoutes() { if config.WithChartMuseum() { // Labels for chart chartLabelAPIType := &api.ChartLabelAPI{} - beego.Router("/api/"+version+"/chartrepo/:repo/charts/:name/:version/labels", chartLabelAPIType, "get:GetLabels;post:MarkLabel") - beego.Router("/api/"+version+"/chartrepo/:repo/charts/:name/:version/labels/:id([0-9]+)", chartLabelAPIType, "delete:RemoveLabel") + web.Router("/api/"+version+"/chartrepo/:repo/charts/:name/:version/labels", chartLabelAPIType, "get:GetLabels;post:MarkLabel") + web.Router("/api/"+version+"/chartrepo/:repo/charts/:name/:version/labels/:id([0-9]+)", chartLabelAPIType, "delete:RemoveLabel") } } diff --git a/src/testing/lib/orm/creator.go b/src/testing/lib/orm/creator.go index 7bbcb9ad9..a19a1cf29 100644 --- a/src/testing/lib/orm/creator.go +++ b/src/testing/lib/orm/creator.go @@ -3,7 +3,7 @@ package orm import ( - orm "github.com/beego/beego/orm" + orm "github.com/beego/beego/v2/client/orm" mock "github.com/stretchr/testify/mock" ) diff --git a/src/testing/lib/orm/orm.go b/src/testing/lib/orm/orm.go index 0f05f835e..34bec811d 100644 --- a/src/testing/lib/orm/orm.go +++ b/src/testing/lib/orm/orm.go @@ -17,18 +17,104 @@ package orm import ( "context" "database/sql" + "github.com/beego/beego/v2/core/utils" - "github.com/beego/beego/orm" + "github.com/beego/beego/v2/client/orm" ) // FakeOrmer ... -type FakeOrmer struct{} +type FakeOrmer struct { +} + +func (f *FakeOrmer) LoadRelated(md interface{}, name string, args ...utils.KV) (int64, error) { + return 0, nil +} + +func (f *FakeOrmer) QueryM2MWithCtx(ctx context.Context, md interface{}, name string) orm.QueryM2Mer { + return nil +} + +func (f *FakeOrmer) QueryTableWithCtx(ctx context.Context, ptrStructOrTableName interface{}) orm.QuerySeter { + return nil +} + +func (f *FakeOrmer) InsertWithCtx(ctx context.Context, md interface{}) (int64, error) { + return 0, nil +} + +func (f *FakeOrmer) InsertOrUpdateWithCtx(ctx context.Context, md interface{}, colConflitAndArgs ...string) (int64, error) { + return 0, nil +} + +func (f *FakeOrmer) InsertMultiWithCtx(ctx context.Context, bulk int, mds interface{}) (int64, error) { + return 0, nil +} + +func (f *FakeOrmer) UpdateWithCtx(ctx context.Context, md interface{}, cols ...string) (int64, error) { + return 0, nil +} + +func (f *FakeOrmer) DeleteWithCtx(ctx context.Context, md interface{}, cols ...string) (int64, error) { + return 0, nil +} + +func (f *FakeOrmer) RawWithCtx(ctx context.Context, query string, args ...interface{}) orm.RawSeter { + return nil +} + +func (f *FakeOrmer) Begin() (orm.TxOrmer, error) { + return &FakeTxOrmer{}, nil +} + +func (f *FakeOrmer) BeginWithCtx(ctx context.Context) (orm.TxOrmer, error) { + return nil, nil +} + +func (f *FakeOrmer) BeginWithOpts(opts *sql.TxOptions) (orm.TxOrmer, error) { + return nil, nil +} + +func (f *FakeOrmer) BeginWithCtxAndOpts(ctx context.Context, opts *sql.TxOptions) (orm.TxOrmer, error) { + return nil, nil +} + +func (f *FakeOrmer) DoTx(task func(ctx context.Context, txOrm orm.TxOrmer) error) error { + return nil +} + +func (f *FakeOrmer) DoTxWithCtx(ctx context.Context, task func(ctx context.Context, txOrm orm.TxOrmer) error) error { + return nil +} + +func (f *FakeOrmer) DoTxWithOpts(opts *sql.TxOptions, task func(ctx context.Context, txOrm orm.TxOrmer) error) error { + return nil +} + +func (f *FakeOrmer) DoTxWithCtxAndOpts(ctx context.Context, opts *sql.TxOptions, task func(ctx context.Context, txOrm orm.TxOrmer) error) error { + return nil +} // Read ... func (f *FakeOrmer) Read(md interface{}, cols ...string) error { return nil } +func (f *FakeOrmer) ReadWithCtx(ctx context.Context, md interface{}, cols ...string) error { + return nil +} + +func (f *FakeOrmer) ReadForUpdateWithCtx(ctx context.Context, md interface{}, cols ...string) error { + return nil +} + +func (f *FakeOrmer) ReadOrCreateWithCtx(ctx context.Context, md interface{}, col1 string, cols ...string) (bool, int64, error) { + return false, 0, nil +} + +func (f *FakeOrmer) LoadRelatedWithCtx(_ context.Context, md interface{}, name string, args ...utils.KV) (int64, error) { + return 0, nil +} + // ReadForUpdate ... func (f *FakeOrmer) ReadForUpdate(md interface{}, cols ...string) error { return nil @@ -64,11 +150,6 @@ func (f *FakeOrmer) Delete(md interface{}, cols ...string) (int64, error) { return 0, nil } -// LoadRelated ... -func (f *FakeOrmer) LoadRelated(md interface{}, name string, args ...interface{}) (int64, error) { - return 0, nil -} - // QueryM2M ... func (f *FakeOrmer) QueryM2M(md interface{}, name string) orm.QueryM2Mer { return nil @@ -84,11 +165,6 @@ func (f *FakeOrmer) Using(name string) error { return nil } -// Begin ... -func (f *FakeOrmer) Begin() error { - return nil -} - // BeginTx ... func (f *FakeOrmer) BeginTx(ctx context.Context, opts *sql.TxOptions) error { return nil @@ -96,17 +172,17 @@ func (f *FakeOrmer) BeginTx(ctx context.Context, opts *sql.TxOptions) error { // Commit ... func (f *FakeOrmer) Commit() error { - return nil + return f.Commit() } // Rollback ... func (f *FakeOrmer) Rollback() error { - return nil + return f.Rollback() } // Raw ... func (f *FakeOrmer) Raw(query string, args ...interface{}) orm.RawSeter { - return nil + return &FakeRawSeter{} } // Driver ... @@ -118,3 +194,167 @@ func (f *FakeOrmer) Driver() orm.Driver { func (f *FakeOrmer) DBStats() *sql.DBStats { return nil } + +// FakeTxOrmer ... +type FakeTxOrmer struct { +} + +func (f *FakeTxOrmer) Read(md interface{}, cols ...string) error { + return nil +} + +func (f *FakeTxOrmer) ReadWithCtx(ctx context.Context, md interface{}, cols ...string) error { + return nil +} + +func (f *FakeTxOrmer) ReadForUpdate(md interface{}, cols ...string) error { + return nil +} + +func (f *FakeTxOrmer) ReadForUpdateWithCtx(ctx context.Context, md interface{}, cols ...string) error { + return nil +} + +func (f *FakeTxOrmer) ReadOrCreate(md interface{}, col1 string, cols ...string) (bool, int64, error) { + return false, 0, nil +} + +func (f *FakeTxOrmer) ReadOrCreateWithCtx(ctx context.Context, md interface{}, col1 string, cols ...string) (bool, int64, error) { + return false, 0, nil +} + +func (f *FakeTxOrmer) LoadRelated(md interface{}, name string, args ...utils.KV) (int64, error) { + return 0, nil +} + +func (f *FakeTxOrmer) LoadRelatedWithCtx(ctx context.Context, md interface{}, name string, args ...utils.KV) (int64, error) { + return 0, nil +} + +func (f *FakeTxOrmer) QueryM2M(md interface{}, name string) orm.QueryM2Mer { + return nil +} + +func (f *FakeTxOrmer) QueryM2MWithCtx(ctx context.Context, md interface{}, name string) orm.QueryM2Mer { + return nil +} + +func (f *FakeTxOrmer) QueryTable(ptrStructOrTableName interface{}) orm.QuerySeter { + return nil +} + +func (f *FakeTxOrmer) QueryTableWithCtx(ctx context.Context, ptrStructOrTableName interface{}) orm.QuerySeter { + return nil +} + +func (f *FakeTxOrmer) DBStats() *sql.DBStats { + return nil +} + +func (f *FakeTxOrmer) Insert(md interface{}) (int64, error) { + return 0, nil +} + +func (f *FakeTxOrmer) InsertWithCtx(ctx context.Context, md interface{}) (int64, error) { + return 0, nil +} + +func (f *FakeTxOrmer) InsertOrUpdate(md interface{}, colConflitAndArgs ...string) (int64, error) { + return 0, nil +} + +func (f *FakeTxOrmer) InsertOrUpdateWithCtx(ctx context.Context, md interface{}, colConflitAndArgs ...string) (int64, error) { + return 0, nil +} + +func (f *FakeTxOrmer) InsertMulti(bulk int, mds interface{}) (int64, error) { + return 0, nil +} + +func (f *FakeTxOrmer) InsertMultiWithCtx(ctx context.Context, bulk int, mds interface{}) (int64, error) { + return 0, nil +} + +func (f *FakeTxOrmer) Update(md interface{}, cols ...string) (int64, error) { + return 0, nil +} + +func (f *FakeTxOrmer) UpdateWithCtx(ctx context.Context, md interface{}, cols ...string) (int64, error) { + return 0, nil +} + +func (f *FakeTxOrmer) Delete(md interface{}, cols ...string) (int64, error) { + return 0, nil +} + +func (f *FakeTxOrmer) DeleteWithCtx(ctx context.Context, md interface{}, cols ...string) (int64, error) { + return 0, nil +} + +func (f *FakeTxOrmer) Raw(query string, args ...interface{}) orm.RawSeter { + return &FakeRawSeter{} +} + +func (f *FakeTxOrmer) RawWithCtx(ctx context.Context, query string, args ...interface{}) orm.RawSeter { + return nil +} + +func (f *FakeTxOrmer) Driver() orm.Driver { + return nil +} + +func (f *FakeTxOrmer) Commit() error { + return nil +} + +func (f *FakeTxOrmer) Rollback() error { + return nil +} + +func (f *FakeTxOrmer) RollbackUnlessCommit() error { + return nil +} + +// FakeTxOrmer ... +type FakeRawSeter struct { +} + +func (f FakeRawSeter) Exec() (sql.Result, error) { + return nil, nil +} + +func (f FakeRawSeter) QueryRow(containers ...interface{}) error { + return nil +} + +func (f FakeRawSeter) QueryRows(containers ...interface{}) (int64, error) { + return 0, nil +} + +func (f FakeRawSeter) SetArgs(i ...interface{}) orm.RawSeter { + return nil +} + +func (f FakeRawSeter) Values(container *[]orm.Params, cols ...string) (int64, error) { + return 0, nil +} + +func (f FakeRawSeter) ValuesList(container *[]orm.ParamsList, cols ...string) (int64, error) { + return 0, nil +} + +func (f FakeRawSeter) ValuesFlat(container *orm.ParamsList, cols ...string) (int64, error) { + return 0, nil +} + +func (f FakeRawSeter) RowsToMap(result *orm.Params, keyCol, valueCol string) (int64, error) { + return 0, nil +} + +func (f FakeRawSeter) RowsToStruct(ptrStruct interface{}, keyCol, valueCol string) (int64, error) { + return 0, nil +} + +func (f FakeRawSeter) Prepare() (orm.RawPreparer, error) { + return nil, nil +} diff --git a/src/testing/suite.go b/src/testing/suite.go index 31cd1e0d7..51fcb6e9f 100644 --- a/src/testing/suite.go +++ b/src/testing/suite.go @@ -24,7 +24,7 @@ import ( "sync" "time" - o "github.com/beego/beego/orm" + o "github.com/beego/beego/v2/client/orm" "github.com/opencontainers/go-digest" "github.com/stretchr/testify/suite" diff --git a/src/vendor/github.com/beego/beego/.gitignore b/src/vendor/github.com/beego/beego/.gitignore deleted file mode 100644 index e1b652910..000000000 --- a/src/vendor/github.com/beego/beego/.gitignore +++ /dev/null @@ -1,6 +0,0 @@ -.idea -.vscode -.DS_Store -*.swp -*.swo -beego.iml diff --git a/src/vendor/github.com/beego/beego/.travis.yml b/src/vendor/github.com/beego/beego/.travis.yml deleted file mode 100644 index 05b206701..000000000 --- a/src/vendor/github.com/beego/beego/.travis.yml +++ /dev/null @@ -1,73 +0,0 @@ -language: go - -go: - - "1.13.x" -services: - - redis-server - - mysql - - postgresql - - memcached -env: - global: - - GO_REPO_FULLNAME="github.com/beego/beego" - matrix: - - ORM_DRIVER=sqlite3 ORM_SOURCE=$TRAVIS_BUILD_DIR/orm_test.db - - ORM_DRIVER=postgres ORM_SOURCE="user=postgres dbname=orm_test sslmode=disable" -before_install: - # link the local repo with ${GOPATH}/src// - - GO_REPO_NAMESPACE=${GO_REPO_FULLNAME%/*} - # relies on GOPATH to contain only one directory... - - mkdir -p ${GOPATH}/src/${GO_REPO_NAMESPACE} - - ln -sv ${TRAVIS_BUILD_DIR} ${GOPATH}/src/${GO_REPO_FULLNAME} - - cd ${GOPATH}/src/${GO_REPO_FULLNAME} - # get and build ssdb - - git clone git://github.com/ideawu/ssdb.git - - cd ssdb - - make - - cd .. -install: - - go get github.com/lib/pq - - go get github.com/go-sql-driver/mysql - - go get github.com/mattn/go-sqlite3 - - go get github.com/bradfitz/gomemcache/memcache - - go get github.com/gomodule/redigo/redis - - go get github.com/beego/x2j - - go get github.com/couchbase/go-couchbase - - go get github.com/beego/goyaml2 - - go get gopkg.in/yaml.v2 - - go get github.com/belogik/goes - - go get github.com/ledisdb/ledisdb - - go get github.com/ssdb/gossdb/ssdb - - go get github.com/cloudflare/golz4 - - go get github.com/gogo/protobuf/proto - - go get github.com/Knetic/govaluate - - go get github.com/casbin/casbin - - go get github.com/elazarl/go-bindata-assetfs - - go get github.com/OwnLocal/goes - - go get github.com/shiena/ansicolor - - go get -u honnef.co/go/tools/cmd/staticcheck - - go get -u github.com/mdempsky/unconvert - - go get -u github.com/gordonklaus/ineffassign - - go get -u golang.org/x/lint/golint - - go get -u github.com/go-redis/redis -before_script: - - psql --version - - sh -c "if [ '$ORM_DRIVER' = 'postgres' ]; then psql -c 'create database orm_test;' -U postgres; fi" - - sh -c "if [ '$ORM_DRIVER' = 'mysql' ]; then mysql -u root -e 'create database orm_test;'; fi" - - sh -c "if [ '$ORM_DRIVER' = 'sqlite' ]; then touch $TRAVIS_BUILD_DIR/orm_test.db; fi" - - sh -c "go get github.com/golang/lint/golint; golint ./...;" - - sh -c "go list ./... | grep -v vendor | xargs go vet -v" - - mkdir -p res/var - - ./ssdb/ssdb-server ./ssdb/ssdb.conf -d -after_script: - - killall -w ssdb-server - - rm -rf ./res/var/* -script: - - go test -v ./... - - staticcheck -show-ignored -checks "-ST1017,-U1000,-ST1005,-S1034,-S1012,-SA4006,-SA6005,-SA1019,-SA1024" - - unconvert $(go list ./... | grep -v /vendor/) - - ineffassign . - - find . ! \( -path './vendor' -prune \) -type f -name '*.go' -print0 | xargs -0 gofmt -l -s - - golint ./... -addons: - postgresql: "9.6" diff --git a/src/vendor/github.com/beego/beego/CONTRIBUTING.md b/src/vendor/github.com/beego/beego/CONTRIBUTING.md deleted file mode 100644 index f21f5e5a9..000000000 --- a/src/vendor/github.com/beego/beego/CONTRIBUTING.md +++ /dev/null @@ -1,52 +0,0 @@ -# Contributing to beego - -beego is an open source project. - -It is the work of hundreds of contributors. We appreciate your help! - -Here are instructions to get you started. They are probably not perfect, -please let us know if anything feels wrong or incomplete. - -## Contribution guidelines - -### Pull requests - -First of all. beego follow the gitflow. So please send you pull request -to **develop** branch. We will close the pull request to master branch. - -We are always happy to receive pull requests, and do our best to -review them as fast as possible. Not sure if that typo is worth a pull -request? Do it! We will appreciate it. - -If your pull request is not accepted on the first try, don't be -discouraged! Sometimes we can make a mistake, please do more explaining -for us. We will appreciate it. - -We're trying very hard to keep beego simple and fast. We don't want it -to do everything for everybody. This means that we might decide against -incorporating a new feature. But we will give you some advice on how to -do it in other way. - -### Create issues - -Any significant improvement should be documented as [a GitHub -issue](https://github.com/beego/beego/issues) before anybody -starts working on it. - -Also when filing an issue, make sure to answer these five questions: - -- What version of beego are you using (bee version)? -- What operating system and processor architecture are you using? -- What did you do? -- What did you expect to see? -- What did you see instead? - -### but check existing issues and docs first! - -Please take a moment to check that an issue doesn't already exist -documenting your bug report or improvement proposal. If it does, it -never hurts to add a quick "+1" or "I have this problem too". This will -help prioritize the most common problems and requests. - -Also if you don't know how to use it. please make sure you have read though -the docs in http://beego.me/docs \ No newline at end of file diff --git a/src/vendor/github.com/beego/beego/README.md b/src/vendor/github.com/beego/beego/README.md deleted file mode 100644 index f7d3a0772..000000000 --- a/src/vendor/github.com/beego/beego/README.md +++ /dev/null @@ -1,73 +0,0 @@ -# Beego [![Build Status](https://travis-ci.org/astaxie/beego.svg?branch=master)](https://travis-ci.org/astaxie/beego) [![GoDoc](http://godoc.org/github.com/beego/beego?status.svg)](http://godoc.org/github.com/beego/beego) [![Foundation](https://img.shields.io/badge/Golang-Foundation-green.svg)](http://golangfoundation.org) [![Go Report Card](https://goreportcard.com/badge/github.com/beego/beego)](https://goreportcard.com/report/github.com/beego/beego) - - -beego is used for rapid development of RESTful APIs, web apps and backend services in Go. -It is inspired by Tornado, Sinatra and Flask. beego has some Go-specific features such as interfaces and struct embedding. - -###### More info at [beego.me](http://beego.me). - -## Quick Start - -#### Create `hello` directory, cd `hello` directory - - mkdir hello - cd hello - -#### Init module - - go mod init - -#### Download and install - - go get github.com/beego/beego - -#### Create file `hello.go` -```go -package main - -import "github.com/beego/beego" - -func main(){ - beego.Run() -} -``` -#### Build and run - - go build hello.go - ./hello - -#### Go to [http://localhost:8080](http://localhost:8080) - -Congratulations! You've just built your first **beego** app. - -###### Please see [Documentation](http://beego.me/docs) for more. - -###### [beego-example](https://github.com/beego-dev/beego-example) - -## Features - -* RESTful support -* MVC architecture -* Modularity -* Auto API documents -* Annotation router -* Namespace -* Powerful development tools -* Full stack for Web & API - -## Documentation - -* [English](http://beego.me/docs/intro/) -* [中文文档](http://beego.me/docs/intro/) -* [Русский](http://beego.me/docs/intro/) - -## Community - -* [http://beego.me/community](http://beego.me/community) -* Welcome to join us in Slack: [https://beego.slack.com](https://beego.slack.com), you can get invited from [here](https://github.com/beego/beedoc/issues/232) -* QQ Group Group ID:523992905 - -## License - -beego source code is licensed under the Apache Licence, Version 2.0 -(http://www.apache.org/licenses/LICENSE-2.0.html). diff --git a/src/vendor/github.com/beego/beego/admin.go b/src/vendor/github.com/beego/beego/admin.go deleted file mode 100644 index 08991ff8b..000000000 --- a/src/vendor/github.com/beego/beego/admin.go +++ /dev/null @@ -1,458 +0,0 @@ -// Copyright 2014 beego Author. All Rights Reserved. -// -// 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 beego - -import ( - "bytes" - "encoding/json" - "fmt" - "net/http" - "os" - "reflect" - "strconv" - "text/template" - "time" - - "github.com/prometheus/client_golang/prometheus/promhttp" - - "github.com/beego/beego/grace" - "github.com/beego/beego/logs" - "github.com/beego/beego/toolbox" - "github.com/beego/beego/utils" -) - -// BeeAdminApp is the default adminApp used by admin module. -var beeAdminApp *adminApp - -// FilterMonitorFunc is default monitor filter when admin module is enable. -// if this func returns, admin module records qps for this request by condition of this function logic. -// usage: -// func MyFilterMonitor(method, requestPath string, t time.Duration, pattern string, statusCode int) bool { -// if method == "POST" { -// return false -// } -// if t.Nanoseconds() < 100 { -// return false -// } -// if strings.HasPrefix(requestPath, "/astaxie") { -// return false -// } -// return true -// } -// beego.FilterMonitorFunc = MyFilterMonitor. -var FilterMonitorFunc func(string, string, time.Duration, string, int) bool - -func init() { - beeAdminApp = &adminApp{ - routers: make(map[string]http.HandlerFunc), - } - // keep in mind that all data should be html escaped to avoid XSS attack - beeAdminApp.Route("/", adminIndex) - beeAdminApp.Route("/qps", qpsIndex) - beeAdminApp.Route("/prof", profIndex) - beeAdminApp.Route("/healthcheck", healthcheck) - beeAdminApp.Route("/task", taskStatus) - beeAdminApp.Route("/listconf", listConf) - beeAdminApp.Route("/metrics", promhttp.Handler().ServeHTTP) - FilterMonitorFunc = func(string, string, time.Duration, string, int) bool { return true } -} - -// AdminIndex is the default http.Handler for admin module. -// it matches url pattern "/". -func adminIndex(rw http.ResponseWriter, _ *http.Request) { - writeTemplate(rw, map[interface{}]interface{}{}, indexTpl, defaultScriptsTpl) -} - -// QpsIndex is the http.Handler for writing qps statistics map result info in http.ResponseWriter. -// it's registered with url pattern "/qps" in admin module. -func qpsIndex(rw http.ResponseWriter, _ *http.Request) { - data := make(map[interface{}]interface{}) - data["Content"] = toolbox.StatisticsMap.GetMap() - - // do html escape before display path, avoid xss - if content, ok := (data["Content"]).(M); ok { - if resultLists, ok := (content["Data"]).([][]string); ok { - for i := range resultLists { - if len(resultLists[i]) > 0 { - resultLists[i][0] = template.HTMLEscapeString(resultLists[i][0]) - } - } - } - } - - writeTemplate(rw, data, qpsTpl, defaultScriptsTpl) -} - -// ListConf is the http.Handler of displaying all beego configuration values as key/value pair. -// it's registered with url pattern "/listconf" in admin module. -func listConf(rw http.ResponseWriter, r *http.Request) { - r.ParseForm() - command := r.Form.Get("command") - if command == "" { - rw.Write([]byte("command not support")) - return - } - - data := make(map[interface{}]interface{}) - switch command { - case "conf": - m := make(M) - list("BConfig", BConfig, m) - m["AppConfigPath"] = template.HTMLEscapeString(appConfigPath) - m["AppConfigProvider"] = template.HTMLEscapeString(appConfigProvider) - tmpl := template.Must(template.New("dashboard").Parse(dashboardTpl)) - tmpl = template.Must(tmpl.Parse(configTpl)) - tmpl = template.Must(tmpl.Parse(defaultScriptsTpl)) - - data["Content"] = m - - tmpl.Execute(rw, data) - - case "router": - content := PrintTree() - content["Fields"] = []string{ - "Router Pattern", - "Methods", - "Controller", - } - data["Content"] = content - data["Title"] = "Routers" - writeTemplate(rw, data, routerAndFilterTpl, defaultScriptsTpl) - case "filter": - var ( - content = M{ - "Fields": []string{ - "Router Pattern", - "Filter Function", - }, - } - filterTypes = []string{} - filterTypeData = make(M) - ) - - if BeeApp.Handlers.enableFilter { - var filterType string - for k, fr := range map[int]string{ - BeforeStatic: "Before Static", - BeforeRouter: "Before Router", - BeforeExec: "Before Exec", - AfterExec: "After Exec", - FinishRouter: "Finish Router"} { - if bf := BeeApp.Handlers.filters[k]; len(bf) > 0 { - filterType = fr - filterTypes = append(filterTypes, filterType) - resultList := new([][]string) - for _, f := range bf { - var result = []string{ - // void xss - template.HTMLEscapeString(f.pattern), - template.HTMLEscapeString(utils.GetFuncName(f.filterFunc)), - } - *resultList = append(*resultList, result) - } - filterTypeData[filterType] = resultList - } - } - } - - content["Data"] = filterTypeData - content["Methods"] = filterTypes - - data["Content"] = content - data["Title"] = "Filters" - writeTemplate(rw, data, routerAndFilterTpl, defaultScriptsTpl) - default: - rw.Write([]byte("command not support")) - } -} - -func list(root string, p interface{}, m M) { - pt := reflect.TypeOf(p) - pv := reflect.ValueOf(p) - if pt.Kind() == reflect.Ptr { - pt = pt.Elem() - pv = pv.Elem() - } - for i := 0; i < pv.NumField(); i++ { - var key string - if root == "" { - key = pt.Field(i).Name - } else { - key = root + "." + pt.Field(i).Name - } - if pv.Field(i).Kind() == reflect.Struct { - list(key, pv.Field(i).Interface(), m) - } else { - m[key] = pv.Field(i).Interface() - } - } -} - -// PrintTree prints all registered routers. -func PrintTree() M { - var ( - content = M{} - methods = []string{} - methodsData = make(M) - ) - for method, t := range BeeApp.Handlers.routers { - - resultList := new([][]string) - - printTree(resultList, t) - - methods = append(methods, template.HTMLEscapeString(method)) - methodsData[template.HTMLEscapeString(method)] = resultList - } - - content["Data"] = methodsData - content["Methods"] = methods - return content -} - -func printTree(resultList *[][]string, t *Tree) { - for _, tr := range t.fixrouters { - printTree(resultList, tr) - } - if t.wildcard != nil { - printTree(resultList, t.wildcard) - } - for _, l := range t.leaves { - if v, ok := l.runObject.(*ControllerInfo); ok { - if v.routerType == routerTypeBeego { - var result = []string{ - template.HTMLEscapeString(v.pattern), - template.HTMLEscapeString(fmt.Sprintf("%s", v.methods)), - template.HTMLEscapeString(v.controllerType.String()), - } - *resultList = append(*resultList, result) - } else if v.routerType == routerTypeRESTFul { - var result = []string{ - template.HTMLEscapeString(v.pattern), - template.HTMLEscapeString(fmt.Sprintf("%s", v.methods)), - "", - } - *resultList = append(*resultList, result) - } else if v.routerType == routerTypeHandler { - var result = []string{ - template.HTMLEscapeString(v.pattern), - "", - "", - } - *resultList = append(*resultList, result) - } - } - } -} - -// ProfIndex is a http.Handler for showing profile command. -// it's in url pattern "/prof" in admin module. -func profIndex(rw http.ResponseWriter, r *http.Request) { - r.ParseForm() - command := r.Form.Get("command") - if command == "" { - return - } - - var ( - format = r.Form.Get("format") - data = make(map[interface{}]interface{}) - result bytes.Buffer - ) - toolbox.ProcessInput(command, &result) - data["Content"] = template.HTMLEscapeString(result.String()) - - if format == "json" && command == "gc summary" { - dataJSON, err := json.Marshal(data) - if err != nil { - http.Error(rw, err.Error(), http.StatusInternalServerError) - return - } - writeJSON(rw, dataJSON) - return - } - - data["Title"] = template.HTMLEscapeString(command) - defaultTpl := defaultScriptsTpl - if command == "gc summary" { - defaultTpl = gcAjaxTpl - } - writeTemplate(rw, data, profillingTpl, defaultTpl) -} - -// Healthcheck is a http.Handler calling health checking and showing the result. -// it's in "/healthcheck" pattern in admin module. -func healthcheck(rw http.ResponseWriter, r *http.Request) { - var ( - result []string - data = make(map[interface{}]interface{}) - resultList = new([][]string) - content = M{ - "Fields": []string{"Name", "Message", "Status"}, - } - ) - - for name, h := range toolbox.AdminCheckList { - if err := h.Check(); err != nil { - result = []string{ - "error", - template.HTMLEscapeString(name), - template.HTMLEscapeString(err.Error()), - } - } else { - result = []string{ - "success", - template.HTMLEscapeString(name), - "OK", - } - } - *resultList = append(*resultList, result) - } - - queryParams := r.URL.Query() - jsonFlag := queryParams.Get("json") - shouldReturnJSON, _ := strconv.ParseBool(jsonFlag) - - if shouldReturnJSON { - response := buildHealthCheckResponseList(resultList) - jsonResponse, err := json.Marshal(response) - - if err != nil { - http.Error(rw, err.Error(), http.StatusInternalServerError) - } else { - writeJSON(rw, jsonResponse) - } - return - } - - content["Data"] = resultList - data["Content"] = content - data["Title"] = "Health Check" - - writeTemplate(rw, data, healthCheckTpl, defaultScriptsTpl) -} - -func buildHealthCheckResponseList(healthCheckResults *[][]string) []map[string]interface{} { - response := make([]map[string]interface{}, len(*healthCheckResults)) - - for i, healthCheckResult := range *healthCheckResults { - currentResultMap := make(map[string]interface{}) - - currentResultMap["name"] = healthCheckResult[0] - currentResultMap["message"] = healthCheckResult[1] - currentResultMap["status"] = healthCheckResult[2] - - response[i] = currentResultMap - } - - return response - -} - -func writeJSON(rw http.ResponseWriter, jsonData []byte) { - rw.Header().Set("Content-Type", "application/json") - rw.Write(jsonData) -} - -// TaskStatus is a http.Handler with running task status (task name, status and the last execution). -// it's in "/task" pattern in admin module. -func taskStatus(rw http.ResponseWriter, req *http.Request) { - data := make(map[interface{}]interface{}) - - // Run Task - req.ParseForm() - taskname := req.Form.Get("taskname") - if taskname != "" { - if t, ok := toolbox.AdminTaskList[taskname]; ok { - if err := t.Run(); err != nil { - data["Message"] = []string{"error", template.HTMLEscapeString(fmt.Sprintf("%s", err))} - } - data["Message"] = []string{"success", template.HTMLEscapeString(fmt.Sprintf("%s run success,Now the Status is
%s", taskname, t.GetStatus()))} - } else { - data["Message"] = []string{"warning", template.HTMLEscapeString(fmt.Sprintf("there's no task which named: %s", taskname))} - } - } - - // List Tasks - content := make(M) - resultList := new([][]string) - var fields = []string{ - "Task Name", - "Task Spec", - "Task Status", - "Last Time", - "", - } - for tname, tk := range toolbox.AdminTaskList { - result := []string{ - template.HTMLEscapeString(tname), - template.HTMLEscapeString(tk.GetSpec()), - template.HTMLEscapeString(tk.GetStatus()), - template.HTMLEscapeString(tk.GetPrev().String()), - } - *resultList = append(*resultList, result) - } - - content["Fields"] = fields - content["Data"] = resultList - data["Content"] = content - data["Title"] = "Tasks" - writeTemplate(rw, data, tasksTpl, defaultScriptsTpl) -} - -func writeTemplate(rw http.ResponseWriter, data map[interface{}]interface{}, tpls ...string) { - tmpl := template.Must(template.New("dashboard").Parse(dashboardTpl)) - for _, tpl := range tpls { - tmpl = template.Must(tmpl.Parse(tpl)) - } - tmpl.Execute(rw, data) -} - -// adminApp is an http.HandlerFunc map used as beeAdminApp. -type adminApp struct { - routers map[string]http.HandlerFunc -} - -// Route adds http.HandlerFunc to adminApp with url pattern. -func (admin *adminApp) Route(pattern string, f http.HandlerFunc) { - admin.routers[pattern] = f -} - -// Run adminApp http server. -// Its addr is defined in configuration file as adminhttpaddr and adminhttpport. -func (admin *adminApp) Run() { - if len(toolbox.AdminTaskList) > 0 { - toolbox.StartTask() - } - addr := BConfig.Listen.AdminAddr - - if BConfig.Listen.AdminPort != 0 { - addr = fmt.Sprintf("%s:%d", BConfig.Listen.AdminAddr, BConfig.Listen.AdminPort) - } - for p, f := range admin.routers { - http.Handle(p, f) - } - logs.Info("Admin server Running on %s", addr) - - var err error - if BConfig.Listen.Graceful { - err = grace.ListenAndServe(addr, nil) - } else { - err = http.ListenAndServe(addr, nil) - } - if err != nil { - logs.Critical("Admin ListenAndServe: ", err, fmt.Sprintf("%d", os.Getpid())) - } -} diff --git a/src/vendor/github.com/beego/beego/app.go b/src/vendor/github.com/beego/beego/app.go deleted file mode 100644 index 6dc185e7a..000000000 --- a/src/vendor/github.com/beego/beego/app.go +++ /dev/null @@ -1,496 +0,0 @@ -// Copyright 2014 beego Author. All Rights Reserved. -// -// 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 beego - -import ( - "crypto/tls" - "crypto/x509" - "fmt" - "io/ioutil" - "net" - "net/http" - "net/http/fcgi" - "os" - "path" - "strings" - "time" - - "github.com/beego/beego/grace" - "github.com/beego/beego/logs" - "github.com/beego/beego/utils" - "golang.org/x/crypto/acme/autocert" -) - -var ( - // BeeApp is an application instance - BeeApp *App -) - -func init() { - // create beego application - BeeApp = NewApp() -} - -// App defines beego application with a new PatternServeMux. -type App struct { - Handlers *ControllerRegister - Server *http.Server -} - -// NewApp returns a new beego application. -func NewApp() *App { - cr := NewControllerRegister() - app := &App{Handlers: cr, Server: &http.Server{}} - return app -} - -// MiddleWare function for http.Handler -type MiddleWare func(http.Handler) http.Handler - -// Run beego application. -func (app *App) Run(mws ...MiddleWare) { - addr := BConfig.Listen.HTTPAddr - - if BConfig.Listen.HTTPPort != 0 { - addr = fmt.Sprintf("%s:%d", BConfig.Listen.HTTPAddr, BConfig.Listen.HTTPPort) - } - - var ( - err error - l net.Listener - endRunning = make(chan bool, 1) - ) - - // run cgi server - if BConfig.Listen.EnableFcgi { - if BConfig.Listen.EnableStdIo { - if err = fcgi.Serve(nil, app.Handlers); err == nil { // standard I/O - logs.Info("Use FCGI via standard I/O") - } else { - logs.Critical("Cannot use FCGI via standard I/O", err) - } - return - } - if BConfig.Listen.HTTPPort == 0 { - // remove the Socket file before start - if utils.FileExists(addr) { - os.Remove(addr) - } - l, err = net.Listen("unix", addr) - } else { - l, err = net.Listen("tcp", addr) - } - if err != nil { - logs.Critical("Listen: ", err) - } - if err = fcgi.Serve(l, app.Handlers); err != nil { - logs.Critical("fcgi.Serve: ", err) - } - return - } - - app.Server.Handler = app.Handlers - for i := len(mws) - 1; i >= 0; i-- { - if mws[i] == nil { - continue - } - app.Server.Handler = mws[i](app.Server.Handler) - } - app.Server.ReadTimeout = time.Duration(BConfig.Listen.ServerTimeOut) * time.Second - app.Server.WriteTimeout = time.Duration(BConfig.Listen.ServerTimeOut) * time.Second - app.Server.ErrorLog = logs.GetLogger("HTTP") - - // run graceful mode - if BConfig.Listen.Graceful { - httpsAddr := BConfig.Listen.HTTPSAddr - app.Server.Addr = httpsAddr - if BConfig.Listen.EnableHTTPS || BConfig.Listen.EnableMutualHTTPS { - go func() { - time.Sleep(1000 * time.Microsecond) - if BConfig.Listen.HTTPSPort != 0 { - httpsAddr = fmt.Sprintf("%s:%d", BConfig.Listen.HTTPSAddr, BConfig.Listen.HTTPSPort) - app.Server.Addr = httpsAddr - } - server := grace.NewServer(httpsAddr, app.Server.Handler) - server.Server.ReadTimeout = app.Server.ReadTimeout - server.Server.WriteTimeout = app.Server.WriteTimeout - if BConfig.Listen.EnableMutualHTTPS { - if err := server.ListenAndServeMutualTLS(BConfig.Listen.HTTPSCertFile, BConfig.Listen.HTTPSKeyFile, BConfig.Listen.TrustCaFile); err != nil { - logs.Critical("ListenAndServeTLS: ", err, fmt.Sprintf("%d", os.Getpid())) - time.Sleep(100 * time.Microsecond) - } - } else { - if BConfig.Listen.AutoTLS { - m := autocert.Manager{ - Prompt: autocert.AcceptTOS, - HostPolicy: autocert.HostWhitelist(BConfig.Listen.Domains...), - Cache: autocert.DirCache(BConfig.Listen.TLSCacheDir), - } - app.Server.TLSConfig = &tls.Config{GetCertificate: m.GetCertificate} - BConfig.Listen.HTTPSCertFile, BConfig.Listen.HTTPSKeyFile = "", "" - } - if err := server.ListenAndServeTLS(BConfig.Listen.HTTPSCertFile, BConfig.Listen.HTTPSKeyFile); err != nil { - logs.Critical("ListenAndServeTLS: ", err, fmt.Sprintf("%d", os.Getpid())) - time.Sleep(100 * time.Microsecond) - } - } - endRunning <- true - }() - } - if BConfig.Listen.EnableHTTP { - go func() { - server := grace.NewServer(addr, app.Server.Handler) - server.Server.ReadTimeout = app.Server.ReadTimeout - server.Server.WriteTimeout = app.Server.WriteTimeout - if BConfig.Listen.ListenTCP4 { - server.Network = "tcp4" - } - if err := server.ListenAndServe(); err != nil { - logs.Critical("ListenAndServe: ", err, fmt.Sprintf("%d", os.Getpid())) - time.Sleep(100 * time.Microsecond) - } - endRunning <- true - }() - } - <-endRunning - return - } - - // run normal mode - if BConfig.Listen.EnableHTTPS || BConfig.Listen.EnableMutualHTTPS { - go func() { - time.Sleep(1000 * time.Microsecond) - if BConfig.Listen.HTTPSPort != 0 { - app.Server.Addr = fmt.Sprintf("%s:%d", BConfig.Listen.HTTPSAddr, BConfig.Listen.HTTPSPort) - } else if BConfig.Listen.EnableHTTP { - logs.Info("Start https server error, conflict with http. Please reset https port") - return - } - logs.Info("https server Running on https://%s", app.Server.Addr) - if BConfig.Listen.AutoTLS { - m := autocert.Manager{ - Prompt: autocert.AcceptTOS, - HostPolicy: autocert.HostWhitelist(BConfig.Listen.Domains...), - Cache: autocert.DirCache(BConfig.Listen.TLSCacheDir), - } - app.Server.TLSConfig = &tls.Config{GetCertificate: m.GetCertificate} - BConfig.Listen.HTTPSCertFile, BConfig.Listen.HTTPSKeyFile = "", "" - } else if BConfig.Listen.EnableMutualHTTPS { - pool := x509.NewCertPool() - data, err := ioutil.ReadFile(BConfig.Listen.TrustCaFile) - if err != nil { - logs.Info("MutualHTTPS should provide TrustCaFile") - return - } - pool.AppendCertsFromPEM(data) - app.Server.TLSConfig = &tls.Config{ - ClientCAs: pool, - ClientAuth: BConfig.Listen.ClientAuth, - } - } - if err := app.Server.ListenAndServeTLS(BConfig.Listen.HTTPSCertFile, BConfig.Listen.HTTPSKeyFile); err != nil { - logs.Critical("ListenAndServeTLS: ", err) - time.Sleep(100 * time.Microsecond) - endRunning <- true - } - }() - - } - if BConfig.Listen.EnableHTTP { - go func() { - app.Server.Addr = addr - logs.Info("http server Running on http://%s", app.Server.Addr) - if BConfig.Listen.ListenTCP4 { - ln, err := net.Listen("tcp4", app.Server.Addr) - if err != nil { - logs.Critical("ListenAndServe: ", err) - time.Sleep(100 * time.Microsecond) - endRunning <- true - return - } - if err = app.Server.Serve(ln); err != nil { - logs.Critical("ListenAndServe: ", err) - time.Sleep(100 * time.Microsecond) - endRunning <- true - return - } - } else { - if err := app.Server.ListenAndServe(); err != nil { - logs.Critical("ListenAndServe: ", err) - time.Sleep(100 * time.Microsecond) - endRunning <- true - } - } - }() - } - <-endRunning -} - -// Router adds a patterned controller handler to BeeApp. -// it's an alias method of App.Router. -// usage: -// simple router -// beego.Router("/admin", &admin.UserController{}) -// beego.Router("/admin/index", &admin.ArticleController{}) -// -// regex router -// -// beego.Router("/api/:id([0-9]+)", &controllers.RController{}) -// -// custom rules -// beego.Router("/api/list",&RestController{},"*:ListFood") -// beego.Router("/api/create",&RestController{},"post:CreateFood") -// beego.Router("/api/update",&RestController{},"put:UpdateFood") -// beego.Router("/api/delete",&RestController{},"delete:DeleteFood") -func Router(rootpath string, c ControllerInterface, mappingMethods ...string) *App { - BeeApp.Handlers.Add(rootpath, c, mappingMethods...) - return BeeApp -} - -// UnregisterFixedRoute unregisters the route with the specified fixedRoute. It is particularly useful -// in web applications that inherit most routes from a base webapp via the underscore -// import, and aim to overwrite only certain paths. -// The method parameter can be empty or "*" for all HTTP methods, or a particular -// method type (e.g. "GET" or "POST") for selective removal. -// -// Usage (replace "GET" with "*" for all methods): -// beego.UnregisterFixedRoute("/yourpreviouspath", "GET") -// beego.Router("/yourpreviouspath", yourControllerAddress, "get:GetNewPage") -func UnregisterFixedRoute(fixedRoute string, method string) *App { - subPaths := splitPath(fixedRoute) - if method == "" || method == "*" { - for m := range HTTPMETHOD { - if _, ok := BeeApp.Handlers.routers[m]; !ok { - continue - } - if BeeApp.Handlers.routers[m].prefix == strings.Trim(fixedRoute, "/ ") { - findAndRemoveSingleTree(BeeApp.Handlers.routers[m]) - continue - } - findAndRemoveTree(subPaths, BeeApp.Handlers.routers[m], m) - } - return BeeApp - } - // Single HTTP method - um := strings.ToUpper(method) - if _, ok := BeeApp.Handlers.routers[um]; ok { - if BeeApp.Handlers.routers[um].prefix == strings.Trim(fixedRoute, "/ ") { - findAndRemoveSingleTree(BeeApp.Handlers.routers[um]) - return BeeApp - } - findAndRemoveTree(subPaths, BeeApp.Handlers.routers[um], um) - } - return BeeApp -} - -func findAndRemoveTree(paths []string, entryPointTree *Tree, method string) { - for i := range entryPointTree.fixrouters { - if entryPointTree.fixrouters[i].prefix == paths[0] { - if len(paths) == 1 { - if len(entryPointTree.fixrouters[i].fixrouters) > 0 { - // If the route had children subtrees, remove just the functional leaf, - // to allow children to function as before - if len(entryPointTree.fixrouters[i].leaves) > 0 { - entryPointTree.fixrouters[i].leaves[0] = nil - entryPointTree.fixrouters[i].leaves = entryPointTree.fixrouters[i].leaves[1:] - } - } else { - // Remove the *Tree from the fixrouters slice - entryPointTree.fixrouters[i] = nil - - if i == len(entryPointTree.fixrouters)-1 { - entryPointTree.fixrouters = entryPointTree.fixrouters[:i] - } else { - entryPointTree.fixrouters = append(entryPointTree.fixrouters[:i], entryPointTree.fixrouters[i+1:len(entryPointTree.fixrouters)]...) - } - } - return - } - findAndRemoveTree(paths[1:], entryPointTree.fixrouters[i], method) - } - } -} - -func findAndRemoveSingleTree(entryPointTree *Tree) { - if entryPointTree == nil { - return - } - if len(entryPointTree.fixrouters) > 0 { - // If the route had children subtrees, remove just the functional leaf, - // to allow children to function as before - if len(entryPointTree.leaves) > 0 { - entryPointTree.leaves[0] = nil - entryPointTree.leaves = entryPointTree.leaves[1:] - } - } -} - -// Include will generate router file in the router/xxx.go from the controller's comments -// usage: -// beego.Include(&BankAccount{}, &OrderController{},&RefundController{},&ReceiptController{}) -// type BankAccount struct{ -// beego.Controller -// } -// -// register the function -// func (b *BankAccount)Mapping(){ -// b.Mapping("ShowAccount" , b.ShowAccount) -// b.Mapping("ModifyAccount", b.ModifyAccount) -//} -// -// //@router /account/:id [get] -// func (b *BankAccount) ShowAccount(){ -// //logic -// } -// -// -// //@router /account/:id [post] -// func (b *BankAccount) ModifyAccount(){ -// //logic -// } -// -// the comments @router url methodlist -// url support all the function Router's pattern -// methodlist [get post head put delete options *] -func Include(cList ...ControllerInterface) *App { - BeeApp.Handlers.Include(cList...) - return BeeApp -} - -// RESTRouter adds a restful controller handler to BeeApp. -// its' controller implements beego.ControllerInterface and -// defines a param "pattern/:objectId" to visit each resource. -func RESTRouter(rootpath string, c ControllerInterface) *App { - Router(rootpath, c) - Router(path.Join(rootpath, ":objectId"), c) - return BeeApp -} - -// AutoRouter adds defined controller handler to BeeApp. -// it's same to App.AutoRouter. -// if beego.AddAuto(&MainContorlller{}) and MainController has methods List and Page, -// visit the url /main/list to exec List function or /main/page to exec Page function. -func AutoRouter(c ControllerInterface) *App { - BeeApp.Handlers.AddAuto(c) - return BeeApp -} - -// AutoPrefix adds controller handler to BeeApp with prefix. -// it's same to App.AutoRouterWithPrefix. -// if beego.AutoPrefix("/admin",&MainContorlller{}) and MainController has methods List and Page, -// visit the url /admin/main/list to exec List function or /admin/main/page to exec Page function. -func AutoPrefix(prefix string, c ControllerInterface) *App { - BeeApp.Handlers.AddAutoPrefix(prefix, c) - return BeeApp -} - -// Get used to register router for Get method -// usage: -// beego.Get("/", func(ctx *context.Context){ -// ctx.Output.Body("hello world") -// }) -func Get(rootpath string, f FilterFunc) *App { - BeeApp.Handlers.Get(rootpath, f) - return BeeApp -} - -// Post used to register router for Post method -// usage: -// beego.Post("/api", func(ctx *context.Context){ -// ctx.Output.Body("hello world") -// }) -func Post(rootpath string, f FilterFunc) *App { - BeeApp.Handlers.Post(rootpath, f) - return BeeApp -} - -// Delete used to register router for Delete method -// usage: -// beego.Delete("/api", func(ctx *context.Context){ -// ctx.Output.Body("hello world") -// }) -func Delete(rootpath string, f FilterFunc) *App { - BeeApp.Handlers.Delete(rootpath, f) - return BeeApp -} - -// Put used to register router for Put method -// usage: -// beego.Put("/api", func(ctx *context.Context){ -// ctx.Output.Body("hello world") -// }) -func Put(rootpath string, f FilterFunc) *App { - BeeApp.Handlers.Put(rootpath, f) - return BeeApp -} - -// Head used to register router for Head method -// usage: -// beego.Head("/api", func(ctx *context.Context){ -// ctx.Output.Body("hello world") -// }) -func Head(rootpath string, f FilterFunc) *App { - BeeApp.Handlers.Head(rootpath, f) - return BeeApp -} - -// Options used to register router for Options method -// usage: -// beego.Options("/api", func(ctx *context.Context){ -// ctx.Output.Body("hello world") -// }) -func Options(rootpath string, f FilterFunc) *App { - BeeApp.Handlers.Options(rootpath, f) - return BeeApp -} - -// Patch used to register router for Patch method -// usage: -// beego.Patch("/api", func(ctx *context.Context){ -// ctx.Output.Body("hello world") -// }) -func Patch(rootpath string, f FilterFunc) *App { - BeeApp.Handlers.Patch(rootpath, f) - return BeeApp -} - -// Any used to register router for all methods -// usage: -// beego.Any("/api", func(ctx *context.Context){ -// ctx.Output.Body("hello world") -// }) -func Any(rootpath string, f FilterFunc) *App { - BeeApp.Handlers.Any(rootpath, f) - return BeeApp -} - -// Handler used to register a Handler router -// usage: -// beego.Handler("/api", http.HandlerFunc(func (w http.ResponseWriter, r *http.Request) { -// fmt.Fprintf(w, "Hello, %q", html.EscapeString(r.URL.Path)) -// })) -func Handler(rootpath string, h http.Handler, options ...interface{}) *App { - BeeApp.Handlers.Handler(rootpath, h, options...) - return BeeApp -} - -// InsertFilter adds a FilterFunc with pattern condition and action constant. -// The pos means action constant including -// beego.BeforeStatic, beego.BeforeRouter, beego.BeforeExec, beego.AfterExec and beego.FinishRouter. -// The bool params is for setting the returnOnOutput value (false allows multiple filters to execute) -func InsertFilter(pattern string, pos int, filter FilterFunc, params ...bool) *App { - BeeApp.Handlers.InsertFilter(pattern, pos, filter, params...) - return BeeApp -} diff --git a/src/vendor/github.com/beego/beego/cache/file.go b/src/vendor/github.com/beego/beego/cache/file.go deleted file mode 100644 index 6f12d3eee..000000000 --- a/src/vendor/github.com/beego/beego/cache/file.go +++ /dev/null @@ -1,258 +0,0 @@ -// Copyright 2014 beego Author. All Rights Reserved. -// -// 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 cache - -import ( - "bytes" - "crypto/md5" - "encoding/gob" - "encoding/hex" - "encoding/json" - "fmt" - "io" - "io/ioutil" - "os" - "path/filepath" - "reflect" - "strconv" - "time" -) - -// FileCacheItem is basic unit of file cache adapter. -// it contains data and expire time. -type FileCacheItem struct { - Data interface{} - Lastaccess time.Time - Expired time.Time -} - -// FileCache Config -var ( - FileCachePath = "cache" // cache directory - FileCacheFileSuffix = ".bin" // cache file suffix - FileCacheDirectoryLevel = 2 // cache file deep level if auto generated cache files. - FileCacheEmbedExpiry time.Duration // cache expire time, default is no expire forever. -) - -// FileCache is cache adapter for file storage. -type FileCache struct { - CachePath string - FileSuffix string - DirectoryLevel int - EmbedExpiry int -} - -// NewFileCache Create new file cache with no config. -// the level and expiry need set in method StartAndGC as config string. -func NewFileCache() Cache { - // return &FileCache{CachePath:FileCachePath, FileSuffix:FileCacheFileSuffix} - return &FileCache{} -} - -// StartAndGC will start and begin gc for file cache. -// the config need to be like {CachePath:"/cache","FileSuffix":".bin","DirectoryLevel":"2","EmbedExpiry":"0"} -func (fc *FileCache) StartAndGC(config string) error { - - cfg := make(map[string]string) - err := json.Unmarshal([]byte(config), &cfg) - if err != nil { - return err - } - if _, ok := cfg["CachePath"]; !ok { - cfg["CachePath"] = FileCachePath - } - if _, ok := cfg["FileSuffix"]; !ok { - cfg["FileSuffix"] = FileCacheFileSuffix - } - if _, ok := cfg["DirectoryLevel"]; !ok { - cfg["DirectoryLevel"] = strconv.Itoa(FileCacheDirectoryLevel) - } - if _, ok := cfg["EmbedExpiry"]; !ok { - cfg["EmbedExpiry"] = strconv.FormatInt(int64(FileCacheEmbedExpiry.Seconds()), 10) - } - fc.CachePath = cfg["CachePath"] - fc.FileSuffix = cfg["FileSuffix"] - fc.DirectoryLevel, _ = strconv.Atoi(cfg["DirectoryLevel"]) - fc.EmbedExpiry, _ = strconv.Atoi(cfg["EmbedExpiry"]) - - fc.Init() - return nil -} - -// Init will make new dir for file cache if not exist. -func (fc *FileCache) Init() { - if ok, _ := exists(fc.CachePath); !ok { // todo : error handle - _ = os.MkdirAll(fc.CachePath, os.ModePerm) // todo : error handle - } -} - -// get cached file name. it's md5 encoded. -func (fc *FileCache) getCacheFileName(key string) string { - m := md5.New() - io.WriteString(m, key) - keyMd5 := hex.EncodeToString(m.Sum(nil)) - cachePath := fc.CachePath - switch fc.DirectoryLevel { - case 2: - cachePath = filepath.Join(cachePath, keyMd5[0:2], keyMd5[2:4]) - case 1: - cachePath = filepath.Join(cachePath, keyMd5[0:2]) - } - - if ok, _ := exists(cachePath); !ok { // todo : error handle - _ = os.MkdirAll(cachePath, os.ModePerm) // todo : error handle - } - - return filepath.Join(cachePath, fmt.Sprintf("%s%s", keyMd5, fc.FileSuffix)) -} - -// Get value from file cache. -// if non-exist or expired, return empty string. -func (fc *FileCache) Get(key string) interface{} { - fileData, err := FileGetContents(fc.getCacheFileName(key)) - if err != nil { - return "" - } - var to FileCacheItem - GobDecode(fileData, &to) - if to.Expired.Before(time.Now()) { - return "" - } - return to.Data -} - -// GetMulti gets values from file cache. -// if non-exist or expired, return empty string. -func (fc *FileCache) GetMulti(keys []string) []interface{} { - var rc []interface{} - for _, key := range keys { - rc = append(rc, fc.Get(key)) - } - return rc -} - -// Put value into file cache. -// timeout means how long to keep this file, unit of ms. -// if timeout equals fc.EmbedExpiry(default is 0), cache this item forever. -func (fc *FileCache) Put(key string, val interface{}, timeout time.Duration) error { - gob.Register(val) - - item := FileCacheItem{Data: val} - if timeout == time.Duration(fc.EmbedExpiry) { - item.Expired = time.Now().Add((86400 * 365 * 10) * time.Second) // ten years - } else { - item.Expired = time.Now().Add(timeout) - } - item.Lastaccess = time.Now() - data, err := GobEncode(item) - if err != nil { - return err - } - return FilePutContents(fc.getCacheFileName(key), data) -} - -// Delete file cache value. -func (fc *FileCache) Delete(key string) error { - filename := fc.getCacheFileName(key) - if ok, _ := exists(filename); ok { - return os.Remove(filename) - } - return nil -} - -// Incr will increase cached int value. -// fc value is saving forever unless Delete. -func (fc *FileCache) Incr(key string) error { - data := fc.Get(key) - var incr int - if reflect.TypeOf(data).Name() != "int" { - incr = 0 - } else { - incr = data.(int) + 1 - } - fc.Put(key, incr, time.Duration(fc.EmbedExpiry)) - return nil -} - -// Decr will decrease cached int value. -func (fc *FileCache) Decr(key string) error { - data := fc.Get(key) - var decr int - if reflect.TypeOf(data).Name() != "int" || data.(int)-1 <= 0 { - decr = 0 - } else { - decr = data.(int) - 1 - } - fc.Put(key, decr, time.Duration(fc.EmbedExpiry)) - return nil -} - -// IsExist check value is exist. -func (fc *FileCache) IsExist(key string) bool { - ret, _ := exists(fc.getCacheFileName(key)) - return ret -} - -// ClearAll will clean cached files. -// not implemented. -func (fc *FileCache) ClearAll() error { - return nil -} - -// check file exist. -func exists(path string) (bool, error) { - _, err := os.Stat(path) - if err == nil { - return true, nil - } - if os.IsNotExist(err) { - return false, nil - } - return false, err -} - -// FileGetContents Get bytes to file. -// if non-exist, create this file. -func FileGetContents(filename string) (data []byte, e error) { - return ioutil.ReadFile(filename) -} - -// FilePutContents Put bytes to file. -// if non-exist, create this file. -func FilePutContents(filename string, content []byte) error { - return ioutil.WriteFile(filename, content, os.ModePerm) -} - -// GobEncode Gob encodes file cache item. -func GobEncode(data interface{}) ([]byte, error) { - buf := bytes.NewBuffer(nil) - enc := gob.NewEncoder(buf) - err := enc.Encode(data) - if err != nil { - return nil, err - } - return buf.Bytes(), err -} - -// GobDecode Gob decodes file cache item. -func GobDecode(data []byte, to *FileCacheItem) error { - buf := bytes.NewBuffer(data) - dec := gob.NewDecoder(buf) - return dec.Decode(&to) -} - -func init() { - Register("file", NewFileCache) -} diff --git a/src/vendor/github.com/beego/beego/cache/memory.go b/src/vendor/github.com/beego/beego/cache/memory.go deleted file mode 100644 index d8314e3cc..000000000 --- a/src/vendor/github.com/beego/beego/cache/memory.go +++ /dev/null @@ -1,256 +0,0 @@ -// Copyright 2014 beego Author. All Rights Reserved. -// -// 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 cache - -import ( - "encoding/json" - "errors" - "sync" - "time" -) - -var ( - // DefaultEvery means the clock time of recycling the expired cache items in memory. - DefaultEvery = 60 // 1 minute -) - -// MemoryItem store memory cache item. -type MemoryItem struct { - val interface{} - createdTime time.Time - lifespan time.Duration -} - -func (mi *MemoryItem) isExpire() bool { - // 0 means forever - if mi.lifespan == 0 { - return false - } - return time.Now().Sub(mi.createdTime) > mi.lifespan -} - -// MemoryCache is Memory cache adapter. -// it contains a RW locker for safe map storage. -type MemoryCache struct { - sync.RWMutex - dur time.Duration - items map[string]*MemoryItem - Every int // run an expiration check Every clock time -} - -// NewMemoryCache returns a new MemoryCache. -func NewMemoryCache() Cache { - cache := MemoryCache{items: make(map[string]*MemoryItem)} - return &cache -} - -// Get cache from memory. -// if non-existed or expired, return nil. -func (bc *MemoryCache) Get(name string) interface{} { - bc.RLock() - defer bc.RUnlock() - if itm, ok := bc.items[name]; ok { - if itm.isExpire() { - return nil - } - return itm.val - } - return nil -} - -// GetMulti gets caches from memory. -// if non-existed or expired, return nil. -func (bc *MemoryCache) GetMulti(names []string) []interface{} { - var rc []interface{} - for _, name := range names { - rc = append(rc, bc.Get(name)) - } - return rc -} - -// Put cache to memory. -// if lifespan is 0, it will be forever till restart. -func (bc *MemoryCache) Put(name string, value interface{}, lifespan time.Duration) error { - bc.Lock() - defer bc.Unlock() - bc.items[name] = &MemoryItem{ - val: value, - createdTime: time.Now(), - lifespan: lifespan, - } - return nil -} - -// Delete cache in memory. -func (bc *MemoryCache) Delete(name string) error { - bc.Lock() - defer bc.Unlock() - if _, ok := bc.items[name]; !ok { - return errors.New("key not exist") - } - delete(bc.items, name) - if _, ok := bc.items[name]; ok { - return errors.New("delete key error") - } - return nil -} - -// Incr increase cache counter in memory. -// it supports int,int32,int64,uint,uint32,uint64. -func (bc *MemoryCache) Incr(key string) error { - bc.Lock() - defer bc.Unlock() - itm, ok := bc.items[key] - if !ok { - return errors.New("key not exist") - } - switch val := itm.val.(type) { - case int: - itm.val = val + 1 - case int32: - itm.val = val + 1 - case int64: - itm.val = val + 1 - case uint: - itm.val = val + 1 - case uint32: - itm.val = val + 1 - case uint64: - itm.val = val + 1 - default: - return errors.New("item val is not (u)int (u)int32 (u)int64") - } - return nil -} - -// Decr decrease counter in memory. -func (bc *MemoryCache) Decr(key string) error { - bc.Lock() - defer bc.Unlock() - itm, ok := bc.items[key] - if !ok { - return errors.New("key not exist") - } - switch val := itm.val.(type) { - case int: - itm.val = val - 1 - case int64: - itm.val = val - 1 - case int32: - itm.val = val - 1 - case uint: - if val > 0 { - itm.val = val - 1 - } else { - return errors.New("item val is less than 0") - } - case uint32: - if val > 0 { - itm.val = val - 1 - } else { - return errors.New("item val is less than 0") - } - case uint64: - if val > 0 { - itm.val = val - 1 - } else { - return errors.New("item val is less than 0") - } - default: - return errors.New("item val is not int int64 int32") - } - return nil -} - -// IsExist check cache exist in memory. -func (bc *MemoryCache) IsExist(name string) bool { - bc.RLock() - defer bc.RUnlock() - if v, ok := bc.items[name]; ok { - return !v.isExpire() - } - return false -} - -// ClearAll will delete all cache in memory. -func (bc *MemoryCache) ClearAll() error { - bc.Lock() - defer bc.Unlock() - bc.items = make(map[string]*MemoryItem) - return nil -} - -// StartAndGC start memory cache. it will check expiration in every clock time. -func (bc *MemoryCache) StartAndGC(config string) error { - var cf map[string]int - json.Unmarshal([]byte(config), &cf) - if _, ok := cf["interval"]; !ok { - cf = make(map[string]int) - cf["interval"] = DefaultEvery - } - dur := time.Duration(cf["interval"]) * time.Second - bc.Every = cf["interval"] - bc.dur = dur - go bc.vacuum() - return nil -} - -// check expiration. -func (bc *MemoryCache) vacuum() { - bc.RLock() - every := bc.Every - bc.RUnlock() - - if every < 1 { - return - } - for { - <-time.After(bc.dur) - bc.RLock() - if bc.items == nil { - bc.RUnlock() - return - } - bc.RUnlock() - if keys := bc.expiredKeys(); len(keys) != 0 { - bc.clearItems(keys) - } - } -} - -// expiredKeys returns key list which are expired. -func (bc *MemoryCache) expiredKeys() (keys []string) { - bc.RLock() - defer bc.RUnlock() - for key, itm := range bc.items { - if itm.isExpire() { - keys = append(keys, key) - } - } - return -} - -// clearItems removes all the items which key in keys. -func (bc *MemoryCache) clearItems(keys []string) { - bc.Lock() - defer bc.Unlock() - for _, key := range keys { - delete(bc.items, key) - } -} - -func init() { - Register("memory", NewMemoryCache) -} diff --git a/src/vendor/github.com/beego/beego/config.go b/src/vendor/github.com/beego/beego/config.go deleted file mode 100644 index ffa89edde..000000000 --- a/src/vendor/github.com/beego/beego/config.go +++ /dev/null @@ -1,537 +0,0 @@ -// Copyright 2014 beego Author. All Rights Reserved. -// -// 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 beego - -import ( - "crypto/tls" - "fmt" - "net/http" - "os" - "path/filepath" - "reflect" - "runtime" - "strings" - - "github.com/beego/beego/config" - "github.com/beego/beego/context" - "github.com/beego/beego/logs" - "github.com/beego/beego/session" - "github.com/beego/beego/utils" -) - -// Config is the main struct for BConfig -type Config struct { - AppName string // Application name - RunMode string // Running Mode: dev | prod - RouterCaseSensitive bool - ServerName string - RecoverPanic bool - RecoverFunc func(*context.Context) - CopyRequestBody bool - EnableGzip bool - MaxMemory int64 - EnableErrorsShow bool - EnableErrorsRender bool - Listen Listen - WebConfig WebConfig - Log LogConfig -} - -// Listen holds for http and https related config -type Listen struct { - Graceful bool // Graceful means use graceful module to start the server - ServerTimeOut int64 - ListenTCP4 bool - EnableHTTP bool - HTTPAddr string - HTTPPort int - AutoTLS bool - Domains []string - TLSCacheDir string - EnableHTTPS bool - EnableMutualHTTPS bool - HTTPSAddr string - HTTPSPort int - HTTPSCertFile string - HTTPSKeyFile string - TrustCaFile string - ClientAuth tls.ClientAuthType - EnableAdmin bool - AdminAddr string - AdminPort int - EnableFcgi bool - EnableStdIo bool // EnableStdIo works with EnableFcgi Use FCGI via standard I/O -} - -// WebConfig holds web related config -type WebConfig struct { - AutoRender bool - EnableDocs bool - FlashName string - FlashSeparator string - DirectoryIndex bool - StaticDir map[string]string - StaticExtensionsToGzip []string - StaticCacheFileSize int - StaticCacheFileNum int - TemplateLeft string - TemplateRight string - ViewsPath string - EnableXSRF bool - XSRFKey string - XSRFExpire int - XSRFSecure bool - XSRFHttpOnly bool - Session SessionConfig -} - -// SessionConfig holds session related config -type SessionConfig struct { - SessionOn bool - SessionProvider string - SessionName string - SessionGCMaxLifetime int64 - SessionProviderConfig string - SessionCookieLifeTime int - SessionAutoSetCookie bool - SessionDomain string - SessionDisableHTTPOnly bool // used to allow for cross domain cookies/javascript cookies. - SessionEnableSidInHTTPHeader bool // enable store/get the sessionId into/from http headers - SessionNameInHTTPHeader string - SessionEnableSidInURLQuery bool // enable get the sessionId from Url Query params - SessionCookieSameSite http.SameSite -} - -// LogConfig holds Log related config -type LogConfig struct { - AccessLogs bool - EnableStaticLogs bool // log static files requests default: false - AccessLogsFormat string // access log format: JSON_FORMAT, APACHE_FORMAT or empty string - FileLineNum bool - Outputs map[string]string // Store Adaptor : config -} - -var ( - // BConfig is the default config for Application - BConfig *Config - // AppConfig is the instance of Config, store the config information from file - AppConfig *beegoAppConfig - // AppPath is the absolute path to the app - AppPath string - // GlobalSessions is the instance for the session manager - GlobalSessions *session.Manager - - // appConfigPath is the path to the config files - appConfigPath string - // appConfigProvider is the provider for the config, default is ini - appConfigProvider = "ini" - // WorkPath is the absolute path to project root directory - WorkPath string -) - -func init() { - BConfig = newBConfig() - var err error - if AppPath, err = filepath.Abs(filepath.Dir(os.Args[0])); err != nil { - panic(err) - } - WorkPath, err = os.Getwd() - if err != nil { - panic(err) - } - var filename = "app.conf" - if os.Getenv("BEEGO_RUNMODE") != "" { - filename = os.Getenv("BEEGO_RUNMODE") + ".app.conf" - } - appConfigPath = filepath.Join(WorkPath, "conf", filename) - if configPath := os.Getenv("BEEGO_CONFIG_PATH"); configPath != "" { - appConfigPath = configPath - } - if !utils.FileExists(appConfigPath) { - appConfigPath = filepath.Join(AppPath, "conf", filename) - if !utils.FileExists(appConfigPath) { - AppConfig = &beegoAppConfig{innerConfig: config.NewFakeConfig()} - return - } - } - if err = parseConfig(appConfigPath); err != nil { - panic(err) - } -} - -func recoverPanic(ctx *context.Context) { - if err := recover(); err != nil { - if err == ErrAbort { - return - } - if !BConfig.RecoverPanic { - panic(err) - } - if BConfig.EnableErrorsShow { - if _, ok := ErrorMaps[fmt.Sprint(err)]; ok { - exception(fmt.Sprint(err), ctx) - return - } - } - var stack string - logs.Critical("the request url is ", ctx.Input.URL()) - logs.Critical("Handler crashed with error", err) - for i := 1; ; i++ { - _, file, line, ok := runtime.Caller(i) - if !ok { - break - } - logs.Critical(fmt.Sprintf("%s:%d", file, line)) - stack = stack + fmt.Sprintln(fmt.Sprintf("%s:%d", file, line)) - } - if BConfig.RunMode == DEV && BConfig.EnableErrorsRender { - showErr(err, ctx, stack) - } - if ctx.Output.Status != 0 { - ctx.ResponseWriter.WriteHeader(ctx.Output.Status) - } else { - ctx.ResponseWriter.WriteHeader(500) - } - } -} - -func newBConfig() *Config { - return &Config{ - AppName: "beego", - RunMode: PROD, - RouterCaseSensitive: true, - ServerName: "beegoServer:" + VERSION, - RecoverPanic: true, - RecoverFunc: recoverPanic, - CopyRequestBody: false, - EnableGzip: false, - MaxMemory: 1 << 26, // 64MB - EnableErrorsShow: true, - EnableErrorsRender: true, - Listen: Listen{ - Graceful: false, - ServerTimeOut: 0, - ListenTCP4: false, - EnableHTTP: true, - AutoTLS: false, - Domains: []string{}, - TLSCacheDir: ".", - HTTPAddr: "", - HTTPPort: 8080, - EnableHTTPS: false, - HTTPSAddr: "", - HTTPSPort: 10443, - HTTPSCertFile: "", - HTTPSKeyFile: "", - EnableAdmin: false, - AdminAddr: "", - AdminPort: 8088, - EnableFcgi: false, - EnableStdIo: false, - ClientAuth: tls.RequireAndVerifyClientCert, - }, - WebConfig: WebConfig{ - AutoRender: true, - EnableDocs: false, - FlashName: "BEEGO_FLASH", - FlashSeparator: "BEEGOFLASH", - DirectoryIndex: false, - StaticDir: map[string]string{"/static": "static"}, - StaticExtensionsToGzip: []string{".css", ".js"}, - StaticCacheFileSize: 1024 * 100, - StaticCacheFileNum: 1000, - TemplateLeft: "{{", - TemplateRight: "}}", - ViewsPath: "views", - EnableXSRF: false, - XSRFKey: "beegoxsrf", - XSRFExpire: 0, - XSRFSecure: false, - XSRFHttpOnly: false, - Session: SessionConfig{ - SessionOn: false, - SessionProvider: "memory", - SessionName: "beegosessionID", - SessionGCMaxLifetime: 3600, - SessionProviderConfig: "", - SessionDisableHTTPOnly: false, - SessionCookieLifeTime: 0, // set cookie default is the browser life - SessionAutoSetCookie: true, - SessionDomain: "", - SessionEnableSidInHTTPHeader: false, // enable store/get the sessionId into/from http headers - SessionNameInHTTPHeader: "Beegosessionid", - SessionEnableSidInURLQuery: false, // enable get the sessionId from Url Query params - SessionCookieSameSite: http.SameSiteDefaultMode, - }, - }, - Log: LogConfig{ - AccessLogs: false, - EnableStaticLogs: false, - AccessLogsFormat: "APACHE_FORMAT", - FileLineNum: true, - Outputs: map[string]string{"console": ""}, - }, - } -} - -// now only support ini, next will support json. -func parseConfig(appConfigPath string) (err error) { - AppConfig, err = newAppConfig(appConfigProvider, appConfigPath) - if err != nil { - return err - } - return assignConfig(AppConfig) -} - -func assignConfig(ac config.Configer) error { - for _, i := range []interface{}{BConfig, &BConfig.Listen, &BConfig.WebConfig, &BConfig.Log, &BConfig.WebConfig.Session} { - assignSingleConfig(i, ac) - } - // set the run mode first - if envRunMode := os.Getenv("BEEGO_RUNMODE"); envRunMode != "" { - BConfig.RunMode = envRunMode - } else if runMode := ac.String("RunMode"); runMode != "" { - BConfig.RunMode = runMode - } - - if sd := ac.String("StaticDir"); sd != "" { - BConfig.WebConfig.StaticDir = map[string]string{} - sds := strings.Fields(sd) - for _, v := range sds { - if url2fsmap := strings.SplitN(v, ":", 2); len(url2fsmap) == 2 { - BConfig.WebConfig.StaticDir["/"+strings.Trim(url2fsmap[0], "/")] = url2fsmap[1] - } else { - BConfig.WebConfig.StaticDir["/"+strings.Trim(url2fsmap[0], "/")] = url2fsmap[0] - } - } - } - - if sgz := ac.String("StaticExtensionsToGzip"); sgz != "" { - extensions := strings.Split(sgz, ",") - fileExts := []string{} - for _, ext := range extensions { - ext = strings.TrimSpace(ext) - if ext == "" { - continue - } - if !strings.HasPrefix(ext, ".") { - ext = "." + ext - } - fileExts = append(fileExts, ext) - } - if len(fileExts) > 0 { - BConfig.WebConfig.StaticExtensionsToGzip = fileExts - } - } - - if sfs, err := ac.Int("StaticCacheFileSize"); err == nil { - BConfig.WebConfig.StaticCacheFileSize = sfs - } - - if sfn, err := ac.Int("StaticCacheFileNum"); err == nil { - BConfig.WebConfig.StaticCacheFileNum = sfn - } - - if lo := ac.String("LogOutputs"); lo != "" { - // if lo is not nil or empty - // means user has set his own LogOutputs - // clear the default setting to BConfig.Log.Outputs - BConfig.Log.Outputs = make(map[string]string) - los := strings.Split(lo, ";") - for _, v := range los { - if logType2Config := strings.SplitN(v, ",", 2); len(logType2Config) == 2 { - BConfig.Log.Outputs[logType2Config[0]] = logType2Config[1] - } else { - continue - } - } - } - - // init log - logs.Reset() - for adaptor, config := range BConfig.Log.Outputs { - err := logs.SetLogger(adaptor, config) - if err != nil { - fmt.Fprintln(os.Stderr, fmt.Sprintf("%s with the config %q got err:%s", adaptor, config, err.Error())) - } - } - logs.SetLogFuncCall(BConfig.Log.FileLineNum) - - return nil -} - -func assignSingleConfig(p interface{}, ac config.Configer) { - pt := reflect.TypeOf(p) - if pt.Kind() != reflect.Ptr { - return - } - pt = pt.Elem() - if pt.Kind() != reflect.Struct { - return - } - pv := reflect.ValueOf(p).Elem() - - for i := 0; i < pt.NumField(); i++ { - pf := pv.Field(i) - if !pf.CanSet() { - continue - } - name := pt.Field(i).Name - switch pf.Kind() { - case reflect.String: - pf.SetString(ac.DefaultString(name, pf.String())) - case reflect.Int, reflect.Int64: - pf.SetInt(ac.DefaultInt64(name, pf.Int())) - case reflect.Bool: - pf.SetBool(ac.DefaultBool(name, pf.Bool())) - case reflect.Struct: - default: - // do nothing here - } - } - -} - -// LoadAppConfig allow developer to apply a config file -func LoadAppConfig(adapterName, configPath string) error { - absConfigPath, err := filepath.Abs(configPath) - if err != nil { - return err - } - - if !utils.FileExists(absConfigPath) { - return fmt.Errorf("the target config file: %s don't exist", configPath) - } - - appConfigPath = absConfigPath - appConfigProvider = adapterName - - return parseConfig(appConfigPath) -} - -type beegoAppConfig struct { - innerConfig config.Configer -} - -func newAppConfig(appConfigProvider, appConfigPath string) (*beegoAppConfig, error) { - ac, err := config.NewConfig(appConfigProvider, appConfigPath) - if err != nil { - return nil, err - } - return &beegoAppConfig{ac}, nil -} - -func (b *beegoAppConfig) Set(key, val string) error { - if err := b.innerConfig.Set(BConfig.RunMode+"::"+key, val); err != nil { - return b.innerConfig.Set(key, val) - } - return nil -} - -func (b *beegoAppConfig) String(key string) string { - if v := b.innerConfig.String(BConfig.RunMode + "::" + key); v != "" { - return v - } - return b.innerConfig.String(key) -} - -func (b *beegoAppConfig) Strings(key string) []string { - if v := b.innerConfig.Strings(BConfig.RunMode + "::" + key); len(v) > 0 { - return v - } - return b.innerConfig.Strings(key) -} - -func (b *beegoAppConfig) Int(key string) (int, error) { - if v, err := b.innerConfig.Int(BConfig.RunMode + "::" + key); err == nil { - return v, nil - } - return b.innerConfig.Int(key) -} - -func (b *beegoAppConfig) Int64(key string) (int64, error) { - if v, err := b.innerConfig.Int64(BConfig.RunMode + "::" + key); err == nil { - return v, nil - } - return b.innerConfig.Int64(key) -} - -func (b *beegoAppConfig) Bool(key string) (bool, error) { - if v, err := b.innerConfig.Bool(BConfig.RunMode + "::" + key); err == nil { - return v, nil - } - return b.innerConfig.Bool(key) -} - -func (b *beegoAppConfig) Float(key string) (float64, error) { - if v, err := b.innerConfig.Float(BConfig.RunMode + "::" + key); err == nil { - return v, nil - } - return b.innerConfig.Float(key) -} - -func (b *beegoAppConfig) DefaultString(key string, defaultVal string) string { - if v := b.String(key); v != "" { - return v - } - return defaultVal -} - -func (b *beegoAppConfig) DefaultStrings(key string, defaultVal []string) []string { - if v := b.Strings(key); len(v) != 0 { - return v - } - return defaultVal -} - -func (b *beegoAppConfig) DefaultInt(key string, defaultVal int) int { - if v, err := b.Int(key); err == nil { - return v - } - return defaultVal -} - -func (b *beegoAppConfig) DefaultInt64(key string, defaultVal int64) int64 { - if v, err := b.Int64(key); err == nil { - return v - } - return defaultVal -} - -func (b *beegoAppConfig) DefaultBool(key string, defaultVal bool) bool { - if v, err := b.Bool(key); err == nil { - return v - } - return defaultVal -} - -func (b *beegoAppConfig) DefaultFloat(key string, defaultVal float64) float64 { - if v, err := b.Float(key); err == nil { - return v - } - return defaultVal -} - -func (b *beegoAppConfig) DIY(key string) (interface{}, error) { - return b.innerConfig.DIY(key) -} - -func (b *beegoAppConfig) GetSection(section string) (map[string]string, error) { - return b.innerConfig.GetSection(section) -} - -func (b *beegoAppConfig) SaveConfigFile(filename string) error { - return b.innerConfig.SaveConfigFile(filename) -} diff --git a/src/vendor/github.com/beego/beego/config/json.go b/src/vendor/github.com/beego/beego/config/json.go deleted file mode 100644 index c4ef25cd3..000000000 --- a/src/vendor/github.com/beego/beego/config/json.go +++ /dev/null @@ -1,269 +0,0 @@ -// Copyright 2014 beego Author. All Rights Reserved. -// -// 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 ( - "encoding/json" - "errors" - "fmt" - "io/ioutil" - "os" - "strconv" - "strings" - "sync" -) - -// JSONConfig is a json config parser and implements Config interface. -type JSONConfig struct { -} - -// Parse returns a ConfigContainer with parsed json config map. -func (js *JSONConfig) Parse(filename string) (Configer, error) { - file, err := os.Open(filename) - if err != nil { - return nil, err - } - defer file.Close() - content, err := ioutil.ReadAll(file) - if err != nil { - return nil, err - } - - return js.ParseData(content) -} - -// ParseData returns a ConfigContainer with json string -func (js *JSONConfig) ParseData(data []byte) (Configer, error) { - x := &JSONConfigContainer{ - data: make(map[string]interface{}), - } - err := json.Unmarshal(data, &x.data) - if err != nil { - var wrappingArray []interface{} - err2 := json.Unmarshal(data, &wrappingArray) - if err2 != nil { - return nil, err - } - x.data["rootArray"] = wrappingArray - } - - x.data = ExpandValueEnvForMap(x.data) - - return x, nil -} - -// JSONConfigContainer A Config represents the json configuration. -// Only when get value, support key as section:name type. -type JSONConfigContainer struct { - data map[string]interface{} - sync.RWMutex -} - -// Bool returns the boolean value for a given key. -func (c *JSONConfigContainer) Bool(key string) (bool, error) { - val := c.getData(key) - if val != nil { - return ParseBool(val) - } - return false, fmt.Errorf("not exist key: %q", key) -} - -// DefaultBool return the bool value if has no error -// otherwise return the defaultval -func (c *JSONConfigContainer) DefaultBool(key string, defaultval bool) bool { - if v, err := c.Bool(key); err == nil { - return v - } - return defaultval -} - -// Int returns the integer value for a given key. -func (c *JSONConfigContainer) Int(key string) (int, error) { - val := c.getData(key) - if val != nil { - if v, ok := val.(float64); ok { - return int(v), nil - } else if v, ok := val.(string); ok { - return strconv.Atoi(v) - } - return 0, errors.New("not valid value") - } - return 0, errors.New("not exist key:" + key) -} - -// DefaultInt returns the integer value for a given key. -// if err != nil return defaultval -func (c *JSONConfigContainer) DefaultInt(key string, defaultval int) int { - if v, err := c.Int(key); err == nil { - return v - } - return defaultval -} - -// Int64 returns the int64 value for a given key. -func (c *JSONConfigContainer) Int64(key string) (int64, error) { - val := c.getData(key) - if val != nil { - if v, ok := val.(float64); ok { - return int64(v), nil - } - return 0, errors.New("not int64 value") - } - return 0, errors.New("not exist key:" + key) -} - -// DefaultInt64 returns the int64 value for a given key. -// if err != nil return defaultval -func (c *JSONConfigContainer) DefaultInt64(key string, defaultval int64) int64 { - if v, err := c.Int64(key); err == nil { - return v - } - return defaultval -} - -// Float returns the float value for a given key. -func (c *JSONConfigContainer) Float(key string) (float64, error) { - val := c.getData(key) - if val != nil { - if v, ok := val.(float64); ok { - return v, nil - } - return 0.0, errors.New("not float64 value") - } - return 0.0, errors.New("not exist key:" + key) -} - -// DefaultFloat returns the float64 value for a given key. -// if err != nil return defaultval -func (c *JSONConfigContainer) DefaultFloat(key string, defaultval float64) float64 { - if v, err := c.Float(key); err == nil { - return v - } - return defaultval -} - -// String returns the string value for a given key. -func (c *JSONConfigContainer) String(key string) string { - val := c.getData(key) - if val != nil { - if v, ok := val.(string); ok { - return v - } - } - return "" -} - -// DefaultString returns the string value for a given key. -// if err != nil return defaultval -func (c *JSONConfigContainer) DefaultString(key string, defaultval string) string { - // TODO FIXME should not use "" to replace non existence - if v := c.String(key); v != "" { - return v - } - return defaultval -} - -// Strings returns the []string value for a given key. -func (c *JSONConfigContainer) Strings(key string) []string { - stringVal := c.String(key) - if stringVal == "" { - return nil - } - return strings.Split(c.String(key), ";") -} - -// DefaultStrings returns the []string value for a given key. -// if err != nil return defaultval -func (c *JSONConfigContainer) DefaultStrings(key string, defaultval []string) []string { - if v := c.Strings(key); v != nil { - return v - } - return defaultval -} - -// GetSection returns map for the given section -func (c *JSONConfigContainer) GetSection(section string) (map[string]string, error) { - if v, ok := c.data[section]; ok { - return v.(map[string]string), nil - } - return nil, errors.New("nonexist section " + section) -} - -// SaveConfigFile save the config into file -func (c *JSONConfigContainer) SaveConfigFile(filename string) (err error) { - // Write configuration file by filename. - f, err := os.Create(filename) - if err != nil { - return err - } - defer f.Close() - b, err := json.MarshalIndent(c.data, "", " ") - if err != nil { - return err - } - _, err = f.Write(b) - return err -} - -// Set writes a new value for key. -func (c *JSONConfigContainer) Set(key, val string) error { - c.Lock() - defer c.Unlock() - c.data[key] = val - return nil -} - -// DIY returns the raw value by a given key. -func (c *JSONConfigContainer) DIY(key string) (v interface{}, err error) { - val := c.getData(key) - if val != nil { - return val, nil - } - return nil, errors.New("not exist key") -} - -// section.key or key -func (c *JSONConfigContainer) getData(key string) interface{} { - if len(key) == 0 { - return nil - } - - c.RLock() - defer c.RUnlock() - - sectionKeys := strings.Split(key, "::") - if len(sectionKeys) >= 2 { - curValue, ok := c.data[sectionKeys[0]] - if !ok { - return nil - } - for _, key := range sectionKeys[1:] { - if v, ok := curValue.(map[string]interface{}); ok { - if curValue, ok = v[key]; !ok { - return nil - } - } - } - return curValue - } - if v, ok := c.data[key]; ok { - return v - } - return nil -} - -func init() { - Register("json", &JSONConfig{}) -} diff --git a/src/vendor/github.com/beego/beego/context/context.go b/src/vendor/github.com/beego/beego/context/context.go deleted file mode 100644 index bd83e825c..000000000 --- a/src/vendor/github.com/beego/beego/context/context.go +++ /dev/null @@ -1,273 +0,0 @@ -// Copyright 2014 beego Author. All Rights Reserved. -// -// 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 context provide the context utils -// Usage: -// -// import "github.com/beego/beego/context" -// -// ctx := context.Context{Request:req,ResponseWriter:rw} -// -// more docs http://beego.me/docs/module/context.md -package context - -import ( - "bufio" - "crypto/hmac" - "crypto/sha256" - "encoding/base64" - "errors" - "fmt" - "net" - "net/http" - "strconv" - "strings" - "time" - - "github.com/beego/beego/utils" -) - -//commonly used mime-types -const ( - ApplicationJSON = "application/json" - ApplicationXML = "application/xml" - ApplicationYAML = "application/x-yaml" - TextXML = "text/xml" -) - -// NewContext return the Context with Input and Output -func NewContext() *Context { - return &Context{ - Input: NewInput(), - Output: NewOutput(), - } -} - -// Context Http request context struct including BeegoInput, BeegoOutput, http.Request and http.ResponseWriter. -// BeegoInput and BeegoOutput provides some api to operate request and response more easily. -type Context struct { - Input *BeegoInput - Output *BeegoOutput - Request *http.Request - ResponseWriter *Response - _xsrfToken string -} - -// Reset init Context, BeegoInput and BeegoOutput -func (ctx *Context) Reset(rw http.ResponseWriter, r *http.Request) { - ctx.Request = r - if ctx.ResponseWriter == nil { - ctx.ResponseWriter = &Response{} - } - ctx.ResponseWriter.reset(rw) - ctx.Input.Reset(ctx) - ctx.Output.Reset(ctx) - ctx._xsrfToken = "" -} - -// Redirect does redirection to localurl with http header status code. -func (ctx *Context) Redirect(status int, localurl string) { - http.Redirect(ctx.ResponseWriter, ctx.Request, localurl, status) -} - -// Abort stops this request. -// if beego.ErrorMaps exists, panic body. -func (ctx *Context) Abort(status int, body string) { - ctx.Output.SetStatus(status) - panic(body) -} - -// WriteString Write string to response body. -// it sends response body. -func (ctx *Context) WriteString(content string) { - ctx.ResponseWriter.Write([]byte(content)) -} - -// GetCookie Get cookie from request by a given key. -// It's alias of BeegoInput.Cookie. -func (ctx *Context) GetCookie(key string) string { - return ctx.Input.Cookie(key) -} - -// SetCookie Set cookie for response. -// It's alias of BeegoOutput.Cookie. -func (ctx *Context) SetCookie(name string, value string, others ...interface{}) { - ctx.Output.Cookie(name, value, others...) -} - -// GetSecureCookie Get secure cookie from request by a given key. -func (ctx *Context) GetSecureCookie(Secret, key string) (string, bool) { - val := ctx.Input.Cookie(key) - if val == "" { - return "", false - } - - parts := strings.SplitN(val, "|", 3) - - if len(parts) != 3 { - return "", false - } - - vs := parts[0] - timestamp := parts[1] - sig := parts[2] - - h := hmac.New(sha256.New, []byte(Secret)) - fmt.Fprintf(h, "%s%s", vs, timestamp) - - if fmt.Sprintf("%02x", h.Sum(nil)) != sig { - return "", false - } - res, _ := base64.URLEncoding.DecodeString(vs) - return string(res), true -} - -// SetSecureCookie Set Secure cookie for response. -func (ctx *Context) SetSecureCookie(Secret, name, value string, others ...interface{}) { - vs := base64.URLEncoding.EncodeToString([]byte(value)) - timestamp := strconv.FormatInt(time.Now().UnixNano(), 10) - h := hmac.New(sha256.New, []byte(Secret)) - fmt.Fprintf(h, "%s%s", vs, timestamp) - sig := fmt.Sprintf("%02x", h.Sum(nil)) - cookie := strings.Join([]string{vs, timestamp, sig}, "|") - ctx.Output.Cookie(name, cookie, others...) -} - -// XSRFToken creates a xsrf token string and returns. -// others[0] bool secure -// others[1] bool http-only -func (ctx *Context) XSRFToken(key string, expire int64, others ...interface{}) string { - if ctx._xsrfToken == "" { - token, ok := ctx.GetSecureCookie(key, "_xsrf") - if !ok { - token = string(utils.RandomCreateBytes(32)) - secure := false - if len(others) > 0 { - secure = others[0].(bool) - } - httpOnly := false - if len(others) > 1 { - httpOnly = others[1].(bool) - } - ctx.SetSecureCookie(key, "_xsrf", token, expire, "", "", secure, httpOnly) - } - ctx._xsrfToken = token - } - return ctx._xsrfToken -} - -// CheckXSRFCookie checks xsrf token in this request is valid or not. -// the token can provided in request header "X-Xsrftoken" and "X-CsrfToken" -// or in form field value named as "_xsrf". -func (ctx *Context) CheckXSRFCookie() bool { - token := ctx.Input.Query("_xsrf") - if token == "" { - token = ctx.Request.Header.Get("X-Xsrftoken") - } - if token == "" { - token = ctx.Request.Header.Get("X-Csrftoken") - } - if token == "" { - ctx.Abort(422, "422") - return false - } - if ctx._xsrfToken != token { - ctx.Abort(417, "417") - return false - } - return true -} - -// RenderMethodResult renders the return value of a controller method to the output -func (ctx *Context) RenderMethodResult(result interface{}) { - if result != nil { - renderer, ok := result.(Renderer) - if !ok { - err, ok := result.(error) - if ok { - renderer = errorRenderer(err) - } else { - renderer = jsonRenderer(result) - } - } - renderer.Render(ctx) - } -} - -//Response is a wrapper for the http.ResponseWriter -//started set to true if response was written to then don't execute other handler -type Response struct { - http.ResponseWriter - Started bool - Status int - Elapsed time.Duration -} - -func (r *Response) reset(rw http.ResponseWriter) { - r.ResponseWriter = rw - r.Status = 0 - r.Started = false -} - -// Write writes the data to the connection as part of an HTTP reply, -// and sets `started` to true. -// started means the response has sent out. -func (r *Response) Write(p []byte) (int, error) { - r.Started = true - return r.ResponseWriter.Write(p) -} - -// WriteHeader sends an HTTP response header with status code, -// and sets `started` to true. -func (r *Response) WriteHeader(code int) { - if r.Status > 0 { - //prevent multiple response.WriteHeader calls - return - } - r.Status = code - r.Started = true - r.ResponseWriter.WriteHeader(code) -} - -// Hijack hijacker for http -func (r *Response) Hijack() (net.Conn, *bufio.ReadWriter, error) { - hj, ok := r.ResponseWriter.(http.Hijacker) - if !ok { - return nil, nil, errors.New("webserver doesn't support hijacking") - } - return hj.Hijack() -} - -// Flush http.Flusher -func (r *Response) Flush() { - if f, ok := r.ResponseWriter.(http.Flusher); ok { - f.Flush() - } -} - -// CloseNotify http.CloseNotifier -func (r *Response) CloseNotify() <-chan bool { - if cn, ok := r.ResponseWriter.(http.CloseNotifier); ok { - return cn.CloseNotify() - } - return nil -} - -// Pusher http.Pusher -func (r *Response) Pusher() (pusher http.Pusher) { - if pusher, ok := r.ResponseWriter.(http.Pusher); ok { - return pusher - } - return nil -} diff --git a/src/vendor/github.com/beego/beego/filter.go b/src/vendor/github.com/beego/beego/filter.go deleted file mode 100644 index f4e74aaf1..000000000 --- a/src/vendor/github.com/beego/beego/filter.go +++ /dev/null @@ -1,44 +0,0 @@ -// Copyright 2014 beego Author. All Rights Reserved. -// -// 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 beego - -import "github.com/beego/beego/context" - -// FilterFunc defines a filter function which is invoked before the controller handler is executed. -type FilterFunc func(*context.Context) - -// FilterRouter defines a filter operation which is invoked before the controller handler is executed. -// It can match the URL against a pattern, and execute a filter function -// when a request with a matching URL arrives. -type FilterRouter struct { - filterFunc FilterFunc - tree *Tree - pattern string - returnOnOutput bool - resetParams bool -} - -// ValidRouter checks if the current request is matched by this filter. -// If the request is matched, the values of the URL parameters defined -// by the filter pattern are also returned. -func (f *FilterRouter) ValidRouter(url string, ctx *context.Context) bool { - isOk := f.tree.Match(url, ctx) - if isOk != nil { - if b, ok := isOk.(bool); ok { - return b - } - } - return false -} diff --git a/src/vendor/github.com/beego/beego/log.go b/src/vendor/github.com/beego/beego/log.go deleted file mode 100644 index 27ea86bae..000000000 --- a/src/vendor/github.com/beego/beego/log.go +++ /dev/null @@ -1,127 +0,0 @@ -// Copyright 2014 beego Author. All Rights Reserved. -// -// 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 beego - -import ( - "strings" - - "github.com/beego/beego/logs" -) - -// Log levels to control the logging output. -// Deprecated: use github.com/beego/beego/logs instead. -const ( - LevelEmergency = iota - LevelAlert - LevelCritical - LevelError - LevelWarning - LevelNotice - LevelInformational - LevelDebug -) - -// BeeLogger references the used application logger. -// Deprecated: use github.com/beego/beego/logs instead. -var BeeLogger = logs.GetBeeLogger() - -// SetLevel sets the global log level used by the simple logger. -// Deprecated: use github.com/beego/beego/logs instead. -func SetLevel(l int) { - logs.SetLevel(l) -} - -// SetLogFuncCall set the CallDepth, default is 3 -// Deprecated: use github.com/beego/beego/logs instead. -func SetLogFuncCall(b bool) { - logs.SetLogFuncCall(b) -} - -// SetLogger sets a new logger. -// Deprecated: use github.com/beego/beego/logs instead. -func SetLogger(adaptername string, config string) error { - return logs.SetLogger(adaptername, config) -} - -// Emergency logs a message at emergency level. -// Deprecated: use github.com/beego/beego/logs instead. -func Emergency(v ...interface{}) { - logs.Emergency(generateFmtStr(len(v)), v...) -} - -// Alert logs a message at alert level. -// Deprecated: use github.com/beego/beego/logs instead. -func Alert(v ...interface{}) { - logs.Alert(generateFmtStr(len(v)), v...) -} - -// Critical logs a message at critical level. -// Deprecated: use github.com/beego/beego/logs instead. -func Critical(v ...interface{}) { - logs.Critical(generateFmtStr(len(v)), v...) -} - -// Error logs a message at error level. -// Deprecated: use github.com/beego/beego/logs instead. -func Error(v ...interface{}) { - logs.Error(generateFmtStr(len(v)), v...) -} - -// Warning logs a message at warning level. -// Deprecated: use github.com/beego/beego/logs instead. -func Warning(v ...interface{}) { - logs.Warning(generateFmtStr(len(v)), v...) -} - -// Warn compatibility alias for Warning() -// Deprecated: use github.com/beego/beego/logs instead. -func Warn(v ...interface{}) { - logs.Warn(generateFmtStr(len(v)), v...) -} - -// Notice logs a message at notice level. -// Deprecated: use github.com/beego/beego/logs instead. -func Notice(v ...interface{}) { - logs.Notice(generateFmtStr(len(v)), v...) -} - -// Informational logs a message at info level. -// Deprecated: use github.com/beego/beego/logs instead. -func Informational(v ...interface{}) { - logs.Informational(generateFmtStr(len(v)), v...) -} - -// Info compatibility alias for Warning() -// Deprecated: use github.com/beego/beego/logs instead. -func Info(v ...interface{}) { - logs.Info(generateFmtStr(len(v)), v...) -} - -// Debug logs a message at debug level. -// Deprecated: use github.com/beego/beego/logs instead. -func Debug(v ...interface{}) { - logs.Debug(generateFmtStr(len(v)), v...) -} - -// Trace logs a message at trace level. -// compatibility alias for Warning() -// Deprecated: use github.com/beego/beego/logs instead. -func Trace(v ...interface{}) { - logs.Trace(generateFmtStr(len(v)), v...) -} - -func generateFmtStr(n int) string { - return strings.Repeat("%v ", n) -} diff --git a/src/vendor/github.com/beego/beego/logs/slack.go b/src/vendor/github.com/beego/beego/logs/slack.go deleted file mode 100644 index 1cd2e5aee..000000000 --- a/src/vendor/github.com/beego/beego/logs/slack.go +++ /dev/null @@ -1,60 +0,0 @@ -package logs - -import ( - "encoding/json" - "fmt" - "net/http" - "net/url" - "time" -) - -// SLACKWriter implements beego LoggerInterface and is used to send jiaoliao webhook -type SLACKWriter struct { - WebhookURL string `json:"webhookurl"` - Level int `json:"level"` -} - -// newSLACKWriter create jiaoliao writer. -func newSLACKWriter() Logger { - return &SLACKWriter{Level: LevelTrace} -} - -// Init SLACKWriter with json config string -func (s *SLACKWriter) Init(jsonconfig string) error { - return json.Unmarshal([]byte(jsonconfig), s) -} - -// WriteMsg write message in smtp writer. -// it will send an email with subject and only this message. -func (s *SLACKWriter) WriteMsg(when time.Time, msg string, level int) error { - if level > s.Level { - return nil - } - - text := fmt.Sprintf("{\"text\": \"%s %s\"}", when.Format("2006-01-02 15:04:05"), msg) - - form := url.Values{} - form.Add("payload", text) - - resp, err := http.PostForm(s.WebhookURL, form) - if err != nil { - return err - } - defer resp.Body.Close() - if resp.StatusCode != http.StatusOK { - return fmt.Errorf("Post webhook failed %s %d", resp.Status, resp.StatusCode) - } - return nil -} - -// Flush implementing method. empty. -func (s *SLACKWriter) Flush() { -} - -// Destroy implementing method. empty. -func (s *SLACKWriter) Destroy() { -} - -func init() { - Register(AdapterSlack, newSLACKWriter) -} diff --git a/src/vendor/github.com/beego/beego/orm/cmd_utils.go b/src/vendor/github.com/beego/beego/orm/cmd_utils.go deleted file mode 100644 index 692a079fa..000000000 --- a/src/vendor/github.com/beego/beego/orm/cmd_utils.go +++ /dev/null @@ -1,320 +0,0 @@ -// Copyright 2014 beego Author. All Rights Reserved. -// -// 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 orm - -import ( - "fmt" - "os" - "strings" -) - -type dbIndex struct { - Table string - Name string - SQL string -} - -// create database drop sql. -func getDbDropSQL(al *alias) (sqls []string) { - if len(modelCache.cache) == 0 { - fmt.Println("no Model found, need register your model") - os.Exit(2) - } - - Q := al.DbBaser.TableQuote() - - for _, mi := range modelCache.allOrdered() { - sqls = append(sqls, fmt.Sprintf(`DROP TABLE IF EXISTS %s%s%s`, Q, mi.table, Q)) - } - return sqls -} - -// get database column type string. -func getColumnTyp(al *alias, fi *fieldInfo) (col string) { - T := al.DbBaser.DbTypes() - fieldType := fi.fieldType - fieldSize := fi.size - -checkColumn: - switch fieldType { - case TypeBooleanField: - col = T["bool"] - case TypeVarCharField: - if al.Driver == DRPostgres && fi.toText { - col = T["string-text"] - } else { - col = fmt.Sprintf(T["string"], fieldSize) - } - case TypeCharField: - col = fmt.Sprintf(T["string-char"], fieldSize) - case TypeTextField: - col = T["string-text"] - case TypeTimeField: - col = T["time.Time-clock"] - case TypeDateField: - col = T["time.Time-date"] - case TypeDateTimeField: - col = T["time.Time"] - case TypeBitField: - col = T["int8"] - case TypeSmallIntegerField: - col = T["int16"] - case TypeIntegerField: - col = T["int32"] - case TypeBigIntegerField: - if al.Driver == DRSqlite { - fieldType = TypeIntegerField - goto checkColumn - } - col = T["int64"] - case TypePositiveBitField: - col = T["uint8"] - case TypePositiveSmallIntegerField: - col = T["uint16"] - case TypePositiveIntegerField: - col = T["uint32"] - case TypePositiveBigIntegerField: - col = T["uint64"] - case TypeFloatField: - col = T["float64"] - case TypeDecimalField: - s := T["float64-decimal"] - if !strings.Contains(s, "%d") { - col = s - } else { - col = fmt.Sprintf(s, fi.digits, fi.decimals) - } - case TypeJSONField: - if al.Driver != DRPostgres { - fieldType = TypeVarCharField - goto checkColumn - } - col = T["json"] - case TypeJsonbField: - if al.Driver != DRPostgres { - fieldType = TypeVarCharField - goto checkColumn - } - col = T["jsonb"] - case RelForeignKey, RelOneToOne: - fieldType = fi.relModelInfo.fields.pk.fieldType - fieldSize = fi.relModelInfo.fields.pk.size - goto checkColumn - } - - return -} - -// create alter sql string. -func getColumnAddQuery(al *alias, fi *fieldInfo) string { - Q := al.DbBaser.TableQuote() - typ := getColumnTyp(al, fi) - - if !fi.null { - typ += " " + "NOT NULL" - } - - return fmt.Sprintf("ALTER TABLE %s%s%s ADD COLUMN %s%s%s %s %s", - Q, fi.mi.table, Q, - Q, fi.column, Q, - typ, getColumnDefault(fi), - ) -} - -// create database creation string. -func getDbCreateSQL(al *alias) (sqls []string, tableIndexes map[string][]dbIndex) { - if len(modelCache.cache) == 0 { - fmt.Println("no Model found, need register your model") - os.Exit(2) - } - - Q := al.DbBaser.TableQuote() - T := al.DbBaser.DbTypes() - sep := fmt.Sprintf("%s, %s", Q, Q) - - tableIndexes = make(map[string][]dbIndex) - - for _, mi := range modelCache.allOrdered() { - sql := fmt.Sprintf("-- %s\n", strings.Repeat("-", 50)) - sql += fmt.Sprintf("-- Table Structure for `%s`\n", mi.fullName) - sql += fmt.Sprintf("-- %s\n", strings.Repeat("-", 50)) - - sql += fmt.Sprintf("CREATE TABLE IF NOT EXISTS %s%s%s (\n", Q, mi.table, Q) - - columns := make([]string, 0, len(mi.fields.fieldsDB)) - - sqlIndexes := [][]string{} - - for _, fi := range mi.fields.fieldsDB { - - column := fmt.Sprintf(" %s%s%s ", Q, fi.column, Q) - col := getColumnTyp(al, fi) - - if fi.auto { - switch al.Driver { - case DRSqlite, DRPostgres: - column += T["auto"] - default: - column += col + " " + T["auto"] - } - } else if fi.pk { - column += col + " " + T["pk"] - } else { - column += col - - if !fi.null { - column += " " + "NOT NULL" - } - - //if fi.initial.String() != "" { - // column += " DEFAULT " + fi.initial.String() - //} - - // Append attribute DEFAULT - column += getColumnDefault(fi) - - if fi.unique { - column += " " + "UNIQUE" - } - - if fi.index { - sqlIndexes = append(sqlIndexes, []string{fi.column}) - } - } - - if strings.Contains(column, "%COL%") { - column = strings.Replace(column, "%COL%", fi.column, -1) - } - - if fi.description != "" && al.Driver != DRSqlite { - column += " " + fmt.Sprintf("COMMENT '%s'", fi.description) - } - - columns = append(columns, column) - } - - if mi.model != nil { - allnames := getTableUnique(mi.addrField) - if !mi.manual && len(mi.uniques) > 0 { - allnames = append(allnames, mi.uniques) - } - for _, names := range allnames { - cols := make([]string, 0, len(names)) - for _, name := range names { - if fi, ok := mi.fields.GetByAny(name); ok && fi.dbcol { - cols = append(cols, fi.column) - } else { - panic(fmt.Errorf("cannot found column `%s` when parse UNIQUE in `%s.TableUnique`", name, mi.fullName)) - } - } - column := fmt.Sprintf(" UNIQUE (%s%s%s)", Q, strings.Join(cols, sep), Q) - columns = append(columns, column) - } - } - - sql += strings.Join(columns, ",\n") - sql += "\n)" - - if al.Driver == DRMySQL { - var engine string - if mi.model != nil { - engine = getTableEngine(mi.addrField) - } - if engine == "" { - engine = al.Engine - } - sql += " ENGINE=" + engine - } - - sql += ";" - sqls = append(sqls, sql) - - if mi.model != nil { - for _, names := range getTableIndex(mi.addrField) { - cols := make([]string, 0, len(names)) - for _, name := range names { - if fi, ok := mi.fields.GetByAny(name); ok && fi.dbcol { - cols = append(cols, fi.column) - } else { - panic(fmt.Errorf("cannot found column `%s` when parse INDEX in `%s.TableIndex`", name, mi.fullName)) - } - } - sqlIndexes = append(sqlIndexes, cols) - } - } - - for _, names := range sqlIndexes { - name := mi.table + "_" + strings.Join(names, "_") - cols := strings.Join(names, sep) - sql := fmt.Sprintf("CREATE INDEX %s%s%s ON %s%s%s (%s%s%s);", Q, name, Q, Q, mi.table, Q, Q, cols, Q) - - index := dbIndex{} - index.Table = mi.table - index.Name = name - index.SQL = sql - - tableIndexes[mi.table] = append(tableIndexes[mi.table], index) - } - - } - - return -} - -// Get string value for the attribute "DEFAULT" for the CREATE, ALTER commands -func getColumnDefault(fi *fieldInfo) string { - var ( - v, t, d string - ) - - // Skip default attribute if field is in relations - if fi.rel || fi.reverse { - return v - } - - t = " DEFAULT '%s' " - - // These defaults will be useful if there no config value orm:"default" and NOT NULL is on - switch fi.fieldType { - case TypeTimeField, TypeDateField, TypeDateTimeField, TypeTextField: - return v - - case TypeBitField, TypeSmallIntegerField, TypeIntegerField, - TypeBigIntegerField, TypePositiveBitField, TypePositiveSmallIntegerField, - TypePositiveIntegerField, TypePositiveBigIntegerField, TypeFloatField, - TypeDecimalField: - t = " DEFAULT %s " - d = "0" - case TypeBooleanField: - t = " DEFAULT %s " - d = "FALSE" - case TypeJSONField, TypeJsonbField: - d = "{}" - } - - if fi.colDefault { - if !fi.initial.Exist() { - v = fmt.Sprintf(t, "") - } else { - v = fmt.Sprintf(t, fi.initial.String()) - } - } else { - if !fi.null { - v = fmt.Sprintf(t, d) - } - } - - return v -} diff --git a/src/vendor/github.com/beego/beego/orm/models.go b/src/vendor/github.com/beego/beego/orm/models.go deleted file mode 100644 index 4776bcba6..000000000 --- a/src/vendor/github.com/beego/beego/orm/models.go +++ /dev/null @@ -1,99 +0,0 @@ -// Copyright 2014 beego Author. All Rights Reserved. -// -// 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 orm - -import ( - "sync" -) - -const ( - odCascade = "cascade" - odSetNULL = "set_null" - odSetDefault = "set_default" - odDoNothing = "do_nothing" - defaultStructTagName = "orm" - defaultStructTagDelim = ";" -) - -var ( - modelCache = &_modelCache{ - cache: make(map[string]*modelInfo), - cacheByFullName: make(map[string]*modelInfo), - } -) - -// model info collection -type _modelCache struct { - sync.RWMutex // only used outsite for bootStrap - orders []string - cache map[string]*modelInfo - cacheByFullName map[string]*modelInfo - done bool -} - -// get all model info -func (mc *_modelCache) all() map[string]*modelInfo { - m := make(map[string]*modelInfo, len(mc.cache)) - for k, v := range mc.cache { - m[k] = v - } - return m -} - -// get ordered model info -func (mc *_modelCache) allOrdered() []*modelInfo { - m := make([]*modelInfo, 0, len(mc.orders)) - for _, table := range mc.orders { - m = append(m, mc.cache[table]) - } - return m -} - -// get model info by table name -func (mc *_modelCache) get(table string) (mi *modelInfo, ok bool) { - mi, ok = mc.cache[table] - return -} - -// get model info by full name -func (mc *_modelCache) getByFullName(name string) (mi *modelInfo, ok bool) { - mi, ok = mc.cacheByFullName[name] - return -} - -// set model info to collection -func (mc *_modelCache) set(table string, mi *modelInfo) *modelInfo { - mii := mc.cache[table] - mc.cache[table] = mi - mc.cacheByFullName[mi.fullName] = mi - if mii == nil { - mc.orders = append(mc.orders, table) - } - return mii -} - -// clean all model info. -func (mc *_modelCache) clean() { - mc.orders = make([]string, 0) - mc.cache = make(map[string]*modelInfo) - mc.cacheByFullName = make(map[string]*modelInfo) - mc.done = false -} - -// ResetModelCache Clean model cache. Then you can re-RegisterModel. -// Common use this api for test case. -func ResetModelCache() { - modelCache.clean() -} diff --git a/src/vendor/github.com/beego/beego/orm/models_boot.go b/src/vendor/github.com/beego/beego/orm/models_boot.go deleted file mode 100644 index 8c56b3c44..000000000 --- a/src/vendor/github.com/beego/beego/orm/models_boot.go +++ /dev/null @@ -1,347 +0,0 @@ -// Copyright 2014 beego Author. All Rights Reserved. -// -// 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 orm - -import ( - "fmt" - "os" - "reflect" - "runtime/debug" - "strings" -) - -// register models. -// PrefixOrSuffix means table name prefix or suffix. -// isPrefix whether the prefix is prefix or suffix -func registerModel(PrefixOrSuffix string, model interface{}, isPrefix bool) { - val := reflect.ValueOf(model) - typ := reflect.Indirect(val).Type() - - if val.Kind() != reflect.Ptr { - panic(fmt.Errorf(" cannot use non-ptr model struct `%s`", getFullName(typ))) - } - // For this case: - // u := &User{} - // registerModel(&u) - if typ.Kind() == reflect.Ptr { - panic(fmt.Errorf(" only allow ptr model struct, it looks you use two reference to the struct `%s`", typ)) - } - - table := getTableName(val) - - if PrefixOrSuffix != "" { - if isPrefix { - table = PrefixOrSuffix + table - } else { - table = table + PrefixOrSuffix - } - } - // models's fullname is pkgpath + struct name - name := getFullName(typ) - if _, ok := modelCache.getByFullName(name); ok { - fmt.Printf(" model `%s` repeat register, must be unique\n", name) - os.Exit(2) - } - - if _, ok := modelCache.get(table); ok { - fmt.Printf(" table name `%s` repeat register, must be unique\n", table) - os.Exit(2) - } - - mi := newModelInfo(val) - if mi.fields.pk == nil { - outFor: - for _, fi := range mi.fields.fieldsDB { - if strings.ToLower(fi.name) == "id" { - switch fi.addrValue.Elem().Kind() { - case reflect.Int, reflect.Int32, reflect.Int64, reflect.Uint, reflect.Uint32, reflect.Uint64: - fi.auto = true - fi.pk = true - mi.fields.pk = fi - break outFor - } - } - } - - if mi.fields.pk == nil { - fmt.Printf(" `%s` needs a primary key field, default is to use 'id' if not set\n", name) - os.Exit(2) - } - - } - - mi.table = table - mi.pkg = typ.PkgPath() - mi.model = model - mi.manual = true - - modelCache.set(table, mi) -} - -// bootstrap models -func bootStrap() { - if modelCache.done { - return - } - var ( - err error - models map[string]*modelInfo - ) - if dataBaseCache.getDefault() == nil { - err = fmt.Errorf("must have one register DataBase alias named `default`") - goto end - } - - // set rel and reverse model - // RelManyToMany set the relTable - models = modelCache.all() - for _, mi := range models { - for _, fi := range mi.fields.columns { - if fi.rel || fi.reverse { - elm := fi.addrValue.Type().Elem() - if fi.fieldType == RelReverseMany || fi.fieldType == RelManyToMany { - elm = elm.Elem() - } - // check the rel or reverse model already register - name := getFullName(elm) - mii, ok := modelCache.getByFullName(name) - if !ok || mii.pkg != elm.PkgPath() { - err = fmt.Errorf("can not find rel in field `%s`, `%s` may be miss register", fi.fullName, elm.String()) - goto end - } - fi.relModelInfo = mii - - switch fi.fieldType { - case RelManyToMany: - if fi.relThrough != "" { - if i := strings.LastIndex(fi.relThrough, "."); i != -1 && len(fi.relThrough) > (i+1) { - pn := fi.relThrough[:i] - rmi, ok := modelCache.getByFullName(fi.relThrough) - if !ok || pn != rmi.pkg { - err = fmt.Errorf("field `%s` wrong rel_through value `%s` cannot find table", fi.fullName, fi.relThrough) - goto end - } - fi.relThroughModelInfo = rmi - fi.relTable = rmi.table - } else { - err = fmt.Errorf("field `%s` wrong rel_through value `%s`", fi.fullName, fi.relThrough) - goto end - } - } else { - i := newM2MModelInfo(mi, mii) - if fi.relTable != "" { - i.table = fi.relTable - } - if v := modelCache.set(i.table, i); v != nil { - err = fmt.Errorf("the rel table name `%s` already registered, cannot be use, please change one", fi.relTable) - goto end - } - fi.relTable = i.table - fi.relThroughModelInfo = i - } - - fi.relThroughModelInfo.isThrough = true - } - } - } - } - - // check the rel filed while the relModelInfo also has filed point to current model - // if not exist, add a new field to the relModelInfo - models = modelCache.all() - for _, mi := range models { - for _, fi := range mi.fields.fieldsRel { - switch fi.fieldType { - case RelForeignKey, RelOneToOne, RelManyToMany: - inModel := false - for _, ffi := range fi.relModelInfo.fields.fieldsReverse { - if ffi.relModelInfo == mi { - inModel = true - break - } - } - if !inModel { - rmi := fi.relModelInfo - ffi := new(fieldInfo) - ffi.name = mi.name - ffi.column = ffi.name - ffi.fullName = rmi.fullName + "." + ffi.name - ffi.reverse = true - ffi.relModelInfo = mi - ffi.mi = rmi - if fi.fieldType == RelOneToOne { - ffi.fieldType = RelReverseOne - } else { - ffi.fieldType = RelReverseMany - } - if !rmi.fields.Add(ffi) { - added := false - for cnt := 0; cnt < 5; cnt++ { - ffi.name = fmt.Sprintf("%s%d", mi.name, cnt) - ffi.column = ffi.name - ffi.fullName = rmi.fullName + "." + ffi.name - if added = rmi.fields.Add(ffi); added { - break - } - } - if !added { - panic(fmt.Errorf("cannot generate auto reverse field info `%s` to `%s`", fi.fullName, ffi.fullName)) - } - } - } - } - } - } - - models = modelCache.all() - for _, mi := range models { - for _, fi := range mi.fields.fieldsRel { - switch fi.fieldType { - case RelManyToMany: - for _, ffi := range fi.relThroughModelInfo.fields.fieldsRel { - switch ffi.fieldType { - case RelOneToOne, RelForeignKey: - if ffi.relModelInfo == fi.relModelInfo { - fi.reverseFieldInfoTwo = ffi - } - if ffi.relModelInfo == mi { - fi.reverseField = ffi.name - fi.reverseFieldInfo = ffi - } - } - } - if fi.reverseFieldInfoTwo == nil { - err = fmt.Errorf("can not find m2m field for m2m model `%s`, ensure your m2m model defined correct", - fi.relThroughModelInfo.fullName) - goto end - } - } - } - } - - models = modelCache.all() - for _, mi := range models { - for _, fi := range mi.fields.fieldsReverse { - switch fi.fieldType { - case RelReverseOne: - found := false - mForA: - for _, ffi := range fi.relModelInfo.fields.fieldsByType[RelOneToOne] { - if ffi.relModelInfo == mi { - found = true - fi.reverseField = ffi.name - fi.reverseFieldInfo = ffi - - ffi.reverseField = fi.name - ffi.reverseFieldInfo = fi - break mForA - } - } - if !found { - err = fmt.Errorf("reverse field `%s` not found in model `%s`", fi.fullName, fi.relModelInfo.fullName) - goto end - } - case RelReverseMany: - found := false - mForB: - for _, ffi := range fi.relModelInfo.fields.fieldsByType[RelForeignKey] { - if ffi.relModelInfo == mi { - found = true - fi.reverseField = ffi.name - fi.reverseFieldInfo = ffi - - ffi.reverseField = fi.name - ffi.reverseFieldInfo = fi - - break mForB - } - } - if !found { - mForC: - for _, ffi := range fi.relModelInfo.fields.fieldsByType[RelManyToMany] { - conditions := fi.relThrough != "" && fi.relThrough == ffi.relThrough || - fi.relTable != "" && fi.relTable == ffi.relTable || - fi.relThrough == "" && fi.relTable == "" - if ffi.relModelInfo == mi && conditions { - found = true - - fi.reverseField = ffi.reverseFieldInfoTwo.name - fi.reverseFieldInfo = ffi.reverseFieldInfoTwo - fi.relThroughModelInfo = ffi.relThroughModelInfo - fi.reverseFieldInfoTwo = ffi.reverseFieldInfo - fi.reverseFieldInfoM2M = ffi - ffi.reverseFieldInfoM2M = fi - - break mForC - } - } - } - if !found { - err = fmt.Errorf("reverse field for `%s` not found in model `%s`", fi.fullName, fi.relModelInfo.fullName) - goto end - } - } - } - } - -end: - if err != nil { - fmt.Println(err) - debug.PrintStack() - os.Exit(2) - } -} - -// RegisterModel register models -func RegisterModel(models ...interface{}) { - if modelCache.done { - panic(fmt.Errorf("RegisterModel must be run before BootStrap")) - } - RegisterModelWithPrefix("", models...) -} - -// RegisterModelWithPrefix register models with a prefix -func RegisterModelWithPrefix(prefix string, models ...interface{}) { - if modelCache.done { - panic(fmt.Errorf("RegisterModelWithPrefix must be run before BootStrap")) - } - - for _, model := range models { - registerModel(prefix, model, true) - } -} - -// RegisterModelWithSuffix register models with a suffix -func RegisterModelWithSuffix(suffix string, models ...interface{}) { - if modelCache.done { - panic(fmt.Errorf("RegisterModelWithSuffix must be run before BootStrap")) - } - - for _, model := range models { - registerModel(suffix, model, false) - } -} - -// BootStrap bootstrap models. -// make all model parsed and can not add more models -func BootStrap() { - modelCache.Lock() - defer modelCache.Unlock() - if modelCache.done { - return - } - bootStrap() - modelCache.done = true -} diff --git a/src/vendor/github.com/beego/beego/orm/orm.go b/src/vendor/github.com/beego/beego/orm/orm.go deleted file mode 100644 index 1a4bb9b6b..000000000 --- a/src/vendor/github.com/beego/beego/orm/orm.go +++ /dev/null @@ -1,579 +0,0 @@ -// Copyright 2014 beego Author. All Rights Reserved. -// -// 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. - -// +build go1.8 - -// Package orm provide ORM for MySQL/PostgreSQL/sqlite -// Simple Usage -// -// package main -// -// import ( -// "fmt" -// "github.com/beego/beego/orm" -// _ "github.com/go-sql-driver/mysql" // import your used driver -// ) -// -// // Model Struct -// type User struct { -// Id int `orm:"auto"` -// Name string `orm:"size(100)"` -// } -// -// func init() { -// orm.RegisterDataBase("default", "mysql", "root:root@/my_db?charset=utf8", 30) -// } -// -// func main() { -// o := orm.NewOrm() -// user := User{Name: "slene"} -// // insert -// id, err := o.Insert(&user) -// // update -// user.Name = "astaxie" -// num, err := o.Update(&user) -// // read one -// u := User{Id: user.Id} -// err = o.Read(&u) -// // delete -// num, err = o.Delete(&u) -// } -// -// more docs: http://beego.me/docs/mvc/model/overview.md -package orm - -import ( - "context" - "database/sql" - "errors" - "fmt" - "os" - "reflect" - "sync" - "time" -) - -// DebugQueries define the debug -const ( - DebugQueries = iota -) - -// Define common vars -var ( - Debug = false - DebugLog = NewLog(os.Stdout) - DefaultRowsLimit = -1 - DefaultRelsDepth = 2 - DefaultTimeLoc = time.Local - ErrTxHasBegan = errors.New(" transaction already begin") - ErrTxDone = errors.New(" transaction not begin") - ErrMultiRows = errors.New(" return multi rows") - ErrNoRows = errors.New(" no row found") - ErrStmtClosed = errors.New(" stmt already closed") - ErrArgs = errors.New(" args error may be empty") - ErrNotImplement = errors.New("have not implement") -) - -// Params stores the Params -type Params map[string]interface{} - -// ParamsList stores paramslist -type ParamsList []interface{} - -type orm struct { - alias *alias - db dbQuerier - isTx bool -} - -var _ Ormer = new(orm) - -// get model info and model reflect value -func (o *orm) getMiInd(md interface{}, needPtr bool) (mi *modelInfo, ind reflect.Value) { - val := reflect.ValueOf(md) - ind = reflect.Indirect(val) - typ := ind.Type() - if needPtr && val.Kind() != reflect.Ptr { - panic(fmt.Errorf(" cannot use non-ptr model struct `%s`", getFullName(typ))) - } - name := getFullName(typ) - if mi, ok := modelCache.getByFullName(name); ok { - return mi, ind - } - panic(fmt.Errorf(" table: `%s` not found, make sure it was registered with `RegisterModel()`", name)) -} - -// get field info from model info by given field name -func (o *orm) getFieldInfo(mi *modelInfo, name string) *fieldInfo { - fi, ok := mi.fields.GetByAny(name) - if !ok { - panic(fmt.Errorf(" cannot find field `%s` for model `%s`", name, mi.fullName)) - } - return fi -} - -// read data to model -func (o *orm) Read(md interface{}, cols ...string) error { - mi, ind := o.getMiInd(md, true) - return o.alias.DbBaser.Read(o.db, mi, ind, o.alias.TZ, cols, false) -} - -// read data to model, like Read(), but use "SELECT FOR UPDATE" form -func (o *orm) ReadForUpdate(md interface{}, cols ...string) error { - mi, ind := o.getMiInd(md, true) - return o.alias.DbBaser.Read(o.db, mi, ind, o.alias.TZ, cols, true) -} - -// Try to read a row from the database, or insert one if it doesn't exist -func (o *orm) ReadOrCreate(md interface{}, col1 string, cols ...string) (bool, int64, error) { - cols = append([]string{col1}, cols...) - mi, ind := o.getMiInd(md, true) - err := o.alias.DbBaser.Read(o.db, mi, ind, o.alias.TZ, cols, false) - if err == ErrNoRows { - // Create - id, err := o.Insert(md) - return (err == nil), id, err - } - - id, vid := int64(0), ind.FieldByIndex(mi.fields.pk.fieldIndex) - if mi.fields.pk.fieldType&IsPositiveIntegerField > 0 { - id = int64(vid.Uint()) - } else if mi.fields.pk.rel { - return o.ReadOrCreate(vid.Interface(), mi.fields.pk.relModelInfo.fields.pk.name) - } else { - id = vid.Int() - } - - return false, id, err -} - -// insert model data to database -func (o *orm) Insert(md interface{}) (int64, error) { - mi, ind := o.getMiInd(md, true) - id, err := o.alias.DbBaser.Insert(o.db, mi, ind, o.alias.TZ) - if err != nil { - return id, err - } - - o.setPk(mi, ind, id) - - return id, nil -} - -// set auto pk field -func (o *orm) setPk(mi *modelInfo, ind reflect.Value, id int64) { - if mi.fields.pk.auto { - if mi.fields.pk.fieldType&IsPositiveIntegerField > 0 { - ind.FieldByIndex(mi.fields.pk.fieldIndex).SetUint(uint64(id)) - } else { - ind.FieldByIndex(mi.fields.pk.fieldIndex).SetInt(id) - } - } -} - -// insert some models to database -func (o *orm) InsertMulti(bulk int, mds interface{}) (int64, error) { - var cnt int64 - - sind := reflect.Indirect(reflect.ValueOf(mds)) - - switch sind.Kind() { - case reflect.Array, reflect.Slice: - if sind.Len() == 0 { - return cnt, ErrArgs - } - default: - return cnt, ErrArgs - } - - if bulk <= 1 { - for i := 0; i < sind.Len(); i++ { - ind := reflect.Indirect(sind.Index(i)) - mi, _ := o.getMiInd(ind.Interface(), false) - id, err := o.alias.DbBaser.Insert(o.db, mi, ind, o.alias.TZ) - if err != nil { - return cnt, err - } - - o.setPk(mi, ind, id) - - cnt++ - } - } else { - mi, _ := o.getMiInd(sind.Index(0).Interface(), false) - return o.alias.DbBaser.InsertMulti(o.db, mi, sind, bulk, o.alias.TZ) - } - return cnt, nil -} - -// InsertOrUpdate data to database -func (o *orm) InsertOrUpdate(md interface{}, colConflitAndArgs ...string) (int64, error) { - mi, ind := o.getMiInd(md, true) - id, err := o.alias.DbBaser.InsertOrUpdate(o.db, mi, ind, o.alias, colConflitAndArgs...) - if err != nil { - return id, err - } - - o.setPk(mi, ind, id) - - return id, nil -} - -// update model to database. -// cols set the columns those want to update. -func (o *orm) Update(md interface{}, cols ...string) (int64, error) { - mi, ind := o.getMiInd(md, true) - return o.alias.DbBaser.Update(o.db, mi, ind, o.alias.TZ, cols) -} - -// delete model in database -// cols shows the delete conditions values read from. default is pk -func (o *orm) Delete(md interface{}, cols ...string) (int64, error) { - mi, ind := o.getMiInd(md, true) - num, err := o.alias.DbBaser.Delete(o.db, mi, ind, o.alias.TZ, cols) - if err != nil { - return num, err - } - if num > 0 { - o.setPk(mi, ind, 0) - } - return num, nil -} - -// create a models to models queryer -func (o *orm) QueryM2M(md interface{}, name string) QueryM2Mer { - mi, ind := o.getMiInd(md, true) - fi := o.getFieldInfo(mi, name) - - switch { - case fi.fieldType == RelManyToMany: - case fi.fieldType == RelReverseMany && fi.reverseFieldInfo.mi.isThrough: - default: - panic(fmt.Errorf(" model `%s` . name `%s` is not a m2m field", fi.name, mi.fullName)) - } - - return newQueryM2M(md, o, mi, fi, ind) -} - -// load related models to md model. -// args are limit, offset int and order string. -// -// example: -// orm.LoadRelated(post,"Tags") -// for _,tag := range post.Tags{...} -// -// make sure the relation is defined in model struct tags. -func (o *orm) LoadRelated(md interface{}, name string, args ...interface{}) (int64, error) { - _, fi, ind, qseter := o.queryRelated(md, name) - - qs := qseter.(*querySet) - - var relDepth int - var limit, offset int64 - var order string - for i, arg := range args { - switch i { - case 0: - if v, ok := arg.(bool); ok { - if v { - relDepth = DefaultRelsDepth - } - } else if v, ok := arg.(int); ok { - relDepth = v - } - case 1: - limit = ToInt64(arg) - case 2: - offset = ToInt64(arg) - case 3: - order, _ = arg.(string) - } - } - - switch fi.fieldType { - case RelOneToOne, RelForeignKey, RelReverseOne: - limit = 1 - offset = 0 - } - - qs.limit = limit - qs.offset = offset - qs.relDepth = relDepth - - if len(order) > 0 { - qs.orders = []string{order} - } - - find := ind.FieldByIndex(fi.fieldIndex) - - var nums int64 - var err error - switch fi.fieldType { - case RelOneToOne, RelForeignKey, RelReverseOne: - val := reflect.New(find.Type().Elem()) - container := val.Interface() - err = qs.One(container) - if err == nil { - find.Set(val) - nums = 1 - } - default: - nums, err = qs.All(find.Addr().Interface()) - } - - return nums, err -} - -// return a QuerySeter for related models to md model. -// it can do all, update, delete in QuerySeter. -// example: -// qs := orm.QueryRelated(post,"Tag") -// qs.All(&[]*Tag{}) -// -func (o *orm) QueryRelated(md interface{}, name string) QuerySeter { - // is this api needed ? - _, _, _, qs := o.queryRelated(md, name) - return qs -} - -// get QuerySeter for related models to md model -func (o *orm) queryRelated(md interface{}, name string) (*modelInfo, *fieldInfo, reflect.Value, QuerySeter) { - mi, ind := o.getMiInd(md, true) - fi := o.getFieldInfo(mi, name) - - _, _, exist := getExistPk(mi, ind) - if !exist { - panic(ErrMissPK) - } - - var qs *querySet - - switch fi.fieldType { - case RelOneToOne, RelForeignKey, RelManyToMany: - if !fi.inModel { - break - } - qs = o.getRelQs(md, mi, fi) - case RelReverseOne, RelReverseMany: - if !fi.inModel { - break - } - qs = o.getReverseQs(md, mi, fi) - } - - if qs == nil { - panic(fmt.Errorf(" name `%s` for model `%s` is not an available rel/reverse field", md, name)) - } - - return mi, fi, ind, qs -} - -// get reverse relation QuerySeter -func (o *orm) getReverseQs(md interface{}, mi *modelInfo, fi *fieldInfo) *querySet { - switch fi.fieldType { - case RelReverseOne, RelReverseMany: - default: - panic(fmt.Errorf(" name `%s` for model `%s` is not an available reverse field", fi.name, mi.fullName)) - } - - var q *querySet - - if fi.fieldType == RelReverseMany && fi.reverseFieldInfo.mi.isThrough { - q = newQuerySet(o, fi.relModelInfo).(*querySet) - q.cond = NewCondition().And(fi.reverseFieldInfoM2M.column+ExprSep+fi.reverseFieldInfo.column, md) - } else { - q = newQuerySet(o, fi.reverseFieldInfo.mi).(*querySet) - q.cond = NewCondition().And(fi.reverseFieldInfo.column, md) - } - - return q -} - -// get relation QuerySeter -func (o *orm) getRelQs(md interface{}, mi *modelInfo, fi *fieldInfo) *querySet { - switch fi.fieldType { - case RelOneToOne, RelForeignKey, RelManyToMany: - default: - panic(fmt.Errorf(" name `%s` for model `%s` is not an available rel field", fi.name, mi.fullName)) - } - - q := newQuerySet(o, fi.relModelInfo).(*querySet) - q.cond = NewCondition() - - if fi.fieldType == RelManyToMany { - q.cond = q.cond.And(fi.reverseFieldInfoM2M.column+ExprSep+fi.reverseFieldInfo.column, md) - } else { - q.cond = q.cond.And(fi.reverseFieldInfo.column, md) - } - - return q -} - -// return a QuerySeter for table operations. -// table name can be string or struct. -// e.g. QueryTable("user"), QueryTable(&user{}) or QueryTable((*User)(nil)), -func (o *orm) QueryTable(ptrStructOrTableName interface{}) (qs QuerySeter) { - var name string - if table, ok := ptrStructOrTableName.(string); ok { - name = nameStrategyMap[defaultNameStrategy](table) - if mi, ok := modelCache.get(name); ok { - qs = newQuerySet(o, mi) - } - } else { - name = getFullName(indirectType(reflect.TypeOf(ptrStructOrTableName))) - if mi, ok := modelCache.getByFullName(name); ok { - qs = newQuerySet(o, mi) - } - } - if qs == nil { - panic(fmt.Errorf(" table name: `%s` not exists", name)) - } - return -} - -// switch to another registered database driver by given name. -func (o *orm) Using(name string) error { - if o.isTx { - panic(fmt.Errorf(" transaction has been start, cannot change db")) - } - if al, ok := dataBaseCache.get(name); ok { - o.alias = al - if Debug { - o.db = newDbQueryLog(al, al.DB) - } else { - o.db = al.DB - } - } else { - return fmt.Errorf(" unknown db alias name `%s`", name) - } - return nil -} - -// begin transaction -func (o *orm) Begin() error { - return o.BeginTx(context.Background(), nil) -} - -func (o *orm) BeginTx(ctx context.Context, opts *sql.TxOptions) error { - if o.isTx { - return ErrTxHasBegan - } - var tx *sql.Tx - tx, err := o.db.(txer).BeginTx(ctx, opts) - if err != nil { - return err - } - o.isTx = true - if Debug { - o.db.(*dbQueryLog).SetDB(tx) - } else { - o.db = tx - } - return nil -} - -// commit transaction -func (o *orm) Commit() error { - if !o.isTx { - return ErrTxDone - } - err := o.db.(txEnder).Commit() - if err == nil { - o.isTx = false - o.Using(o.alias.Name) - } else if err == sql.ErrTxDone { - return ErrTxDone - } - return err -} - -// rollback transaction -func (o *orm) Rollback() error { - if !o.isTx { - return ErrTxDone - } - err := o.db.(txEnder).Rollback() - if err == nil { - o.isTx = false - o.Using(o.alias.Name) - } else if err == sql.ErrTxDone { - return ErrTxDone - } - return err -} - -// return a raw query seter for raw sql string. -func (o *orm) Raw(query string, args ...interface{}) RawSeter { - return newRawSet(o, query, args) -} - -// return current using database Driver -func (o *orm) Driver() Driver { - return driver(o.alias.Name) -} - -// return sql.DBStats for current database -func (o *orm) DBStats() *sql.DBStats { - if o.alias != nil && o.alias.DB != nil { - stats := o.alias.DB.DB.Stats() - return &stats - } - return nil -} - -// NewOrm create new orm -func NewOrm() Ormer { - BootStrap() // execute only once - - o := new(orm) - err := o.Using("default") - if err != nil { - panic(err) - } - return o -} - -// NewOrmWithDB create a new ormer object with specify *sql.DB for query -func NewOrmWithDB(driverName, aliasName string, db *sql.DB) (Ormer, error) { - var al *alias - - if dr, ok := drivers[driverName]; ok { - al = new(alias) - al.DbBaser = dbBasers[dr] - al.Driver = dr - } else { - return nil, fmt.Errorf("driver name `%s` have not registered", driverName) - } - - al.Name = aliasName - al.DriverName = driverName - al.DB = &DB{ - RWMutex: new(sync.RWMutex), - DB: db, - stmtDecorators: newStmtDecoratorLruWithEvict(), - } - - detectTZ(al) - - o := new(orm) - o.alias = al - - if Debug { - o.db = newDbQueryLog(o.alias, db) - } else { - o.db = db - } - - return o, nil -} diff --git a/src/vendor/github.com/beego/beego/orm/qb_tidb.go b/src/vendor/github.com/beego/beego/orm/qb_tidb.go deleted file mode 100644 index 87b3ae84f..000000000 --- a/src/vendor/github.com/beego/beego/orm/qb_tidb.go +++ /dev/null @@ -1,182 +0,0 @@ -// Copyright 2015 TiDB Author. All Rights Reserved. -// -// 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 orm - -import ( - "fmt" - "strconv" - "strings" -) - -// TiDBQueryBuilder is the SQL build -type TiDBQueryBuilder struct { - Tokens []string -} - -// Select will join the fields -func (qb *TiDBQueryBuilder) Select(fields ...string) QueryBuilder { - qb.Tokens = append(qb.Tokens, "SELECT", strings.Join(fields, CommaSpace)) - return qb -} - -// ForUpdate add the FOR UPDATE clause -func (qb *TiDBQueryBuilder) ForUpdate() QueryBuilder { - qb.Tokens = append(qb.Tokens, "FOR UPDATE") - return qb -} - -// From join the tables -func (qb *TiDBQueryBuilder) From(tables ...string) QueryBuilder { - qb.Tokens = append(qb.Tokens, "FROM", strings.Join(tables, CommaSpace)) - return qb -} - -// InnerJoin INNER JOIN the table -func (qb *TiDBQueryBuilder) InnerJoin(table string) QueryBuilder { - qb.Tokens = append(qb.Tokens, "INNER JOIN", table) - return qb -} - -// LeftJoin LEFT JOIN the table -func (qb *TiDBQueryBuilder) LeftJoin(table string) QueryBuilder { - qb.Tokens = append(qb.Tokens, "LEFT JOIN", table) - return qb -} - -// RightJoin RIGHT JOIN the table -func (qb *TiDBQueryBuilder) RightJoin(table string) QueryBuilder { - qb.Tokens = append(qb.Tokens, "RIGHT JOIN", table) - return qb -} - -// On join with on cond -func (qb *TiDBQueryBuilder) On(cond string) QueryBuilder { - qb.Tokens = append(qb.Tokens, "ON", cond) - return qb -} - -// Where join the Where cond -func (qb *TiDBQueryBuilder) Where(cond string) QueryBuilder { - qb.Tokens = append(qb.Tokens, "WHERE", cond) - return qb -} - -// And join the and cond -func (qb *TiDBQueryBuilder) And(cond string) QueryBuilder { - qb.Tokens = append(qb.Tokens, "AND", cond) - return qb -} - -// Or join the or cond -func (qb *TiDBQueryBuilder) Or(cond string) QueryBuilder { - qb.Tokens = append(qb.Tokens, "OR", cond) - return qb -} - -// In join the IN (vals) -func (qb *TiDBQueryBuilder) In(vals ...string) QueryBuilder { - qb.Tokens = append(qb.Tokens, "IN", "(", strings.Join(vals, CommaSpace), ")") - return qb -} - -// OrderBy join the Order by fields -func (qb *TiDBQueryBuilder) OrderBy(fields ...string) QueryBuilder { - qb.Tokens = append(qb.Tokens, "ORDER BY", strings.Join(fields, CommaSpace)) - return qb -} - -// Asc join the asc -func (qb *TiDBQueryBuilder) Asc() QueryBuilder { - qb.Tokens = append(qb.Tokens, "ASC") - return qb -} - -// Desc join the desc -func (qb *TiDBQueryBuilder) Desc() QueryBuilder { - qb.Tokens = append(qb.Tokens, "DESC") - return qb -} - -// Limit join the limit num -func (qb *TiDBQueryBuilder) Limit(limit int) QueryBuilder { - qb.Tokens = append(qb.Tokens, "LIMIT", strconv.Itoa(limit)) - return qb -} - -// Offset join the offset num -func (qb *TiDBQueryBuilder) Offset(offset int) QueryBuilder { - qb.Tokens = append(qb.Tokens, "OFFSET", strconv.Itoa(offset)) - return qb -} - -// GroupBy join the Group by fields -func (qb *TiDBQueryBuilder) GroupBy(fields ...string) QueryBuilder { - qb.Tokens = append(qb.Tokens, "GROUP BY", strings.Join(fields, CommaSpace)) - return qb -} - -// Having join the Having cond -func (qb *TiDBQueryBuilder) Having(cond string) QueryBuilder { - qb.Tokens = append(qb.Tokens, "HAVING", cond) - return qb -} - -// Update join the update table -func (qb *TiDBQueryBuilder) Update(tables ...string) QueryBuilder { - qb.Tokens = append(qb.Tokens, "UPDATE", strings.Join(tables, CommaSpace)) - return qb -} - -// Set join the set kv -func (qb *TiDBQueryBuilder) Set(kv ...string) QueryBuilder { - qb.Tokens = append(qb.Tokens, "SET", strings.Join(kv, CommaSpace)) - return qb -} - -// Delete join the Delete tables -func (qb *TiDBQueryBuilder) Delete(tables ...string) QueryBuilder { - qb.Tokens = append(qb.Tokens, "DELETE") - if len(tables) != 0 { - qb.Tokens = append(qb.Tokens, strings.Join(tables, CommaSpace)) - } - return qb -} - -// InsertInto join the insert SQL -func (qb *TiDBQueryBuilder) InsertInto(table string, fields ...string) QueryBuilder { - qb.Tokens = append(qb.Tokens, "INSERT INTO", table) - if len(fields) != 0 { - fieldsStr := strings.Join(fields, CommaSpace) - qb.Tokens = append(qb.Tokens, "(", fieldsStr, ")") - } - return qb -} - -// Values join the Values(vals) -func (qb *TiDBQueryBuilder) Values(vals ...string) QueryBuilder { - valsStr := strings.Join(vals, CommaSpace) - qb.Tokens = append(qb.Tokens, "VALUES", "(", valsStr, ")") - return qb -} - -// Subquery join the sub as alias -func (qb *TiDBQueryBuilder) Subquery(sub string, alias string) string { - return fmt.Sprintf("(%s) AS %s", sub, alias) -} - -// String join all Tokens -func (qb *TiDBQueryBuilder) String() string { - return strings.Join(qb.Tokens, " ") -} diff --git a/src/vendor/github.com/beego/beego/parser.go b/src/vendor/github.com/beego/beego/parser.go deleted file mode 100644 index de4ce8dc2..000000000 --- a/src/vendor/github.com/beego/beego/parser.go +++ /dev/null @@ -1,590 +0,0 @@ -// Copyright 2014 beego Author. All Rights Reserved. -// -// 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 beego - -import ( - "encoding/json" - "errors" - "fmt" - "go/ast" - "go/parser" - "go/token" - "io/ioutil" - "os" - "path/filepath" - "regexp" - "sort" - "strconv" - "strings" - "unicode" - - "github.com/beego/beego/context/param" - "github.com/beego/beego/logs" - "github.com/beego/beego/utils" -) - -var globalRouterTemplate = `package {{.routersDir}} - -import ( - "github.com/beego/beego" - "github.com/beego/beego/context/param"{{.globalimport}} -) - -func init() { -{{.globalinfo}} -} -` - -var ( - lastupdateFilename = "lastupdate.tmp" - pkgLastupdate map[string]int64 - genInfoList map[string][]ControllerComments - - routerHooks = map[string]int{ - "beego.BeforeStatic": BeforeStatic, - "beego.BeforeRouter": BeforeRouter, - "beego.BeforeExec": BeforeExec, - "beego.AfterExec": AfterExec, - "beego.FinishRouter": FinishRouter, - } - - routerHooksMapping = map[int]string{ - BeforeStatic: "beego.BeforeStatic", - BeforeRouter: "beego.BeforeRouter", - BeforeExec: "beego.BeforeExec", - AfterExec: "beego.AfterExec", - FinishRouter: "beego.FinishRouter", - } -) - -const commentFilename = "commentsRouter.go" - -func init() { - pkgLastupdate = make(map[string]int64) -} - -func parserPkg(pkgRealpath, pkgpath string) error { - if !compareFile(pkgRealpath) { - logs.Info(pkgRealpath + " no changed") - return nil - } - genInfoList = make(map[string][]ControllerComments) - fileSet := token.NewFileSet() - astPkgs, err := parser.ParseDir(fileSet, pkgRealpath, func(info os.FileInfo) bool { - name := info.Name() - return !info.IsDir() && !strings.HasPrefix(name, ".") && strings.HasSuffix(name, ".go") - }, parser.ParseComments) - - if err != nil { - return err - } - for _, pkg := range astPkgs { - for _, fl := range pkg.Files { - for _, d := range fl.Decls { - switch specDecl := d.(type) { - case *ast.FuncDecl: - if specDecl.Recv != nil { - exp, ok := specDecl.Recv.List[0].Type.(*ast.StarExpr) // Check that the type is correct first beforing throwing to parser - if ok { - err = parserComments(specDecl, fmt.Sprint(exp.X), pkgpath) - if err != nil { - return err - } - } - } - } - } - } - } - genRouterCode(pkgRealpath) - savetoFile(pkgRealpath) - return nil -} - -type parsedComment struct { - routerPath string - methods []string - params map[string]parsedParam - filters []parsedFilter - imports []parsedImport -} - -type parsedImport struct { - importPath string - importAlias string -} - -type parsedFilter struct { - pattern string - pos int - filter string - params []bool -} - -type parsedParam struct { - name string - datatype string - location string - defValue string - required bool -} - -func parserComments(f *ast.FuncDecl, controllerName, pkgpath string) error { - if f.Doc != nil { - parsedComments, err := parseComment(f.Doc.List) - if err != nil { - return err - } - for _, parsedComment := range parsedComments { - if parsedComment.routerPath != "" { - key := pkgpath + ":" + controllerName - cc := ControllerComments{} - cc.Method = f.Name.String() - cc.Router = parsedComment.routerPath - cc.AllowHTTPMethods = parsedComment.methods - cc.MethodParams = buildMethodParams(f.Type.Params.List, parsedComment) - cc.FilterComments = buildFilters(parsedComment.filters) - cc.ImportComments = buildImports(parsedComment.imports) - genInfoList[key] = append(genInfoList[key], cc) - } - } - } - return nil -} - -func buildImports(pis []parsedImport) []*ControllerImportComments { - var importComments []*ControllerImportComments - - for _, pi := range pis { - importComments = append(importComments, &ControllerImportComments{ - ImportPath: pi.importPath, - ImportAlias: pi.importAlias, - }) - } - - return importComments -} - -func buildFilters(pfs []parsedFilter) []*ControllerFilterComments { - var filterComments []*ControllerFilterComments - - for _, pf := range pfs { - var ( - returnOnOutput bool - resetParams bool - ) - - if len(pf.params) >= 1 { - returnOnOutput = pf.params[0] - } - - if len(pf.params) >= 2 { - resetParams = pf.params[1] - } - - filterComments = append(filterComments, &ControllerFilterComments{ - Filter: pf.filter, - Pattern: pf.pattern, - Pos: pf.pos, - ReturnOnOutput: returnOnOutput, - ResetParams: resetParams, - }) - } - - return filterComments -} - -func buildMethodParams(funcParams []*ast.Field, pc *parsedComment) []*param.MethodParam { - result := make([]*param.MethodParam, 0, len(funcParams)) - for _, fparam := range funcParams { - for _, pName := range fparam.Names { - methodParam := buildMethodParam(fparam, pName.Name, pc) - result = append(result, methodParam) - } - } - return result -} - -func buildMethodParam(fparam *ast.Field, name string, pc *parsedComment) *param.MethodParam { - options := []param.MethodParamOption{} - if cparam, ok := pc.params[name]; ok { - //Build param from comment info - name = cparam.name - if cparam.required { - options = append(options, param.IsRequired) - } - switch cparam.location { - case "body": - options = append(options, param.InBody) - case "header": - options = append(options, param.InHeader) - case "path": - options = append(options, param.InPath) - } - if cparam.defValue != "" { - options = append(options, param.Default(cparam.defValue)) - } - } else { - if paramInPath(name, pc.routerPath) { - options = append(options, param.InPath) - } - } - return param.New(name, options...) -} - -func paramInPath(name, route string) bool { - return strings.HasSuffix(route, ":"+name) || - strings.Contains(route, ":"+name+"/") -} - -var routeRegex = regexp.MustCompile(`@router\s+(\S+)(?:\s+\[(\S+)\])?`) - -func parseComment(lines []*ast.Comment) (pcs []*parsedComment, err error) { - pcs = []*parsedComment{} - params := map[string]parsedParam{} - filters := []parsedFilter{} - imports := []parsedImport{} - - for _, c := range lines { - t := strings.TrimSpace(strings.TrimLeft(c.Text, "//")) - if strings.HasPrefix(t, "@Param") { - pv := getparams(strings.TrimSpace(strings.TrimLeft(t, "@Param"))) - if len(pv) < 4 { - logs.Error("Invalid @Param format. Needs at least 4 parameters") - } - p := parsedParam{} - names := strings.SplitN(pv[0], "=>", 2) - p.name = names[0] - funcParamName := p.name - if len(names) > 1 { - funcParamName = names[1] - } - p.location = pv[1] - p.datatype = pv[2] - switch len(pv) { - case 5: - p.required, _ = strconv.ParseBool(pv[3]) - case 6: - p.defValue = pv[3] - p.required, _ = strconv.ParseBool(pv[4]) - } - params[funcParamName] = p - } - } - - for _, c := range lines { - t := strings.TrimSpace(strings.TrimLeft(c.Text, "//")) - if strings.HasPrefix(t, "@Import") { - iv := getparams(strings.TrimSpace(strings.TrimLeft(t, "@Import"))) - if len(iv) == 0 || len(iv) > 2 { - logs.Error("Invalid @Import format. Only accepts 1 or 2 parameters") - continue - } - - p := parsedImport{} - p.importPath = iv[0] - - if len(iv) == 2 { - p.importAlias = iv[1] - } - - imports = append(imports, p) - } - } - -filterLoop: - for _, c := range lines { - t := strings.TrimSpace(strings.TrimLeft(c.Text, "//")) - if strings.HasPrefix(t, "@Filter") { - fv := getparams(strings.TrimSpace(strings.TrimLeft(t, "@Filter"))) - if len(fv) < 3 { - logs.Error("Invalid @Filter format. Needs at least 3 parameters") - continue filterLoop - } - - p := parsedFilter{} - p.pattern = fv[0] - posName := fv[1] - if pos, exists := routerHooks[posName]; exists { - p.pos = pos - } else { - logs.Error("Invalid @Filter pos: ", posName) - continue filterLoop - } - - p.filter = fv[2] - fvParams := fv[3:] - for _, fvParam := range fvParams { - switch fvParam { - case "true": - p.params = append(p.params, true) - case "false": - p.params = append(p.params, false) - default: - logs.Error("Invalid @Filter param: ", fvParam) - continue filterLoop - } - } - - filters = append(filters, p) - } - } - - for _, c := range lines { - var pc = &parsedComment{} - pc.params = params - pc.filters = filters - pc.imports = imports - - t := strings.TrimSpace(strings.TrimLeft(c.Text, "//")) - if strings.HasPrefix(t, "@router") { - t := strings.TrimSpace(strings.TrimLeft(c.Text, "//")) - matches := routeRegex.FindStringSubmatch(t) - if len(matches) == 3 { - pc.routerPath = matches[1] - methods := matches[2] - if methods == "" { - pc.methods = []string{"get"} - //pc.hasGet = true - } else { - pc.methods = strings.Split(methods, ",") - //pc.hasGet = strings.Contains(methods, "get") - } - pcs = append(pcs, pc) - } else { - return nil, errors.New("Router information is missing") - } - } - } - return -} - -// direct copy from bee\g_docs.go -// analysis params return []string -// @Param query form string true "The email for login" -// [query form string true "The email for login"] -func getparams(str string) []string { - var s []rune - var j int - var start bool - var r []string - var quoted int8 - for _, c := range str { - if unicode.IsSpace(c) && quoted == 0 { - if !start { - continue - } else { - start = false - j++ - r = append(r, string(s)) - s = make([]rune, 0) - continue - } - } - - start = true - if c == '"' { - quoted ^= 1 - continue - } - s = append(s, c) - } - if len(s) > 0 { - r = append(r, string(s)) - } - return r -} - -func genRouterCode(pkgRealpath string) { - os.Mkdir(getRouterDir(pkgRealpath), 0755) - logs.Info("generate router from comments") - var ( - globalinfo string - globalimport string - sortKey []string - ) - for k := range genInfoList { - sortKey = append(sortKey, k) - } - sort.Strings(sortKey) - for _, k := range sortKey { - cList := genInfoList[k] - sort.Sort(ControllerCommentsSlice(cList)) - for _, c := range cList { - allmethod := "nil" - if len(c.AllowHTTPMethods) > 0 { - allmethod = "[]string{" - for _, m := range c.AllowHTTPMethods { - allmethod += `"` + m + `",` - } - allmethod = strings.TrimRight(allmethod, ",") + "}" - } - - params := "nil" - if len(c.Params) > 0 { - params = "[]map[string]string{" - for _, p := range c.Params { - for k, v := range p { - params = params + `map[string]string{` + k + `:"` + v + `"},` - } - } - params = strings.TrimRight(params, ",") + "}" - } - - methodParams := "param.Make(" - if len(c.MethodParams) > 0 { - lines := make([]string, 0, len(c.MethodParams)) - for _, m := range c.MethodParams { - lines = append(lines, fmt.Sprint(m)) - } - methodParams += "\n " + - strings.Join(lines, ",\n ") + - ",\n " - } - methodParams += ")" - - imports := "" - if len(c.ImportComments) > 0 { - for _, i := range c.ImportComments { - var s string - if i.ImportAlias != "" { - s = fmt.Sprintf(` - %s "%s"`, i.ImportAlias, i.ImportPath) - } else { - s = fmt.Sprintf(` - "%s"`, i.ImportPath) - } - if !strings.Contains(globalimport, s) { - imports += s - } - } - } - - filters := "" - if len(c.FilterComments) > 0 { - for _, f := range c.FilterComments { - filters += fmt.Sprintf(` &beego.ControllerFilter{ - Pattern: "%s", - Pos: %s, - Filter: %s, - ReturnOnOutput: %v, - ResetParams: %v, - },`, f.Pattern, routerHooksMapping[f.Pos], f.Filter, f.ReturnOnOutput, f.ResetParams) - } - } - - if filters == "" { - filters = "nil" - } else { - filters = fmt.Sprintf(`[]*beego.ControllerFilter{ -%s - }`, filters) - } - - globalimport += imports - - globalinfo = globalinfo + ` - beego.GlobalControllerRouter["` + k + `"] = append(beego.GlobalControllerRouter["` + k + `"], - beego.ControllerComments{ - Method: "` + strings.TrimSpace(c.Method) + `", - ` + "Router: `" + c.Router + "`" + `, - AllowHTTPMethods: ` + allmethod + `, - MethodParams: ` + methodParams + `, - Filters: ` + filters + `, - Params: ` + params + `}) -` - } - } - - if globalinfo != "" { - f, err := os.Create(filepath.Join(getRouterDir(pkgRealpath), commentFilename)) - if err != nil { - panic(err) - } - defer f.Close() - - routersDir := AppConfig.DefaultString("routersdir", "routers") - content := strings.Replace(globalRouterTemplate, "{{.globalinfo}}", globalinfo, -1) - content = strings.Replace(content, "{{.routersDir}}", routersDir, -1) - content = strings.Replace(content, "{{.globalimport}}", globalimport, -1) - f.WriteString(content) - } -} - -func compareFile(pkgRealpath string) bool { - if !utils.FileExists(filepath.Join(getRouterDir(pkgRealpath), commentFilename)) { - return true - } - if utils.FileExists(lastupdateFilename) { - content, err := ioutil.ReadFile(lastupdateFilename) - if err != nil { - return true - } - json.Unmarshal(content, &pkgLastupdate) - lastupdate, err := getpathTime(pkgRealpath) - if err != nil { - return true - } - if v, ok := pkgLastupdate[pkgRealpath]; ok { - if lastupdate <= v { - return false - } - } - } - return true -} - -func savetoFile(pkgRealpath string) { - lastupdate, err := getpathTime(pkgRealpath) - if err != nil { - return - } - pkgLastupdate[pkgRealpath] = lastupdate - d, err := json.Marshal(pkgLastupdate) - if err != nil { - return - } - ioutil.WriteFile(lastupdateFilename, d, os.ModePerm) -} - -func getpathTime(pkgRealpath string) (lastupdate int64, err error) { - fl, err := ioutil.ReadDir(pkgRealpath) - if err != nil { - return lastupdate, err - } - for _, f := range fl { - if lastupdate < f.ModTime().UnixNano() { - lastupdate = f.ModTime().UnixNano() - } - } - return lastupdate, nil -} - -func getRouterDir(pkgRealpath string) string { - dir := filepath.Dir(pkgRealpath) - for { - routersDir := AppConfig.DefaultString("routersdir", "routers") - d := filepath.Join(dir, routersDir) - if utils.FileExists(d) { - return d - } - - if r, _ := filepath.Rel(dir, AppPath); r == "." { - return d - } - // Parent dir. - dir = filepath.Dir(dir) - } -} diff --git a/src/vendor/github.com/beego/beego/router.go b/src/vendor/github.com/beego/beego/router.go deleted file mode 100644 index df8e89dca..000000000 --- a/src/vendor/github.com/beego/beego/router.go +++ /dev/null @@ -1,1052 +0,0 @@ -// Copyright 2014 beego Author. All Rights Reserved. -// -// 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 beego - -import ( - "errors" - "fmt" - "net/http" - "os" - "path" - "path/filepath" - "reflect" - "strconv" - "strings" - "sync" - "time" - - beecontext "github.com/beego/beego/context" - "github.com/beego/beego/context/param" - "github.com/beego/beego/logs" - "github.com/beego/beego/toolbox" - "github.com/beego/beego/utils" -) - -// default filter execution points -const ( - BeforeStatic = iota - BeforeRouter - BeforeExec - AfterExec - FinishRouter -) - -const ( - routerTypeBeego = iota - routerTypeRESTFul - routerTypeHandler -) - -var ( - // HTTPMETHOD list the supported http methods. - HTTPMETHOD = map[string]bool{ - "GET": true, - "POST": true, - "PUT": true, - "DELETE": true, - "PATCH": true, - "OPTIONS": true, - "HEAD": true, - "TRACE": true, - "CONNECT": true, - "MKCOL": true, - "COPY": true, - "MOVE": true, - "PROPFIND": true, - "PROPPATCH": true, - "LOCK": true, - "UNLOCK": true, - } - // these beego.Controller's methods shouldn't reflect to AutoRouter - exceptMethod = []string{"Init", "Prepare", "Finish", "Render", "RenderString", - "RenderBytes", "Redirect", "Abort", "StopRun", "UrlFor", "ServeJSON", "ServeJSONP", - "ServeYAML", "ServeXML", "Input", "ParseForm", "GetString", "GetStrings", "GetInt", "GetBool", - "GetFloat", "GetFile", "SaveToFile", "StartSession", "SetSession", "GetSession", - "DelSession", "SessionRegenerateID", "DestroySession", "IsAjax", "GetSecureCookie", - "SetSecureCookie", "XsrfToken", "CheckXsrfCookie", "XsrfFormHtml", - "GetControllerAndAction", "ServeFormatted"} - - urlPlaceholder = "{{placeholder}}" - // DefaultAccessLogFilter will skip the accesslog if return true - DefaultAccessLogFilter FilterHandler = &logFilter{} -) - -// FilterHandler is an interface for -type FilterHandler interface { - Filter(*beecontext.Context) bool -} - -// default log filter static file will not show -type logFilter struct { -} - -func (l *logFilter) Filter(ctx *beecontext.Context) bool { - requestPath := path.Clean(ctx.Request.URL.Path) - if requestPath == "/favicon.ico" || requestPath == "/robots.txt" { - return true - } - for prefix := range BConfig.WebConfig.StaticDir { - if strings.HasPrefix(requestPath, prefix) { - return true - } - } - return false -} - -// ExceptMethodAppend to append a slice's value into "exceptMethod", for controller's methods shouldn't reflect to AutoRouter -func ExceptMethodAppend(action string) { - exceptMethod = append(exceptMethod, action) -} - -// ControllerInfo holds information about the controller. -type ControllerInfo struct { - pattern string - controllerType reflect.Type - methods map[string]string - handler http.Handler - runFunction FilterFunc - routerType int - initialize func() ControllerInterface - methodParams []*param.MethodParam -} - -func (c *ControllerInfo) GetPattern() string { - return c.pattern -} - -// ControllerRegister containers registered router rules, controller handlers and filters. -type ControllerRegister struct { - routers map[string]*Tree - enablePolicy bool - policies map[string]*Tree - enableFilter bool - filters [FinishRouter + 1][]*FilterRouter - pool sync.Pool -} - -// NewControllerRegister returns a new ControllerRegister. -func NewControllerRegister() *ControllerRegister { - return &ControllerRegister{ - routers: make(map[string]*Tree), - policies: make(map[string]*Tree), - pool: sync.Pool{ - New: func() interface{} { - return beecontext.NewContext() - }, - }, - } -} - -// Add controller handler and pattern rules to ControllerRegister. -// usage: -// default methods is the same name as method -// Add("/user",&UserController{}) -// Add("/api/list",&RestController{},"*:ListFood") -// Add("/api/create",&RestController{},"post:CreateFood") -// Add("/api/update",&RestController{},"put:UpdateFood") -// Add("/api/delete",&RestController{},"delete:DeleteFood") -// Add("/api",&RestController{},"get,post:ApiFunc" -// Add("/simple",&SimpleController{},"get:GetFunc;post:PostFunc") -func (p *ControllerRegister) Add(pattern string, c ControllerInterface, mappingMethods ...string) { - p.addWithMethodParams(pattern, c, nil, mappingMethods...) -} - -func (p *ControllerRegister) addWithMethodParams(pattern string, c ControllerInterface, methodParams []*param.MethodParam, mappingMethods ...string) { - reflectVal := reflect.ValueOf(c) - t := reflect.Indirect(reflectVal).Type() - methods := make(map[string]string) - if len(mappingMethods) > 0 { - semi := strings.Split(mappingMethods[0], ";") - for _, v := range semi { - colon := strings.Split(v, ":") - if len(colon) != 2 { - panic("method mapping format is invalid") - } - comma := strings.Split(colon[0], ",") - for _, m := range comma { - if m == "*" || HTTPMETHOD[strings.ToUpper(m)] { - if val := reflectVal.MethodByName(colon[1]); val.IsValid() { - methods[strings.ToUpper(m)] = colon[1] - } else { - panic("'" + colon[1] + "' method doesn't exist in the controller " + t.Name()) - } - } else { - panic(v + " is an invalid method mapping. Method doesn't exist " + m) - } - } - } - } - - route := &ControllerInfo{} - route.pattern = pattern - route.methods = methods - route.routerType = routerTypeBeego - route.controllerType = t - route.initialize = func() ControllerInterface { - vc := reflect.New(route.controllerType) - execController, ok := vc.Interface().(ControllerInterface) - if !ok { - panic("controller is not ControllerInterface") - } - - elemVal := reflect.ValueOf(c).Elem() - elemType := reflect.TypeOf(c).Elem() - execElem := reflect.ValueOf(execController).Elem() - - numOfFields := elemVal.NumField() - for i := 0; i < numOfFields; i++ { - fieldType := elemType.Field(i) - elemField := execElem.FieldByName(fieldType.Name) - if elemField.CanSet() { - fieldVal := elemVal.Field(i) - elemField.Set(fieldVal) - } - } - - return execController - } - - route.methodParams = methodParams - if len(methods) == 0 { - for m := range HTTPMETHOD { - p.addToRouter(m, pattern, route) - } - } else { - for k := range methods { - if k == "*" { - for m := range HTTPMETHOD { - p.addToRouter(m, pattern, route) - } - } else { - p.addToRouter(k, pattern, route) - } - } - } -} - -func (p *ControllerRegister) addToRouter(method, pattern string, r *ControllerInfo) { - if !BConfig.RouterCaseSensitive { - pattern = strings.ToLower(pattern) - } - if t, ok := p.routers[method]; ok { - t.AddRouter(pattern, r) - } else { - t := NewTree() - t.AddRouter(pattern, r) - p.routers[method] = t - } -} - -// Include only when the Runmode is dev will generate router file in the router/auto.go from the controller -// Include(&BankAccount{}, &OrderController{},&RefundController{},&ReceiptController{}) -func (p *ControllerRegister) Include(cList ...ControllerInterface) { - if BConfig.RunMode == DEV { - skip := make(map[string]bool, 10) - wgopath := utils.GetGOPATHs() - go111module := os.Getenv(`GO111MODULE`) - for _, c := range cList { - reflectVal := reflect.ValueOf(c) - t := reflect.Indirect(reflectVal).Type() - // for go modules - if go111module == `on` { - pkgpath := filepath.Join(WorkPath, "..", t.PkgPath()) - if utils.FileExists(pkgpath) { - if pkgpath != "" { - if _, ok := skip[pkgpath]; !ok { - skip[pkgpath] = true - parserPkg(pkgpath, t.PkgPath()) - } - } - } - } else { - if len(wgopath) == 0 { - panic("you are in dev mode. So please set gopath") - } - pkgpath := "" - for _, wg := range wgopath { - wg, _ = filepath.EvalSymlinks(filepath.Join(wg, "src", t.PkgPath())) - if utils.FileExists(wg) { - pkgpath = wg - break - } - } - if pkgpath != "" { - if _, ok := skip[pkgpath]; !ok { - skip[pkgpath] = true - parserPkg(pkgpath, t.PkgPath()) - } - } - } - } - } - for _, c := range cList { - reflectVal := reflect.ValueOf(c) - t := reflect.Indirect(reflectVal).Type() - key := t.PkgPath() + ":" + t.Name() - if comm, ok := GlobalControllerRouter[key]; ok { - for _, a := range comm { - for _, f := range a.Filters { - p.InsertFilter(f.Pattern, f.Pos, f.Filter, f.ReturnOnOutput, f.ResetParams) - } - - p.addWithMethodParams(a.Router, c, a.MethodParams, strings.Join(a.AllowHTTPMethods, ",")+":"+a.Method) - } - } - } -} - -// GetContext returns a context from pool, so usually you should remember to call Reset function to clean the context -// And don't forget to give back context to pool -// example: -// ctx := p.GetContext() -// ctx.Reset(w, q) -// defer p.GiveBackContext(ctx) -func (p *ControllerRegister) GetContext() *beecontext.Context { - return p.pool.Get().(*beecontext.Context) -} - -// GiveBackContext put the ctx into pool so that it could be reuse -func (p *ControllerRegister) GiveBackContext(ctx *beecontext.Context) { - p.pool.Put(ctx) -} - -// Get add get method -// usage: -// Get("/", func(ctx *context.Context){ -// ctx.Output.Body("hello world") -// }) -func (p *ControllerRegister) Get(pattern string, f FilterFunc) { - p.AddMethod("get", pattern, f) -} - -// Post add post method -// usage: -// Post("/api", func(ctx *context.Context){ -// ctx.Output.Body("hello world") -// }) -func (p *ControllerRegister) Post(pattern string, f FilterFunc) { - p.AddMethod("post", pattern, f) -} - -// Put add put method -// usage: -// Put("/api/:id", func(ctx *context.Context){ -// ctx.Output.Body("hello world") -// }) -func (p *ControllerRegister) Put(pattern string, f FilterFunc) { - p.AddMethod("put", pattern, f) -} - -// Delete add delete method -// usage: -// Delete("/api/:id", func(ctx *context.Context){ -// ctx.Output.Body("hello world") -// }) -func (p *ControllerRegister) Delete(pattern string, f FilterFunc) { - p.AddMethod("delete", pattern, f) -} - -// Head add head method -// usage: -// Head("/api/:id", func(ctx *context.Context){ -// ctx.Output.Body("hello world") -// }) -func (p *ControllerRegister) Head(pattern string, f FilterFunc) { - p.AddMethod("head", pattern, f) -} - -// Patch add patch method -// usage: -// Patch("/api/:id", func(ctx *context.Context){ -// ctx.Output.Body("hello world") -// }) -func (p *ControllerRegister) Patch(pattern string, f FilterFunc) { - p.AddMethod("patch", pattern, f) -} - -// Options add options method -// usage: -// Options("/api/:id", func(ctx *context.Context){ -// ctx.Output.Body("hello world") -// }) -func (p *ControllerRegister) Options(pattern string, f FilterFunc) { - p.AddMethod("options", pattern, f) -} - -// Any add all method -// usage: -// Any("/api/:id", func(ctx *context.Context){ -// ctx.Output.Body("hello world") -// }) -func (p *ControllerRegister) Any(pattern string, f FilterFunc) { - p.AddMethod("*", pattern, f) -} - -// AddMethod add http method router -// usage: -// AddMethod("get","/api/:id", func(ctx *context.Context){ -// ctx.Output.Body("hello world") -// }) -func (p *ControllerRegister) AddMethod(method, pattern string, f FilterFunc) { - method = strings.ToUpper(method) - if method != "*" && !HTTPMETHOD[method] { - panic("not support http method: " + method) - } - route := &ControllerInfo{} - route.pattern = pattern - route.routerType = routerTypeRESTFul - route.runFunction = f - methods := make(map[string]string) - if method == "*" { - for val := range HTTPMETHOD { - methods[val] = val - } - } else { - methods[method] = method - } - route.methods = methods - for k := range methods { - if k == "*" { - for m := range HTTPMETHOD { - p.addToRouter(m, pattern, route) - } - } else { - p.addToRouter(k, pattern, route) - } - } -} - -// Handler add user defined Handler -func (p *ControllerRegister) Handler(pattern string, h http.Handler, options ...interface{}) { - route := &ControllerInfo{} - route.pattern = pattern - route.routerType = routerTypeHandler - route.handler = h - if len(options) > 0 { - if _, ok := options[0].(bool); ok { - pattern = path.Join(pattern, "?:all(.*)") - } - } - for m := range HTTPMETHOD { - p.addToRouter(m, pattern, route) - } -} - -// AddAuto router to ControllerRegister. -// example beego.AddAuto(&MainContorlller{}), -// MainController has method List and Page. -// visit the url /main/list to execute List function -// /main/page to execute Page function. -func (p *ControllerRegister) AddAuto(c ControllerInterface) { - p.AddAutoPrefix("/", c) -} - -// AddAutoPrefix Add auto router to ControllerRegister with prefix. -// example beego.AddAutoPrefix("/admin",&MainContorlller{}), -// MainController has method List and Page. -// visit the url /admin/main/list to execute List function -// /admin/main/page to execute Page function. -func (p *ControllerRegister) AddAutoPrefix(prefix string, c ControllerInterface) { - reflectVal := reflect.ValueOf(c) - rt := reflectVal.Type() - ct := reflect.Indirect(reflectVal).Type() - controllerName := strings.TrimSuffix(ct.Name(), "Controller") - for i := 0; i < rt.NumMethod(); i++ { - if !utils.InSlice(rt.Method(i).Name, exceptMethod) { - route := &ControllerInfo{} - route.routerType = routerTypeBeego - route.methods = map[string]string{"*": rt.Method(i).Name} - route.controllerType = ct - pattern := path.Join(prefix, strings.ToLower(controllerName), strings.ToLower(rt.Method(i).Name), "*") - patternInit := path.Join(prefix, controllerName, rt.Method(i).Name, "*") - patternFix := path.Join(prefix, strings.ToLower(controllerName), strings.ToLower(rt.Method(i).Name)) - patternFixInit := path.Join(prefix, controllerName, rt.Method(i).Name) - route.pattern = pattern - for m := range HTTPMETHOD { - p.addToRouter(m, pattern, route) - p.addToRouter(m, patternInit, route) - p.addToRouter(m, patternFix, route) - p.addToRouter(m, patternFixInit, route) - } - } - } -} - -// InsertFilter Add a FilterFunc with pattern rule and action constant. -// params is for: -// 1. setting the returnOnOutput value (false allows multiple filters to execute) -// 2. determining whether or not params need to be reset. -func (p *ControllerRegister) InsertFilter(pattern string, pos int, filter FilterFunc, params ...bool) error { - mr := &FilterRouter{ - tree: NewTree(), - pattern: pattern, - filterFunc: filter, - returnOnOutput: true, - } - if !BConfig.RouterCaseSensitive { - mr.pattern = strings.ToLower(pattern) - } - - paramsLen := len(params) - if paramsLen > 0 { - mr.returnOnOutput = params[0] - } - if paramsLen > 1 { - mr.resetParams = params[1] - } - mr.tree.AddRouter(pattern, true) - return p.insertFilterRouter(pos, mr) -} - -// add Filter into -func (p *ControllerRegister) insertFilterRouter(pos int, mr *FilterRouter) (err error) { - if pos < BeforeStatic || pos > FinishRouter { - return errors.New("can not find your filter position") - } - p.enableFilter = true - p.filters[pos] = append(p.filters[pos], mr) - return nil -} - -// URLFor does another controller handler in this request function. -// it can access any controller method. -func (p *ControllerRegister) URLFor(endpoint string, values ...interface{}) string { - paths := strings.Split(endpoint, ".") - if len(paths) <= 1 { - logs.Warn("urlfor endpoint must like path.controller.method") - return "" - } - if len(values)%2 != 0 { - logs.Warn("urlfor params must key-value pair") - return "" - } - params := make(map[string]string) - if len(values) > 0 { - key := "" - for k, v := range values { - if k%2 == 0 { - key = fmt.Sprint(v) - } else { - params[key] = fmt.Sprint(v) - } - } - } - controllerName := strings.Join(paths[:len(paths)-1], "/") - methodName := paths[len(paths)-1] - for m, t := range p.routers { - ok, url := p.getURL(t, "/", controllerName, methodName, params, m) - if ok { - return url - } - } - return "" -} - -func (p *ControllerRegister) getURL(t *Tree, url, controllerName, methodName string, params map[string]string, httpMethod string) (bool, string) { - for _, subtree := range t.fixrouters { - u := path.Join(url, subtree.prefix) - ok, u := p.getURL(subtree, u, controllerName, methodName, params, httpMethod) - if ok { - return ok, u - } - } - if t.wildcard != nil { - u := path.Join(url, urlPlaceholder) - ok, u := p.getURL(t.wildcard, u, controllerName, methodName, params, httpMethod) - if ok { - return ok, u - } - } - for _, l := range t.leaves { - if c, ok := l.runObject.(*ControllerInfo); ok { - if c.routerType == routerTypeBeego && - strings.HasSuffix(path.Join(c.controllerType.PkgPath(), c.controllerType.Name()), controllerName) { - find := false - if HTTPMETHOD[strings.ToUpper(methodName)] { - if len(c.methods) == 0 { - find = true - } else if m, ok := c.methods[strings.ToUpper(methodName)]; ok && m == strings.ToUpper(methodName) { - find = true - } else if m, ok = c.methods["*"]; ok && m == methodName { - find = true - } - } - if !find { - for m, md := range c.methods { - if (m == "*" || m == httpMethod) && md == methodName { - find = true - } - } - } - if find { - if l.regexps == nil { - if len(l.wildcards) == 0 { - return true, strings.Replace(url, "/"+urlPlaceholder, "", 1) + toURL(params) - } - if len(l.wildcards) == 1 { - if v, ok := params[l.wildcards[0]]; ok { - delete(params, l.wildcards[0]) - return true, strings.Replace(url, urlPlaceholder, v, 1) + toURL(params) - } - return false, "" - } - if len(l.wildcards) == 3 && l.wildcards[0] == "." { - if p, ok := params[":path"]; ok { - if e, isok := params[":ext"]; isok { - delete(params, ":path") - delete(params, ":ext") - return true, strings.Replace(url, urlPlaceholder, p+"."+e, -1) + toURL(params) - } - } - } - canSkip := false - for _, v := range l.wildcards { - if v == ":" { - canSkip = true - continue - } - if u, ok := params[v]; ok { - delete(params, v) - url = strings.Replace(url, urlPlaceholder, u, 1) - } else { - if canSkip { - canSkip = false - continue - } - return false, "" - } - } - return true, url + toURL(params) - } - var i int - var startReg bool - regURL := "" - for _, v := range strings.Trim(l.regexps.String(), "^$") { - if v == '(' { - startReg = true - continue - } else if v == ')' { - startReg = false - if v, ok := params[l.wildcards[i]]; ok { - delete(params, l.wildcards[i]) - regURL = regURL + v - i++ - } else { - break - } - } else if !startReg { - regURL = string(append([]rune(regURL), v)) - } - } - if l.regexps.MatchString(regURL) { - ps := strings.Split(regURL, "/") - for _, p := range ps { - url = strings.Replace(url, urlPlaceholder, p, 1) - } - return true, url + toURL(params) - } - } - } - } - } - - return false, "" -} - -func (p *ControllerRegister) execFilter(context *beecontext.Context, urlPath string, pos int) (started bool) { - var preFilterParams map[string]string - for _, filterR := range p.filters[pos] { - if filterR.returnOnOutput && context.ResponseWriter.Started { - return true - } - if filterR.resetParams { - preFilterParams = context.Input.Params() - } - if ok := filterR.ValidRouter(urlPath, context); ok { - filterR.filterFunc(context) - if filterR.resetParams { - context.Input.ResetParams() - for k, v := range preFilterParams { - context.Input.SetParam(k, v) - } - } - } - if filterR.returnOnOutput && context.ResponseWriter.Started { - return true - } - } - return false -} - -// Implement http.Handler interface. -func (p *ControllerRegister) ServeHTTP(rw http.ResponseWriter, r *http.Request) { - startTime := time.Now() - var ( - runRouter reflect.Type - findRouter bool - runMethod string - methodParams []*param.MethodParam - routerInfo *ControllerInfo - isRunnable bool - ) - context := p.GetContext() - - context.Reset(rw, r) - - defer p.GiveBackContext(context) - if BConfig.RecoverFunc != nil { - defer BConfig.RecoverFunc(context) - } - - context.Output.EnableGzip = BConfig.EnableGzip - - if BConfig.RunMode == DEV { - context.Output.Header("Server", BConfig.ServerName) - } - - var urlPath = r.URL.Path - - if !BConfig.RouterCaseSensitive { - urlPath = strings.ToLower(urlPath) - } - - // filter wrong http method - if !HTTPMETHOD[r.Method] { - exception("405", context) - goto Admin - } - - // filter for static file - if len(p.filters[BeforeStatic]) > 0 && p.execFilter(context, urlPath, BeforeStatic) { - goto Admin - } - - serverStaticRouter(context) - - if context.ResponseWriter.Started { - findRouter = true - goto Admin - } - - if r.Method != http.MethodGet && r.Method != http.MethodHead { - if BConfig.CopyRequestBody && !context.Input.IsUpload() { - // connection will close if the incoming data are larger (RFC 7231, 6.5.11) - if r.ContentLength > BConfig.MaxMemory { - logs.Error(errors.New("payload too large")) - exception("413", context) - goto Admin - } - context.Input.CopyBody(BConfig.MaxMemory) - } - context.Input.ParseFormOrMulitForm(BConfig.MaxMemory) - } - - // session init - if BConfig.WebConfig.Session.SessionOn { - var err error - context.Input.CruSession, err = GlobalSessions.SessionStart(rw, r) - if err != nil { - logs.Error(err) - exception("503", context) - goto Admin - } - defer func() { - if context.Input.CruSession != nil { - context.Input.CruSession.SessionRelease(rw) - } - }() - } - if len(p.filters[BeforeRouter]) > 0 && p.execFilter(context, urlPath, BeforeRouter) { - goto Admin - } - // User can define RunController and RunMethod in filter - if context.Input.RunController != nil && context.Input.RunMethod != "" { - findRouter = true - runMethod = context.Input.RunMethod - runRouter = context.Input.RunController - } else { - routerInfo, findRouter = p.FindRouter(context) - } - - // if no matches to url, throw a not found exception - if !findRouter { - exception("404", context) - goto Admin - } - if splat := context.Input.Param(":splat"); splat != "" { - for k, v := range strings.Split(splat, "/") { - context.Input.SetParam(strconv.Itoa(k), v) - } - } - - if routerInfo != nil { - // store router pattern into context - context.Input.SetData("RouterPattern", routerInfo.pattern) - } - - // execute middleware filters - if len(p.filters[BeforeExec]) > 0 && p.execFilter(context, urlPath, BeforeExec) { - goto Admin - } - - // check policies - if p.execPolicy(context, urlPath) { - goto Admin - } - - if routerInfo != nil { - if routerInfo.routerType == routerTypeRESTFul { - if _, ok := routerInfo.methods[r.Method]; ok { - isRunnable = true - routerInfo.runFunction(context) - } else { - exception("405", context) - goto Admin - } - } else if routerInfo.routerType == routerTypeHandler { - isRunnable = true - routerInfo.handler.ServeHTTP(context.ResponseWriter, context.Request) - } else { - runRouter = routerInfo.controllerType - methodParams = routerInfo.methodParams - method := r.Method - if r.Method == http.MethodPost && context.Input.Query("_method") == http.MethodPut { - method = http.MethodPut - } - if r.Method == http.MethodPost && context.Input.Query("_method") == http.MethodDelete { - method = http.MethodDelete - } - if m, ok := routerInfo.methods[method]; ok { - runMethod = m - } else if m, ok = routerInfo.methods["*"]; ok { - runMethod = m - } else { - runMethod = method - } - } - } - - // also defined runRouter & runMethod from filter - if !isRunnable { - // Invoke the request handler - var execController ControllerInterface - if routerInfo != nil && routerInfo.initialize != nil { - execController = routerInfo.initialize() - } else { - vc := reflect.New(runRouter) - var ok bool - execController, ok = vc.Interface().(ControllerInterface) - if !ok { - panic("controller is not ControllerInterface") - } - } - - // call the controller init function - execController.Init(context, runRouter.Name(), runMethod, execController) - - // call prepare function - execController.Prepare() - - // if XSRF is Enable then check cookie where there has any cookie in the request's cookie _csrf - if BConfig.WebConfig.EnableXSRF { - execController.XSRFToken() - if r.Method == http.MethodPost || r.Method == http.MethodDelete || r.Method == http.MethodPut || - (r.Method == http.MethodPost && (context.Input.Query("_method") == http.MethodDelete || context.Input.Query("_method") == http.MethodPut)) { - execController.CheckXSRFCookie() - } - } - - execController.URLMapping() - - if !context.ResponseWriter.Started { - // exec main logic - switch runMethod { - case http.MethodGet: - execController.Get() - case http.MethodPost: - execController.Post() - case http.MethodDelete: - execController.Delete() - case http.MethodPut: - execController.Put() - case http.MethodHead: - execController.Head() - case http.MethodPatch: - execController.Patch() - case http.MethodOptions: - execController.Options() - case http.MethodTrace: - execController.Trace() - default: - if !execController.HandlerFunc(runMethod) { - vc := reflect.ValueOf(execController) - method := vc.MethodByName(runMethod) - in := param.ConvertParams(methodParams, method.Type(), context) - out := method.Call(in) - - // For backward compatibility we only handle response if we had incoming methodParams - if methodParams != nil { - p.handleParamResponse(context, execController, out) - } - } - } - - // render template - if !context.ResponseWriter.Started && context.Output.Status == 0 { - if BConfig.WebConfig.AutoRender { - if err := execController.Render(); err != nil { - logs.Error(err) - } - } - } - } - - // finish all runRouter. release resource - execController.Finish() - } - - // execute middleware filters - if len(p.filters[AfterExec]) > 0 && p.execFilter(context, urlPath, AfterExec) { - goto Admin - } - - if len(p.filters[FinishRouter]) > 0 && p.execFilter(context, urlPath, FinishRouter) { - goto Admin - } - -Admin: - // admin module record QPS - - statusCode := context.ResponseWriter.Status - if statusCode == 0 { - statusCode = 200 - } - - LogAccess(context, &startTime, statusCode) - - timeDur := time.Since(startTime) - context.ResponseWriter.Elapsed = timeDur - if BConfig.Listen.EnableAdmin { - pattern := "" - if routerInfo != nil { - pattern = routerInfo.pattern - } - - if FilterMonitorFunc(r.Method, r.URL.Path, timeDur, pattern, statusCode) { - routerName := "" - if runRouter != nil { - routerName = runRouter.Name() - } - go toolbox.StatisticsMap.AddStatistics(r.Method, r.URL.Path, routerName, timeDur) - } - } - - if BConfig.RunMode == DEV && !BConfig.Log.AccessLogs { - match := map[bool]string{true: "match", false: "nomatch"} - devInfo := fmt.Sprintf("|%15s|%s %3d %s|%13s|%8s|%s %-7s %s %-3s", - context.Input.IP(), - logs.ColorByStatus(statusCode), statusCode, logs.ResetColor(), - timeDur.String(), - match[findRouter], - logs.ColorByMethod(r.Method), r.Method, logs.ResetColor(), - r.URL.Path) - if routerInfo != nil { - devInfo += fmt.Sprintf(" r:%s", routerInfo.pattern) - } - - logs.Debug(devInfo) - } - // Call WriteHeader if status code has been set changed - if context.Output.Status != 0 { - context.ResponseWriter.WriteHeader(context.Output.Status) - } -} - -func (p *ControllerRegister) handleParamResponse(context *beecontext.Context, execController ControllerInterface, results []reflect.Value) { - // looping in reverse order for the case when both error and value are returned and error sets the response status code - for i := len(results) - 1; i >= 0; i-- { - result := results[i] - if result.Kind() != reflect.Interface || !result.IsNil() { - resultValue := result.Interface() - context.RenderMethodResult(resultValue) - } - } - if !context.ResponseWriter.Started && len(results) > 0 && context.Output.Status == 0 { - context.Output.SetStatus(200) - } -} - -// FindRouter Find Router info for URL -func (p *ControllerRegister) FindRouter(context *beecontext.Context) (routerInfo *ControllerInfo, isFind bool) { - var urlPath = context.Input.URL() - if !BConfig.RouterCaseSensitive { - urlPath = strings.ToLower(urlPath) - } - httpMethod := context.Input.Method() - if t, ok := p.routers[httpMethod]; ok { - runObject := t.Match(urlPath, context) - if r, ok := runObject.(*ControllerInfo); ok { - return r, true - } - } - return -} - -func toURL(params map[string]string) string { - if len(params) == 0 { - return "" - } - u := "?" - for k, v := range params { - u += k + "=" + v + "&" - } - return strings.TrimRight(u, "&") -} - -// LogAccess logging info HTTP Access -func LogAccess(ctx *beecontext.Context, startTime *time.Time, statusCode int) { - // Skip logging if AccessLogs config is false - if !BConfig.Log.AccessLogs { - return - } - // Skip logging static requests unless EnableStaticLogs config is true - if !BConfig.Log.EnableStaticLogs && DefaultAccessLogFilter.Filter(ctx) { - return - } - var ( - requestTime time.Time - elapsedTime time.Duration - r = ctx.Request - ) - if startTime != nil { - requestTime = *startTime - elapsedTime = time.Since(*startTime) - } - record := &logs.AccessLogRecord{ - RemoteAddr: ctx.Input.IP(), - RequestTime: requestTime, - RequestMethod: r.Method, - Request: fmt.Sprintf("%s %s %s", r.Method, r.RequestURI, r.Proto), - ServerProtocol: r.Proto, - Host: r.Host, - Status: statusCode, - ElapsedTime: elapsedTime, - HTTPReferrer: r.Header.Get("Referer"), - HTTPUserAgent: r.Header.Get("User-Agent"), - RemoteUser: r.Header.Get("Remote-User"), - BodyBytesSent: 0, // @todo this one is missing! - } - logs.AccessLog(record, BConfig.Log.AccessLogsFormat) -} diff --git a/src/vendor/github.com/beego/beego/test.sh b/src/vendor/github.com/beego/beego/test.sh deleted file mode 100644 index 78928fea9..000000000 --- a/src/vendor/github.com/beego/beego/test.sh +++ /dev/null @@ -1,14 +0,0 @@ -#!/bin/bash - -docker-compose -f test_docker_compose.yaml up -d - -export ORM_DRIVER=mysql -export TZ=UTC -export ORM_SOURCE="beego:test@tcp(localhost:13306)/orm_test?charset=utf8" - -go test ./... - -# clear all container -docker-compose -f test_docker_compose.yaml down - - diff --git a/src/vendor/github.com/beego/beego/test_docker_compose.yaml b/src/vendor/github.com/beego/beego/test_docker_compose.yaml deleted file mode 100644 index 54ca40971..000000000 --- a/src/vendor/github.com/beego/beego/test_docker_compose.yaml +++ /dev/null @@ -1,39 +0,0 @@ -version: "3.8" -services: - redis: - container_name: "beego-redis" - image: redis - environment: - - ALLOW_EMPTY_PASSWORD=yes - ports: - - "6379:6379" - - mysql: - container_name: "beego-mysql" - image: mysql:5.7.30 - ports: - - "13306:3306" - environment: - - MYSQL_ROOT_PASSWORD=1q2w3e - - MYSQL_DATABASE=orm_test - - MYSQL_USER=beego - - MYSQL_PASSWORD=test - - postgresql: - container_name: "beego-postgresql" - image: bitnami/postgresql:latest - ports: - - "5432:5432" - environment: - - ALLOW_EMPTY_PASSWORD=yes - ssdb: - container_name: "beego-ssdb" - image: wendal/ssdb - ports: - - "8888:8888" - memcache: - container_name: "beego-memcache" - image: memcached - ports: - - "11211:11211" - diff --git a/src/vendor/github.com/beego/beego/toolbox/task.go b/src/vendor/github.com/beego/beego/toolbox/task.go deleted file mode 100644 index d2a94ba95..000000000 --- a/src/vendor/github.com/beego/beego/toolbox/task.go +++ /dev/null @@ -1,634 +0,0 @@ -// Copyright 2014 beego Author. All Rights Reserved. -// -// 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 toolbox - -import ( - "log" - "math" - "sort" - "strconv" - "strings" - "sync" - "time" -) - -// bounds provides a range of acceptable values (plus a map of name to value). -type bounds struct { - min, max uint - names map[string]uint -} - -// The bounds for each field. -var ( - AdminTaskList map[string]Tasker - taskLock sync.RWMutex - stop chan bool - changed chan bool - isstart bool - seconds = bounds{0, 59, nil} - minutes = bounds{0, 59, nil} - hours = bounds{0, 23, nil} - days = bounds{1, 31, nil} - months = bounds{1, 12, map[string]uint{ - "jan": 1, - "feb": 2, - "mar": 3, - "apr": 4, - "may": 5, - "jun": 6, - "jul": 7, - "aug": 8, - "sep": 9, - "oct": 10, - "nov": 11, - "dec": 12, - }} - weeks = bounds{0, 6, map[string]uint{ - "sun": 0, - "mon": 1, - "tue": 2, - "wed": 3, - "thu": 4, - "fri": 5, - "sat": 6, - }} -) - -const ( - // Set the top bit if a star was included in the expression. - starBit = 1 << 63 -) - -// Schedule time taks schedule -type Schedule struct { - Second uint64 - Minute uint64 - Hour uint64 - Day uint64 - Month uint64 - Week uint64 -} - -// TaskFunc task func type -type TaskFunc func() error - -// Tasker task interface -type Tasker interface { - GetSpec() string - GetStatus() string - Run() error - SetNext(time.Time) - GetNext() time.Time - SetPrev(time.Time) - GetPrev() time.Time -} - -// task error -type taskerr struct { - t time.Time - errinfo string -} - -// Task task struct -type Task struct { - Taskname string - Spec *Schedule - SpecStr string - DoFunc TaskFunc - Prev time.Time - Next time.Time - Errlist []*taskerr // like errtime:errinfo - ErrLimit int // max length for the errlist, 0 stand for no limit -} - -// NewTask add new task with name, time and func -func NewTask(tname string, spec string, f TaskFunc) *Task { - - task := &Task{ - Taskname: tname, - DoFunc: f, - ErrLimit: 100, - SpecStr: spec, - } - task.SetCron(spec) - return task -} - -// GetSpec get spec string -func (t *Task) GetSpec() string { - return t.SpecStr -} - -// GetStatus get current task status -func (t *Task) GetStatus() string { - var str string - for _, v := range t.Errlist { - str += v.t.String() + ":" + v.errinfo + "
" - } - return str -} - -// Run run all tasks -func (t *Task) Run() error { - err := t.DoFunc() - if err != nil { - if t.ErrLimit > 0 && t.ErrLimit > len(t.Errlist) { - t.Errlist = append(t.Errlist, &taskerr{t: t.Next, errinfo: err.Error()}) - } - } - return err -} - -// SetNext set next time for this task -func (t *Task) SetNext(now time.Time) { - t.Next = t.Spec.Next(now) -} - -// GetNext get the next call time of this task -func (t *Task) GetNext() time.Time { - return t.Next -} - -// SetPrev set prev time of this task -func (t *Task) SetPrev(now time.Time) { - t.Prev = now -} - -// GetPrev get prev time of this task -func (t *Task) GetPrev() time.Time { - return t.Prev -} - -// six columns mean: -// second:0-59 -// minute:0-59 -// hour:1-23 -// day:1-31 -// month:1-12 -// week:0-6(0 means Sunday) - -// SetCron some signals: -// *: any time -// ,:  separate signal -//   -:duration -// /n : do as n times of time duration -///////////////////////////////////////////////////////// -// 0/30 * * * * * every 30s -// 0 43 21 * * * 21:43 -// 0 15 05 * * *    05:15 -// 0 0 17 * * * 17:00 -// 0 0 17 * * 1 17:00 in every Monday -// 0 0,10 17 * * 0,2,3 17:00 and 17:10 in every Sunday, Tuesday and Wednesday -// 0 0-10 17 1 * * 17:00 to 17:10 in 1 min duration each time on the first day of month -// 0 0 0 1,15 * 1 0:00 on the 1st day and 15th day of month -// 0 42 4 1 * *     4:42 on the 1st day of month -// 0 0 21 * * 1-6   21:00 from Monday to Saturday -// 0 0,10,20,30,40,50 * * * *  every 10 min duration -// 0 */10 * * * *        every 10 min duration -// 0 * 1 * * *         1:00 to 1:59 in 1 min duration each time -// 0 0 1 * * *         1:00 -// 0 0 */1 * * *        0 min of hour in 1 hour duration -// 0 0 * * * *         0 min of hour in 1 hour duration -// 0 2 8-20/3 * * *       8:02, 11:02, 14:02, 17:02, 20:02 -// 0 30 5 1,15 * *       5:30 on the 1st day and 15th day of month -func (t *Task) SetCron(spec string) { - t.Spec = t.parse(spec) -} - -func (t *Task) parse(spec string) *Schedule { - if len(spec) > 0 && spec[0] == '@' { - return t.parseSpec(spec) - } - // Split on whitespace. We require 5 or 6 fields. - // (second) (minute) (hour) (day of month) (month) (day of week, optional) - fields := strings.Fields(spec) - if len(fields) != 5 && len(fields) != 6 { - log.Panicf("Expected 5 or 6 fields, found %d: %s", len(fields), spec) - } - - // If a sixth field is not provided (DayOfWeek), then it is equivalent to star. - if len(fields) == 5 { - fields = append(fields, "*") - } - - schedule := &Schedule{ - Second: getField(fields[0], seconds), - Minute: getField(fields[1], minutes), - Hour: getField(fields[2], hours), - Day: getField(fields[3], days), - Month: getField(fields[4], months), - Week: getField(fields[5], weeks), - } - - return schedule -} - -func (t *Task) parseSpec(spec string) *Schedule { - switch spec { - case "@yearly", "@annually": - return &Schedule{ - Second: 1 << seconds.min, - Minute: 1 << minutes.min, - Hour: 1 << hours.min, - Day: 1 << days.min, - Month: 1 << months.min, - Week: all(weeks), - } - - case "@monthly": - return &Schedule{ - Second: 1 << seconds.min, - Minute: 1 << minutes.min, - Hour: 1 << hours.min, - Day: 1 << days.min, - Month: all(months), - Week: all(weeks), - } - - case "@weekly": - return &Schedule{ - Second: 1 << seconds.min, - Minute: 1 << minutes.min, - Hour: 1 << hours.min, - Day: all(days), - Month: all(months), - Week: 1 << weeks.min, - } - - case "@daily", "@midnight": - return &Schedule{ - Second: 1 << seconds.min, - Minute: 1 << minutes.min, - Hour: 1 << hours.min, - Day: all(days), - Month: all(months), - Week: all(weeks), - } - - case "@hourly": - return &Schedule{ - Second: 1 << seconds.min, - Minute: 1 << minutes.min, - Hour: all(hours), - Day: all(days), - Month: all(months), - Week: all(weeks), - } - } - log.Panicf("Unrecognized descriptor: %s", spec) - return nil -} - -// Next set schedule to next time -func (s *Schedule) Next(t time.Time) time.Time { - - // Start at the earliest possible time (the upcoming second). - t = t.Add(1*time.Second - time.Duration(t.Nanosecond())*time.Nanosecond) - - // This flag indicates whether a field has been incremented. - added := false - - // If no time is found within five years, return zero. - yearLimit := t.Year() + 5 - -WRAP: - if t.Year() > yearLimit { - return time.Time{} - } - - // Find the first applicable month. - // If it's this month, then do nothing. - for 1< 0 - dowMatch = 1< 0 - ) - - if s.Day&starBit > 0 || s.Week&starBit > 0 { - return domMatch && dowMatch - } - return domMatch || dowMatch -} - -// StartTask start all tasks -func StartTask() { - taskLock.Lock() - defer taskLock.Unlock() - if isstart { - //If already started, no need to start another goroutine. - return - } - isstart = true - go run() -} - -func run() { - now := time.Now().Local() - for _, t := range AdminTaskList { - t.SetNext(now) - } - - for { - // we only use RLock here because NewMapSorter copy the reference, do not change any thing - taskLock.RLock() - sortList := NewMapSorter(AdminTaskList) - taskLock.RUnlock() - sortList.Sort() - var effective time.Time - if len(AdminTaskList) == 0 || sortList.Vals[0].GetNext().IsZero() { - // If there are no entries yet, just sleep - it still handles new entries - // and stop requests. - effective = now.AddDate(10, 0, 0) - } else { - effective = sortList.Vals[0].GetNext() - } - select { - case now = <-time.After(effective.Sub(now)): - // Run every entry whose next time was this effective time. - for _, e := range sortList.Vals { - if e.GetNext() != effective { - break - } - go e.Run() - e.SetPrev(e.GetNext()) - e.SetNext(effective) - } - continue - case <-changed: - now = time.Now().Local() - taskLock.Lock() - for _, t := range AdminTaskList { - t.SetNext(now) - } - taskLock.Unlock() - continue - case <-stop: - return - } - } -} - -// StopTask stop all tasks -func StopTask() { - taskLock.Lock() - defer taskLock.Unlock() - if isstart { - isstart = false - stop <- true - } - -} - -// AddTask add task with name -func AddTask(taskname string, t Tasker) { - taskLock.Lock() - defer taskLock.Unlock() - t.SetNext(time.Now().Local()) - AdminTaskList[taskname] = t - if isstart { - changed <- true - } -} - -// DeleteTask delete task with name -func DeleteTask(taskname string) { - taskLock.Lock() - defer taskLock.Unlock() - delete(AdminTaskList, taskname) - if isstart { - changed <- true - } -} - -// MapSorter sort map for tasker -type MapSorter struct { - Keys []string - Vals []Tasker -} - -// NewMapSorter create new tasker map -func NewMapSorter(m map[string]Tasker) *MapSorter { - ms := &MapSorter{ - Keys: make([]string, 0, len(m)), - Vals: make([]Tasker, 0, len(m)), - } - for k, v := range m { - ms.Keys = append(ms.Keys, k) - ms.Vals = append(ms.Vals, v) - } - return ms -} - -// Sort sort tasker map -func (ms *MapSorter) Sort() { - sort.Sort(ms) -} - -func (ms *MapSorter) Len() int { return len(ms.Keys) } -func (ms *MapSorter) Less(i, j int) bool { - if ms.Vals[i].GetNext().IsZero() { - return false - } - if ms.Vals[j].GetNext().IsZero() { - return true - } - return ms.Vals[i].GetNext().Before(ms.Vals[j].GetNext()) -} -func (ms *MapSorter) Swap(i, j int) { - ms.Vals[i], ms.Vals[j] = ms.Vals[j], ms.Vals[i] - ms.Keys[i], ms.Keys[j] = ms.Keys[j], ms.Keys[i] -} - -func getField(field string, r bounds) uint64 { - // list = range {"," range} - var bits uint64 - ranges := strings.FieldsFunc(field, func(r rune) bool { return r == ',' }) - for _, expr := range ranges { - bits |= getRange(expr, r) - } - return bits -} - -// getRange returns the bits indicated by the given expression: -// number | number "-" number [ "/" number ] -func getRange(expr string, r bounds) uint64 { - - var ( - start, end, step uint - rangeAndStep = strings.Split(expr, "/") - lowAndHigh = strings.Split(rangeAndStep[0], "-") - singleDigit = len(lowAndHigh) == 1 - ) - - var extrastar uint64 - if lowAndHigh[0] == "*" || lowAndHigh[0] == "?" { - start = r.min - end = r.max - extrastar = starBit - } else { - start = parseIntOrName(lowAndHigh[0], r.names) - switch len(lowAndHigh) { - case 1: - end = start - case 2: - end = parseIntOrName(lowAndHigh[1], r.names) - default: - log.Panicf("Too many hyphens: %s", expr) - } - } - - switch len(rangeAndStep) { - case 1: - step = 1 - case 2: - step = mustParseInt(rangeAndStep[1]) - - // Special handling: "N/step" means "N-max/step". - if singleDigit { - end = r.max - } - default: - log.Panicf("Too many slashes: %s", expr) - } - - if start < r.min { - log.Panicf("Beginning of range (%d) below minimum (%d): %s", start, r.min, expr) - } - if end > r.max { - log.Panicf("End of range (%d) above maximum (%d): %s", end, r.max, expr) - } - if start > end { - log.Panicf("Beginning of range (%d) beyond end of range (%d): %s", start, end, expr) - } - - return getBits(start, end, step) | extrastar -} - -// parseIntOrName returns the (possibly-named) integer contained in expr. -func parseIntOrName(expr string, names map[string]uint) uint { - if names != nil { - if namedInt, ok := names[strings.ToLower(expr)]; ok { - return namedInt - } - } - return mustParseInt(expr) -} - -// mustParseInt parses the given expression as an int or panics. -func mustParseInt(expr string) uint { - num, err := strconv.Atoi(expr) - if err != nil { - log.Panicf("Failed to parse int from %s: %s", expr, err) - } - if num < 0 { - log.Panicf("Negative number (%d) not allowed: %s", num, expr) - } - - return uint(num) -} - -// getBits sets all bits in the range [min, max], modulo the given step size. -func getBits(min, max, step uint) uint64 { - var bits uint64 - - // If step is 1, use shifts. - if step == 1 { - return ^(math.MaxUint64 << (max + 1)) & (math.MaxUint64 << min) - } - - // Else, use a simple loop. - for i := min; i <= max; i += step { - bits |= 1 << i - } - return bits -} - -// all returns all bits within the given bounds. (plus the star bit) -func all(r bounds) uint64 { - return getBits(r.min, r.max, 1) | starBit -} - -func init() { - AdminTaskList = make(map[string]Tasker) - stop = make(chan bool) - changed = make(chan bool) -} diff --git a/src/vendor/github.com/beego/beego/v2/.deepsource.toml b/src/vendor/github.com/beego/beego/v2/.deepsource.toml new file mode 100644 index 000000000..7901d2d2a --- /dev/null +++ b/src/vendor/github.com/beego/beego/v2/.deepsource.toml @@ -0,0 +1,12 @@ +version = 1 + +test_patterns = ["**/*_test.go"] + +exclude_patterns = ["scripts/**"] + +[[analyzers]] +name = "go" +enabled = true + + [analyzers.meta] + import_paths = ["github.com/beego/beego"] diff --git a/src/vendor/github.com/beego/beego/v2/.gitignore b/src/vendor/github.com/beego/beego/v2/.gitignore new file mode 100644 index 000000000..9a2af3cfc --- /dev/null +++ b/src/vendor/github.com/beego/beego/v2/.gitignore @@ -0,0 +1,17 @@ +.idea +.vscode +.DS_Store +*.swp +*.swo +beego.iml + +_beeTmp/ +_beeTmp2/ +pkg/_beeTmp/ +pkg/_beeTmp2/ +test/tmp/ +core/config/env/pkg/ + +my save path/ + +profile.out diff --git a/src/vendor/github.com/beego/beego/v2/CHANGELOG.md b/src/vendor/github.com/beego/beego/v2/CHANGELOG.md new file mode 100644 index 000000000..7e803c991 --- /dev/null +++ b/src/vendor/github.com/beego/beego/v2/CHANGELOG.md @@ -0,0 +1,136 @@ +# developing + +# v2.0.5 + +Note: now we force the web admin service serving HTTP only. + +- [Fix 4984: random expire cache](https://github.com/beego/beego/pull/4984) +- [Fix 4907: make admin serve HTTP only](https://github.com/beego/beego/pull/5005) +- [Feat 4999: add get all tasks function](https://github.com/beego/beego/pull/4999) +- [Fix 5012: fix some bug, pass []any as any in variadic function](https://github.com/beego/beego/pull/5012) +- [Fix 5022: Miss assigning listener to graceful Server](https://github.com/beego/beego/pull/5028) +- [Fix 4955: Make commands and Docker compose for ORM unit tests](https://github.com/beego/beego/pull/5031) + + +# v2.0.4 + +Note: now we force the web admin service serving HTTP only. + +- [Fix issue 4961, `leafInfo.match()` use `path.join()` to deal with `wildcardValues`, which may lead to cross directory risk ](https://github.com/beego/beego/pull/4964) +- [Fix 4975: graceful server listen the specific address](https://github.com/beego/beego/pull/4979) +- [Fix 4976: make admin serve HTTP only](https://github.com/beego/beego/pull/4980) + +# v2.0.3 +- [upgrade redisgo to v1.8.8](https://github.com/beego/beego/pull/4872) +- [fix prometheus CVE-2022-21698](https://github.com/beego/beego/pull/4878) +- [upgrade to Go 1.18](https://github.com/beego/beego/pull/4896) +- [make `PatternLogFormatter` handling the arguments](https://github.com/beego/beego/pull/4914/files) +- [Add httplib OpenTelemetry Filter](https://github.com/beego/beego/pull/4888, https://github.com/beego/beego/pull/4915) +- [Add orm OpenTelemetry Filter](https://github.com/beego/beego/issues/4944) +- [Support NewBeegoRequestWithCtx in httplib](https://github.com/beego/beego/pull/4895) +- [Support lifecycle callback](https://github.com/beego/beego/pull/4918) +- [Append column comments to create table sentence when using postgres](https://github.com/beego/beego/pull/4940) +- [logs: multiFileLogWriter uses incorrect formatter](https://github.com/beego/beego/pull/4943) +- [fix issue 4946 CVE-2022-31259](https://github.com/beego/beego/pull/4954) + +# v2.0.2 +See v2.0.2-beta.1 +- [fix bug: etcd should use etcd as adapter name](https://github.com/beego/beego/pull/4845) +# v2.0.2-beta.1 +- Add a custom option for whether to escape HTML special characters when processing http request parameters. [4701](https://github.com/beego/beego/pull/4701) +- Always set the response status in the CustomAbort function. [4686](https://github.com/beego/beego/pull/4686) +- Add template functions eq,lt to support uint and int compare. [4607](https://github.com/beego/beego/pull/4607) +- Migrate tests to GitHub Actions. [4663](https://github.com/beego/beego/issues/4663) +- Add http client and option func. [4455](https://github.com/beego/beego/issues/4455) +- Add: Convenient way to generate mock object [4620](https://github.com/beego/beego/issues/4620) +- Infra: use dependabot to update dependencies. [4623](https://github.com/beego/beego/pull/4623) +- Lint: use golangci-lint. [4619](https://github.com/beego/beego/pull/4619) +- Chore: format code. [4615](https://github.com/beego/beego/pull/4615) +- Test on Go v1.15.x & v1.16.x. [4614](https://github.com/beego/beego/pull/4614) +- Env: non-empty GOBIN & GOPATH. [4613](https://github.com/beego/beego/pull/4613) +- Chore: update dependencies. [4611](https://github.com/beego/beego/pull/4611) +- Update orm_test.go/TestInsertOrUpdate with table-driven. [4609](https://github.com/beego/beego/pull/4609) +- Add: Resp() method for web.Controller. [4588](https://github.com/beego/beego/pull/4588) +- Web mock and test support. [4565](https://github.com/beego/beego/pull/4565) [4574](https://github.com/beego/beego/pull/4574) +- Error codes definition of cache module. [4493](https://github.com/beego/beego/pull/4493) +- Remove generateCommentRoute http hook. Using `bee generate routers` commands instead.[4486](https://github.com/beego/beego/pull/4486) [bee PR 762](https://github.com/beego/bee/pull/762) +- Fix: /abc.html/aaa match /abc/aaa. [4459](https://github.com/beego/beego/pull/4459) +- ORM mock. [4407](https://github.com/beego/beego/pull/4407) +- Add sonar check and ignore test. [4432](https://github.com/beego/beego/pull/4432) [4433](https://github.com/beego/beego/pull/4433) +- Update changlog.yml to check every PR to develop branch.[4427](https://github.com/beego/beego/pull/4427) +- Fix 4396: Add context.param module into adapter. [4398](https://github.com/beego/beego/pull/4398) +- Support `RollbackUnlessCommit` API. [4542](https://github.com/beego/beego/pull/4542) +- Fix 4503 and 4504: Add `when` to `Write([]byte)` method and add `prefix` to `writeMsg`. [4507](https://github.com/beego/beego/pull/4507) +- Fix 4480: log format incorrect. [4482](https://github.com/beego/beego/pull/4482) +- Remove `duration` from prometheus labels. [4391](https://github.com/beego/beego/pull/4391) +- Fix `unknown escape sequence` in generated code. [4385](https://github.com/beego/beego/pull/4385) +- Fix 4590: Forget to check URL when FilterChain invoke `next()`. [4593](https://github.com/beego/beego/pull/4593) +- Fix 4727: CSS when request URI is invalid. [4729](https://github.com/beego/beego/pull/4729) +- Using fixed name `commentRouter.go` as generated file name. [4385](https://github.com/beego/beego/pull/4385) +- Fix 4383: ORM Adapter produces panic when using orm.RegisterModelWithPrefix. [4386](https://github.com/beego/beego/pull/4386) +- Support 4144: Add new api for order by for supporting multiple way to query [4294](https://github.com/beego/beego/pull/4294) +- Support session Filter chain. [4404](https://github.com/beego/beego/pull/4404) +- Feature issue #4402 finish router get example. [4416](https://github.com/beego/beego/pull/4416) +- Implement context.Context support and deprecate `QueryM2MWithCtx` and `QueryTableWithCtx` [4424](https://github.com/beego/beego/pull/4424) +- Finish timeout option for tasks #4441 [4441](https://github.com/beego/beego/pull/4441) +- Error Module brief design & using httplib module to validate this design. [4453](https://github.com/beego/beego/pull/4453) +- Fix 4444: panic when 404 not found. [4446](https://github.com/beego/beego/pull/4446) +- Fix 4435: fix panic when controller dir not found. [4452](https://github.com/beego/beego/pull/4452) +- Hotfix:reflect.ValueOf(nil) in getFlatParams [4716](https://github.com/beego/beego/issues/4716) +- Fix 4456: Fix router method expression [4456](https://github.com/beego/beego/pull/4456) +- Remove some `go get` lines in `.travis.yml` file [4469](https://github.com/beego/beego/pull/4469) +- Fix 4451: support QueryExecutor interface. [4461](https://github.com/beego/beego/pull/4461) +- Add some testing scripts [4461](https://github.com/beego/beego/pull/4461) +- Refactor httplib: Move debug code to a filter [4440](https://github.com/beego/beego/issues/4440) +- fix: code quality issues [4513](https://github.com/beego/beego/pull/4513) +- Optimize maligned structs to reduce memory foot-print [4525](https://github.com/beego/beego/pull/4525) +- Feat: add token bucket ratelimit filter [4508](https://github.com/beego/beego/pull/4508) +- Improve: Avoid ignoring mistakes that need attention [4548](https://github.com/beego/beego/pull/4548) +- Integration: DeepSource [4560](https://github.com/beego/beego/pull/4560) +- Integration: Remove unnecessary function call [4577](https://github.com/beego/beego/pull/4577) +- Feature issue #4402 finish router get example. [4416](https://github.com/beego/beego/pull/4416) +- Proposal: Add Bind() method for `web.Controller` [4491](https://github.com/beego/beego/issues/4579) +- Optimize AddAutoPrefix: only register one router in case-insensitive mode. [4582](https://github.com/beego/beego/pull/4582) +- Init exceptMethod by using reflection. [4583](https://github.com/beego/beego/pull/4583) +- Deprecated BeeMap and replace all usage with `sync.map` [4616](https://github.com/beego/beego/pull/4616) +- TaskManager support graceful shutdown [4635](https://github.com/beego/beego/pull/4635) +- Add comments to `web.Config`, rename `RouterXXX` to `CtrlXXX`, define `HandleFunc` [4714](https://github.com/beego/beego/pull/4714) +- Refactor: Move `BindXXX` and `XXXResp` methods to `context.Context`. [4718](https://github.com/beego/beego/pull/4718) +- Fix 4728: Print wrong file name. [4737](https://github.com/beego/beego/pull/4737) +- fix bug:reflect.ValueOf(nil) in getFlatParams [4715](https://github.com/beego/beego/pull/4715) +- Fix 4736: set a fixed value "/" to the "Path" of "_xsrf" cookie. [4736](https://github.com/beego/beego/issues/4735) [4739](https://github.com/beego/beego/issues/4739) +- Fix 4734: do not reset id in Delete function. [4738](https://github.com/beego/beego/pull/4738) [4742](https://github.com/beego/beego/pull/4742) +- Fix 4699: Remove Remove goyaml2 dependency. [4755](https://github.com/beego/beego/pull/4755) +- Fix 4698: Prompt error when config format is incorrect. [4757](https://github.com/beego/beego/pull/4757) +- Fix 4674: Tx Orm missing debug log [4756](https://github.com/beego/beego/pull/4756) +- Fix 4759: fix numeric notation of permissions [4759](https://github.com/beego/beego/pull/4759) +- set default rate and capacity for ratelimit filter [4796](https://github.com/beego/beego/pull/4796) +- Fix 4782: must set status before rendering error page [4797](https://github.com/beego/beego/pull/4797) +- Fix 4791: delay to format parameter in log module [4804](https://github.com/beego/beego/pull/4804) + +## Fix Sonar + +- [4677](https://github.com/beego/beego/pull/4677) +- [4624](https://github.com/beego/beego/pull/4624) +- [4608](https://github.com/beego/beego/pull/4608) +- [4473](https://github.com/beego/beego/pull/4473) +- [4474](https://github.com/beego/beego/pull/4474) +- [4479](https://github.com/beego/beego/pull/4479) +- [4639](https://github.com/beego/beego/pull/4639) +- [4668](https://github.com/beego/beego/pull/4668) + +## Fix lint and format code + +- [4644](https://github.com/beego/beego/pull/4644) +- [4645](https://github.com/beego/beego/pull/4645) +- [4646](https://github.com/beego/beego/pull/4646) +- [4647](https://github.com/beego/beego/pull/4647) +- [4648](https://github.com/beego/beego/pull/4648) +- [4649](https://github.com/beego/beego/pull/4649) +- [4651](https://github.com/beego/beego/pull/4651) +- [4652](https://github.com/beego/beego/pull/4652) +- [4653](https://github.com/beego/beego/pull/4653) +- [4654](https://github.com/beego/beego/pull/4654) +- [4655](https://github.com/beego/beego/pull/4655) +- [4656](https://github.com/beego/beego/pull/4656) +- [4660](https://github.com/beego/beego/pull/4660) diff --git a/src/vendor/github.com/beego/beego/v2/CONTRIBUTING.md b/src/vendor/github.com/beego/beego/v2/CONTRIBUTING.md new file mode 100644 index 000000000..59cc5682c --- /dev/null +++ b/src/vendor/github.com/beego/beego/v2/CONTRIBUTING.md @@ -0,0 +1,93 @@ +# Contributing to beego + +beego is an open source project. + +It is the work of hundreds of contributors. We appreciate your help! + +Here are instructions to get you started. They are probably not perfect, please let us know if anything feels wrong or +incomplete. + +## Prepare environment + +Firstly, install some tools. Execute those commands **outside** the project. Or those command will modify go.mod file. + +```shell script +go get -u golang.org/x/tools/cmd/goimports + +go get -u github.com/gordonklaus/ineffassign +``` + +Put those lines into your pre-commit githook script: + +```shell script +goimports -w -format-only ./ + +ineffassign . + +staticcheck -show-ignored -checks "-ST1017,-U1000,-ST1005,-S1034,-S1012,-SA4006,-SA6005,-SA1019,-SA1024" ./ +``` + +## Prepare middleware + +Beego uses many middlewares, including MySQL, Redis, SSDB and so on. + +We provide docker compose file to start all middlewares. + +You can run: + +```shell script +docker-compose -f scripts/test_docker_compose.yaml up -d +``` + +Unit tests read addresses from environment, here is an example: + +```shell script +export ORM_DRIVER=mysql +export ORM_SOURCE="beego:test@tcp(192.168.0.105:13306)/orm_test?charset=utf8" +export MEMCACHE_ADDR="192.168.0.105:11211" +export REDIS_ADDR="192.168.0.105:6379" +export SSDB_ADDR="192.168.0.105:8888" +``` + +## Contribution guidelines + +### Pull requests + +First, beego follow the gitflow. So please send you pull request to **develop** branch. We will close the pull +request to master branch. + +By the way, please don't forget update the `CHANGELOG.md` before you send pull request. +You can just add your pull request following 'developing' section in `CHANGELOG.md`. +We'll release them in the next Beego version. + +We are always happy to receive pull requests, and do our best to review them as fast as possible. Not sure if that typo +is worth a pull request? Do it! We will appreciate it. + +Don't forget to rebase your commits! + +If your pull request is not accepted on the first try, don't be discouraged! Sometimes we can make a mistake, please do +more explaining for us. We will appreciate it. + +We're trying very hard to keep beego simple and fast. We don't want it to do everything for everybody. This means that +we might decide against incorporating a new feature. But we will give you some advice on how to do it in other way. + +### Create issues + +Any significant improvement should be documented as [a GitHub issue](https://github.com/beego/beego/v2/issues) before +anybody starts working on it. + +Also when filing an issue, make sure to answer these five questions: + +- What version of beego are you using (bee version)? +- What operating system and processor architecture are you using? +- What did you do? +- What did you expect to see? +- What did you see instead? + +### but check existing issues and docs first! + +Please take a moment to check that an issue doesn't already exist documenting your bug report or improvement proposal. +If it does, it never hurts to add a quick "+1" or "I have this problem too". This will help prioritize the most common +problems and requests. + +Also, if you don't know how to use it. please make sure you have read through the docs in http://beego.vip/docs diff --git a/src/vendor/github.com/beego/beego/v2/ERROR_SPECIFICATION.md b/src/vendor/github.com/beego/beego/v2/ERROR_SPECIFICATION.md new file mode 100644 index 000000000..68a04bd11 --- /dev/null +++ b/src/vendor/github.com/beego/beego/v2/ERROR_SPECIFICATION.md @@ -0,0 +1,5 @@ +# Error Module + +## Module code +- httplib 1 +- cache 2 \ No newline at end of file diff --git a/src/vendor/github.com/beego/beego/LICENSE b/src/vendor/github.com/beego/beego/v2/LICENSE similarity index 94% rename from src/vendor/github.com/beego/beego/LICENSE rename to src/vendor/github.com/beego/beego/v2/LICENSE index 5dbd42435..26050108e 100644 --- a/src/vendor/github.com/beego/beego/LICENSE +++ b/src/vendor/github.com/beego/beego/v2/LICENSE @@ -10,4 +10,4 @@ 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. \ No newline at end of file +limitations under the License. diff --git a/src/vendor/github.com/beego/beego/v2/Makefile b/src/vendor/github.com/beego/beego/v2/Makefile new file mode 100644 index 000000000..d2a2e169b --- /dev/null +++ b/src/vendor/github.com/beego/beego/v2/Makefile @@ -0,0 +1,64 @@ +# Copyright 2020 +# +# 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. + + +##@ General + +# The help target prints out all targets with their descriptions organized +# beneath their categories. The categories are represented by '##@' and the +# target descriptions by '##'. The awk commands is responsible for reading the +# entire set of makefiles included in this invocation, looking for lines of the +# file as xyz: ## something, and then pretty-format the target and help. Then, +# if there's a line with ##@ something, that gets pretty-printed as a category. +# More info on the usage of ANSI control characters for terminal formatting: +# https://en.wikipedia.org/wiki/ANSI_escape_code#SGR_parameters +# More info on the awk command: +# http://linuxcommand.org/lc3_adv_awk.php + +help: ## Display this help. + @awk 'BEGIN {FS = ":.*##"; printf "\nUsage:\n make \033[36m\033[0m\n"} /^[a-zA-Z_0-9-]+:.*?##/ { printf " \033[36m%-15s\033[0m %s\n", $$1, $$2 } /^##@/ { printf "\n\033[1m%s\033[0m\n", substr($$0, 5) } ' $(MAKEFILE_LIST) + + +##@ Test + +test-orm-mysql5: ## Run ORM unit tests on mysql5. + docker-compose -f scripts/orm_docker_compose.yaml up -d + export ORM_DRIVER=mysql + export ORM_SOURCE="beego:test@tcp(localhost:13306)/orm_test?charset=utf8" + go test -v github.com/beego/beego/v2/client/orm + docker-compose -f scripts/orm_docker_compose.yaml down + +test-orm-mysql8: ## Run ORM unit tests on mysql8. + docker-compose -f scripts/orm_docker_compose.yaml up -d + export ORM_DRIVER=mysql + export ORM_SOURCE="beego:test@tcp(localhost:23306)/orm_test?charset=utf8" + go test -v github.com/beego/beego/v2/client/orm + docker-compose -f scripts/orm_docker_compose.yaml down + +test-orm-pgsql: ## Run ORM unit tests on postgresql. + docker-compose -f scripts/orm_docker_compose.yaml up -d + export ORM_DRIVER=postgres + export ORM_SOURCE="user=postgres password=postgres dbname=orm_test sslmode=disable" + go test -v github.com/beego/beego/v2/client/orm + docker-compose -f scripts/orm_docker_compose.yaml down + +test-orm-tidb: ## Run ORM unit tests on tidb. + docker-compose -f scripts/orm_docker_compose.yaml up -d + export ORM_DRIVER=tidb + export ORM_SOURCE="memory://test/test" + go test -v github.com/beego/beego/v2/client/orm + docker-compose -f scripts/orm_docker_compose.yaml down + +.PHONY: test-orm-all +test-orm-all: test-orm-mysql5 test-orm-mysql8 test-orm-pgsql test-orm-tidb diff --git a/src/vendor/github.com/beego/beego/v2/README.md b/src/vendor/github.com/beego/beego/v2/README.md new file mode 100644 index 000000000..d361f66ed --- /dev/null +++ b/src/vendor/github.com/beego/beego/v2/README.md @@ -0,0 +1,99 @@ +# Beego [![Test](https://github.com/beego/beego/actions/workflows/test.yml/badge.svg?branch=develop)](https://github.com/beego/beego/actions/workflows/test.yml) [![Go Report Card](https://goreportcard.com/badge/github.com/beego/beego)](https://goreportcard.com/report/github.com/beego/beego) [![Go Reference](https://pkg.go.dev/badge/github.com/beego/beego/v2.svg)](https://pkg.go.dev/github.com/beego/beego/v2) + +Beego is used for rapid development of enterprise application in Go, including RESTful APIs, web apps and backend services. + +It is inspired by Tornado, Sinatra and Flask. beego has some Go-specific features such as interfaces and struct embedding. + +![architecture](https://cdn.nlark.com/yuque/0/2020/png/755700/1607857489109-1e267fce-d65f-4c5e-b915-5c475df33c58.png) + +Beego is composed of four parts: + +1. Base modules: including log module, config module, governor module; +2. Task: is used for running timed tasks or periodic tasks; +3. Client: including ORM module, httplib module, cache module; +4. Server: including web module. We will support gRPC in the future; + +**Please use RELEASE version, or master branch which contains the latest bug fix** + +## Quick Start + +[Official website](http://beego.vip) +[中文新版文档网站](https://beego.gocn.vip) + +[Example](https://github.com/beego/beego-example) + +> If you could not open official website, go to [beedoc](https://github.com/beego/beedoc) + +### Web Application + +![Http Request](https://cdn.nlark.com/yuque/0/2020/png/755700/1607857462507-855ec543-7ce3-402d-a0cb-b2524d5a4b60.png) + +#### Create `hello` directory, cd `hello` directory + + mkdir hello + cd hello + +#### Init module + + go mod init + +#### Download and install + + go get github.com/beego/beego/v2@latest + +#### Create file `hello.go` + +```go +package main + +import "github.com/beego/beego/v2/server/web" + +func main() { + web.Run() +} +``` + +#### Build and run + + go build hello.go + ./hello + +#### Go to [http://localhost:8080](http://localhost:8080) + +Congratulations! You've just built your first **beego** app. + +## Features + +* RESTful support +* [MVC architecture](https://github.com/beego/beedoc/tree/master/en-US/mvc) +* Modularity +* [Auto API documents](https://github.com/beego/beedoc/blob/master/en-US/advantage/docs.md) +* [Annotation router](https://github.com/beego/beedoc/blob/master/en-US/mvc/controller/router.md) +* [Namespace](https://github.com/beego/beedoc/blob/master/en-US/mvc/controller/router.md#namespace) +* [Powerful development tools](https://github.com/beego/bee) +* Full stack for Web & API + +## Modules + +* [orm](https://github.com/beego/beedoc/tree/master/en-US/mvc/model) +* [session](https://github.com/beego/beedoc/blob/master/en-US/module/session.md) +* [logs](https://github.com/beego/beedoc/blob/master/en-US/module/logs.md) +* [config](https://github.com/beego/beedoc/blob/master/en-US/module/config.md) +* [cache](https://github.com/beego/beedoc/blob/master/en-US/module/cache.md) +* [context](https://github.com/beego/beedoc/blob/master/en-US/module/context.md) +* [admin](https://github.com/beego/beedoc/blob/master/en-US/module/admin.md) +* [httplib](https://github.com/beego/beedoc/blob/master/en-US/module/httplib.md) +* [task](https://github.com/beego/beedoc/blob/master/en-US/module/task.md) +* [i18n](https://github.com/beego/beedoc/blob/master/en-US/module/i18n.md) + +## Community + +* [http://beego.vip/community](http://beego.vip/community) +* Welcome to join us in Slack: [https://beego.slack.com invite](https://join.slack.com/t/beego/shared_invite/zt-fqlfjaxs-_CRmiITCSbEqQG9NeBqXKA), +* QQ Group ID:523992905 +* [Contribution Guide](https://github.com/beego/beedoc/blob/master/en-US/intro/contributing.md). + +## License + +beego source code is licensed under the Apache Licence, Version 2.0 +([https://www.apache.org/licenses/LICENSE-2.0.html](https://www.apache.org/licenses/LICENSE-2.0.html)). diff --git a/src/vendor/github.com/beego/beego/build_info.go b/src/vendor/github.com/beego/beego/v2/build_info.go similarity index 90% rename from src/vendor/github.com/beego/beego/build_info.go rename to src/vendor/github.com/beego/beego/v2/build_info.go index c31152ea3..23f74b53a 100644 --- a/src/vendor/github.com/beego/beego/build_info.go +++ b/src/vendor/github.com/beego/beego/v2/build_info.go @@ -25,3 +25,8 @@ var ( GitBranch string ) + +const ( + // VERSION represent beego web framework version. + VERSION = "2.0.0" +) diff --git a/src/vendor/github.com/beego/beego/cache/README.md b/src/vendor/github.com/beego/beego/v2/client/cache/README.md similarity index 89% rename from src/vendor/github.com/beego/beego/cache/README.md rename to src/vendor/github.com/beego/beego/v2/client/cache/README.md index 1c037c87c..df1ea0957 100644 --- a/src/vendor/github.com/beego/beego/cache/README.md +++ b/src/vendor/github.com/beego/beego/v2/client/cache/README.md @@ -1,37 +1,34 @@ ## cache -cache is a Go cache manager. It can use many cache adapters. The repo is inspired by `database/sql` . +cache is a Go cache manager. It can use many cache adapters. The repo is inspired by `database/sql` . ## How to install? - go get github.com/beego/beego/cache - + go get github.com/beego/beego/v2/client/cache ## What adapters are supported? As of now this cache support memory, Memcache and Redis. - ## How to use it? First you must import it import ( - "github.com/beego/beego/cache" + "github.com/beego/beego/v2/client/cache" ) Then init a Cache (example with memory adapter) bm, err := cache.NewCache("memory", `{"interval":60}`) -Use it like this: - +Use it like this: + bm.Put("astaxie", 1, 10 * time.Second) bm.Get("astaxie") bm.IsExist("astaxie") bm.Delete("astaxie") - ## Memory adapter Configure memory adapter like this: @@ -40,7 +37,6 @@ Configure memory adapter like this: interval means the gc time. The cache will check at each time interval, whether item has expired. - ## Memcache adapter Memcache adapter use the [gomemcache](http://github.com/bradfitz/gomemcache) client. @@ -49,7 +45,6 @@ Configure like this: {"conn":"127.0.0.1:11211"} - ## Redis adapter Redis adapter use the [redigo](http://github.com/gomodule/redigo) client. diff --git a/src/vendor/github.com/beego/beego/cache/cache.go b/src/vendor/github.com/beego/beego/v2/client/cache/cache.go similarity index 60% rename from src/vendor/github.com/beego/beego/cache/cache.go rename to src/vendor/github.com/beego/beego/v2/client/cache/cache.go index 1a4926420..1eafccdc1 100644 --- a/src/vendor/github.com/beego/beego/cache/cache.go +++ b/src/vendor/github.com/beego/beego/v2/client/cache/cache.go @@ -16,7 +16,7 @@ // Usage: // // import( -// "github.com/beego/beego/cache" +// "github.com/beego/beego/v2/client/cache" // ) // // bm, err := cache.NewCache("memory", `{"interval":60}`) @@ -28,12 +28,14 @@ // bm.IsExist("astaxie") // bm.Delete("astaxie") // -// more docs http://beego.me/docs/module/cache.md +// more docs http://beego.vip/docs/module/cache.md package cache import ( - "fmt" + "context" "time" + + "github.com/beego/beego/v2/core/berror" ) // Cache interface contains all behaviors for cache adapter. @@ -47,23 +49,25 @@ import ( // c.Incr("counter") // now is 2 // count := c.Get("counter").(int) type Cache interface { - // get cached value by key. - Get(key string) interface{} + // Get a cached value by key. + Get(ctx context.Context, key string) (interface{}, error) // GetMulti is a batch version of Get. - GetMulti(keys []string) []interface{} - // set cached value with key and expire time. - Put(key string, val interface{}, timeout time.Duration) error - // delete cached value by key. - Delete(key string) error - // increase cached int value by key, as a counter. - Incr(key string) error - // decrease cached int value by key, as a counter. - Decr(key string) error - // check if cached value exists or not. - IsExist(key string) bool - // clear all cache. - ClearAll() error - // start gc routine based on config string settings. + GetMulti(ctx context.Context, keys []string) ([]interface{}, error) + // Put Set a cached value with key and expire time. + Put(ctx context.Context, key string, val interface{}, timeout time.Duration) error + // Delete cached value by key. + // Should not return error if key not found + Delete(ctx context.Context, key string) error + // Incr Increment a cached int value by key, as a counter. + Incr(ctx context.Context, key string) error + // Decr Decrement a cached int value by key, as a counter. + Decr(ctx context.Context, key string) error + // IsExist Check if a cached value exists or not. + // if key is expired, return (false, nil) + IsExist(ctx context.Context, key string) (bool, error) + // ClearAll Clear all cache. + ClearAll(ctx context.Context) error + // StartAndGC Start gc routine based on config string settings. StartAndGC(config string) error } @@ -77,7 +81,7 @@ var adapters = make(map[string]Instance) // it panics. func Register(name string, adapter Instance) { if adapter == nil { - panic("cache: Register adapter is nil") + panic(berror.Error(NilCacheAdapter, "cache: Register adapter is nil").Error()) } if _, ok := adapters[name]; ok { panic("cache: Register called twice for adapter " + name) @@ -85,13 +89,13 @@ func Register(name string, adapter Instance) { adapters[name] = adapter } -// NewCache Create a new cache driver by adapter name and config string. -// config need to be correct JSON as string: {"interval":360}. -// it will start gc automatically. +// NewCache creates a new cache driver by adapter name and config string. +// config: must be in JSON format such as {"interval":360}. +// Starts gc automatically. func NewCache(adapterName, config string) (adapter Cache, err error) { instanceFunc, ok := adapters[adapterName] if !ok { - err = fmt.Errorf("cache: unknown adapter name %q (forgot to import?)", adapterName) + err = berror.Errorf(UnknownAdapter, "cache: unknown adapter name %s (forgot to import?)", adapterName) return } adapter = instanceFunc() diff --git a/src/vendor/github.com/beego/beego/v2/client/cache/calc_utils.go b/src/vendor/github.com/beego/beego/v2/client/cache/calc_utils.go new file mode 100644 index 000000000..f8b7f24ac --- /dev/null +++ b/src/vendor/github.com/beego/beego/v2/client/cache/calc_utils.go @@ -0,0 +1,95 @@ +package cache + +import ( + "math" + + "github.com/beego/beego/v2/core/berror" +) + +var ( + ErrIncrementOverflow = berror.Error(IncrementOverflow, "this incr invocation will overflow.") + ErrDecrementOverflow = berror.Error(DecrementOverflow, "this decr invocation will overflow.") + ErrNotIntegerType = berror.Error(NotIntegerType, "item val is not (u)int (u)int32 (u)int64") +) + +const ( + MinUint32 uint32 = 0 + MinUint64 uint64 = 0 +) + +func incr(originVal interface{}) (interface{}, error) { + switch val := originVal.(type) { + case int: + tmp := val + 1 + if val > 0 && tmp < 0 { + return nil, ErrIncrementOverflow + } + return tmp, nil + case int32: + if val == math.MaxInt32 { + return nil, ErrIncrementOverflow + } + return val + 1, nil + case int64: + if val == math.MaxInt64 { + return nil, ErrIncrementOverflow + } + return val + 1, nil + case uint: + tmp := val + 1 + if tmp < val { + return nil, ErrIncrementOverflow + } + return tmp, nil + case uint32: + if val == math.MaxUint32 { + return nil, ErrIncrementOverflow + } + return val + 1, nil + case uint64: + if val == math.MaxUint64 { + return nil, ErrIncrementOverflow + } + return val + 1, nil + default: + return nil, ErrNotIntegerType + } +} + +func decr(originVal interface{}) (interface{}, error) { + switch val := originVal.(type) { + case int: + tmp := val - 1 + if val < 0 && tmp > 0 { + return nil, ErrDecrementOverflow + } + return tmp, nil + case int32: + if val == math.MinInt32 { + return nil, ErrDecrementOverflow + } + return val - 1, nil + case int64: + if val == math.MinInt64 { + return nil, ErrDecrementOverflow + } + return val - 1, nil + case uint: + if val == 0 { + return nil, ErrDecrementOverflow + } + return val - 1, nil + case uint32: + if val == MinUint32 { + return nil, ErrDecrementOverflow + } + return val - 1, nil + case uint64: + if val == MinUint64 { + return nil, ErrDecrementOverflow + } + return val - 1, nil + default: + return nil, ErrNotIntegerType + } +} diff --git a/src/vendor/github.com/beego/beego/cache/conv.go b/src/vendor/github.com/beego/beego/v2/client/cache/conv.go similarity index 90% rename from src/vendor/github.com/beego/beego/cache/conv.go rename to src/vendor/github.com/beego/beego/v2/client/cache/conv.go index 878005864..158f7f413 100644 --- a/src/vendor/github.com/beego/beego/cache/conv.go +++ b/src/vendor/github.com/beego/beego/v2/client/cache/conv.go @@ -19,7 +19,7 @@ import ( "strconv" ) -// GetString convert interface to string. +// GetString converts interface to string. func GetString(v interface{}) string { switch result := v.(type) { case string: @@ -34,7 +34,7 @@ func GetString(v interface{}) string { return "" } -// GetInt convert interface to int. +// GetInt converts interface to int. func GetInt(v interface{}) int { switch result := v.(type) { case int: @@ -52,7 +52,7 @@ func GetInt(v interface{}) int { return 0 } -// GetInt64 convert interface to int64. +// GetInt64 converts interface to int64. func GetInt64(v interface{}) int64 { switch result := v.(type) { case int: @@ -71,7 +71,7 @@ func GetInt64(v interface{}) int64 { return 0 } -// GetFloat64 convert interface to float64. +// GetFloat64 converts interface to float64. func GetFloat64(v interface{}) float64 { switch result := v.(type) { case float64: @@ -85,7 +85,7 @@ func GetFloat64(v interface{}) float64 { return 0 } -// GetBool convert interface to bool. +// GetBool converts interface to bool. func GetBool(v interface{}) bool { switch result := v.(type) { case bool: diff --git a/src/vendor/github.com/beego/beego/v2/client/cache/error_code.go b/src/vendor/github.com/beego/beego/v2/client/cache/error_code.go new file mode 100644 index 000000000..5611f065f --- /dev/null +++ b/src/vendor/github.com/beego/beego/v2/client/cache/error_code.go @@ -0,0 +1,176 @@ +// Copyright 2021 beego +// +// 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 cache + +import ( + "github.com/beego/beego/v2/core/berror" +) + +var NilCacheAdapter = berror.DefineCode(4002001, moduleName, "NilCacheAdapter", ` +It means that you register cache adapter by pass nil. +A cache adapter is an instance of Cache interface. +`) + +var DuplicateAdapter = berror.DefineCode(4002002, moduleName, "DuplicateAdapter", ` +You register two adapter with same name. In beego cache module, one name one adapter. +Once you got this error, please check the error stack, search adapter +`) + +var UnknownAdapter = berror.DefineCode(4002003, moduleName, "UnknownAdapter", ` +Unknown adapter, do you forget to register the adapter? +You must register adapter before use it. For example, if you want to use redis implementation, +you must import the cache/redis package. +`) + +var IncrementOverflow = berror.DefineCode(4002004, moduleName, "IncrementOverflow", ` +The increment operation will overflow. +`) + +var DecrementOverflow = berror.DefineCode(4002005, moduleName, "DecrementOverflow", ` +The decrement operation will overflow. +`) + +var NotIntegerType = berror.DefineCode(4002006, moduleName, "NotIntegerType", ` +The type of value is not (u)int (u)int32 (u)int64. +When you want to call Incr or Decr function of Cache API, you must confirm that the value's type is one of (u)int (u)int32 (u)int64. +`) + +var InvalidFileCacheDirectoryLevelCfg = berror.DefineCode(4002007, moduleName, "InvalidFileCacheDirectoryLevelCfg", ` +You pass invalid DirectoryLevel parameter when you try to StartAndGC file cache instance. +This parameter must be a integer, and please check your input. +`) + +var InvalidFileCacheEmbedExpiryCfg = berror.DefineCode(4002008, moduleName, "InvalidFileCacheEmbedExpiryCfg", ` +You pass invalid EmbedExpiry parameter when you try to StartAndGC file cache instance. +This parameter must be a integer, and please check your input. +`) + +var CreateFileCacheDirFailed = berror.DefineCode(4002009, moduleName, "CreateFileCacheDirFailed", ` +Beego failed to create file cache directory. There are two cases: +1. You pass invalid CachePath parameter. Please check your input. +2. Beego doesn't have the permission to create this directory. Please check your file mode. +`) + +var InvalidFileCachePath = berror.DefineCode(4002010, moduleName, "InvalidFilePath", ` +The file path of FileCache is invalid. Please correct the config. +`) + +var ReadFileCacheContentFailed = berror.DefineCode(4002011, moduleName, "ReadFileCacheContentFailed", ` +Usually you won't got this error. It means that Beego cannot read the data from the file. +You need to check whether the file exist. Sometimes it may be deleted by other processes. +If the file exists, please check the permission that Beego is able to read data from the file. +`) + +var InvalidGobEncodedData = berror.DefineCode(4002012, moduleName, "InvalidEncodedData", ` +The data is invalid. When you try to decode the invalid data, you got this error. +Please confirm that the data is encoded by GOB correctly. +`) + +var GobEncodeDataFailed = berror.DefineCode(4002013, moduleName, "GobEncodeDataFailed", ` +Beego could not encode the data to GOB byte array. In general, the data type is invalid. +For example, GOB doesn't support function type. +Basic types, string, structure, structure pointer are supported. +`) + +var KeyExpired = berror.DefineCode(4002014, moduleName, "KeyExpired", ` +Cache key is expired. +You should notice that, a key is expired and then it may be deleted by GC goroutine. +So when you query a key which may be expired, you may got this code, or KeyNotExist. +`) + +var KeyNotExist = berror.DefineCode(4002015, moduleName, "KeyNotExist", ` +Key not found. +`) + +var MultiGetFailed = berror.DefineCode(4002016, moduleName, "MultiGetFailed", ` +Get multiple keys failed. Please check the detail msg to find out the root cause. +`) + +var InvalidMemoryCacheCfg = berror.DefineCode(4002017, moduleName, "InvalidMemoryCacheCfg", ` +The config is invalid. Please check your input. It must be a json string. +`) + +var InvalidMemCacheCfg = berror.DefineCode(4002018, moduleName, "InvalidMemCacheCfg", ` +The config is invalid. Please check your input, it must be json string and contains "conn" field. +`) + +var InvalidMemCacheValue = berror.DefineCode(4002019, moduleName, "InvalidMemCacheValue", ` +The value must be string or byte[], please check your input. +`) + +var InvalidRedisCacheCfg = berror.DefineCode(4002020, moduleName, "InvalidRedisCacheCfg", ` +The config must be json string, and has "conn" field. +`) + +var InvalidSsdbCacheCfg = berror.DefineCode(4002021, moduleName, "InvalidSsdbCacheCfg", ` +The config must be json string, and has "conn" field. The value of "conn" field should be "host:port". +"port" must be a valid integer. +`) + +var InvalidSsdbCacheValue = berror.DefineCode(4002022, moduleName, "InvalidSsdbCacheValue", ` +SSDB cache only accept string value. Please check your input. +`) + +var DeleteFileCacheItemFailed = berror.DefineCode(5002001, moduleName, "DeleteFileCacheItemFailed", ` +Beego try to delete file cache item failed. +Please check whether Beego generated file correctly. +And then confirm whether this file is already deleted by other processes or other people. +`) + +var MemCacheCurdFailed = berror.DefineCode(5002002, moduleName, "MemCacheError", ` +When you want to get, put, delete key-value from remote memcache servers, you may get error: +1. You pass invalid servers address, so Beego could not connect to remote server; +2. The servers address is correct, but there is some net issue. Typically there is some firewalls between application and memcache server; +3. Key is invalid. The key's length should be less than 250 and must not contains special characters; +4. The response from memcache server is invalid; +`) + +var RedisCacheCurdFailed = berror.DefineCode(5002003, moduleName, "RedisCacheCurdFailed", ` +When Beego uses client to send request to redis server, it failed. +1. The server addresses is invalid; +2. Network issue, firewall issue or network is unstable; +3. Client failed to manage connection. In extreme cases, Beego's redis client didn't maintain connections correctly, for example, Beego try to send request via closed connection; +4. The request are huge and redis server spent too much time to process it, and client is timeout; + +In general, if you always got this error whatever you do, in most cases, it was caused by network issue. +You could check your network state, and confirm that firewall rules are correct. +`) + +var InvalidConnection = berror.DefineCode(5002004, moduleName, "InvalidConnection", ` +The connection is invalid. Please check your connection info, network, firewall. +You could simply uses ping, telnet or write some simple tests to test network. +`) + +var DialFailed = berror.DefineCode(5002005, moduleName, "DialFailed", ` +When Beego try to dial to remote servers, it failed. Please check your connection info and network state, server state. +`) + +var SsdbCacheCurdFailed = berror.DefineCode(5002006, moduleName, "SsdbCacheCurdFailed", ` +When you try to use SSDB cache, it failed. There are many cases: +1. servers unavailable; +2. network issue, including network unstable, firewall; +3. connection issue; +4. request are huge and servers spent too much time to process it, got timeout; +`) + +var SsdbBadResponse = berror.DefineCode(5002007, moduleName, "SsdbBadResponse", ` +The reponse from SSDB server is invalid. +Usually it indicates something wrong on server side. +`) + +var ( + ErrKeyExpired = berror.Error(KeyExpired, "the key is expired") + ErrKeyNotExist = berror.Error(KeyNotExist, "the key isn't exist") +) diff --git a/src/vendor/github.com/beego/beego/v2/client/cache/file.go b/src/vendor/github.com/beego/beego/v2/client/cache/file.go new file mode 100644 index 000000000..ae2bc7cf6 --- /dev/null +++ b/src/vendor/github.com/beego/beego/v2/client/cache/file.go @@ -0,0 +1,338 @@ +// Copyright 2014 beego Author. All Rights Reserved. +// +// 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 cache + +import ( + "bytes" + "context" + "crypto/md5" + "encoding/gob" + "encoding/hex" + "encoding/json" + "fmt" + "io" + "io/ioutil" + "os" + "path/filepath" + "strconv" + "strings" + "time" + + "github.com/beego/beego/v2/core/berror" +) + +// FileCacheItem is basic unit of file cache adapter which +// contains data and expire time. +type FileCacheItem struct { + Data interface{} + Lastaccess time.Time + Expired time.Time +} + +// FileCache Config +var ( + FileCachePath = "cache" // cache directory + FileCacheFileSuffix = ".bin" // cache file suffix + FileCacheDirectoryLevel = 2 // cache file deep level if auto generated cache files. + FileCacheEmbedExpiry time.Duration // cache expire time, default is no expire forever. +) + +// FileCache is cache adapter for file storage. +type FileCache struct { + CachePath string + FileSuffix string + DirectoryLevel int + EmbedExpiry int +} + +// NewFileCache creates a new file cache with no config. +// The level and expiry need to be set in the method StartAndGC as config string. +func NewFileCache() Cache { + // return &FileCache{CachePath:FileCachePath, FileSuffix:FileCacheFileSuffix} + return &FileCache{} +} + +// StartAndGC starts gc for file cache. +// config must be in the format {CachePath:"/cache","FileSuffix":".bin","DirectoryLevel":"2","EmbedExpiry":"0"} +func (fc *FileCache) StartAndGC(config string) error { + cfg := make(map[string]string) + err := json.Unmarshal([]byte(config), &cfg) + if err != nil { + return err + } + + const cpKey = "CachePath" + const fsKey = "FileSuffix" + const dlKey = "DirectoryLevel" + const eeKey = "EmbedExpiry" + + if _, ok := cfg[cpKey]; !ok { + cfg[cpKey] = FileCachePath + } + + if _, ok := cfg[fsKey]; !ok { + cfg[fsKey] = FileCacheFileSuffix + } + + if _, ok := cfg[dlKey]; !ok { + cfg[dlKey] = strconv.Itoa(FileCacheDirectoryLevel) + } + + if _, ok := cfg[eeKey]; !ok { + cfg[eeKey] = strconv.FormatInt(int64(FileCacheEmbedExpiry.Seconds()), 10) + } + fc.CachePath = cfg[cpKey] + fc.FileSuffix = cfg[fsKey] + fc.DirectoryLevel, err = strconv.Atoi(cfg[dlKey]) + if err != nil { + return berror.Wrapf(err, InvalidFileCacheDirectoryLevelCfg, + "invalid directory level config, please check your input, it must be integer: %s", cfg[dlKey]) + } + fc.EmbedExpiry, err = strconv.Atoi(cfg[eeKey]) + if err != nil { + return berror.Wrapf(err, InvalidFileCacheEmbedExpiryCfg, + "invalid embed expiry config, please check your input, it must be integer: %s", cfg[eeKey]) + } + return fc.Init() +} + +// Init makes new a dir for file cache if it does not already exist +func (fc *FileCache) Init() error { + ok, err := exists(fc.CachePath) + if err != nil || ok { + return err + } + err = os.MkdirAll(fc.CachePath, os.ModePerm) + if err != nil { + return berror.Wrapf(err, CreateFileCacheDirFailed, + "could not create directory, please check the config [%s] and file mode.", fc.CachePath) + } + return nil +} + +// getCachedFilename returns an md5 encoded file name. +func (fc *FileCache) getCacheFileName(key string) (string, error) { + m := md5.New() + _, _ = io.WriteString(m, key) + keyMd5 := hex.EncodeToString(m.Sum(nil)) + cachePath := fc.CachePath + switch fc.DirectoryLevel { + case 2: + cachePath = filepath.Join(cachePath, keyMd5[0:2], keyMd5[2:4]) + case 1: + cachePath = filepath.Join(cachePath, keyMd5[0:2]) + } + ok, err := exists(cachePath) + if err != nil { + return "", err + } + if !ok { + err = os.MkdirAll(cachePath, os.ModePerm) + if err != nil { + return "", berror.Wrapf(err, CreateFileCacheDirFailed, + "could not create the directory: %s", cachePath) + } + } + + return filepath.Join(cachePath, fmt.Sprintf("%s%s", keyMd5, fc.FileSuffix)), nil +} + +// Get value from file cache. +// if nonexistent or expired return an empty string. +func (fc *FileCache) Get(ctx context.Context, key string) (interface{}, error) { + fn, err := fc.getCacheFileName(key) + if err != nil { + return nil, err + } + fileData, err := FileGetContents(fn) + if err != nil { + return nil, err + } + + var to FileCacheItem + err = GobDecode(fileData, &to) + if err != nil { + return nil, err + } + + if to.Expired.Before(time.Now()) { + return nil, ErrKeyExpired + } + return to.Data, nil +} + +// GetMulti gets values from file cache. +// if nonexistent or expired return an empty string. +func (fc *FileCache) GetMulti(ctx context.Context, keys []string) ([]interface{}, error) { + rc := make([]interface{}, len(keys)) + keysErr := make([]string, 0) + + for i, ki := range keys { + val, err := fc.Get(context.Background(), ki) + if err != nil { + keysErr = append(keysErr, fmt.Sprintf("key [%s] error: %s", ki, err.Error())) + continue + } + rc[i] = val + } + + if len(keysErr) == 0 { + return rc, nil + } + return rc, berror.Error(MultiGetFailed, strings.Join(keysErr, "; ")) +} + +// Put value into file cache. +// timeout: how long this file should be kept in ms +// if timeout equals fc.EmbedExpiry(default is 0), cache this item forever. +func (fc *FileCache) Put(ctx context.Context, key string, val interface{}, timeout time.Duration) error { + gob.Register(val) + + item := FileCacheItem{Data: val} + if timeout == time.Duration(fc.EmbedExpiry) { + item.Expired = time.Now().Add((86400 * 365 * 10) * time.Second) // ten years + } else { + item.Expired = time.Now().Add(timeout) + } + item.Lastaccess = time.Now() + data, err := GobEncode(item) + if err != nil { + return err + } + + fn, err := fc.getCacheFileName(key) + if err != nil { + return err + } + return FilePutContents(fn, data) +} + +// Delete file cache value. +func (fc *FileCache) Delete(ctx context.Context, key string) error { + filename, err := fc.getCacheFileName(key) + if err != nil { + return err + } + if ok, _ := exists(filename); ok { + err = os.Remove(filename) + if err != nil { + return berror.Wrapf(err, DeleteFileCacheItemFailed, + "can not delete this file cache key-value, key is %s and file name is %s", key, filename) + } + } + return nil +} + +// Incr increases cached int value. +// fc value is saved forever unless deleted. +func (fc *FileCache) Incr(ctx context.Context, key string) error { + data, err := fc.Get(context.Background(), key) + if err != nil { + return err + } + + val, err := incr(data) + if err != nil { + return err + } + + return fc.Put(context.Background(), key, val, time.Duration(fc.EmbedExpiry)) +} + +// Decr decreases cached int value. +func (fc *FileCache) Decr(ctx context.Context, key string) error { + data, err := fc.Get(context.Background(), key) + if err != nil { + return err + } + + val, err := decr(data) + if err != nil { + return err + } + + return fc.Put(context.Background(), key, val, time.Duration(fc.EmbedExpiry)) +} + +// IsExist checks if value exists. +func (fc *FileCache) IsExist(ctx context.Context, key string) (bool, error) { + fn, err := fc.getCacheFileName(key) + if err != nil { + return false, err + } + return exists(fn) +} + +// ClearAll cleans cached files (not implemented) +func (fc *FileCache) ClearAll(context.Context) error { + return nil +} + +// Check if a file exists +func exists(path string) (bool, error) { + _, err := os.Stat(path) + if err == nil { + return true, nil + } + if os.IsNotExist(err) { + return false, nil + } + return false, berror.Wrapf(err, InvalidFileCachePath, "file cache path is invalid: %s", path) +} + +// FileGetContents Reads bytes from a file. +// if non-existent, create this file. +func FileGetContents(filename string) ([]byte, error) { + data, err := ioutil.ReadFile(filename) + if err != nil { + return nil, berror.Wrapf(err, ReadFileCacheContentFailed, + "could not read the data from the file: %s, "+ + "please confirm that file exist and Beego has the permission to read the content.", filename) + } + return data, nil +} + +// FilePutContents puts bytes into a file. +// if non-existent, create this file. +func FilePutContents(filename string, content []byte) error { + return ioutil.WriteFile(filename, content, os.ModePerm) +} + +// GobEncode Gob encodes a file cache item. +func GobEncode(data interface{}) ([]byte, error) { + buf := bytes.NewBuffer(nil) + enc := gob.NewEncoder(buf) + err := enc.Encode(data) + if err != nil { + return nil, berror.Wrap(err, GobEncodeDataFailed, "could not encode this data") + } + return buf.Bytes(), nil +} + +// GobDecode Gob decodes a file cache item. +func GobDecode(data []byte, to *FileCacheItem) error { + buf := bytes.NewBuffer(data) + dec := gob.NewDecoder(buf) + err := dec.Decode(&to) + if err != nil { + return berror.Wrap(err, InvalidGobEncodedData, + "could not decode this data to FileCacheItem. Make sure that the data is encoded by GOB.") + } + return nil +} + +func init() { + Register("file", NewFileCache) +} diff --git a/src/vendor/github.com/beego/beego/v2/client/cache/memory.go b/src/vendor/github.com/beego/beego/v2/client/cache/memory.go new file mode 100644 index 000000000..c1d1a2e50 --- /dev/null +++ b/src/vendor/github.com/beego/beego/v2/client/cache/memory.go @@ -0,0 +1,235 @@ +// Copyright 2014 beego Author. All Rights Reserved. +// +// 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 cache + +import ( + "context" + "encoding/json" + "fmt" + "strings" + "sync" + "time" + + "github.com/beego/beego/v2/core/berror" +) + +// DefaultEvery sets a timer for how often to recycle the expired cache items in memory (in seconds) +var DefaultEvery = 60 // 1 minute + +// MemoryItem stores memory cache item. +type MemoryItem struct { + val interface{} + createdTime time.Time + lifespan time.Duration +} + +func (mi *MemoryItem) isExpire() bool { + // 0 means forever + if mi.lifespan == 0 { + return false + } + return time.Since(mi.createdTime) > mi.lifespan +} + +// MemoryCache is a memory cache adapter. +// Contains a RW locker for safe map storage. +type MemoryCache struct { + sync.RWMutex + dur time.Duration + items map[string]*MemoryItem + Every int // run an expiration check Every clock time +} + +// NewMemoryCache returns a new MemoryCache. +func NewMemoryCache() Cache { + cache := MemoryCache{items: make(map[string]*MemoryItem)} + return &cache +} + +// Get returns cache from memory. +// If non-existent or expired, return nil. +func (bc *MemoryCache) Get(ctx context.Context, key string) (interface{}, error) { + bc.RLock() + defer bc.RUnlock() + if itm, ok := + bc.items[key]; ok { + if itm.isExpire() { + return nil, ErrKeyExpired + } + return itm.val, nil + } + return nil, ErrKeyNotExist +} + +// GetMulti gets caches from memory. +// If non-existent or expired, return nil. +func (bc *MemoryCache) GetMulti(ctx context.Context, keys []string) ([]interface{}, error) { + rc := make([]interface{}, len(keys)) + keysErr := make([]string, 0) + + for i, ki := range keys { + val, err := bc.Get(context.Background(), ki) + if err != nil { + keysErr = append(keysErr, fmt.Sprintf("key [%s] error: %s", ki, err.Error())) + continue + } + rc[i] = val + } + + if len(keysErr) == 0 { + return rc, nil + } + return rc, berror.Error(MultiGetFailed, strings.Join(keysErr, "; ")) +} + +// Put puts cache into memory. +// If lifespan is 0, it will never overwrite this value unless restarted +func (bc *MemoryCache) Put(ctx context.Context, key string, val interface{}, timeout time.Duration) error { + bc.Lock() + defer bc.Unlock() + bc.items[key] = &MemoryItem{ + val: val, + createdTime: time.Now(), + lifespan: timeout, + } + return nil +} + +// Delete cache in memory. +// If the key is not found, it will not return error +func (bc *MemoryCache) Delete(ctx context.Context, key string) error { + bc.Lock() + defer bc.Unlock() + delete(bc.items, key) + return nil +} + +// Incr increases cache counter in memory. +// Supports int,int32,int64,uint,uint32,uint64. +func (bc *MemoryCache) Incr(ctx context.Context, key string) error { + bc.Lock() + defer bc.Unlock() + itm, ok := bc.items[key] + if !ok { + return ErrKeyNotExist + } + + val, err := incr(itm.val) + if err != nil { + return err + } + itm.val = val + return nil +} + +// Decr decreases counter in memory. +func (bc *MemoryCache) Decr(ctx context.Context, key string) error { + bc.Lock() + defer bc.Unlock() + itm, ok := bc.items[key] + if !ok { + return ErrKeyNotExist + } + + val, err := decr(itm.val) + if err != nil { + return err + } + itm.val = val + return nil +} + +// IsExist checks if cache exists in memory. +func (bc *MemoryCache) IsExist(ctx context.Context, key string) (bool, error) { + bc.RLock() + defer bc.RUnlock() + if v, ok := bc.items[key]; ok { + return !v.isExpire(), nil + } + return false, nil +} + +// ClearAll deletes all cache in memory. +func (bc *MemoryCache) ClearAll(context.Context) error { + bc.Lock() + defer bc.Unlock() + bc.items = make(map[string]*MemoryItem) + return nil +} + +// StartAndGC starts memory cache. Checks expiration in every clock time. +func (bc *MemoryCache) StartAndGC(config string) error { + var cf map[string]int + if err := json.Unmarshal([]byte(config), &cf); err != nil { + return berror.Wrapf(err, InvalidMemoryCacheCfg, "invalid config, please check your input: %s", config) + } + if _, ok := cf["interval"]; !ok { + cf = make(map[string]int) + cf["interval"] = DefaultEvery + } + dur := time.Duration(cf["interval"]) * time.Second + bc.Every = cf["interval"] + bc.dur = dur + go bc.vacuum() + return nil +} + +// check expiration. +func (bc *MemoryCache) vacuum() { + bc.RLock() + every := bc.Every + bc.RUnlock() + + if every < 1 { + return + } + for { + <-time.After(bc.dur) + bc.RLock() + if bc.items == nil { + bc.RUnlock() + return + } + bc.RUnlock() + if keys := bc.expiredKeys(); len(keys) != 0 { + bc.clearItems(keys) + } + } +} + +// expiredKeys returns keys list which are expired. +func (bc *MemoryCache) expiredKeys() (keys []string) { + bc.RLock() + defer bc.RUnlock() + for key, itm := range bc.items { + if itm.isExpire() { + keys = append(keys, key) + } + } + return +} + +// ClearItems removes all items who's key is in keys +func (bc *MemoryCache) clearItems(keys []string) { + bc.Lock() + defer bc.Unlock() + for _, key := range keys { + delete(bc.items, key) + } +} + +func init() { + Register("memory", NewMemoryCache) +} diff --git a/src/vendor/github.com/beego/beego/v2/client/cache/module.go b/src/vendor/github.com/beego/beego/v2/client/cache/module.go new file mode 100644 index 000000000..5a4e499ea --- /dev/null +++ b/src/vendor/github.com/beego/beego/v2/client/cache/module.go @@ -0,0 +1,17 @@ +// Copyright 2021 beego +// +// 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 cache + +const moduleName = "cache" diff --git a/src/vendor/github.com/beego/beego/v2/client/cache/random_expired_cache.go b/src/vendor/github.com/beego/beego/v2/client/cache/random_expired_cache.go new file mode 100644 index 000000000..262001124 --- /dev/null +++ b/src/vendor/github.com/beego/beego/v2/client/cache/random_expired_cache.go @@ -0,0 +1,75 @@ +// Copyright 2014 beego Author. All Rights Reserved. +// +// 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 cache + +import ( + "context" + "math/rand" + "sync/atomic" + "time" +) + +// RandomExpireCacheOption implement genreate random time offset expired option +type RandomExpireCacheOption func(*RandomExpireCache) + +// WithOffsetFunc returns a RandomExpireCacheOption that configures the offset function +func WithOffsetFunc(fn func() time.Duration) RandomExpireCacheOption { + return func(cache *RandomExpireCache) { + cache.offset = fn + } +} + +// RandomExpireCache prevent cache batch invalidation +// Cache random time offset expired +type RandomExpireCache struct { + Cache + offset func() time.Duration +} + +// Put random time offset expired +func (rec *RandomExpireCache) Put(ctx context.Context, key string, val interface{}, timeout time.Duration) error { + timeout += rec.offset() + return rec.Cache.Put(ctx, key, val, timeout) +} + +// NewRandomExpireCache return random expire cache struct +func NewRandomExpireCache(adapter Cache, opts ...RandomExpireCacheOption) Cache { + rec := RandomExpireCache{ + Cache: adapter, + offset: defaultExpiredFunc(), + } + for _, fn := range opts { + fn(&rec) + } + return &rec +} + +// defaultExpiredFunc return a func that used to generate random time offset (range: [3s,8s)) expired +func defaultExpiredFunc() func() time.Duration { + const size = 5 + var randTimes [size]time.Duration + for i := range randTimes { + randTimes[i] = time.Duration(i+3) * time.Second + } + // shuffle values + for i := range randTimes { + n := rand.Intn(size) + randTimes[i], randTimes[n] = randTimes[n], randTimes[i] + } + var i uint64 + return func() time.Duration { + return randTimes[atomic.AddUint64(&i, 1)%size] + } +} diff --git a/src/vendor/github.com/beego/beego/cache/redis/redis.go b/src/vendor/github.com/beego/beego/v2/client/cache/redis/redis.go similarity index 58% rename from src/vendor/github.com/beego/beego/cache/redis/redis.go rename to src/vendor/github.com/beego/beego/v2/client/cache/redis/redis.go index 06115aefd..9462bcd8e 100644 --- a/src/vendor/github.com/beego/beego/cache/redis/redis.go +++ b/src/vendor/github.com/beego/beego/v2/client/cache/redis/redis.go @@ -20,33 +20,31 @@ // // Usage: // import( -// _ "github.com/beego/beego/cache/redis" -// "github.com/beego/beego/cache" +// _ "github.com/beego/beego/v2/client/cache/redis" +// "github.com/beego/beego/v2/client/cache" // ) // // bm, err := cache.NewCache("redis", `{"conn":"127.0.0.1:11211"}`) // -// more docs http://beego.me/docs/module/cache.md +// more docs http://beego.vip/docs/module/cache.md package redis import ( + "context" "encoding/json" - "errors" "fmt" "strconv" + "strings" "time" "github.com/gomodule/redigo/redis" - "strings" - - "github.com/beego/beego/cache" + "github.com/beego/beego/v2/client/cache" + "github.com/beego/beego/v2/core/berror" ) -var ( - // DefaultKey the collection name of redis for cache adapter. - DefaultKey = "beecacheRedis" -) +// DefaultKey defines the collection name of redis for the cache adapter. +var DefaultKey = "beecacheRedis" // Cache is Redis cache adapter. type Cache struct { @@ -57,25 +55,30 @@ type Cache struct { password string maxIdle int - //the timeout to a value less than the redis server's timeout. + // Timeout value (less than the redis server's timeout value) timeout time.Duration } -// NewRedisCache create new redis cache with default collection name. +// NewRedisCache creates a new redis cache with default collection name. func NewRedisCache() cache.Cache { return &Cache{key: DefaultKey} } -// actually do the redis cmds, args[0] must be the key name. -func (rc *Cache) do(commandName string, args ...interface{}) (reply interface{}, err error) { - if len(args) < 1 { - return nil, errors.New("missing required arguments") - } +// Execute the redis commands. args[0] must be the key name +func (rc *Cache) do(commandName string, args ...interface{}) (interface{}, error) { args[0] = rc.associate(args[0]) c := rc.p.Get() - defer c.Close() + defer func() { + _ = c.Close() + }() - return c.Do(commandName, args...) + reply, err := c.Do(commandName, args...) + if err != nil { + return nil, berror.Wrapf(err, cache.RedisCacheCurdFailed, + "could not execute this command: %s", commandName) + } + + return reply, nil } // associate with config key. @@ -84,69 +87,71 @@ func (rc *Cache) associate(originKey interface{}) string { } // Get cache from redis. -func (rc *Cache) Get(key string) interface{} { +func (rc *Cache) Get(ctx context.Context, key string) (interface{}, error) { if v, err := rc.do("GET", key); err == nil { - return v + return v, nil + } else { + return nil, err } - return nil } -// GetMulti get cache from redis. -func (rc *Cache) GetMulti(keys []string) []interface{} { +// GetMulti gets cache from redis. +func (rc *Cache) GetMulti(ctx context.Context, keys []string) ([]interface{}, error) { c := rc.p.Get() - defer c.Close() + defer func() { + _ = c.Close() + }() var args []interface{} for _, key := range keys { args = append(args, rc.associate(key)) } - values, err := redis.Values(c.Do("MGET", args...)) - if err != nil { - return nil - } - return values + return redis.Values(c.Do("MGET", args...)) } -// Put put cache to redis. -func (rc *Cache) Put(key string, val interface{}, timeout time.Duration) error { +// Put puts cache into redis. +func (rc *Cache) Put(ctx context.Context, key string, val interface{}, timeout time.Duration) error { _, err := rc.do("SETEX", key, int64(timeout/time.Second), val) return err } -// Delete delete cache in redis. -func (rc *Cache) Delete(key string) error { +// Delete deletes a key's cache in redis. +func (rc *Cache) Delete(ctx context.Context, key string) error { _, err := rc.do("DEL", key) return err } -// IsExist check cache's existence in redis. -func (rc *Cache) IsExist(key string) bool { +// IsExist checks cache's existence in redis. +func (rc *Cache) IsExist(ctx context.Context, key string) (bool, error) { v, err := redis.Bool(rc.do("EXISTS", key)) if err != nil { - return false + return false, err } - return v + return v, nil } -// Incr increase counter in redis. -func (rc *Cache) Incr(key string) error { +// Incr increases a key's counter in redis. +func (rc *Cache) Incr(ctx context.Context, key string) error { _, err := redis.Bool(rc.do("INCRBY", key, 1)) return err } -// Decr decrease counter in redis. -func (rc *Cache) Decr(key string) error { +// Decr decreases a key's counter in redis. +func (rc *Cache) Decr(ctx context.Context, key string) error { _, err := redis.Bool(rc.do("INCRBY", key, -1)) return err } -// ClearAll clean all cache in redis. delete this redis collection. -func (rc *Cache) ClearAll() error { +// ClearAll deletes all cache in the redis collection +// Be careful about this method, because it scans all keys and the delete them one by one +func (rc *Cache) ClearAll(context.Context) error { cachedKeys, err := rc.Scan(rc.key + ":*") if err != nil { return err } c := rc.p.Get() - defer c.Close() + defer func() { + _ = c.Close() + }() for _, str := range cachedKeys { if _, err = c.Do("DEL", str); err != nil { return err @@ -155,10 +160,12 @@ func (rc *Cache) ClearAll() error { return err } -// Scan scan all keys matching the pattern. a better choice than `keys` +// Scan scans all keys matching a given pattern. func (rc *Cache) Scan(pattern string) (keys []string, err error) { c := rc.p.Get() - defer c.Close() + defer func() { + _ = c.Close() + }() var ( cursor uint64 = 0 // start result []interface{} @@ -184,19 +191,21 @@ func (rc *Cache) Scan(pattern string) (keys []string, err error) { } } -// StartAndGC start redis cache adapter. -// config is like {"key":"collection key","conn":"connection info","dbNum":"0"} -// the cache item in redis are stored forever, -// so no gc operation. +// StartAndGC starts the redis cache adapter. +// config: must be in this format {"key":"collection key","conn":"connection info","dbNum":"0"} +// Cached items in redis are stored forever, no garbage collection happens func (rc *Cache) StartAndGC(config string) error { var cf map[string]string - json.Unmarshal([]byte(config), &cf) + err := json.Unmarshal([]byte(config), &cf) + if err != nil { + return berror.Wrapf(err, cache.InvalidRedisCacheCfg, "could not unmarshal the config: %s", config) + } if _, ok := cf["key"]; !ok { cf["key"] = DefaultKey } if _, ok := cf["conn"]; !ok { - return errors.New("config has no conn key") + return berror.Wrapf(err, cache.InvalidRedisCacheCfg, "config missing conn field: %s", config) } // Format redis://@: @@ -233,9 +242,16 @@ func (rc *Cache) StartAndGC(config string) error { rc.connectInit() c := rc.p.Get() - defer c.Close() + defer func() { + _ = c.Close() + }() - return c.Err() + // test connection + if err = c.Err(); err != nil { + return berror.Wrapf(err, cache.InvalidConnection, + "can not connect to remote redis server, please check the connection info and network state: %s", config) + } + return nil } // connect to redis. @@ -243,19 +259,20 @@ func (rc *Cache) connectInit() { dialFunc := func() (c redis.Conn, err error) { c, err = redis.Dial("tcp", rc.conninfo) if err != nil { - return nil, err + return nil, berror.Wrapf(err, cache.DialFailed, + "could not dial to remote server: %s ", rc.conninfo) } if rc.password != "" { - if _, err := c.Do("AUTH", rc.password); err != nil { - c.Close() + if _, err = c.Do("AUTH", rc.password); err != nil { + _ = c.Close() return nil, err } } _, selecterr := c.Do("SELECT", rc.dbNum) if selecterr != nil { - c.Close() + _ = c.Close() return nil, selecterr } return diff --git a/src/vendor/github.com/beego/beego/orm/README.md b/src/vendor/github.com/beego/beego/v2/client/orm/README.md similarity index 91% rename from src/vendor/github.com/beego/beego/orm/README.md rename to src/vendor/github.com/beego/beego/v2/client/orm/README.md index d3ef8362a..15fd1b11c 100644 --- a/src/vendor/github.com/beego/beego/orm/README.md +++ b/src/vendor/github.com/beego/beego/v2/client/orm/README.md @@ -1,6 +1,6 @@ # beego orm -[![Build Status](https://drone.io/github.com/beego/beego/status.png)](https://drone.io/github.com/beego/beego/latest) +[![Build Status](https://drone.io/github.com/beego/beego/v2/status.png)](https://drone.io/github.com/beego/beego/v2/latest) A powerful orm framework for go. @@ -27,7 +27,7 @@ more features please read the docs **Install:** - go get github.com/beego/beego/orm + go get github.com/beego/beego/v2/client/orm ## Changelog @@ -45,7 +45,7 @@ package main import ( "fmt" - "github.com/beego/beego/orm" + "github.com/beego/beego/v2/client/orm" _ "github.com/go-sql-driver/mysql" // import your used driver ) @@ -155,5 +155,5 @@ note: not recommend use this in product env. more details and examples in docs and test -[documents](http://beego.me/docs/mvc/model/overview.md) +[documents](http://beego.vip/docs/mvc/model/overview.md) diff --git a/src/vendor/github.com/beego/beego/v2/client/orm/clauses/const.go b/src/vendor/github.com/beego/beego/v2/client/orm/clauses/const.go new file mode 100644 index 000000000..38a6d5569 --- /dev/null +++ b/src/vendor/github.com/beego/beego/v2/client/orm/clauses/const.go @@ -0,0 +1,6 @@ +package clauses + +const ( + ExprSep = "__" + ExprDot = "." +) diff --git a/src/vendor/github.com/beego/beego/v2/client/orm/clauses/order_clause/order.go b/src/vendor/github.com/beego/beego/v2/client/orm/clauses/order_clause/order.go new file mode 100644 index 000000000..bdb2d1ca0 --- /dev/null +++ b/src/vendor/github.com/beego/beego/v2/client/orm/clauses/order_clause/order.go @@ -0,0 +1,104 @@ +package order_clause + +import ( + "strings" + + "github.com/beego/beego/v2/client/orm/clauses" +) + +type Sort int8 + +const ( + None Sort = 0 + Ascending Sort = 1 + Descending Sort = 2 +) + +type Option func(order *Order) + +type Order struct { + column string + sort Sort + isRaw bool +} + +func Clause(options ...Option) *Order { + o := &Order{} + for _, option := range options { + option(o) + } + + return o +} + +func (o *Order) GetColumn() string { + return o.column +} + +func (o *Order) GetSort() Sort { + return o.sort +} + +func (o *Order) SortString() string { + switch o.GetSort() { + case Ascending: + return "ASC" + case Descending: + return "DESC" + } + + return `` +} + +func (o *Order) IsRaw() bool { + return o.isRaw +} + +func ParseOrder(expressions ...string) []*Order { + var orders []*Order + for _, expression := range expressions { + sort := Ascending + column := strings.ReplaceAll(expression, clauses.ExprSep, clauses.ExprDot) + if column[0] == '-' { + sort = Descending + column = column[1:] + } + + orders = append(orders, &Order{ + column: column, + sort: sort, + }) + } + + return orders +} + +func Column(column string) Option { + return func(order *Order) { + order.column = strings.ReplaceAll(column, clauses.ExprSep, clauses.ExprDot) + } +} + +func sort(sort Sort) Option { + return func(order *Order) { + order.sort = sort + } +} + +func SortAscending() Option { + return sort(Ascending) +} + +func SortDescending() Option { + return sort(Descending) +} + +func SortNone() Option { + return sort(None) +} + +func Raw() Option { + return func(order *Order) { + order.isRaw = true + } +} diff --git a/src/vendor/github.com/beego/beego/orm/cmd.go b/src/vendor/github.com/beego/beego/v2/client/orm/cmd.go similarity index 79% rename from src/vendor/github.com/beego/beego/orm/cmd.go rename to src/vendor/github.com/beego/beego/v2/client/orm/cmd.go index 0ff4dc40d..9819badb1 100644 --- a/src/vendor/github.com/beego/beego/orm/cmd.go +++ b/src/vendor/github.com/beego/beego/v2/client/orm/cmd.go @@ -15,6 +15,7 @@ package orm import ( + "context" "flag" "fmt" "os" @@ -26,9 +27,7 @@ type commander interface { Run() error } -var ( - commands = make(map[string]commander) -) +var commands = make(map[string]commander) // print help. func printHelp(errs ...string) { @@ -46,7 +45,7 @@ func printHelp(errs ...string) { os.Exit(2) } -// RunCommand listen for orm command and then run it if command arguments passed. +// RunCommand listens for orm command and runs if command arguments have been passed. func RunCommand() { if len(os.Args) < 2 || os.Args[1] != "orm" { return @@ -83,7 +82,7 @@ type commandSyncDb struct { rtOnError bool } -// parse orm command line arguments. +// Parse the orm command line arguments. func (d *commandSyncDb) Parse(args []string) { var name string @@ -96,17 +95,21 @@ func (d *commandSyncDb) Parse(args []string) { d.al = getDbAlias(name) } -// run orm line command. +// Run orm line command. func (d *commandSyncDb) Run() error { var drops []string + var err error if d.force { - drops = getDbDropSQL(d.al) + drops, err = defaultModelCache.getDbDropSQL(d.al) + if err != nil { + return err + } } db := d.al.DB - if d.force { - for i, mi := range modelCache.allOrdered() { + if d.force && len(drops) > 0 { + for i, mi := range defaultModelCache.allOrdered() { query := drops[i] if !d.noInfo { fmt.Printf("drop table `%s`\n", mi.table) @@ -124,7 +127,10 @@ func (d *commandSyncDb) Run() error { } } - sqls, indexes := getDbCreateSQL(d.al) + createQueries, indexes, err := defaultModelCache.getDbCreateSQL(d.al) + if err != nil { + return err + } tables, err := d.al.DbBaser.GetTables(db) if err != nil { @@ -134,14 +140,21 @@ func (d *commandSyncDb) Run() error { fmt.Printf(" %s\n", err.Error()) } - for i, mi := range modelCache.allOrdered() { + ctx := context.Background() + for i, mi := range defaultModelCache.allOrdered() { + + if !isApplicableTableForDB(mi.addrField, d.al.Name) { + fmt.Printf("table `%s` is not applicable to database '%s'\n", mi.table, d.al.Name) + continue + } + if tables[mi.table] { if !d.noInfo { fmt.Printf("table `%s` already exists, skip\n", mi.table) } var fields []*fieldInfo - columns, err := d.al.DbBaser.GetColumns(db, mi.table) + columns, err := d.al.DbBaser.GetColumns(ctx, db, mi.table) if err != nil { if d.rtOnError { return err @@ -175,7 +188,7 @@ func (d *commandSyncDb) Run() error { } for _, idx := range indexes[mi.table] { - if !d.al.DbBaser.IndexExists(db, idx.Table, idx.Name) { + if !d.al.DbBaser.IndexExists(ctx, db, idx.Table, idx.Name) { if !d.noInfo { fmt.Printf("create index `%s` for table `%s`\n", idx.Name, idx.Table) } @@ -201,7 +214,7 @@ func (d *commandSyncDb) Run() error { fmt.Printf("create table `%s` \n", mi.table) } - queries := []string{sqls[i]} + queries := []string{createQueries[i]} for _, idx := range indexes[mi.table] { queries = append(queries, idx.SQL) } @@ -232,7 +245,7 @@ type commandSQLAll struct { al *alias } -// parse orm command line arguments. +// Parse orm command line arguments. func (d *commandSQLAll) Parse(args []string) { var name string @@ -243,12 +256,15 @@ func (d *commandSQLAll) Parse(args []string) { d.al = getDbAlias(name) } -// run orm line command. +// Run orm line command. func (d *commandSQLAll) Run() error { - sqls, indexes := getDbCreateSQL(d.al) + createQueries, indexes, err := defaultModelCache.getDbCreateSQL(d.al) + if err != nil { + return err + } var all []string - for i, mi := range modelCache.allOrdered() { - queries := []string{sqls[i]} + for i, mi := range defaultModelCache.allOrdered() { + queries := []string{createQueries[i]} for _, idx := range indexes[mi.table] { queries = append(queries, idx.SQL) } @@ -266,9 +282,9 @@ func init() { } // RunSyncdb run syncdb command line. -// name means table's alias name. default is "default". -// force means run next sql if the current is error. -// verbose means show all info when running command or not. +// name: Table's alias name (default is "default") +// force: Run the next sql command even if the current gave an error +// verbose: Print all information, useful for debugging func RunSyncdb(name string, force bool, verbose bool) error { BootStrap() diff --git a/src/vendor/github.com/beego/beego/v2/client/orm/cmd_utils.go b/src/vendor/github.com/beego/beego/v2/client/orm/cmd_utils.go new file mode 100644 index 000000000..7b795b22e --- /dev/null +++ b/src/vendor/github.com/beego/beego/v2/client/orm/cmd_utils.go @@ -0,0 +1,169 @@ +// Copyright 2014 beego Author. All Rights Reserved. +// +// 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 orm + +import ( + "fmt" + "strings" +) + +type dbIndex struct { + Table string + Name string + SQL string +} + +// get database column type string. +func getColumnTyp(al *alias, fi *fieldInfo) (col string) { + T := al.DbBaser.DbTypes() + fieldType := fi.fieldType + fieldSize := fi.size + +checkColumn: + switch fieldType { + case TypeBooleanField: + col = T["bool"] + case TypeVarCharField: + if al.Driver == DRPostgres && fi.toText { + col = T["string-text"] + } else { + col = fmt.Sprintf(T["string"], fieldSize) + } + case TypeCharField: + col = fmt.Sprintf(T["string-char"], fieldSize) + case TypeTextField: + col = T["string-text"] + case TypeTimeField: + col = T["time.Time-clock"] + case TypeDateField: + col = T["time.Time-date"] + case TypeDateTimeField: + // the precision of sqlite is not implemented + if al.Driver == 2 || fi.timePrecision == nil { + col = T["time.Time"] + } else { + s := T["time.Time-precision"] + col = fmt.Sprintf(s, *fi.timePrecision) + } + + case TypeBitField: + col = T["int8"] + case TypeSmallIntegerField: + col = T["int16"] + case TypeIntegerField: + col = T["int32"] + case TypeBigIntegerField: + if al.Driver == DRSqlite { + fieldType = TypeIntegerField + goto checkColumn + } + col = T["int64"] + case TypePositiveBitField: + col = T["uint8"] + case TypePositiveSmallIntegerField: + col = T["uint16"] + case TypePositiveIntegerField: + col = T["uint32"] + case TypePositiveBigIntegerField: + col = T["uint64"] + case TypeFloatField: + col = T["float64"] + case TypeDecimalField: + s := T["float64-decimal"] + if !strings.Contains(s, "%d") { + col = s + } else { + col = fmt.Sprintf(s, fi.digits, fi.decimals) + } + case TypeJSONField: + if al.Driver != DRPostgres { + fieldType = TypeVarCharField + goto checkColumn + } + col = T["json"] + case TypeJsonbField: + if al.Driver != DRPostgres { + fieldType = TypeVarCharField + goto checkColumn + } + col = T["jsonb"] + case RelForeignKey, RelOneToOne: + fieldType = fi.relModelInfo.fields.pk.fieldType + fieldSize = fi.relModelInfo.fields.pk.size + goto checkColumn + } + + return +} + +// create alter sql string. +func getColumnAddQuery(al *alias, fi *fieldInfo) string { + Q := al.DbBaser.TableQuote() + typ := getColumnTyp(al, fi) + + if !fi.null { + typ += " " + "NOT NULL" + } + + return fmt.Sprintf("ALTER TABLE %s%s%s ADD COLUMN %s%s%s %s %s", + Q, fi.mi.table, Q, + Q, fi.column, Q, + typ, getColumnDefault(fi), + ) +} + +// Get string value for the attribute "DEFAULT" for the CREATE, ALTER commands +func getColumnDefault(fi *fieldInfo) string { + var v, t, d string + + // Skip default attribute if field is in relations + if fi.rel || fi.reverse { + return v + } + + t = " DEFAULT '%s' " + + // These defaults will be useful if there no config value orm:"default" and NOT NULL is on + switch fi.fieldType { + case TypeTimeField, TypeDateField, TypeDateTimeField, TypeTextField: + return v + + case TypeBitField, TypeSmallIntegerField, TypeIntegerField, + TypeBigIntegerField, TypePositiveBitField, TypePositiveSmallIntegerField, + TypePositiveIntegerField, TypePositiveBigIntegerField, TypeFloatField, + TypeDecimalField: + t = " DEFAULT %s " + d = "0" + case TypeBooleanField: + t = " DEFAULT %s " + d = "FALSE" + case TypeJSONField, TypeJsonbField: + d = "{}" + } + + if fi.colDefault { + if !fi.initial.Exist() { + v = fmt.Sprintf(t, "") + } else { + v = fmt.Sprintf(t, fi.initial.String()) + } + } else { + if !fi.null { + v = fmt.Sprintf(t, d) + } + } + + return v +} diff --git a/src/vendor/github.com/beego/beego/orm/db.go b/src/vendor/github.com/beego/beego/v2/client/orm/db.go similarity index 81% rename from src/vendor/github.com/beego/beego/orm/db.go rename to src/vendor/github.com/beego/beego/v2/client/orm/db.go index 7536a4224..cbaa81ad2 100644 --- a/src/vendor/github.com/beego/beego/orm/db.go +++ b/src/vendor/github.com/beego/beego/v2/client/orm/db.go @@ -15,12 +15,15 @@ package orm import ( + "context" "database/sql" "errors" "fmt" "reflect" "strings" "time" + + "github.com/beego/beego/v2/client/orm/hints" ) const ( @@ -29,41 +32,37 @@ const ( formatDateTime = "2006-01-02 15:04:05" ) -var ( - // ErrMissPK missing pk error - ErrMissPK = errors.New("missed pk value") -) +// ErrMissPK missing pk error +var ErrMissPK = errors.New("missed pk value") -var ( - operators = map[string]bool{ - "exact": true, - "iexact": true, - "strictexact": true, - "contains": true, - "icontains": true, - // "regex": true, - // "iregex": true, - "gt": true, - "gte": true, - "lt": true, - "lte": true, - "eq": true, - "nq": true, - "ne": true, - "startswith": true, - "endswith": true, - "istartswith": true, - "iendswith": true, - "in": true, - "between": true, - // "year": true, - // "month": true, - // "day": true, - // "week_day": true, - "isnull": true, - // "search": true, - } -) +var operators = map[string]bool{ + "exact": true, + "iexact": true, + "strictexact": true, + "contains": true, + "icontains": true, + // "regex": true, + // "iregex": true, + "gt": true, + "gte": true, + "lt": true, + "lte": true, + "eq": true, + "nq": true, + "ne": true, + "startswith": true, + "endswith": true, + "istartswith": true, + "iendswith": true, + "in": true, + "between": true, + // "year": true, + // "month": true, + // "day": true, + // "week_day": true, + "isnull": true, + // "search": true, +} // an instance of dbBaser interface/ type dbBase struct { @@ -265,8 +264,8 @@ func (d *dbBase) collectFieldValue(mi *modelInfo, fi *fieldInfo, ind reflect.Val return value, nil } -// create insert sql preparation statement object. -func (d *dbBase) PrepareInsert(q dbQuerier, mi *modelInfo) (stmtQuerier, string, error) { +// PrepareInsert create insert sql preparation statement object. +func (d *dbBase) PrepareInsert(ctx context.Context, q dbQuerier, mi *modelInfo) (stmtQuerier, string, error) { Q := d.ins.TableQuote() dbcols := make([]string, 0, len(mi.fields.dbcols)) @@ -287,12 +286,12 @@ func (d *dbBase) PrepareInsert(q dbQuerier, mi *modelInfo) (stmtQuerier, string, d.ins.HasReturningID(mi, &query) - stmt, err := q.Prepare(query) + stmt, err := q.PrepareContext(ctx, query) return stmt, query, err } -// insert struct with prepared statement and given struct reflect value. -func (d *dbBase) InsertStmt(stmt stmtQuerier, mi *modelInfo, ind reflect.Value, tz *time.Location) (int64, error) { +// InsertStmt insert struct with prepared statement and given struct reflect value. +func (d *dbBase) InsertStmt(ctx context.Context, stmt stmtQuerier, mi *modelInfo, ind reflect.Value, tz *time.Location) (int64, error) { values, _, err := d.collectValues(mi, ind, mi.fields.dbcols, true, true, nil, tz) if err != nil { return 0, err @@ -304,7 +303,7 @@ func (d *dbBase) InsertStmt(stmt stmtQuerier, mi *modelInfo, ind reflect.Value, err := row.Scan(&id) return id, err } - res, err := stmt.Exec(values...) + res, err := stmt.ExecContext(ctx, values...) if err == nil { return res.LastInsertId() } @@ -312,7 +311,7 @@ func (d *dbBase) InsertStmt(stmt stmtQuerier, mi *modelInfo, ind reflect.Value, } // query sql ,read records and persist in dbBaser. -func (d *dbBase) Read(q dbQuerier, mi *modelInfo, ind reflect.Value, tz *time.Location, cols []string, isForUpdate bool) error { +func (d *dbBase) Read(ctx context.Context, q dbQuerier, mi *modelInfo, ind reflect.Value, tz *time.Location, cols []string, isForUpdate bool) error { var whereCols []string var args []interface{} @@ -358,7 +357,7 @@ func (d *dbBase) Read(q dbQuerier, mi *modelInfo, ind reflect.Value, tz *time.Lo d.ins.ReplaceMarks(&query) - row := q.QueryRow(query, args...) + row := q.QueryRowContext(ctx, query, args...) if err := row.Scan(refs...); err != nil { if err == sql.ErrNoRows { return ErrNoRows @@ -372,27 +371,27 @@ func (d *dbBase) Read(q dbQuerier, mi *modelInfo, ind reflect.Value, tz *time.Lo return nil } -// execute insert sql dbQuerier with given struct reflect.Value. -func (d *dbBase) Insert(q dbQuerier, mi *modelInfo, ind reflect.Value, tz *time.Location) (int64, error) { +// Insert execute insert sql dbQuerier with given struct reflect.Value. +func (d *dbBase) Insert(ctx context.Context, q dbQuerier, mi *modelInfo, ind reflect.Value, tz *time.Location) (int64, error) { names := make([]string, 0, len(mi.fields.dbcols)) values, autoFields, err := d.collectValues(mi, ind, mi.fields.dbcols, false, true, &names, tz) if err != nil { return 0, err } - id, err := d.InsertValue(q, mi, false, names, values) + id, err := d.InsertValue(ctx, q, mi, false, names, values) if err != nil { return 0, err } if len(autoFields) > 0 { - err = d.ins.setval(q, mi, autoFields) + err = d.ins.setval(ctx, q, mi, autoFields) } return id, err } -// multi-insert sql with given slice struct reflect.Value. -func (d *dbBase) InsertMulti(q dbQuerier, mi *modelInfo, sind reflect.Value, bulk int, tz *time.Location) (int64, error) { +// InsertMulti multi-insert sql with given slice struct reflect.Value. +func (d *dbBase) InsertMulti(ctx context.Context, q dbQuerier, mi *modelInfo, sind reflect.Value, bulk int, tz *time.Location) (int64, error) { var ( cnt int64 nums int @@ -438,7 +437,7 @@ func (d *dbBase) InsertMulti(q dbQuerier, mi *modelInfo, sind reflect.Value, bul } if i > 1 && i%bulk == 0 || length == i { - num, err := d.InsertValue(q, mi, true, names, values[:nums]) + num, err := d.InsertValue(ctx, q, mi, true, names, values[:nums]) if err != nil { return cnt, err } @@ -449,15 +448,15 @@ func (d *dbBase) InsertMulti(q dbQuerier, mi *modelInfo, sind reflect.Value, bul var err error if len(autoFields) > 0 { - err = d.ins.setval(q, mi, autoFields) + err = d.ins.setval(ctx, q, mi, autoFields) } return cnt, err } -// execute insert sql with given struct and given values. +// InsertValue execute insert sql with given struct and given values. // insert the given values, not the field values in struct. -func (d *dbBase) InsertValue(q dbQuerier, mi *modelInfo, isMulti bool, names []string, values []interface{}) (int64, error) { +func (d *dbBase) InsertValue(ctx context.Context, q dbQuerier, mi *modelInfo, isMulti bool, names []string, values []interface{}) (int64, error) { Q := d.ins.TableQuote() marks := make([]string, len(names)) @@ -480,16 +479,23 @@ func (d *dbBase) InsertValue(q dbQuerier, mi *modelInfo, isMulti bool, names []s d.ins.ReplaceMarks(&query) if isMulti || !d.ins.HasReturningID(mi, &query) { - res, err := q.Exec(query, values...) + res, err := q.ExecContext(ctx, query, values...) if err == nil { if isMulti { return res.RowsAffected() } - return res.LastInsertId() + + lastInsertId, err := res.LastInsertId() + if err != nil { + DebugLog.Println(ErrLastInsertIdUnavailable, ':', err) + return lastInsertId, ErrLastInsertIdUnavailable + } else { + return lastInsertId, nil + } } return 0, err } - row := q.QueryRow(query, values...) + row := q.QueryRowContext(ctx, query, values...) var id int64 err := row.Scan(&id) return id, err @@ -498,7 +504,7 @@ func (d *dbBase) InsertValue(q dbQuerier, mi *modelInfo, isMulti bool, names []s // InsertOrUpdate a row // If your primary key or unique column conflict will update // If no will insert -func (d *dbBase) InsertOrUpdate(q dbQuerier, mi *modelInfo, ind reflect.Value, a *alias, args ...string) (int64, error) { +func (d *dbBase) InsertOrUpdate(ctx context.Context, q dbQuerier, mi *modelInfo, ind reflect.Value, a *alias, args ...string) (int64, error) { args0 := "" iouStr := "" argsMap := map[string]string{} @@ -515,7 +521,7 @@ func (d *dbBase) InsertOrUpdate(q dbQuerier, mi *modelInfo, ind reflect.Value, a return 0, fmt.Errorf("`%s` nonsupport InsertOrUpdate in beego", a.DriverName) } - //Get on the key-value pairs + // Get on the key-value pairs for _, v := range args { kv := strings.Split(v, "=") if len(kv) == 2 { @@ -527,7 +533,6 @@ func (d *dbBase) InsertOrUpdate(q dbQuerier, mi *modelInfo, ind reflect.Value, a names := make([]string, 0, len(mi.fields.dbcols)-1) Q := d.ins.TableQuote() values, _, err := d.collectValues(mi, ind, mi.fields.dbcols, true, true, &names, a.TZ) - if err != nil { return 0, err } @@ -550,7 +555,7 @@ func (d *dbBase) InsertOrUpdate(q dbQuerier, mi *modelInfo, ind reflect.Value, a updates[i] = v + "=" + valueStr case DRPostgres: if conflitValue != nil { - //postgres ON CONFLICT DO UPDATE SET can`t use colu=colu+values + // postgres ON CONFLICT DO UPDATE SET can`t use colu=colu+values updates[i] = fmt.Sprintf("%s=(select %s from %s where %s = ? )", v, valueStr, mi.table, args0) updateValues = append(updateValues, conflitValue) } else { @@ -575,23 +580,30 @@ func (d *dbBase) InsertOrUpdate(q dbQuerier, mi *modelInfo, ind reflect.Value, a if isMulti { qmarks = strings.Repeat(qmarks+"), (", multi-1) + qmarks } - //conflitValue maybe is a int,can`t use fmt.Sprintf + // conflitValue maybe is a int,can`t use fmt.Sprintf query := fmt.Sprintf("INSERT INTO %s%s%s (%s%s%s) VALUES (%s) %s "+qupdates, Q, mi.table, Q, Q, columns, Q, qmarks, iouStr) d.ins.ReplaceMarks(&query) if isMulti || !d.ins.HasReturningID(mi, &query) { - res, err := q.Exec(query, values...) + res, err := q.ExecContext(ctx, query, values...) if err == nil { if isMulti { return res.RowsAffected() } - return res.LastInsertId() + + lastInsertId, err := res.LastInsertId() + if err != nil { + DebugLog.Println(ErrLastInsertIdUnavailable, ':', err) + return lastInsertId, ErrLastInsertIdUnavailable + } else { + return lastInsertId, nil + } } return 0, err } - row := q.QueryRow(query, values...) + row := q.QueryRowContext(ctx, query, values...) var id int64 err = row.Scan(&id) if err != nil && err.Error() == `pq: syntax error at or near "ON"` { @@ -600,8 +612,8 @@ func (d *dbBase) InsertOrUpdate(q dbQuerier, mi *modelInfo, ind reflect.Value, a return id, err } -// execute update sql dbQuerier with given struct reflect.Value. -func (d *dbBase) Update(q dbQuerier, mi *modelInfo, ind reflect.Value, tz *time.Location, cols []string) (int64, error) { +// Update execute update sql dbQuerier with given struct reflect.Value. +func (d *dbBase) Update(ctx context.Context, q dbQuerier, mi *modelInfo, ind reflect.Value, tz *time.Location, cols []string) (int64, error) { pkName, pkValue, ok := getExistPk(mi, ind) if !ok { return 0, ErrMissPK @@ -658,16 +670,16 @@ func (d *dbBase) Update(q dbQuerier, mi *modelInfo, ind reflect.Value, tz *time. d.ins.ReplaceMarks(&query) - res, err := q.Exec(query, setValues...) + res, err := q.ExecContext(ctx, query, setValues...) if err == nil { return res.RowsAffected() } return 0, err } -// execute delete sql dbQuerier with given struct reflect.Value. +// Delete execute delete sql dbQuerier with given struct reflect.Value. // delete index is pk. -func (d *dbBase) Delete(q dbQuerier, mi *modelInfo, ind reflect.Value, tz *time.Location, cols []string) (int64, error) { +func (d *dbBase) Delete(ctx context.Context, q dbQuerier, mi *modelInfo, ind reflect.Value, tz *time.Location, cols []string) (int64, error) { var whereCols []string var args []interface{} // if specify cols length > 0, then use it for where condition. @@ -696,21 +708,14 @@ func (d *dbBase) Delete(q dbQuerier, mi *modelInfo, ind reflect.Value, tz *time. query := fmt.Sprintf("DELETE FROM %s%s%s WHERE %s%s%s = ?", Q, mi.table, Q, Q, wheres, Q) d.ins.ReplaceMarks(&query) - res, err := q.Exec(query, args...) + res, err := q.ExecContext(ctx, query, args...) if err == nil { num, err := res.RowsAffected() if err != nil { return 0, err } if num > 0 { - if mi.fields.pk.auto { - if mi.fields.pk.fieldType&IsPositiveIntegerField > 0 { - ind.FieldByIndex(mi.fields.pk.fieldIndex).SetUint(0) - } else { - ind.FieldByIndex(mi.fields.pk.fieldIndex).SetInt(0) - } - } - err := d.deleteRels(q, mi, args, tz) + err := d.deleteRels(ctx, q, mi, args, tz) if err != nil { return num, err } @@ -720,9 +725,9 @@ func (d *dbBase) Delete(q dbQuerier, mi *modelInfo, ind reflect.Value, tz *time. return 0, err } -// update table-related record by querySet. +// UpdateBatch update table-related record by querySet. // need querySet not struct reflect.Value to update related records. -func (d *dbBase) UpdateBatch(q dbQuerier, qs *querySet, mi *modelInfo, cond *Condition, params Params, tz *time.Location) (int64, error) { +func (d *dbBase) UpdateBatch(ctx context.Context, q dbQuerier, qs *querySet, mi *modelInfo, cond *Condition, params Params, tz *time.Location) (int64, error) { columns := make([]string, 0, len(params)) values := make([]interface{}, 0, len(params)) for col, val := range params { @@ -739,8 +744,10 @@ func (d *dbBase) UpdateBatch(q dbQuerier, qs *querySet, mi *modelInfo, cond *Con } tables := newDbTables(mi, d.ins) + var specifyIndexes string if qs != nil { tables.parseRelated(qs.related, qs.relDepth) + specifyIndexes = tables.getIndexSql(mi.table, qs.useIndex, qs.indexes) } where, args := tables.getCondSQL(cond, false, tz) @@ -791,20 +798,17 @@ func (d *dbBase) UpdateBatch(q dbQuerier, qs *querySet, mi *modelInfo, cond *Con sets := strings.Join(cols, ", ") + " " if d.ins.SupportUpdateJoin() { - query = fmt.Sprintf("UPDATE %s%s%s T0 %sSET %s%s", Q, mi.table, Q, join, sets, where) + query = fmt.Sprintf("UPDATE %s%s%s T0 %s%sSET %s%s", Q, mi.table, Q, specifyIndexes, join, sets, where) } else { - supQuery := fmt.Sprintf("SELECT T0.%s%s%s FROM %s%s%s T0 %s%s", Q, mi.fields.pk.column, Q, Q, mi.table, Q, join, where) + supQuery := fmt.Sprintf("SELECT T0.%s%s%s FROM %s%s%s T0 %s%s%s", + Q, mi.fields.pk.column, Q, + Q, mi.table, Q, + specifyIndexes, join, where) query = fmt.Sprintf("UPDATE %s%s%s SET %sWHERE %s%s%s IN ( %s )", Q, mi.table, Q, sets, Q, mi.fields.pk.column, Q, supQuery) } d.ins.ReplaceMarks(&query) - var err error - var res sql.Result - if qs != nil && qs.forContext { - res, err = q.ExecContext(qs.ctx, query, values...) - } else { - res, err = q.Exec(query, values...) - } + res, err := q.ExecContext(ctx, query, values...) if err == nil { return res.RowsAffected() } @@ -813,13 +817,13 @@ func (d *dbBase) UpdateBatch(q dbQuerier, qs *querySet, mi *modelInfo, cond *Con // delete related records. // do UpdateBanch or DeleteBanch by condition of tables' relationship. -func (d *dbBase) deleteRels(q dbQuerier, mi *modelInfo, args []interface{}, tz *time.Location) error { +func (d *dbBase) deleteRels(ctx context.Context, q dbQuerier, mi *modelInfo, args []interface{}, tz *time.Location) error { for _, fi := range mi.fields.fieldsReverse { fi = fi.reverseFieldInfo switch fi.onDelete { case odCascade: cond := NewCondition().And(fmt.Sprintf("%s__in", fi.name), args...) - _, err := d.DeleteBatch(q, nil, fi.mi, cond, tz) + _, err := d.DeleteBatch(ctx, q, nil, fi.mi, cond, tz) if err != nil { return err } @@ -829,7 +833,7 @@ func (d *dbBase) deleteRels(q dbQuerier, mi *modelInfo, args []interface{}, tz * if fi.onDelete == odSetDefault { params[fi.column] = fi.initial.String() } - _, err := d.UpdateBatch(q, nil, fi.mi, cond, params, tz) + _, err := d.UpdateBatch(ctx, q, nil, fi.mi, cond, params, tz) if err != nil { return err } @@ -839,13 +843,15 @@ func (d *dbBase) deleteRels(q dbQuerier, mi *modelInfo, args []interface{}, tz * return nil } -// delete table-related records. -func (d *dbBase) DeleteBatch(q dbQuerier, qs *querySet, mi *modelInfo, cond *Condition, tz *time.Location) (int64, error) { +// DeleteBatch delete table-related records. +func (d *dbBase) DeleteBatch(ctx context.Context, q dbQuerier, qs *querySet, mi *modelInfo, cond *Condition, tz *time.Location) (int64, error) { tables := newDbTables(mi, d.ins) tables.skipEnd = true + var specifyIndexes string if qs != nil { tables.parseRelated(qs.related, qs.relDepth) + specifyIndexes = tables.getIndexSql(mi.table, qs.useIndex, qs.indexes) } if cond == nil || cond.IsEmpty() { @@ -858,12 +864,12 @@ func (d *dbBase) DeleteBatch(q dbQuerier, qs *querySet, mi *modelInfo, cond *Con join := tables.getJoinSQL() cols := fmt.Sprintf("T0.%s%s%s", Q, mi.fields.pk.column, Q) - query := fmt.Sprintf("SELECT %s FROM %s%s%s T0 %s%s", cols, Q, mi.table, Q, join, where) + query := fmt.Sprintf("SELECT %s FROM %s%s%s T0 %s%s%s", cols, Q, mi.table, Q, specifyIndexes, join, where) d.ins.ReplaceMarks(&query) var rs *sql.Rows - r, err := q.Query(query, args...) + r, err := q.QueryContext(ctx, query, args...) if err != nil { return 0, err } @@ -897,19 +903,14 @@ func (d *dbBase) DeleteBatch(q dbQuerier, qs *querySet, mi *modelInfo, cond *Con query = fmt.Sprintf("DELETE FROM %s%s%s WHERE %s%s%s %s", Q, mi.table, Q, Q, mi.fields.pk.column, Q, sqlIn) d.ins.ReplaceMarks(&query) - var res sql.Result - if qs != nil && qs.forContext { - res, err = q.ExecContext(qs.ctx, query, args...) - } else { - res, err = q.Exec(query, args...) - } + res, err := q.ExecContext(ctx, query, args...) if err == nil { num, err := res.RowsAffected() if err != nil { return 0, err } if num > 0 { - err := d.deleteRels(q, mi, args, tz) + err := d.deleteRels(ctx, q, mi, args, tz) if err != nil { return num, err } @@ -919,15 +920,15 @@ func (d *dbBase) DeleteBatch(q dbQuerier, qs *querySet, mi *modelInfo, cond *Con return 0, err } -// read related records. -func (d *dbBase) ReadBatch(q dbQuerier, qs *querySet, mi *modelInfo, cond *Condition, container interface{}, tz *time.Location, cols []string) (int64, error) { - +// ReadBatch read related records. +func (d *dbBase) ReadBatch(ctx context.Context, q dbQuerier, qs *querySet, mi *modelInfo, cond *Condition, container interface{}, tz *time.Location, cols []string) (int64, error) { val := reflect.ValueOf(container) ind := reflect.Indirect(val) - errTyp := true + unregister := true one := true isPtr := true + name := "" if val.Kind() == reflect.Ptr { fn := "" @@ -940,19 +941,17 @@ func (d *dbBase) ReadBatch(q dbQuerier, qs *querySet, mi *modelInfo, cond *Condi case reflect.Struct: isPtr = false fn = getFullName(typ) + name = getTableName(reflect.New(typ)) } } else { fn = getFullName(ind.Type()) + name = getTableName(ind) } - errTyp = fn != mi.fullName + unregister = fn != mi.fullName } - if errTyp { - if one { - panic(fmt.Errorf("wrong object type `%s` for rows scan, need *%s", val.Type(), mi.fullName)) - } else { - panic(fmt.Errorf("wrong object type `%s` for rows scan, need *[]*%s or *[]%s", val.Type(), mi.fullName, mi.fullName)) - } + if unregister { + RegisterModel(container) } rlimit := qs.limit @@ -1003,6 +1002,7 @@ func (d *dbBase) ReadBatch(q dbQuerier, qs *querySet, mi *modelInfo, cond *Condi orderBy := tables.getOrderSQL(qs.orders) limit := tables.getLimitSQL(mi, offset, rlimit) join := tables.getJoinSQL() + specifyIndexes := tables.getIndexSql(mi.table, qs.useIndex, qs.indexes) for _, tbl := range tables.tables { if tbl.sel { @@ -1016,26 +1016,31 @@ func (d *dbBase) ReadBatch(q dbQuerier, qs *querySet, mi *modelInfo, cond *Condi if qs.distinct { sqlSelect += " DISTINCT" } - query := fmt.Sprintf("%s %s FROM %s%s%s T0 %s%s%s%s%s", sqlSelect, sels, Q, mi.table, Q, join, where, groupBy, orderBy, limit) + if qs.aggregate != "" { + sels = qs.aggregate + } + query := fmt.Sprintf("%s %s FROM %s%s%s T0 %s%s%s%s%s%s", + sqlSelect, sels, Q, mi.table, Q, + specifyIndexes, join, where, groupBy, orderBy, limit) - if qs.forupdate { + if qs.forUpdate { query += " FOR UPDATE" } d.ins.ReplaceMarks(&query) - var rs *sql.Rows - var err error - if qs != nil && qs.forContext { - rs, err = q.QueryContext(qs.ctx, query, args...) - if err != nil { - return 0, err - } - } else { - rs, err = q.Query(query, args...) - if err != nil { - return 0, err - } + rs, err := q.QueryContext(ctx, query, args...) + if err != nil { + return 0, err + } + + defer rs.Close() + + slice := ind + if unregister { + mi, _ = defaultModelCache.get(name) + tCols = mi.fields.dbcols + colsNum = len(tCols) } refs := make([]interface{}, colsNum) @@ -1043,11 +1048,6 @@ func (d *dbBase) ReadBatch(q dbQuerier, qs *querySet, mi *modelInfo, cond *Condi var ref interface{} refs[i] = &ref } - - defer rs.Close() - - slice := ind - var cnt int64 for rs.Next() { if one && cnt == 0 || !one { @@ -1111,7 +1111,7 @@ func (d *dbBase) ReadBatch(q dbQuerier, qs *querySet, mi *modelInfo, cond *Condi ind.Set(mind) } else { if cnt == 0 { - // you can use a empty & caped container list + // you can use an empty & caped container list // orm will not replace it if ind.Len() != 0 { // if container is not empty @@ -1135,7 +1135,7 @@ func (d *dbBase) ReadBatch(q dbQuerier, qs *querySet, mi *modelInfo, cond *Condi ind.Set(slice) } else { // when a result is empty and container is nil - // to set a empty container + // to set an empty container if ind.IsNil() { ind.Set(reflect.MakeSlice(ind.Type(), 0, 0)) } @@ -1145,8 +1145,8 @@ func (d *dbBase) ReadBatch(q dbQuerier, qs *querySet, mi *modelInfo, cond *Condi return cnt, nil } -// excute count sql and return count result int64. -func (d *dbBase) Count(q dbQuerier, qs *querySet, mi *modelInfo, cond *Condition, tz *time.Location) (cnt int64, err error) { +// Count excute count sql and return count result int64. +func (d *dbBase) Count(ctx context.Context, q dbQuerier, qs *querySet, mi *modelInfo, cond *Condition, tz *time.Location) (cnt int64, err error) { tables := newDbTables(mi, d.ins) tables.parseRelated(qs.related, qs.relDepth) @@ -1154,10 +1154,13 @@ func (d *dbBase) Count(q dbQuerier, qs *querySet, mi *modelInfo, cond *Condition groupBy := tables.getGroupSQL(qs.groups) tables.getOrderSQL(qs.orders) join := tables.getJoinSQL() + specifyIndexes := tables.getIndexSql(mi.table, qs.useIndex, qs.indexes) Q := d.ins.TableQuote() - query := fmt.Sprintf("SELECT COUNT(*) FROM %s%s%s T0 %s%s%s", Q, mi.table, Q, join, where, groupBy) + query := fmt.Sprintf("SELECT COUNT(*) FROM %s%s%s T0 %s%s%s%s", + Q, mi.table, Q, + specifyIndexes, join, where, groupBy) if groupBy != "" { query = fmt.Sprintf("SELECT COUNT(*) FROM (%s) AS T", query) @@ -1165,17 +1168,12 @@ func (d *dbBase) Count(q dbQuerier, qs *querySet, mi *modelInfo, cond *Condition d.ins.ReplaceMarks(&query) - var row *sql.Row - if qs != nil && qs.forContext { - row = q.QueryRowContext(qs.ctx, query, args...) - } else { - row = q.QueryRow(query, args...) - } + row := q.QueryRowContext(ctx, query, args...) err = row.Scan(&cnt) return } -// generate sql with replacing operator string placeholders and replaced values. +// GenerateOperatorSQL generate sql with replacing operator string placeholders and replaced values. func (d *dbBase) GenerateOperatorSQL(mi *modelInfo, fi *fieldInfo, operator string, args []interface{}, tz *time.Location) (string, []interface{}) { var sql string params := getFlatParams(fi, args, tz) @@ -1203,7 +1201,7 @@ func (d *dbBase) GenerateOperatorSQL(mi *modelInfo, fi *fieldInfo, operator stri } sql = d.ins.OperatorSQL(operator) switch operator { - case "exact", "strictexact": + case "exact": if arg == nil { params[0] = "IS NULL" } @@ -1235,7 +1233,7 @@ func (d *dbBase) GenerateOperatorSQL(mi *modelInfo, fi *fieldInfo, operator stri return sql, params } -// gernerate sql string with inner function, such as UPPER(text). +// GenerateOperatorLeftCol gernerate sql string with inner function, such as UPPER(text). func (d *dbBase) GenerateOperatorLeftCol(*fieldInfo, string, *string) { // default not use } @@ -1327,7 +1325,14 @@ setValue: t time.Time err error ) - if len(s) >= 19 { + + if fi.timePrecision != nil && len(s) >= (20+*fi.timePrecision) { + layout := formatDateTime + "." + for i := 0; i < *fi.timePrecision; i++ { + layout += "0" + } + t, err = time.ParseInLocation(layout, s[:20+*fi.timePrecision], tz) + } else if len(s) >= 19 { s = s[:19] t, err = time.ParseInLocation(formatDateTime, s, tz) } else if len(s) >= 10 { @@ -1417,12 +1422,10 @@ end: } return value, nil - } // set one value to struct column field. func (d *dbBase) setFieldValue(fi *fieldInfo, value interface{}, field reflect.Value) (interface{}, error) { - fieldType := fi.fieldType isNative := !fi.isFielder @@ -1612,9 +1615,8 @@ setValue: return value, nil } -// query sql, read values , save to *[]ParamList. -func (d *dbBase) ReadValues(q dbQuerier, qs *querySet, mi *modelInfo, cond *Condition, exprs []string, container interface{}, tz *time.Location) (int64, error) { - +// ReadValues query sql, read values , save to *[]ParamList. +func (d *dbBase) ReadValues(ctx context.Context, q dbQuerier, qs *querySet, mi *modelInfo, cond *Condition, exprs []string, container interface{}, tz *time.Location) (int64, error) { var ( maps []Params lists []ParamsList @@ -1681,6 +1683,7 @@ func (d *dbBase) ReadValues(q dbQuerier, qs *querySet, mi *modelInfo, cond *Cond orderBy := tables.getOrderSQL(qs.orders) limit := tables.getLimitSQL(mi, qs.offset, qs.limit) join := tables.getJoinSQL() + specifyIndexes := tables.getIndexSql(mi.table, qs.useIndex, qs.indexes) sels := strings.Join(cols, ", ") @@ -1688,11 +1691,14 @@ func (d *dbBase) ReadValues(q dbQuerier, qs *querySet, mi *modelInfo, cond *Cond if qs.distinct { sqlSelect += " DISTINCT" } - query := fmt.Sprintf("%s %s FROM %s%s%s T0 %s%s%s%s%s", sqlSelect, sels, Q, mi.table, Q, join, where, groupBy, orderBy, limit) + query := fmt.Sprintf("%s %s FROM %s%s%s T0 %s%s%s%s%s%s", + sqlSelect, sels, + Q, mi.table, Q, + specifyIndexes, join, where, groupBy, orderBy, limit) d.ins.ReplaceMarks(&query) - rs, err := q.Query(query, args...) + rs, err := q.QueryContext(ctx, query, args...) if err != nil { return 0, err } @@ -1782,11 +1788,7 @@ func (d *dbBase) ReadValues(q dbQuerier, qs *querySet, mi *modelInfo, cond *Cond return cnt, nil } -func (d *dbBase) RowsTo(dbQuerier, *querySet, *modelInfo, *Condition, interface{}, string, string, *time.Location) (int64, error) { - return 0, nil -} - -// flag of update joined record. +// SupportUpdateJoin flag of update joined record. func (d *dbBase) SupportUpdateJoin() bool { return true } @@ -1795,12 +1797,12 @@ func (d *dbBase) MaxLimit() uint64 { return 18446744073709551615 } -// return quote. +// TableQuote return quote. func (d *dbBase) TableQuote() string { return "`" } -// replace value placeholder in parametered sql string. +// ReplaceMarks replace value placeholder in parametered sql string. func (d *dbBase) ReplaceMarks(query *string) { // default use `?` as mark, do nothing } @@ -1811,26 +1813,26 @@ func (d *dbBase) HasReturningID(*modelInfo, *string) bool { } // sync auto key -func (d *dbBase) setval(db dbQuerier, mi *modelInfo, autoFields []string) error { +func (d *dbBase) setval(ctx context.Context, db dbQuerier, mi *modelInfo, autoFields []string) error { return nil } -// convert time from db. +// TimeFromDB convert time from db. func (d *dbBase) TimeFromDB(t *time.Time, tz *time.Location) { *t = t.In(tz) } -// convert time to db. +// TimeToDB convert time to db. func (d *dbBase) TimeToDB(t *time.Time, tz *time.Location) { *t = t.In(tz) } -// get database types. +// DbTypes get database types. func (d *dbBase) DbTypes() map[string]string { return nil } -// gt all tables. +// GetTables gt all tables. func (d *dbBase) GetTables(db dbQuerier) (map[string]bool, error) { tables := make(map[string]bool) query := d.ins.ShowTablesQuery() @@ -1855,11 +1857,11 @@ func (d *dbBase) GetTables(db dbQuerier) (map[string]bool, error) { return tables, nil } -// get all cloumns in table. -func (d *dbBase) GetColumns(db dbQuerier, table string) (map[string][3]string, error) { +// GetColumns get all cloumns in table. +func (d *dbBase) GetColumns(ctx context.Context, db dbQuerier, table string) (map[string][3]string, error) { columns := make(map[string][3]string) query := d.ins.ShowColumnsQuery(table) - rows, err := db.Query(query) + rows, err := db.QueryContext(ctx, query) if err != nil { return columns, err } @@ -1898,6 +1900,32 @@ func (d *dbBase) ShowColumnsQuery(table string) string { } // not implement. -func (d *dbBase) IndexExists(dbQuerier, string, string) bool { +func (d *dbBase) IndexExists(context.Context, dbQuerier, string, string) bool { panic(ErrNotImplement) } + +// GenerateSpecifyIndex return a specifying index clause +func (d *dbBase) GenerateSpecifyIndex(tableName string, useIndex int, indexes []string) string { + var s []string + Q := d.TableQuote() + for _, index := range indexes { + tmp := fmt.Sprintf(`%s%s%s`, Q, index, Q) + s = append(s, tmp) + } + + var useWay string + + switch useIndex { + case hints.KeyUseIndex: + useWay = `USE` + case hints.KeyForceIndex: + useWay = `FORCE` + case hints.KeyIgnoreIndex: + useWay = `IGNORE` + default: + DebugLog.Println("[WARN] Not a valid specifying action, so that action is ignored") + return `` + } + + return fmt.Sprintf(` %s INDEX(%s) `, useWay, strings.Join(s, `,`)) +} diff --git a/src/vendor/github.com/beego/beego/orm/db_alias.go b/src/vendor/github.com/beego/beego/v2/client/orm/db_alias.go similarity index 63% rename from src/vendor/github.com/beego/beego/orm/db_alias.go rename to src/vendor/github.com/beego/beego/v2/client/orm/db_alias.go index 369802a71..28c8ab8ec 100644 --- a/src/vendor/github.com/beego/beego/orm/db_alias.go +++ b/src/vendor/github.com/beego/beego/v2/client/orm/db_alias.go @@ -18,7 +18,6 @@ import ( "context" "database/sql" "fmt" - "reflect" "sync" "time" @@ -64,7 +63,7 @@ var ( "tidb": DRTiDB, "oracle": DROracle, "oci8": DROracle, // github.com/mattn/go-oci8 - "ora": DROracle, //https://github.com/rana/ora + "ora": DROracle, // https://github.com/rana/ora } dbBasers = map[DriverType]dbBaser{ DRMySQL: newdbBaseMysql(), @@ -108,10 +107,16 @@ func (ac *_dbCache) getDefault() (al *alias) { type DB struct { *sync.RWMutex - DB *sql.DB - stmtDecorators *lru.Cache + DB *sql.DB + stmtDecorators *lru.Cache + stmtDecoratorsLimit int } +var ( + _ dbQuerier = new(DB) + _ txer = new(DB) +) + func (d *DB) Begin() (*sql.Tx, error) { return d.DB.Begin() } @@ -120,7 +125,7 @@ func (d *DB) BeginTx(ctx context.Context, opts *sql.TxOptions) (*sql.Tx, error) return d.DB.BeginTx(ctx, opts) } -//su must call release to release *sql.Stmt after using +// su must call release to release *sql.Stmt after using func (d *DB) getStmtDecorator(query string) (*stmtDecorator, error) { d.RLock() c, ok := d.stmtDecorators.Get(query) @@ -161,16 +166,14 @@ func (d *DB) PrepareContext(ctx context.Context, query string) (*sql.Stmt, error } func (d *DB) Exec(query string, args ...interface{}) (sql.Result, error) { - sd, err := d.getStmtDecorator(query) - if err != nil { - return nil, err - } - stmt := sd.getStmt() - defer sd.release() - return stmt.Exec(args...) + return d.ExecContext(context.Background(), query, args...) } func (d *DB) ExecContext(ctx context.Context, query string, args ...interface{}) (sql.Result, error) { + if d.stmtDecorators == nil { + return d.DB.ExecContext(ctx, query, args...) + } + sd, err := d.getStmtDecorator(query) if err != nil { return nil, err @@ -181,16 +184,14 @@ func (d *DB) ExecContext(ctx context.Context, query string, args ...interface{}) } func (d *DB) Query(query string, args ...interface{}) (*sql.Rows, error) { - sd, err := d.getStmtDecorator(query) - if err != nil { - return nil, err - } - stmt := sd.getStmt() - defer sd.release() - return stmt.Query(args...) + return d.QueryContext(context.Background(), query, args...) } func (d *DB) QueryContext(ctx context.Context, query string, args ...interface{}) (*sql.Rows, error) { + if d.stmtDecorators == nil { + return d.DB.QueryContext(ctx, query, args...) + } + sd, err := d.getStmtDecorator(query) if err != nil { return nil, err @@ -201,37 +202,98 @@ func (d *DB) QueryContext(ctx context.Context, query string, args ...interface{} } func (d *DB) QueryRow(query string, args ...interface{}) *sql.Row { - sd, err := d.getStmtDecorator(query) - if err != nil { - panic(err) - } - stmt := sd.getStmt() - defer sd.release() - return stmt.QueryRow(args...) - + return d.QueryRowContext(context.Background(), query, args...) } func (d *DB) QueryRowContext(ctx context.Context, query string, args ...interface{}) *sql.Row { + if d.stmtDecorators == nil { + return d.DB.QueryRowContext(ctx, query, args...) + } + sd, err := d.getStmtDecorator(query) if err != nil { panic(err) } stmt := sd.getStmt() defer sd.release() - return stmt.QueryRowContext(ctx, args) + return stmt.QueryRowContext(ctx, args...) +} + +type TxDB struct { + tx *sql.Tx +} + +var ( + _ dbQuerier = new(TxDB) + _ txEnder = new(TxDB) +) + +func (t *TxDB) Commit() error { + return t.tx.Commit() +} + +func (t *TxDB) Rollback() error { + return t.tx.Rollback() +} + +func (t *TxDB) RollbackUnlessCommit() error { + err := t.tx.Rollback() + if err != sql.ErrTxDone { + return err + } + return nil +} + +var ( + _ dbQuerier = new(TxDB) + _ txEnder = new(TxDB) +) + +func (t *TxDB) Prepare(query string) (*sql.Stmt, error) { + return t.PrepareContext(context.Background(), query) +} + +func (t *TxDB) PrepareContext(ctx context.Context, query string) (*sql.Stmt, error) { + return t.tx.PrepareContext(ctx, query) +} + +func (t *TxDB) Exec(query string, args ...interface{}) (sql.Result, error) { + return t.ExecContext(context.Background(), query, args...) +} + +func (t *TxDB) ExecContext(ctx context.Context, query string, args ...interface{}) (sql.Result, error) { + return t.tx.ExecContext(ctx, query, args...) +} + +func (t *TxDB) Query(query string, args ...interface{}) (*sql.Rows, error) { + return t.QueryContext(context.Background(), query, args...) +} + +func (t *TxDB) QueryContext(ctx context.Context, query string, args ...interface{}) (*sql.Rows, error) { + return t.tx.QueryContext(ctx, query, args...) +} + +func (t *TxDB) QueryRow(query string, args ...interface{}) *sql.Row { + return t.QueryRowContext(context.Background(), query, args...) +} + +func (t *TxDB) QueryRowContext(ctx context.Context, query string, args ...interface{}) *sql.Row { + return t.tx.QueryRowContext(ctx, query, args...) } type alias struct { - Name string - Driver DriverType - DriverName string - DataSource string - MaxIdleConns int - MaxOpenConns int - DB *DB - DbBaser dbBaser - TZ *time.Location - Engine string + Name string + Driver DriverType + DriverName string + DataSource string + MaxIdleConns int + MaxOpenConns int + ConnMaxLifetime time.Duration + StmtCacheSize int + DB *DB + DbBaser dbBaser + TZ *time.Location + Engine string } func detectTZ(al *alias) { @@ -290,15 +352,52 @@ func detectTZ(al *alias) { } } -func addAliasWthDB(aliasName, driverName string, db *sql.DB) (*alias, error) { - al := new(alias) +func addAliasWthDB(aliasName, driverName string, db *sql.DB, params ...DBOption) (*alias, error) { + existErr := fmt.Errorf("DataBase alias name `%s` already registered, cannot reuse", aliasName) + if _, ok := dataBaseCache.get(aliasName); ok { + return nil, existErr + } + + al, err := newAliasWithDb(aliasName, driverName, db, params...) + if err != nil { + return nil, err + } + + if !dataBaseCache.add(aliasName, al) { + return nil, existErr + } + + return al, nil +} + +func newAliasWithDb(aliasName, driverName string, db *sql.DB, params ...DBOption) (*alias, error) { + al := &alias{} + al.DB = &DB{ + RWMutex: new(sync.RWMutex), + DB: db, + } + + for _, p := range params { + p(al) + } + + var stmtCache *lru.Cache + var stmtCacheSize int + + if al.StmtCacheSize > 0 { + _stmtCache, errC := newStmtDecoratorLruWithEvict(al.StmtCacheSize) + if errC != nil { + return nil, errC + } else { + stmtCache = _stmtCache + stmtCacheSize = al.StmtCacheSize + } + } + al.Name = aliasName al.DriverName = driverName - al.DB = &DB{ - RWMutex: new(sync.RWMutex), - DB: db, - stmtDecorators: newStmtDecoratorLruWithEvict(), - } + al.DB.stmtDecorators = stmtCache + al.DB.stmtDecoratorsLimit = stmtCacheSize if dr, ok := drivers[driverName]; ok { al.DbBaser = dbBasers[dr] @@ -312,21 +411,50 @@ func addAliasWthDB(aliasName, driverName string, db *sql.DB) (*alias, error) { return nil, fmt.Errorf("register db Ping `%s`, %s", aliasName, err.Error()) } - if !dataBaseCache.add(aliasName, al) { - return nil, fmt.Errorf("DataBase alias name `%s` already registered, cannot reuse", aliasName) - } + detectTZ(al) return al, nil } +// SetMaxIdleConns Change the max idle conns for *sql.DB, use specify database alias name +// Deprecated you should not use this, we will remove it in the future +func SetMaxIdleConns(aliasName string, maxIdleConns int) { + al := getDbAlias(aliasName) + al.SetMaxIdleConns(maxIdleConns) +} + +// SetMaxOpenConns Change the max open conns for *sql.DB, use specify database alias name +// Deprecated you should not use this, we will remove it in the future +func SetMaxOpenConns(aliasName string, maxOpenConns int) { + al := getDbAlias(aliasName) + al.SetMaxOpenConns(maxOpenConns) +} + +// SetMaxIdleConns Change the max idle conns for *sql.DB, use specify database alias name +func (al *alias) SetMaxIdleConns(maxIdleConns int) { + al.MaxIdleConns = maxIdleConns + al.DB.DB.SetMaxIdleConns(maxIdleConns) +} + +// SetMaxOpenConns Change the max open conns for *sql.DB, use specify database alias name +func (al *alias) SetMaxOpenConns(maxOpenConns int) { + al.MaxOpenConns = maxOpenConns + al.DB.DB.SetMaxOpenConns(maxOpenConns) +} + +func (al *alias) SetConnMaxLifetime(lifeTime time.Duration) { + al.ConnMaxLifetime = lifeTime + al.DB.DB.SetConnMaxLifetime(lifeTime) +} + // AddAliasWthDB add a aliasName for the drivename -func AddAliasWthDB(aliasName, driverName string, db *sql.DB) error { - _, err := addAliasWthDB(aliasName, driverName, db) +func AddAliasWthDB(aliasName, driverName string, db *sql.DB, params ...DBOption) error { + _, err := addAliasWthDB(aliasName, driverName, db, params...) return err } // RegisterDataBase Setting the database connect params. Use the database driver self dataSource args. -func RegisterDataBase(aliasName, driverName, dataSource string, params ...int) error { +func RegisterDataBase(aliasName, driverName, dataSource string, params ...DBOption) error { var ( err error db *sql.DB @@ -339,24 +467,13 @@ func RegisterDataBase(aliasName, driverName, dataSource string, params ...int) e goto end } - al, err = addAliasWthDB(aliasName, driverName, db) + al, err = addAliasWthDB(aliasName, driverName, db, params...) if err != nil { goto end } al.DataSource = dataSource - detectTZ(al) - - for i, v := range params { - switch i { - case 0: - SetMaxIdleConns(al.Name, v) - case 1: - SetMaxOpenConns(al.Name, v) - } - } - end: if err != nil { if db != nil { @@ -390,24 +507,6 @@ func SetDataBaseTZ(aliasName string, tz *time.Location) error { return nil } -// SetMaxIdleConns Change the max idle conns for *sql.DB, use specify database alias name -func SetMaxIdleConns(aliasName string, maxIdleConns int) { - al := getDbAlias(aliasName) - al.MaxIdleConns = maxIdleConns - al.DB.DB.SetMaxIdleConns(maxIdleConns) -} - -// SetMaxOpenConns Change the max open conns for *sql.DB, use specify database alias name -func SetMaxOpenConns(aliasName string, maxOpenConns int) { - al := getDbAlias(aliasName) - al.MaxOpenConns = maxOpenConns - al.DB.DB.SetMaxOpenConns(maxOpenConns) - // for tip go 1.2 - if fun := reflect.ValueOf(al.DB).MethodByName("SetMaxOpenConns"); fun.IsValid() { - fun.Call([]reflect.Value{reflect.ValueOf(maxOpenConns)}) - } -} - // GetDB Get *sql.DB from registered database by db alias name. // Use "default" as alias name if you not set. func GetDB(aliasNames ...string) (*sql.DB, error) { @@ -445,7 +544,7 @@ func (s *stmtDecorator) release() { s.wg.Done() } -//garbage recycle for stmt +// garbage recycle for stmt func (s *stmtDecorator) destroy() { go func() { s.wg.Wait() @@ -459,11 +558,42 @@ func newStmtDecorator(sqlStmt *sql.Stmt) *stmtDecorator { } } -func newStmtDecoratorLruWithEvict() *lru.Cache { - // temporarily solution - // we fixed this problem in v2.x - cache, _ := lru.NewWithEvict(50, func(key interface{}, value interface{}) { +func newStmtDecoratorLruWithEvict(cacheSize int) (*lru.Cache, error) { + cache, err := lru.NewWithEvict(cacheSize, func(key interface{}, value interface{}) { value.(*stmtDecorator).destroy() }) - return cache + if err != nil { + return nil, err + } + return cache, nil +} + +type DBOption func(al *alias) + +// MaxIdleConnections return a hint about MaxIdleConnections +func MaxIdleConnections(maxIdleConn int) DBOption { + return func(al *alias) { + al.SetMaxIdleConns(maxIdleConn) + } +} + +// MaxOpenConnections return a hint about MaxOpenConnections +func MaxOpenConnections(maxOpenConn int) DBOption { + return func(al *alias) { + al.SetMaxOpenConns(maxOpenConn) + } +} + +// ConnMaxLifetime return a hint about ConnMaxLifetime +func ConnMaxLifetime(v time.Duration) DBOption { + return func(al *alias) { + al.SetConnMaxLifetime(v) + } +} + +// MaxStmtCacheSize return a hint about MaxStmtCacheSize +func MaxStmtCacheSize(v int) DBOption { + return func(al *alias) { + al.StmtCacheSize = v + } } diff --git a/src/vendor/github.com/beego/beego/orm/db_mysql.go b/src/vendor/github.com/beego/beego/v2/client/orm/db_mysql.go similarity index 72% rename from src/vendor/github.com/beego/beego/orm/db_mysql.go rename to src/vendor/github.com/beego/beego/v2/client/orm/db_mysql.go index 8dd1e7550..75d24b2a7 100644 --- a/src/vendor/github.com/beego/beego/orm/db_mysql.go +++ b/src/vendor/github.com/beego/beego/v2/client/orm/db_mysql.go @@ -15,6 +15,7 @@ package orm import ( + "context" "fmt" "reflect" "strings" @@ -43,24 +44,25 @@ var mysqlOperators = map[string]string{ // mysql column field types. var mysqlTypes = map[string]string{ - "auto": "AUTO_INCREMENT NOT NULL PRIMARY KEY", - "pk": "NOT NULL PRIMARY KEY", - "bool": "bool", - "string": "varchar(%d)", - "string-char": "char(%d)", - "string-text": "longtext", - "time.Time-date": "date", - "time.Time": "datetime", - "int8": "tinyint", - "int16": "smallint", - "int32": "integer", - "int64": "bigint", - "uint8": "tinyint unsigned", - "uint16": "smallint unsigned", - "uint32": "integer unsigned", - "uint64": "bigint unsigned", - "float64": "double precision", - "float64-decimal": "numeric(%d, %d)", + "auto": "AUTO_INCREMENT NOT NULL PRIMARY KEY", + "pk": "NOT NULL PRIMARY KEY", + "bool": "bool", + "string": "varchar(%d)", + "string-char": "char(%d)", + "string-text": "longtext", + "time.Time-date": "date", + "time.Time": "datetime", + "int8": "tinyint", + "int16": "smallint", + "int32": "integer", + "int64": "bigint", + "uint8": "tinyint unsigned", + "uint16": "smallint unsigned", + "uint32": "integer unsigned", + "uint64": "bigint unsigned", + "float64": "double precision", + "float64-decimal": "numeric(%d, %d)", + "time.Time-precision": "datetime(%d)", } // mysql dbBaser implementation. @@ -92,8 +94,8 @@ func (d *dbBaseMysql) ShowColumnsQuery(table string) string { } // execute sql to check index exist. -func (d *dbBaseMysql) IndexExists(db dbQuerier, table string, name string) bool { - row := db.QueryRow("SELECT count(*) FROM information_schema.statistics "+ +func (d *dbBaseMysql) IndexExists(ctx context.Context, db dbQuerier, table string, name string) bool { + row := db.QueryRowContext(ctx, "SELECT count(*) FROM information_schema.statistics "+ "WHERE table_schema = DATABASE() AND table_name = ? AND index_name = ?", table, name) var cnt int row.Scan(&cnt) @@ -104,13 +106,13 @@ func (d *dbBaseMysql) IndexExists(db dbQuerier, table string, name string) bool // If your primary key or unique column conflict will update // If no will insert // Add "`" for mysql sql building -func (d *dbBaseMysql) InsertOrUpdate(q dbQuerier, mi *modelInfo, ind reflect.Value, a *alias, args ...string) (int64, error) { +func (d *dbBaseMysql) InsertOrUpdate(ctx context.Context, q dbQuerier, mi *modelInfo, ind reflect.Value, a *alias, args ...string) (int64, error) { var iouStr string argsMap := map[string]string{} iouStr = "ON DUPLICATE KEY UPDATE" - //Get on the key-value pairs + // Get on the key-value pairs for _, v := range args { kv := strings.Split(v, "=") if len(kv) == 2 { @@ -122,7 +124,6 @@ func (d *dbBaseMysql) InsertOrUpdate(q dbQuerier, mi *modelInfo, ind reflect.Val names := make([]string, 0, len(mi.fields.dbcols)-1) Q := d.ins.TableQuote() values, _, err := d.collectValues(mi, ind, mi.fields.dbcols, true, true, &names, a.TZ) - if err != nil { return 0, err } @@ -154,23 +155,30 @@ func (d *dbBaseMysql) InsertOrUpdate(q dbQuerier, mi *modelInfo, ind reflect.Val if isMulti { qmarks = strings.Repeat(qmarks+"), (", multi-1) + qmarks } - //conflitValue maybe is a int,can`t use fmt.Sprintf + // conflitValue maybe is an int,can`t use fmt.Sprintf query := fmt.Sprintf("INSERT INTO %s%s%s (%s%s%s) VALUES (%s) %s "+qupdates, Q, mi.table, Q, Q, columns, Q, qmarks, iouStr) d.ins.ReplaceMarks(&query) if isMulti || !d.ins.HasReturningID(mi, &query) { - res, err := q.Exec(query, values...) + res, err := q.ExecContext(ctx, query, values...) if err == nil { if isMulti { return res.RowsAffected() } - return res.LastInsertId() + + lastInsertId, err := res.LastInsertId() + if err != nil { + DebugLog.Println(ErrLastInsertIdUnavailable, ':', err) + return lastInsertId, ErrLastInsertIdUnavailable + } else { + return lastInsertId, nil + } } return 0, err } - row := q.QueryRow(query, values...) + row := q.QueryRowContext(ctx, query, values...) var id int64 err = row.Scan(&id) return id, err diff --git a/src/vendor/github.com/beego/beego/orm/db_oracle.go b/src/vendor/github.com/beego/beego/v2/client/orm/db_oracle.go similarity index 57% rename from src/vendor/github.com/beego/beego/orm/db_oracle.go rename to src/vendor/github.com/beego/beego/v2/client/orm/db_oracle.go index 5d121f834..a3b93ff31 100644 --- a/src/vendor/github.com/beego/beego/orm/db_oracle.go +++ b/src/vendor/github.com/beego/beego/v2/client/orm/db_oracle.go @@ -15,8 +15,11 @@ package orm import ( + "context" "fmt" "strings" + + "github.com/beego/beego/v2/client/orm/hints" ) // oracle operators. @@ -31,23 +34,24 @@ var oracleOperators = map[string]string{ // oracle column field types. var oracleTypes = map[string]string{ - "pk": "NOT NULL PRIMARY KEY", - "bool": "bool", - "string": "VARCHAR2(%d)", - "string-char": "CHAR(%d)", - "string-text": "VARCHAR2(%d)", - "time.Time-date": "DATE", - "time.Time": "TIMESTAMP", - "int8": "INTEGER", - "int16": "INTEGER", - "int32": "INTEGER", - "int64": "INTEGER", - "uint8": "INTEGER", - "uint16": "INTEGER", - "uint32": "INTEGER", - "uint64": "INTEGER", - "float64": "NUMBER", - "float64-decimal": "NUMBER(%d, %d)", + "pk": "NOT NULL PRIMARY KEY", + "bool": "bool", + "string": "VARCHAR2(%d)", + "string-char": "CHAR(%d)", + "string-text": "VARCHAR2(%d)", + "time.Time-date": "DATE", + "time.Time": "TIMESTAMP", + "int8": "INTEGER", + "int16": "INTEGER", + "int32": "INTEGER", + "int64": "INTEGER", + "uint8": "INTEGER", + "uint16": "INTEGER", + "uint32": "INTEGER", + "uint64": "INTEGER", + "float64": "NUMBER", + "float64-decimal": "NUMBER(%d, %d)", + "time.Time-precision": "TIMESTAMP(%d)", } // oracle dbBaser @@ -74,7 +78,7 @@ func (d *dbBaseOracle) DbTypes() map[string]string { return oracleTypes } -//ShowTablesQuery show all the tables in database +// ShowTablesQuery show all the tables in database func (d *dbBaseOracle) ShowTablesQuery() string { return "SELECT TABLE_NAME FROM USER_TABLES" } @@ -86,8 +90,8 @@ func (d *dbBaseOracle) ShowColumnsQuery(table string) string { } // check index is exist -func (d *dbBaseOracle) IndexExists(db dbQuerier, table string, name string) bool { - row := db.QueryRow("SELECT COUNT(*) FROM USER_IND_COLUMNS, USER_INDEXES "+ +func (d *dbBaseOracle) IndexExists(ctx context.Context, db dbQuerier, table string, name string) bool { + row := db.QueryRowContext(ctx, "SELECT COUNT(*) FROM USER_IND_COLUMNS, USER_INDEXES "+ "WHERE USER_IND_COLUMNS.INDEX_NAME = USER_INDEXES.INDEX_NAME "+ "AND USER_IND_COLUMNS.TABLE_NAME = ? AND USER_IND_COLUMNS.INDEX_NAME = ?", strings.ToUpper(table), strings.ToUpper(name)) @@ -96,9 +100,32 @@ func (d *dbBaseOracle) IndexExists(db dbQuerier, table string, name string) bool return cnt > 0 } +func (d *dbBaseOracle) GenerateSpecifyIndex(tableName string, useIndex int, indexes []string) string { + var s []string + Q := d.TableQuote() + for _, index := range indexes { + tmp := fmt.Sprintf(`%s%s%s`, Q, index, Q) + s = append(s, tmp) + } + + var hint string + + switch useIndex { + case hints.KeyUseIndex, hints.KeyForceIndex: + hint = `INDEX` + case hints.KeyIgnoreIndex: + hint = `NO_INDEX` + default: + DebugLog.Println("[WARN] Not a valid specifying action, so that action is ignored") + return `` + } + + return fmt.Sprintf(` /*+ %s(%s %s)*/ `, hint, tableName, strings.Join(s, `,`)) +} + // execute insert sql with given struct and given values. // insert the given values, not the field values in struct. -func (d *dbBaseOracle) InsertValue(q dbQuerier, mi *modelInfo, isMulti bool, names []string, values []interface{}) (int64, error) { +func (d *dbBaseOracle) InsertValue(ctx context.Context, q dbQuerier, mi *modelInfo, isMulti bool, names []string, values []interface{}) (int64, error) { Q := d.ins.TableQuote() marks := make([]string, len(names)) @@ -121,16 +148,23 @@ func (d *dbBaseOracle) InsertValue(q dbQuerier, mi *modelInfo, isMulti bool, nam d.ins.ReplaceMarks(&query) if isMulti || !d.ins.HasReturningID(mi, &query) { - res, err := q.Exec(query, values...) + res, err := q.ExecContext(ctx, query, values...) if err == nil { if isMulti { return res.RowsAffected() } - return res.LastInsertId() + + lastInsertId, err := res.LastInsertId() + if err != nil { + DebugLog.Println(ErrLastInsertIdUnavailable, ':', err) + return lastInsertId, ErrLastInsertIdUnavailable + } else { + return lastInsertId, nil + } } return 0, err } - row := q.QueryRow(query, values...) + row := q.QueryRowContext(ctx, query, values...) var id int64 err := row.Scan(&id) return id, err diff --git a/src/vendor/github.com/beego/beego/orm/db_postgres.go b/src/vendor/github.com/beego/beego/v2/client/orm/db_postgres.go similarity index 72% rename from src/vendor/github.com/beego/beego/orm/db_postgres.go rename to src/vendor/github.com/beego/beego/v2/client/orm/db_postgres.go index c488fb388..b2f321db6 100644 --- a/src/vendor/github.com/beego/beego/orm/db_postgres.go +++ b/src/vendor/github.com/beego/beego/v2/client/orm/db_postgres.go @@ -15,6 +15,7 @@ package orm import ( + "context" "fmt" "strconv" ) @@ -39,26 +40,27 @@ var postgresOperators = map[string]string{ // postgresql column field types. var postgresTypes = map[string]string{ - "auto": "serial NOT NULL PRIMARY KEY", - "pk": "NOT NULL PRIMARY KEY", - "bool": "bool", - "string": "varchar(%d)", - "string-char": "char(%d)", - "string-text": "text", - "time.Time-date": "date", - "time.Time": "timestamp with time zone", - "int8": `smallint CHECK("%COL%" >= -127 AND "%COL%" <= 128)`, - "int16": "smallint", - "int32": "integer", - "int64": "bigint", - "uint8": `smallint CHECK("%COL%" >= 0 AND "%COL%" <= 255)`, - "uint16": `integer CHECK("%COL%" >= 0)`, - "uint32": `bigint CHECK("%COL%" >= 0)`, - "uint64": `bigint CHECK("%COL%" >= 0)`, - "float64": "double precision", - "float64-decimal": "numeric(%d, %d)", - "json": "json", - "jsonb": "jsonb", + "auto": "serial NOT NULL PRIMARY KEY", + "pk": "NOT NULL PRIMARY KEY", + "bool": "bool", + "string": "varchar(%d)", + "string-char": "char(%d)", + "string-text": "text", + "time.Time-date": "date", + "time.Time": "timestamp with time zone", + "int8": `smallint CHECK("%COL%" >= -127 AND "%COL%" <= 128)`, + "int16": "smallint", + "int32": "integer", + "int64": "bigint", + "uint8": `smallint CHECK("%COL%" >= 0 AND "%COL%" <= 255)`, + "uint16": `integer CHECK("%COL%" >= 0)`, + "uint32": `bigint CHECK("%COL%" >= 0)`, + "uint64": `bigint CHECK("%COL%" >= 0)`, + "float64": "double precision", + "float64-decimal": "numeric(%d, %d)", + "json": "json", + "jsonb": "jsonb", + "time.Time-precision": "timestamp(%d) with time zone", } // postgresql dbBaser. @@ -139,7 +141,7 @@ func (d *dbBasePostgres) HasReturningID(mi *modelInfo, query *string) bool { } // sync auto key -func (d *dbBasePostgres) setval(db dbQuerier, mi *modelInfo, autoFields []string) error { +func (d *dbBasePostgres) setval(ctx context.Context, db dbQuerier, mi *modelInfo, autoFields []string) error { if len(autoFields) == 0 { return nil } @@ -150,7 +152,7 @@ func (d *dbBasePostgres) setval(db dbQuerier, mi *modelInfo, autoFields []string mi.table, name, Q, name, Q, Q, mi.table, Q) - if _, err := db.Exec(query); err != nil { + if _, err := db.ExecContext(ctx, query); err != nil { return err } } @@ -173,14 +175,20 @@ func (d *dbBasePostgres) DbTypes() map[string]string { } // check index exist in postgresql. -func (d *dbBasePostgres) IndexExists(db dbQuerier, table string, name string) bool { +func (d *dbBasePostgres) IndexExists(ctx context.Context, db dbQuerier, table string, name string) bool { query := fmt.Sprintf("SELECT COUNT(*) FROM pg_indexes WHERE tablename = '%s' AND indexname = '%s'", table, name) - row := db.QueryRow(query) + row := db.QueryRowContext(ctx, query) var cnt int row.Scan(&cnt) return cnt > 0 } +// GenerateSpecifyIndex return a specifying index clause +func (d *dbBasePostgres) GenerateSpecifyIndex(tableName string, useIndex int, indexes []string) string { + DebugLog.Println("[WARN] Not support any specifying index action, so that action is ignored") + return `` +} + // create new postgresql dbBaser. func newdbBasePostgres() dbBaser { b := new(dbBasePostgres) diff --git a/src/vendor/github.com/beego/beego/orm/db_sqlite.go b/src/vendor/github.com/beego/beego/v2/client/orm/db_sqlite.go similarity index 64% rename from src/vendor/github.com/beego/beego/orm/db_sqlite.go rename to src/vendor/github.com/beego/beego/v2/client/orm/db_sqlite.go index 1d62ee348..6a4b31312 100644 --- a/src/vendor/github.com/beego/beego/orm/db_sqlite.go +++ b/src/vendor/github.com/beego/beego/v2/client/orm/db_sqlite.go @@ -15,10 +15,14 @@ package orm import ( + "context" "database/sql" "fmt" "reflect" + "strings" "time" + + "github.com/beego/beego/v2/client/orm/hints" ) // sqlite operators. @@ -41,24 +45,25 @@ var sqliteOperators = map[string]string{ // sqlite column types. var sqliteTypes = map[string]string{ - "auto": "integer NOT NULL PRIMARY KEY AUTOINCREMENT", - "pk": "NOT NULL PRIMARY KEY", - "bool": "bool", - "string": "varchar(%d)", - "string-char": "character(%d)", - "string-text": "text", - "time.Time-date": "date", - "time.Time": "datetime", - "int8": "tinyint", - "int16": "smallint", - "int32": "integer", - "int64": "bigint", - "uint8": "tinyint unsigned", - "uint16": "smallint unsigned", - "uint32": "integer unsigned", - "uint64": "bigint unsigned", - "float64": "real", - "float64-decimal": "decimal", + "auto": "integer NOT NULL PRIMARY KEY AUTOINCREMENT", + "pk": "NOT NULL PRIMARY KEY", + "bool": "bool", + "string": "varchar(%d)", + "string-char": "character(%d)", + "string-text": "text", + "time.Time-date": "date", + "time.Time": "datetime", + "time.Time-precision": "datetime(%d)", + "int8": "tinyint", + "int16": "smallint", + "int32": "integer", + "int64": "bigint", + "uint8": "tinyint unsigned", + "uint16": "smallint unsigned", + "uint32": "integer unsigned", + "uint64": "bigint unsigned", + "float64": "real", + "float64-decimal": "decimal", } // sqlite dbBaser. @@ -69,11 +74,11 @@ type dbBaseSqlite struct { var _ dbBaser = new(dbBaseSqlite) // override base db read for update behavior as SQlite does not support syntax -func (d *dbBaseSqlite) Read(q dbQuerier, mi *modelInfo, ind reflect.Value, tz *time.Location, cols []string, isForUpdate bool) error { +func (d *dbBaseSqlite) Read(ctx context.Context, q dbQuerier, mi *modelInfo, ind reflect.Value, tz *time.Location, cols []string, isForUpdate bool) error { if isForUpdate { DebugLog.Println("[WARN] SQLite does not support SELECT FOR UPDATE query, isForUpdate param is ignored and always as false to do the work") } - return d.dbBase.Read(q, mi, ind, tz, cols, false) + return d.dbBase.Read(ctx, q, mi, ind, tz, cols, false) } // get sqlite operator. @@ -110,9 +115,9 @@ func (d *dbBaseSqlite) ShowTablesQuery() string { } // get columns in sqlite. -func (d *dbBaseSqlite) GetColumns(db dbQuerier, table string) (map[string][3]string, error) { +func (d *dbBaseSqlite) GetColumns(ctx context.Context, db dbQuerier, table string) (map[string][3]string, error) { query := d.ins.ShowColumnsQuery(table) - rows, err := db.Query(query) + rows, err := db.QueryContext(ctx, query) if err != nil { return nil, err } @@ -136,9 +141,9 @@ func (d *dbBaseSqlite) ShowColumnsQuery(table string) string { } // check index exist in sqlite. -func (d *dbBaseSqlite) IndexExists(db dbQuerier, table string, name string) bool { +func (d *dbBaseSqlite) IndexExists(ctx context.Context, db dbQuerier, table string, name string) bool { query := fmt.Sprintf("PRAGMA index_list('%s')", table) - rows, err := db.Query(query) + rows, err := db.QueryContext(ctx, query) if err != nil { panic(err) } @@ -153,6 +158,24 @@ func (d *dbBaseSqlite) IndexExists(db dbQuerier, table string, name string) bool return false } +// GenerateSpecifyIndex return a specifying index clause +func (d *dbBaseSqlite) GenerateSpecifyIndex(tableName string, useIndex int, indexes []string) string { + var s []string + Q := d.TableQuote() + for _, index := range indexes { + tmp := fmt.Sprintf(`%s%s%s`, Q, index, Q) + s = append(s, tmp) + } + + switch useIndex { + case hints.KeyUseIndex, hints.KeyForceIndex: + return fmt.Sprintf(` INDEXED BY %s `, strings.Join(s, `,`)) + default: + DebugLog.Println("[WARN] Not a valid specifying action, so that action is ignored") + return `` + } +} + // create new sqlite dbBaser. func newdbBaseSqlite() dbBaser { b := new(dbBaseSqlite) diff --git a/src/vendor/github.com/beego/beego/orm/db_tables.go b/src/vendor/github.com/beego/beego/v2/client/orm/db_tables.go similarity index 88% rename from src/vendor/github.com/beego/beego/orm/db_tables.go rename to src/vendor/github.com/beego/beego/v2/client/orm/db_tables.go index 4b21a6fc7..a0b355ca2 100644 --- a/src/vendor/github.com/beego/beego/orm/db_tables.go +++ b/src/vendor/github.com/beego/beego/v2/client/orm/db_tables.go @@ -18,6 +18,9 @@ import ( "fmt" "strings" "time" + + "github.com/beego/beego/v2/client/orm/clauses" + "github.com/beego/beego/v2/client/orm/clauses/order_clause" ) // table info struct. @@ -103,7 +106,6 @@ func (t *dbTables) loopDepth(depth int, prefix string, fi *fieldInfo, related [] // parse related fields. func (t *dbTables) parseRelated(rels []string, depth int) { - relsNum := len(rels) related := make([]string, relsNum) copy(related, rels) @@ -421,7 +423,7 @@ func (t *dbTables) getGroupSQL(groups []string) (groupSQL string) { } // generate order sql. -func (t *dbTables) getOrderSQL(orders []string) (orderSQL string) { +func (t *dbTables) getOrderSQL(orders []*order_clause.Order) (orderSQL string) { if len(orders) == 0 { return } @@ -430,19 +432,25 @@ func (t *dbTables) getOrderSQL(orders []string) (orderSQL string) { orderSqls := make([]string, 0, len(orders)) for _, order := range orders { - asc := "ASC" - if order[0] == '-' { - asc = "DESC" - order = order[1:] - } - exprs := strings.Split(order, ExprSep) + column := order.GetColumn() + clause := strings.Split(column, clauses.ExprDot) - index, _, fi, suc := t.parseExprs(t.mi, exprs) - if !suc { - panic(fmt.Errorf("unknown field/column name `%s`", strings.Join(exprs, ExprSep))) - } + if order.IsRaw() { + if len(clause) == 2 { + orderSqls = append(orderSqls, fmt.Sprintf("%s.%s%s%s %s", clause[0], Q, clause[1], Q, order.SortString())) + } else if len(clause) == 1 { + orderSqls = append(orderSqls, fmt.Sprintf("%s%s%s %s", Q, clause[0], Q, order.SortString())) + } else { + panic(fmt.Errorf("unknown field/column name `%s`", strings.Join(clause, ExprSep))) + } + } else { + index, _, fi, suc := t.parseExprs(t.mi, clause) + if !suc { + panic(fmt.Errorf("unknown field/column name `%s`", strings.Join(clause, ExprSep))) + } - orderSqls = append(orderSqls, fmt.Sprintf("%s.%s%s%s %s", index, Q, fi.column, Q, asc)) + orderSqls = append(orderSqls, fmt.Sprintf("%s.%s%s%s %s", index, Q, fi.column, Q, order.SortString())) + } } orderSQL = fmt.Sprintf("ORDER BY %s ", strings.Join(orderSqls, ", ")) @@ -472,6 +480,15 @@ func (t *dbTables) getLimitSQL(mi *modelInfo, offset int64, limit int64) (limits return } +// getIndexSql generate index sql. +func (t *dbTables) getIndexSql(tableName string, useIndex int, indexes []string) (clause string) { + if len(indexes) == 0 { + return + } + + return t.base.GenerateSpecifyIndex(tableName, useIndex, indexes) +} + // crete new tables collection. func newDbTables(mi *modelInfo, base dbBaser) *dbTables { tables := &dbTables{} diff --git a/src/vendor/github.com/beego/beego/orm/db_tidb.go b/src/vendor/github.com/beego/beego/v2/client/orm/db_tidb.go similarity index 89% rename from src/vendor/github.com/beego/beego/orm/db_tidb.go rename to src/vendor/github.com/beego/beego/v2/client/orm/db_tidb.go index 6020a488f..48c5b4e73 100644 --- a/src/vendor/github.com/beego/beego/orm/db_tidb.go +++ b/src/vendor/github.com/beego/beego/v2/client/orm/db_tidb.go @@ -15,6 +15,7 @@ package orm import ( + "context" "fmt" ) @@ -47,8 +48,8 @@ func (d *dbBaseTidb) ShowColumnsQuery(table string) string { } // execute sql to check index exist. -func (d *dbBaseTidb) IndexExists(db dbQuerier, table string, name string) bool { - row := db.QueryRow("SELECT count(*) FROM information_schema.statistics "+ +func (d *dbBaseTidb) IndexExists(ctx context.Context, db dbQuerier, table string, name string) bool { + row := db.QueryRowContext(ctx, "SELECT count(*) FROM information_schema.statistics "+ "WHERE table_schema = DATABASE() AND table_name = ? AND index_name = ?", table, name) var cnt int row.Scan(&cnt) diff --git a/src/vendor/github.com/beego/beego/orm/db_utils.go b/src/vendor/github.com/beego/beego/v2/client/orm/db_utils.go similarity index 98% rename from src/vendor/github.com/beego/beego/orm/db_utils.go rename to src/vendor/github.com/beego/beego/v2/client/orm/db_utils.go index 7ae10ca5e..01f5a028f 100644 --- a/src/vendor/github.com/beego/beego/orm/db_utils.go +++ b/src/vendor/github.com/beego/beego/v2/client/orm/db_utils.go @@ -55,16 +55,14 @@ func getExistPk(mi *modelInfo, ind reflect.Value) (column string, value interfac // get fields description as flatted string. func getFlatParams(fi *fieldInfo, args []interface{}, tz *time.Location) (params []interface{}) { - outFor: for _, arg := range args { - val := reflect.ValueOf(arg) - if arg == nil { params = append(params, arg) continue } + val := reflect.ValueOf(arg) kind := val.Kind() if kind == reflect.Ptr { val = val.Elem() @@ -158,7 +156,7 @@ outFor: typ := val.Type() name := getFullName(typ) var value interface{} - if mmi, ok := modelCache.getByFullName(name); ok { + if mmi, ok := defaultModelCache.getByFullName(name); ok { if _, vu, exist := getExistPk(mmi, val); exist { value = vu } diff --git a/src/vendor/github.com/beego/beego/v2/client/orm/do_nothing_orm.go b/src/vendor/github.com/beego/beego/v2/client/orm/do_nothing_orm.go new file mode 100644 index 000000000..d9e574a50 --- /dev/null +++ b/src/vendor/github.com/beego/beego/v2/client/orm/do_nothing_orm.go @@ -0,0 +1,181 @@ +// Copyright 2020 beego +// +// 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 orm + +import ( + "context" + "database/sql" + + "github.com/beego/beego/v2/core/utils" +) + +// DoNothingOrm won't do anything, usually you use this to custom your mock Ormer implementation +// I think golang mocking interface is hard to use +// this may help you to integrate with Ormer + +var _ Ormer = new(DoNothingOrm) + +type DoNothingOrm struct{} + +func (d *DoNothingOrm) Read(md interface{}, cols ...string) error { + return nil +} + +func (d *DoNothingOrm) ReadWithCtx(ctx context.Context, md interface{}, cols ...string) error { + return nil +} + +func (d *DoNothingOrm) ReadForUpdate(md interface{}, cols ...string) error { + return nil +} + +func (d *DoNothingOrm) ReadForUpdateWithCtx(ctx context.Context, md interface{}, cols ...string) error { + return nil +} + +func (d *DoNothingOrm) ReadOrCreate(md interface{}, col1 string, cols ...string) (bool, int64, error) { + return false, 0, nil +} + +func (d *DoNothingOrm) ReadOrCreateWithCtx(ctx context.Context, md interface{}, col1 string, cols ...string) (bool, int64, error) { + return false, 0, nil +} + +func (d *DoNothingOrm) LoadRelated(md interface{}, name string, args ...utils.KV) (int64, error) { + return 0, nil +} + +func (d *DoNothingOrm) LoadRelatedWithCtx(ctx context.Context, md interface{}, name string, args ...utils.KV) (int64, error) { + return 0, nil +} + +func (d *DoNothingOrm) QueryM2M(md interface{}, name string) QueryM2Mer { + return nil +} + +// NOTE: this method is deprecated, context parameter will not take effect. +func (d *DoNothingOrm) QueryM2MWithCtx(ctx context.Context, md interface{}, name string) QueryM2Mer { + return nil +} + +func (d *DoNothingOrm) QueryTable(ptrStructOrTableName interface{}) QuerySeter { + return nil +} + +// NOTE: this method is deprecated, context parameter will not take effect. +func (d *DoNothingOrm) QueryTableWithCtx(ctx context.Context, ptrStructOrTableName interface{}) QuerySeter { + return nil +} + +func (d *DoNothingOrm) DBStats() *sql.DBStats { + return nil +} + +func (d *DoNothingOrm) Insert(md interface{}) (int64, error) { + return 0, nil +} + +func (d *DoNothingOrm) InsertWithCtx(ctx context.Context, md interface{}) (int64, error) { + return 0, nil +} + +func (d *DoNothingOrm) InsertOrUpdate(md interface{}, colConflitAndArgs ...string) (int64, error) { + return 0, nil +} + +func (d *DoNothingOrm) InsertOrUpdateWithCtx(ctx context.Context, md interface{}, colConflitAndArgs ...string) (int64, error) { + return 0, nil +} + +func (d *DoNothingOrm) InsertMulti(bulk int, mds interface{}) (int64, error) { + return 0, nil +} + +func (d *DoNothingOrm) InsertMultiWithCtx(ctx context.Context, bulk int, mds interface{}) (int64, error) { + return 0, nil +} + +func (d *DoNothingOrm) Update(md interface{}, cols ...string) (int64, error) { + return 0, nil +} + +func (d *DoNothingOrm) UpdateWithCtx(ctx context.Context, md interface{}, cols ...string) (int64, error) { + return 0, nil +} + +func (d *DoNothingOrm) Delete(md interface{}, cols ...string) (int64, error) { + return 0, nil +} + +func (d *DoNothingOrm) DeleteWithCtx(ctx context.Context, md interface{}, cols ...string) (int64, error) { + return 0, nil +} + +func (d *DoNothingOrm) Raw(query string, args ...interface{}) RawSeter { + return nil +} + +func (d *DoNothingOrm) RawWithCtx(ctx context.Context, query string, args ...interface{}) RawSeter { + return nil +} + +func (d *DoNothingOrm) Driver() Driver { + return nil +} + +func (d *DoNothingOrm) Begin() (TxOrmer, error) { + return nil, nil +} + +func (d *DoNothingOrm) BeginWithCtx(ctx context.Context) (TxOrmer, error) { + return nil, nil +} + +func (d *DoNothingOrm) BeginWithOpts(opts *sql.TxOptions) (TxOrmer, error) { + return nil, nil +} + +func (d *DoNothingOrm) BeginWithCtxAndOpts(ctx context.Context, opts *sql.TxOptions) (TxOrmer, error) { + return nil, nil +} + +func (d *DoNothingOrm) DoTx(task func(ctx context.Context, txOrm TxOrmer) error) error { + return nil +} + +func (d *DoNothingOrm) DoTxWithCtx(ctx context.Context, task func(ctx context.Context, txOrm TxOrmer) error) error { + return nil +} + +func (d *DoNothingOrm) DoTxWithOpts(opts *sql.TxOptions, task func(ctx context.Context, txOrm TxOrmer) error) error { + return nil +} + +func (d *DoNothingOrm) DoTxWithCtxAndOpts(ctx context.Context, opts *sql.TxOptions, task func(ctx context.Context, txOrm TxOrmer) error) error { + return nil +} + +// DoNothingTxOrm is similar with DoNothingOrm, usually you use it to test +type DoNothingTxOrm struct { + DoNothingOrm +} + +func (d *DoNothingTxOrm) Commit() error { + return nil +} + +func (d *DoNothingTxOrm) Rollback() error { + return nil +} diff --git a/src/vendor/github.com/beego/beego/v2/client/orm/filter.go b/src/vendor/github.com/beego/beego/v2/client/orm/filter.go new file mode 100644 index 000000000..bc13c3fa4 --- /dev/null +++ b/src/vendor/github.com/beego/beego/v2/client/orm/filter.go @@ -0,0 +1,40 @@ +// Copyright 2020 beego +// +// 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 orm + +import ( + "context" +) + +// FilterChain is used to build a Filter +// don't forget to call next(...) inside your Filter +type FilterChain func(next Filter) Filter + +// Filter's behavior is a little big strange. +// it's only be called when users call methods of Ormer +// return value is an array. it's a little bit hard to understand, +// for example, the Ormer's Read method only return error +// so the filter processing this method should return an array whose first element is error +// and, Ormer's ReadOrCreateWithCtx return three values, so the Filter's result should contains three values +type Filter func(ctx context.Context, inv *Invocation) []interface{} + +var globalFilterChains = make([]FilterChain, 0, 4) + +// AddGlobalFilterChain adds a new FilterChain +// All orm instances built after this invocation will use this filterChain, +// but instances built before this invocation will not be affected +func AddGlobalFilterChain(filterChain ...FilterChain) { + globalFilterChains = append(globalFilterChains, filterChain...) +} diff --git a/src/vendor/github.com/beego/beego/v2/client/orm/filter_orm_decorator.go b/src/vendor/github.com/beego/beego/v2/client/orm/filter_orm_decorator.go new file mode 100644 index 000000000..3b23284d7 --- /dev/null +++ b/src/vendor/github.com/beego/beego/v2/client/orm/filter_orm_decorator.go @@ -0,0 +1,534 @@ +// Copyright 2020 beego +// +// 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 orm + +import ( + "context" + "database/sql" + "reflect" + "time" + + "github.com/beego/beego/v2/core/logs" + "github.com/beego/beego/v2/core/utils" +) + +const ( + TxNameKey = "TxName" +) + +var ( + _ Ormer = new(filterOrmDecorator) + _ TxOrmer = new(filterOrmDecorator) +) + +type filterOrmDecorator struct { + ormer + TxBeginner + TxCommitter + + root Filter + + insideTx bool + txStartTime time.Time + txName string +} + +func NewFilterOrmDecorator(delegate Ormer, filterChains ...FilterChain) Ormer { + res := &filterOrmDecorator{ + ormer: delegate, + TxBeginner: delegate, + root: func(ctx context.Context, inv *Invocation) []interface{} { + return inv.execute(ctx) + }, + } + + for i := len(filterChains) - 1; i >= 0; i-- { + node := filterChains[i] + res.root = node(res.root) + } + return res +} + +func NewFilterTxOrmDecorator(delegate TxOrmer, root Filter, txName string) TxOrmer { + res := &filterOrmDecorator{ + ormer: delegate, + TxCommitter: delegate, + root: root, + insideTx: true, + txStartTime: time.Now(), + txName: txName, + } + return res +} + +func (f *filterOrmDecorator) Read(md interface{}, cols ...string) error { + return f.ReadWithCtx(context.Background(), md, cols...) +} + +func (f *filterOrmDecorator) ReadWithCtx(ctx context.Context, md interface{}, cols ...string) error { + mi, _ := defaultModelCache.getByMd(md) + inv := &Invocation{ + Method: "ReadWithCtx", + Args: []interface{}{md, cols}, + Md: md, + mi: mi, + InsideTx: f.insideTx, + TxStartTime: f.txStartTime, + f: func(c context.Context) []interface{} { + err := f.ormer.ReadWithCtx(c, md, cols...) + return []interface{}{err} + }, + } + res := f.root(ctx, inv) + return f.convertError(res[0]) +} + +func (f *filterOrmDecorator) ReadForUpdate(md interface{}, cols ...string) error { + return f.ReadForUpdateWithCtx(context.Background(), md, cols...) +} + +func (f *filterOrmDecorator) ReadForUpdateWithCtx(ctx context.Context, md interface{}, cols ...string) error { + mi, _ := defaultModelCache.getByMd(md) + inv := &Invocation{ + Method: "ReadForUpdateWithCtx", + Args: []interface{}{md, cols}, + Md: md, + mi: mi, + InsideTx: f.insideTx, + TxStartTime: f.txStartTime, + f: func(c context.Context) []interface{} { + err := f.ormer.ReadForUpdateWithCtx(c, md, cols...) + return []interface{}{err} + }, + } + res := f.root(ctx, inv) + return f.convertError(res[0]) +} + +func (f *filterOrmDecorator) ReadOrCreate(md interface{}, col1 string, cols ...string) (bool, int64, error) { + return f.ReadOrCreateWithCtx(context.Background(), md, col1, cols...) +} + +func (f *filterOrmDecorator) ReadOrCreateWithCtx(ctx context.Context, md interface{}, col1 string, cols ...string) (bool, int64, error) { + mi, _ := defaultModelCache.getByMd(md) + inv := &Invocation{ + Method: "ReadOrCreateWithCtx", + Args: []interface{}{md, col1, cols}, + Md: md, + mi: mi, + InsideTx: f.insideTx, + TxStartTime: f.txStartTime, + f: func(c context.Context) []interface{} { + ok, res, err := f.ormer.ReadOrCreateWithCtx(c, md, col1, cols...) + return []interface{}{ok, res, err} + }, + } + res := f.root(ctx, inv) + return res[0].(bool), res[1].(int64), f.convertError(res[2]) +} + +func (f *filterOrmDecorator) LoadRelated(md interface{}, name string, args ...utils.KV) (int64, error) { + return f.LoadRelatedWithCtx(context.Background(), md, name, args...) +} + +func (f *filterOrmDecorator) LoadRelatedWithCtx(ctx context.Context, md interface{}, name string, args ...utils.KV) (int64, error) { + mi, _ := defaultModelCache.getByMd(md) + inv := &Invocation{ + Method: "LoadRelatedWithCtx", + Args: []interface{}{md, name, args}, + Md: md, + mi: mi, + InsideTx: f.insideTx, + TxStartTime: f.txStartTime, + f: func(c context.Context) []interface{} { + res, err := f.ormer.LoadRelatedWithCtx(c, md, name, args...) + return []interface{}{res, err} + }, + } + res := f.root(ctx, inv) + return res[0].(int64), f.convertError(res[1]) +} + +func (f *filterOrmDecorator) QueryM2M(md interface{}, name string) QueryM2Mer { + mi, _ := defaultModelCache.getByMd(md) + inv := &Invocation{ + Method: "QueryM2M", + Args: []interface{}{md, name}, + Md: md, + mi: mi, + InsideTx: f.insideTx, + TxStartTime: f.txStartTime, + f: func(c context.Context) []interface{} { + res := f.ormer.QueryM2M(md, name) + return []interface{}{res} + }, + } + res := f.root(context.Background(), inv) + if res[0] == nil { + return nil + } + return res[0].(QueryM2Mer) +} + +// NOTE: this method is deprecated, context parameter will not take effect. +func (f *filterOrmDecorator) QueryM2MWithCtx(_ context.Context, md interface{}, name string) QueryM2Mer { + logs.Warn("QueryM2MWithCtx is DEPRECATED. Use methods with `WithCtx` on QueryM2Mer suffix as replacement.") + return f.QueryM2M(md, name) +} + +func (f *filterOrmDecorator) QueryTable(ptrStructOrTableName interface{}) QuerySeter { + var ( + name string + md interface{} + mi *modelInfo + ) + + if table, ok := ptrStructOrTableName.(string); ok { + name = table + } else { + name = getFullName(indirectType(reflect.TypeOf(ptrStructOrTableName))) + md = ptrStructOrTableName + } + + if m, ok := defaultModelCache.getByFullName(name); ok { + mi = m + } + + inv := &Invocation{ + Method: "QueryTable", + Args: []interface{}{ptrStructOrTableName}, + InsideTx: f.insideTx, + TxStartTime: f.txStartTime, + Md: md, + mi: mi, + f: func(c context.Context) []interface{} { + res := f.ormer.QueryTable(ptrStructOrTableName) + return []interface{}{res} + }, + } + res := f.root(context.Background(), inv) + + if res[0] == nil { + return nil + } + return res[0].(QuerySeter) +} + +// NOTE: this method is deprecated, context parameter will not take effect. +func (f *filterOrmDecorator) QueryTableWithCtx(_ context.Context, ptrStructOrTableName interface{}) QuerySeter { + logs.Warn("QueryTableWithCtx is DEPRECATED. Use methods with `WithCtx`on QuerySeter suffix as replacement.") + return f.QueryTable(ptrStructOrTableName) +} + +func (f *filterOrmDecorator) DBStats() *sql.DBStats { + inv := &Invocation{ + Method: "DBStats", + InsideTx: f.insideTx, + TxStartTime: f.txStartTime, + f: func(c context.Context) []interface{} { + res := f.ormer.DBStats() + return []interface{}{res} + }, + } + res := f.root(context.Background(), inv) + + if res[0] == nil { + return nil + } + + return res[0].(*sql.DBStats) +} + +func (f *filterOrmDecorator) Insert(md interface{}) (int64, error) { + return f.InsertWithCtx(context.Background(), md) +} + +func (f *filterOrmDecorator) InsertWithCtx(ctx context.Context, md interface{}) (int64, error) { + mi, _ := defaultModelCache.getByMd(md) + inv := &Invocation{ + Method: "InsertWithCtx", + Args: []interface{}{md}, + Md: md, + mi: mi, + InsideTx: f.insideTx, + TxStartTime: f.txStartTime, + f: func(c context.Context) []interface{} { + res, err := f.ormer.InsertWithCtx(c, md) + return []interface{}{res, err} + }, + } + res := f.root(ctx, inv) + return res[0].(int64), f.convertError(res[1]) +} + +func (f *filterOrmDecorator) InsertOrUpdate(md interface{}, colConflitAndArgs ...string) (int64, error) { + return f.InsertOrUpdateWithCtx(context.Background(), md, colConflitAndArgs...) +} + +func (f *filterOrmDecorator) InsertOrUpdateWithCtx(ctx context.Context, md interface{}, colConflitAndArgs ...string) (int64, error) { + mi, _ := defaultModelCache.getByMd(md) + inv := &Invocation{ + Method: "InsertOrUpdateWithCtx", + Args: []interface{}{md, colConflitAndArgs}, + Md: md, + mi: mi, + InsideTx: f.insideTx, + TxStartTime: f.txStartTime, + f: func(c context.Context) []interface{} { + res, err := f.ormer.InsertOrUpdateWithCtx(c, md, colConflitAndArgs...) + return []interface{}{res, err} + }, + } + res := f.root(ctx, inv) + return res[0].(int64), f.convertError(res[1]) +} + +func (f *filterOrmDecorator) InsertMulti(bulk int, mds interface{}) (int64, error) { + return f.InsertMultiWithCtx(context.Background(), bulk, mds) +} + +// InsertMultiWithCtx uses the first element's model info +func (f *filterOrmDecorator) InsertMultiWithCtx(ctx context.Context, bulk int, mds interface{}) (int64, error) { + var ( + md interface{} + mi *modelInfo + ) + + sind := reflect.Indirect(reflect.ValueOf(mds)) + + if (sind.Kind() == reflect.Array || sind.Kind() == reflect.Slice) && sind.Len() > 0 { + ind := reflect.Indirect(sind.Index(0)) + md = ind.Interface() + mi, _ = defaultModelCache.getByMd(md) + } + + inv := &Invocation{ + Method: "InsertMultiWithCtx", + Args: []interface{}{bulk, mds}, + Md: md, + mi: mi, + InsideTx: f.insideTx, + TxStartTime: f.txStartTime, + f: func(c context.Context) []interface{} { + res, err := f.ormer.InsertMultiWithCtx(c, bulk, mds) + return []interface{}{res, err} + }, + } + res := f.root(ctx, inv) + return res[0].(int64), f.convertError(res[1]) +} + +func (f *filterOrmDecorator) Update(md interface{}, cols ...string) (int64, error) { + return f.UpdateWithCtx(context.Background(), md, cols...) +} + +func (f *filterOrmDecorator) UpdateWithCtx(ctx context.Context, md interface{}, cols ...string) (int64, error) { + mi, _ := defaultModelCache.getByMd(md) + inv := &Invocation{ + Method: "UpdateWithCtx", + Args: []interface{}{md, cols}, + Md: md, + mi: mi, + InsideTx: f.insideTx, + TxStartTime: f.txStartTime, + f: func(c context.Context) []interface{} { + res, err := f.ormer.UpdateWithCtx(c, md, cols...) + return []interface{}{res, err} + }, + } + res := f.root(ctx, inv) + return res[0].(int64), f.convertError(res[1]) +} + +func (f *filterOrmDecorator) Delete(md interface{}, cols ...string) (int64, error) { + return f.DeleteWithCtx(context.Background(), md, cols...) +} + +func (f *filterOrmDecorator) DeleteWithCtx(ctx context.Context, md interface{}, cols ...string) (int64, error) { + mi, _ := defaultModelCache.getByMd(md) + inv := &Invocation{ + Method: "DeleteWithCtx", + Args: []interface{}{md, cols}, + Md: md, + mi: mi, + InsideTx: f.insideTx, + TxStartTime: f.txStartTime, + f: func(c context.Context) []interface{} { + res, err := f.ormer.DeleteWithCtx(c, md, cols...) + return []interface{}{res, err} + }, + } + res := f.root(ctx, inv) + return res[0].(int64), f.convertError(res[1]) +} + +func (f *filterOrmDecorator) Raw(query string, args ...interface{}) RawSeter { + return f.RawWithCtx(context.Background(), query, args...) +} + +func (f *filterOrmDecorator) RawWithCtx(ctx context.Context, query string, args ...interface{}) RawSeter { + inv := &Invocation{ + Method: "RawWithCtx", + Args: []interface{}{query, args}, + InsideTx: f.insideTx, + TxStartTime: f.txStartTime, + f: func(c context.Context) []interface{} { + res := f.ormer.RawWithCtx(c, query, args...) + return []interface{}{res} + }, + } + res := f.root(ctx, inv) + + if res[0] == nil { + return nil + } + return res[0].(RawSeter) +} + +func (f *filterOrmDecorator) Driver() Driver { + inv := &Invocation{ + Method: "Driver", + InsideTx: f.insideTx, + TxStartTime: f.txStartTime, + f: func(c context.Context) []interface{} { + res := f.ormer.Driver() + return []interface{}{res} + }, + } + res := f.root(context.Background(), inv) + if res[0] == nil { + return nil + } + return res[0].(Driver) +} + +func (f *filterOrmDecorator) Begin() (TxOrmer, error) { + return f.BeginWithCtxAndOpts(context.Background(), nil) +} + +func (f *filterOrmDecorator) BeginWithCtx(ctx context.Context) (TxOrmer, error) { + return f.BeginWithCtxAndOpts(ctx, nil) +} + +func (f *filterOrmDecorator) BeginWithOpts(opts *sql.TxOptions) (TxOrmer, error) { + return f.BeginWithCtxAndOpts(context.Background(), opts) +} + +func (f *filterOrmDecorator) BeginWithCtxAndOpts(ctx context.Context, opts *sql.TxOptions) (TxOrmer, error) { + inv := &Invocation{ + Method: "BeginWithCtxAndOpts", + Args: []interface{}{opts}, + InsideTx: f.insideTx, + TxStartTime: f.txStartTime, + f: func(c context.Context) []interface{} { + res, err := f.TxBeginner.BeginWithCtxAndOpts(c, opts) + res = NewFilterTxOrmDecorator(res, f.root, getTxNameFromCtx(c)) + return []interface{}{res, err} + }, + } + res := f.root(ctx, inv) + return res[0].(TxOrmer), f.convertError(res[1]) +} + +func (f *filterOrmDecorator) DoTx(task func(ctx context.Context, txOrm TxOrmer) error) error { + return f.DoTxWithCtxAndOpts(context.Background(), nil, task) +} + +func (f *filterOrmDecorator) DoTxWithCtx(ctx context.Context, task func(ctx context.Context, txOrm TxOrmer) error) error { + return f.DoTxWithCtxAndOpts(ctx, nil, task) +} + +func (f *filterOrmDecorator) DoTxWithOpts(opts *sql.TxOptions, task func(ctx context.Context, txOrm TxOrmer) error) error { + return f.DoTxWithCtxAndOpts(context.Background(), opts, task) +} + +func (f *filterOrmDecorator) DoTxWithCtxAndOpts(ctx context.Context, opts *sql.TxOptions, task func(ctx context.Context, txOrm TxOrmer) error) error { + inv := &Invocation{ + Method: "DoTxWithCtxAndOpts", + Args: []interface{}{opts, task}, + InsideTx: f.insideTx, + TxStartTime: f.txStartTime, + TxName: getTxNameFromCtx(ctx), + f: func(c context.Context) []interface{} { + err := doTxTemplate(c, f, opts, task) + return []interface{}{err} + }, + } + res := f.root(ctx, inv) + return f.convertError(res[0]) +} + +func (f *filterOrmDecorator) Commit() error { + inv := &Invocation{ + Method: "Commit", + Args: []interface{}{}, + InsideTx: f.insideTx, + TxStartTime: f.txStartTime, + TxName: f.txName, + f: func(c context.Context) []interface{} { + err := f.TxCommitter.Commit() + return []interface{}{err} + }, + } + res := f.root(context.Background(), inv) + return f.convertError(res[0]) +} + +func (f *filterOrmDecorator) Rollback() error { + inv := &Invocation{ + Method: "Rollback", + Args: []interface{}{}, + InsideTx: f.insideTx, + TxStartTime: f.txStartTime, + TxName: f.txName, + f: func(c context.Context) []interface{} { + err := f.TxCommitter.Rollback() + return []interface{}{err} + }, + } + res := f.root(context.Background(), inv) + return f.convertError(res[0]) +} + +func (f *filterOrmDecorator) RollbackUnlessCommit() error { + inv := &Invocation{ + Method: "RollbackUnlessCommit", + Args: []interface{}{}, + InsideTx: f.insideTx, + TxStartTime: f.txStartTime, + TxName: f.txName, + f: func(c context.Context) []interface{} { + err := f.TxCommitter.RollbackUnlessCommit() + return []interface{}{err} + }, + } + res := f.root(context.Background(), inv) + return f.convertError(res[0]) +} + +func (*filterOrmDecorator) convertError(v interface{}) error { + if v == nil { + return nil + } + return v.(error) +} + +func getTxNameFromCtx(ctx context.Context) string { + txName := "" + if n, ok := ctx.Value(TxNameKey).(string); ok { + txName = n + } + return txName +} diff --git a/src/vendor/github.com/beego/beego/v2/client/orm/hints/db_hints.go b/src/vendor/github.com/beego/beego/v2/client/orm/hints/db_hints.go new file mode 100644 index 000000000..6578a5955 --- /dev/null +++ b/src/vendor/github.com/beego/beego/v2/client/orm/hints/db_hints.go @@ -0,0 +1,103 @@ +// Copyright 2020 beego-dev +// +// 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 hints + +import ( + "github.com/beego/beego/v2/core/utils" +) + +const ( + // query level + KeyForceIndex = iota + KeyUseIndex + KeyIgnoreIndex + KeyForUpdate + KeyLimit + KeyOffset + KeyOrderBy + KeyRelDepth +) + +type Hint struct { + key interface{} + value interface{} +} + +var _ utils.KV = new(Hint) + +// GetKey return key +func (s *Hint) GetKey() interface{} { + return s.key +} + +// GetValue return value +func (s *Hint) GetValue() interface{} { + return s.value +} + +var _ utils.KV = new(Hint) + +// ForceIndex return a hint about ForceIndex +func ForceIndex(indexes ...string) *Hint { + return NewHint(KeyForceIndex, indexes) +} + +// UseIndex return a hint about UseIndex +func UseIndex(indexes ...string) *Hint { + return NewHint(KeyUseIndex, indexes) +} + +// IgnoreIndex return a hint about IgnoreIndex +func IgnoreIndex(indexes ...string) *Hint { + return NewHint(KeyIgnoreIndex, indexes) +} + +// ForUpdate return a hint about ForUpdate +func ForUpdate() *Hint { + return NewHint(KeyForUpdate, true) +} + +// DefaultRelDepth return a hint about DefaultRelDepth +func DefaultRelDepth() *Hint { + return NewHint(KeyRelDepth, true) +} + +// RelDepth return a hint about RelDepth +func RelDepth(d int) *Hint { + return NewHint(KeyRelDepth, d) +} + +// Limit return a hint about Limit +func Limit(d int64) *Hint { + return NewHint(KeyLimit, d) +} + +// Offset return a hint about Offset +func Offset(d int64) *Hint { + return NewHint(KeyOffset, d) +} + +// OrderBy return a hint about OrderBy +func OrderBy(s string) *Hint { + return NewHint(KeyOrderBy, s) +} + +// NewHint return a hint +func NewHint(key interface{}, value interface{}) *Hint { + return &Hint{ + key: key, + value: value, + } +} diff --git a/src/vendor/github.com/beego/beego/v2/client/orm/invocation.go b/src/vendor/github.com/beego/beego/v2/client/orm/invocation.go new file mode 100644 index 000000000..9e7c1974c --- /dev/null +++ b/src/vendor/github.com/beego/beego/v2/client/orm/invocation.go @@ -0,0 +1,58 @@ +// Copyright 2020 beego +// +// 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 orm + +import ( + "context" + "time" +) + +// Invocation represents an "Orm" invocation +type Invocation struct { + Method string + // Md may be nil in some cases. It depends on method + Md interface{} + // the args are all arguments except context.Context + Args []interface{} + + mi *modelInfo + // f is the Orm operation + f func(ctx context.Context) []interface{} + + // insideTx indicates whether this is inside a transaction + InsideTx bool + TxStartTime time.Time + TxName string +} + +func (inv *Invocation) GetTableName() string { + if inv.mi != nil { + return inv.mi.table + } + return "" +} + +func (inv *Invocation) execute(ctx context.Context) []interface{} { + return inv.f(ctx) +} + +// GetPkFieldName return the primary key of this table +// if not found, "" is returned +func (inv *Invocation) GetPkFieldName() string { + if inv.mi.fields.pk != nil { + return inv.mi.fields.pk.name + } + return "" +} diff --git a/src/vendor/github.com/beego/beego/v2/client/orm/models.go b/src/vendor/github.com/beego/beego/v2/client/orm/models.go new file mode 100644 index 000000000..94630ba52 --- /dev/null +++ b/src/vendor/github.com/beego/beego/v2/client/orm/models.go @@ -0,0 +1,573 @@ +// Copyright 2014 beego Author. All Rights Reserved. +// +// 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 orm + +import ( + "errors" + "fmt" + "reflect" + "runtime/debug" + "strings" + "sync" +) + +const ( + odCascade = "cascade" + odSetNULL = "set_null" + odSetDefault = "set_default" + odDoNothing = "do_nothing" + defaultStructTagName = "orm" + defaultStructTagDelim = ";" +) + +var defaultModelCache = NewModelCacheHandler() + +// model info collection +type modelCache struct { + sync.RWMutex // only used outsite for bootStrap + orders []string + cache map[string]*modelInfo + cacheByFullName map[string]*modelInfo + done bool +} + +// NewModelCacheHandler generator of modelCache +func NewModelCacheHandler() *modelCache { + return &modelCache{ + cache: make(map[string]*modelInfo), + cacheByFullName: make(map[string]*modelInfo), + } +} + +// get all model info +func (mc *modelCache) all() map[string]*modelInfo { + m := make(map[string]*modelInfo, len(mc.cache)) + for k, v := range mc.cache { + m[k] = v + } + return m +} + +// get ordered model info +func (mc *modelCache) allOrdered() []*modelInfo { + m := make([]*modelInfo, 0, len(mc.orders)) + for _, table := range mc.orders { + m = append(m, mc.cache[table]) + } + return m +} + +// get model info by table name +func (mc *modelCache) get(table string) (mi *modelInfo, ok bool) { + mi, ok = mc.cache[table] + return +} + +// get model info by full name +func (mc *modelCache) getByFullName(name string) (mi *modelInfo, ok bool) { + mi, ok = mc.cacheByFullName[name] + return +} + +func (mc *modelCache) getByMd(md interface{}) (*modelInfo, bool) { + val := reflect.ValueOf(md) + ind := reflect.Indirect(val) + typ := ind.Type() + name := getFullName(typ) + return mc.getByFullName(name) +} + +// set model info to collection +func (mc *modelCache) set(table string, mi *modelInfo) *modelInfo { + mii := mc.cache[table] + mc.cache[table] = mi + mc.cacheByFullName[mi.fullName] = mi + if mii == nil { + mc.orders = append(mc.orders, table) + } + return mii +} + +// clean all model info. +func (mc *modelCache) clean() { + mc.Lock() + defer mc.Unlock() + + mc.orders = make([]string, 0) + mc.cache = make(map[string]*modelInfo) + mc.cacheByFullName = make(map[string]*modelInfo) + mc.done = false +} + +// bootstrap bootstrap for models +func (mc *modelCache) bootstrap() { + mc.Lock() + defer mc.Unlock() + if mc.done { + return + } + var ( + err error + models map[string]*modelInfo + ) + if dataBaseCache.getDefault() == nil { + err = fmt.Errorf("must have one register DataBase alias named `default`") + goto end + } + + // set rel and reverse model + // RelManyToMany set the relTable + models = mc.all() + for _, mi := range models { + for _, fi := range mi.fields.columns { + if fi.rel || fi.reverse { + elm := fi.addrValue.Type().Elem() + if fi.fieldType == RelReverseMany || fi.fieldType == RelManyToMany { + elm = elm.Elem() + } + // check the rel or reverse model already register + name := getFullName(elm) + mii, ok := mc.getByFullName(name) + if !ok || mii.pkg != elm.PkgPath() { + err = fmt.Errorf("can not find rel in field `%s`, `%s` may be miss register", fi.fullName, elm.String()) + goto end + } + fi.relModelInfo = mii + + switch fi.fieldType { + case RelManyToMany: + if fi.relThrough != "" { + if i := strings.LastIndex(fi.relThrough, "."); i != -1 && len(fi.relThrough) > (i+1) { + pn := fi.relThrough[:i] + rmi, ok := mc.getByFullName(fi.relThrough) + if !ok || pn != rmi.pkg { + err = fmt.Errorf("field `%s` wrong rel_through value `%s` cannot find table", fi.fullName, fi.relThrough) + goto end + } + fi.relThroughModelInfo = rmi + fi.relTable = rmi.table + } else { + err = fmt.Errorf("field `%s` wrong rel_through value `%s`", fi.fullName, fi.relThrough) + goto end + } + } else { + i := newM2MModelInfo(mi, mii) + if fi.relTable != "" { + i.table = fi.relTable + } + if v := mc.set(i.table, i); v != nil { + err = fmt.Errorf("the rel table name `%s` already registered, cannot be use, please change one", fi.relTable) + goto end + } + fi.relTable = i.table + fi.relThroughModelInfo = i + } + + fi.relThroughModelInfo.isThrough = true + } + } + } + } + + // check the rel filed while the relModelInfo also has filed point to current model + // if not exist, add a new field to the relModelInfo + models = mc.all() + for _, mi := range models { + for _, fi := range mi.fields.fieldsRel { + switch fi.fieldType { + case RelForeignKey, RelOneToOne, RelManyToMany: + inModel := false + for _, ffi := range fi.relModelInfo.fields.fieldsReverse { + if ffi.relModelInfo == mi { + inModel = true + break + } + } + if !inModel { + rmi := fi.relModelInfo + ffi := new(fieldInfo) + ffi.name = mi.name + ffi.column = ffi.name + ffi.fullName = rmi.fullName + "." + ffi.name + ffi.reverse = true + ffi.relModelInfo = mi + ffi.mi = rmi + if fi.fieldType == RelOneToOne { + ffi.fieldType = RelReverseOne + } else { + ffi.fieldType = RelReverseMany + } + if !rmi.fields.Add(ffi) { + added := false + for cnt := 0; cnt < 5; cnt++ { + ffi.name = fmt.Sprintf("%s%d", mi.name, cnt) + ffi.column = ffi.name + ffi.fullName = rmi.fullName + "." + ffi.name + if added = rmi.fields.Add(ffi); added { + break + } + } + if !added { + panic(fmt.Errorf("cannot generate auto reverse field info `%s` to `%s`", fi.fullName, ffi.fullName)) + } + } + } + } + } + } + + models = mc.all() + for _, mi := range models { + for _, fi := range mi.fields.fieldsRel { + switch fi.fieldType { + case RelManyToMany: + for _, ffi := range fi.relThroughModelInfo.fields.fieldsRel { + switch ffi.fieldType { + case RelOneToOne, RelForeignKey: + if ffi.relModelInfo == fi.relModelInfo { + fi.reverseFieldInfoTwo = ffi + } + if ffi.relModelInfo == mi { + fi.reverseField = ffi.name + fi.reverseFieldInfo = ffi + } + } + } + if fi.reverseFieldInfoTwo == nil { + err = fmt.Errorf("can not find m2m field for m2m model `%s`, ensure your m2m model defined correct", + fi.relThroughModelInfo.fullName) + goto end + } + } + } + } + + models = mc.all() + for _, mi := range models { + for _, fi := range mi.fields.fieldsReverse { + switch fi.fieldType { + case RelReverseOne: + found := false + mForA: + for _, ffi := range fi.relModelInfo.fields.fieldsByType[RelOneToOne] { + if ffi.relModelInfo == mi { + found = true + fi.reverseField = ffi.name + fi.reverseFieldInfo = ffi + + ffi.reverseField = fi.name + ffi.reverseFieldInfo = fi + break mForA + } + } + if !found { + err = fmt.Errorf("reverse field `%s` not found in model `%s`", fi.fullName, fi.relModelInfo.fullName) + goto end + } + case RelReverseMany: + found := false + mForB: + for _, ffi := range fi.relModelInfo.fields.fieldsByType[RelForeignKey] { + if ffi.relModelInfo == mi { + found = true + fi.reverseField = ffi.name + fi.reverseFieldInfo = ffi + + ffi.reverseField = fi.name + ffi.reverseFieldInfo = fi + + break mForB + } + } + if !found { + mForC: + for _, ffi := range fi.relModelInfo.fields.fieldsByType[RelManyToMany] { + conditions := fi.relThrough != "" && fi.relThrough == ffi.relThrough || + fi.relTable != "" && fi.relTable == ffi.relTable || + fi.relThrough == "" && fi.relTable == "" + if ffi.relModelInfo == mi && conditions { + found = true + + fi.reverseField = ffi.reverseFieldInfoTwo.name + fi.reverseFieldInfo = ffi.reverseFieldInfoTwo + fi.relThroughModelInfo = ffi.relThroughModelInfo + fi.reverseFieldInfoTwo = ffi.reverseFieldInfo + fi.reverseFieldInfoM2M = ffi + ffi.reverseFieldInfoM2M = fi + + break mForC + } + } + } + if !found { + err = fmt.Errorf("reverse field for `%s` not found in model `%s`", fi.fullName, fi.relModelInfo.fullName) + goto end + } + } + } + } + +end: + if err != nil { + fmt.Println(err) + debug.PrintStack() + } + mc.done = true +} + +// register register models to model cache +func (mc *modelCache) register(prefixOrSuffixStr string, prefixOrSuffix bool, models ...interface{}) (err error) { + for _, model := range models { + val := reflect.ValueOf(model) + typ := reflect.Indirect(val).Type() + + if val.Kind() != reflect.Ptr { + err = fmt.Errorf(" cannot use non-ptr model struct `%s`", getFullName(typ)) + return + } + // For this case: + // u := &User{} + // registerModel(&u) + if typ.Kind() == reflect.Ptr { + err = fmt.Errorf(" only allow ptr model struct, it looks you use two reference to the struct `%s`", typ) + return + } + if val.Elem().Kind() == reflect.Slice { + val = reflect.New(val.Elem().Type().Elem()) + } + table := getTableName(val) + + if prefixOrSuffixStr != "" { + if prefixOrSuffix { + table = prefixOrSuffixStr + table + } else { + table = table + prefixOrSuffixStr + } + } + + // models's fullname is pkgpath + struct name + name := getFullName(typ) + if _, ok := mc.getByFullName(name); ok { + err = fmt.Errorf(" model `%s` repeat register, must be unique\n", name) + return + } + + if _, ok := mc.get(table); ok { + return nil + } + + mi := newModelInfo(val) + if mi.fields.pk == nil { + outFor: + for _, fi := range mi.fields.fieldsDB { + if strings.ToLower(fi.name) == "id" { + switch fi.addrValue.Elem().Kind() { + case reflect.Int, reflect.Int32, reflect.Int64, reflect.Uint, reflect.Uint32, reflect.Uint64: + fi.auto = true + fi.pk = true + mi.fields.pk = fi + break outFor + } + } + } + } + + mi.table = table + mi.pkg = typ.PkgPath() + mi.model = model + mi.manual = true + + mc.set(table, mi) + } + return +} + +// getDbDropSQL get database scheme drop sql queries +func (mc *modelCache) getDbDropSQL(al *alias) (queries []string, err error) { + if len(mc.cache) == 0 { + err = errors.New("no Model found, need register your model") + return + } + + Q := al.DbBaser.TableQuote() + + for _, mi := range mc.allOrdered() { + queries = append(queries, fmt.Sprintf(`DROP TABLE IF EXISTS %s%s%s`, Q, mi.table, Q)) + } + return queries, nil +} + +// getDbCreateSQL get database scheme creation sql queries +func (mc *modelCache) getDbCreateSQL(al *alias) (queries []string, tableIndexes map[string][]dbIndex, err error) { + if len(mc.cache) == 0 { + err = errors.New("no Model found, need register your model") + return + } + + Q := al.DbBaser.TableQuote() + T := al.DbBaser.DbTypes() + sep := fmt.Sprintf("%s, %s", Q, Q) + + tableIndexes = make(map[string][]dbIndex) + + for _, mi := range mc.allOrdered() { + sql := fmt.Sprintf("-- %s\n", strings.Repeat("-", 50)) + sql += fmt.Sprintf("-- Table Structure for `%s`\n", mi.fullName) + sql += fmt.Sprintf("-- %s\n", strings.Repeat("-", 50)) + + sql += fmt.Sprintf("CREATE TABLE IF NOT EXISTS %s%s%s (\n", Q, mi.table, Q) + + columns := make([]string, 0, len(mi.fields.fieldsDB)) + + sqlIndexes := [][]string{} + var commentIndexes []int // store comment indexes for postgres + + for i, fi := range mi.fields.fieldsDB { + column := fmt.Sprintf(" %s%s%s ", Q, fi.column, Q) + col := getColumnTyp(al, fi) + + if fi.auto { + switch al.Driver { + case DRSqlite, DRPostgres: + column += T["auto"] + default: + column += col + " " + T["auto"] + } + } else if fi.pk { + column += col + " " + T["pk"] + } else { + column += col + + if !fi.null { + column += " " + "NOT NULL" + } + + // if fi.initial.String() != "" { + // column += " DEFAULT " + fi.initial.String() + // } + + // Append attribute DEFAULT + column += getColumnDefault(fi) + + if fi.unique { + column += " " + "UNIQUE" + } + + if fi.index { + sqlIndexes = append(sqlIndexes, []string{fi.column}) + } + } + + if strings.Contains(column, "%COL%") { + column = strings.Replace(column, "%COL%", fi.column, -1) + } + + if fi.description != "" && al.Driver != DRSqlite { + if al.Driver == DRPostgres { + commentIndexes = append(commentIndexes, i) + } else { + column += " " + fmt.Sprintf("COMMENT '%s'", fi.description) + } + } + + columns = append(columns, column) + } + + if mi.model != nil { + allnames := getTableUnique(mi.addrField) + if !mi.manual && len(mi.uniques) > 0 { + allnames = append(allnames, mi.uniques) + } + for _, names := range allnames { + cols := make([]string, 0, len(names)) + for _, name := range names { + if fi, ok := mi.fields.GetByAny(name); ok && fi.dbcol { + cols = append(cols, fi.column) + } else { + panic(fmt.Errorf("cannot found column `%s` when parse UNIQUE in `%s.TableUnique`", name, mi.fullName)) + } + } + column := fmt.Sprintf(" UNIQUE (%s%s%s)", Q, strings.Join(cols, sep), Q) + columns = append(columns, column) + } + } + + sql += strings.Join(columns, ",\n") + sql += "\n)" + + if al.Driver == DRMySQL { + var engine string + if mi.model != nil { + engine = getTableEngine(mi.addrField) + } + if engine == "" { + engine = al.Engine + } + sql += " ENGINE=" + engine + } + + sql += ";" + if al.Driver == DRPostgres && len(commentIndexes) > 0 { + // append comments for postgres only + for _, index := range commentIndexes { + sql += fmt.Sprintf("\nCOMMENT ON COLUMN %s%s%s.%s%s%s is '%s';", + Q, + mi.table, + Q, + Q, + mi.fields.fieldsDB[index].column, + Q, + mi.fields.fieldsDB[index].description) + } + } + queries = append(queries, sql) + + if mi.model != nil { + for _, names := range getTableIndex(mi.addrField) { + cols := make([]string, 0, len(names)) + for _, name := range names { + if fi, ok := mi.fields.GetByAny(name); ok && fi.dbcol { + cols = append(cols, fi.column) + } else { + panic(fmt.Errorf("cannot found column `%s` when parse INDEX in `%s.TableIndex`", name, mi.fullName)) + } + } + sqlIndexes = append(sqlIndexes, cols) + } + } + + for _, names := range sqlIndexes { + name := mi.table + "_" + strings.Join(names, "_") + cols := strings.Join(names, sep) + sql := fmt.Sprintf("CREATE INDEX %s%s%s ON %s%s%s (%s%s%s);", Q, name, Q, Q, mi.table, Q, Q, cols, Q) + + index := dbIndex{} + index.Table = mi.table + index.Name = name + index.SQL = sql + + tableIndexes[mi.table] = append(tableIndexes[mi.table], index) + } + + } + + return +} + +// ResetModelCache Clean model cache. Then you can re-RegisterModel. +// Common use this api for test case. +func ResetModelCache() { + defaultModelCache.clean() +} diff --git a/src/vendor/github.com/beego/beego/v2/client/orm/models_boot.go b/src/vendor/github.com/beego/beego/v2/client/orm/models_boot.go new file mode 100644 index 000000000..6916f3ba9 --- /dev/null +++ b/src/vendor/github.com/beego/beego/v2/client/orm/models_boot.go @@ -0,0 +1,40 @@ +// Copyright 2014 beego Author. All Rights Reserved. +// +// 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 orm + +// RegisterModel register models +func RegisterModel(models ...interface{}) { + RegisterModelWithPrefix("", models...) +} + +// RegisterModelWithPrefix register models with a prefix +func RegisterModelWithPrefix(prefix string, models ...interface{}) { + if err := defaultModelCache.register(prefix, true, models...); err != nil { + panic(err) + } +} + +// RegisterModelWithSuffix register models with a suffix +func RegisterModelWithSuffix(suffix string, models ...interface{}) { + if err := defaultModelCache.register(suffix, false, models...); err != nil { + panic(err) + } +} + +// BootStrap bootstrap models. +// make all model parsed and can not add more models +func BootStrap() { + defaultModelCache.bootstrap() +} diff --git a/src/vendor/github.com/beego/beego/orm/models_fields.go b/src/vendor/github.com/beego/beego/v2/client/orm/models_fields.go similarity index 100% rename from src/vendor/github.com/beego/beego/orm/models_fields.go rename to src/vendor/github.com/beego/beego/v2/client/orm/models_fields.go diff --git a/src/vendor/github.com/beego/beego/orm/models_info_f.go b/src/vendor/github.com/beego/beego/v2/client/orm/models_info_f.go similarity index 96% rename from src/vendor/github.com/beego/beego/orm/models_info_f.go rename to src/vendor/github.com/beego/beego/v2/client/orm/models_info_f.go index 970bbbfa6..6a9e7a99f 100644 --- a/src/vendor/github.com/beego/beego/orm/models_info_f.go +++ b/src/vendor/github.com/beego/beego/v2/client/orm/models_info_f.go @@ -101,29 +101,30 @@ func newFields() *fields { // single field info type fieldInfo struct { - mi *modelInfo - fieldIndex []int - fieldType int dbcol bool // table column fk and onetoone inModel bool - name string - fullName string - column string - addrValue reflect.Value - sf reflect.StructField auto bool pk bool null bool index bool unique bool - colDefault bool // whether has default tag - initial StrTo // store the default value - size int + colDefault bool // whether has default tag toText bool autoNow bool autoNowAdd bool rel bool // if type equal to RelForeignKey, RelOneToOne, RelManyToMany then true reverse bool + isFielder bool // implement Fielder interface + mi *modelInfo + fieldIndex []int + fieldType int + name string + fullName string + column string + addrValue reflect.Value + sf reflect.StructField + initial StrTo // store the default value + size int reverseField string reverseFieldInfo *fieldInfo reverseFieldInfoTwo *fieldInfo @@ -134,9 +135,9 @@ type fieldInfo struct { relModelInfo *modelInfo digits int decimals int - isFielder bool // implement Fielder interface onDelete string description string + timePrecision *int } // new field info @@ -177,7 +178,7 @@ func newFieldInfo(mi *modelInfo, field reflect.Value, sf reflect.StructField, mN decimals := tags["decimals"] size := tags["size"] onDelete := tags["on_delete"] - + precision := tags["precision"] initial.Clear() if v, ok := tags["default"]; ok { initial.Set(v) @@ -193,7 +194,7 @@ checkType: } fieldType = f.FieldType() if fieldType&IsRelField > 0 { - err = fmt.Errorf("unsupport type custom field, please refer to https://github.com/beego/beego/blob/master/orm/models_fields.go#L24-L42") + err = fmt.Errorf("unsupport type custom field, please refer to https://github.com/beego/beego/v2/blob/master/orm/models_fields.go#L24-L42") goto end } default: @@ -377,6 +378,17 @@ checkType: fi.index = false fi.unique = false case TypeTimeField, TypeDateField, TypeDateTimeField: + if fieldType == TypeDateTimeField { + if precision != "" { + v, e := StrTo(precision).Int() + if e != nil { + err = fmt.Errorf("convert %s to int error:%v", precision, e) + } else { + fi.timePrecision = &v + } + } + } + if attrs["auto_now"] { fi.autoNow = true } else if attrs["auto_now_add"] { diff --git a/src/vendor/github.com/beego/beego/orm/models_info_m.go b/src/vendor/github.com/beego/beego/v2/client/orm/models_info_m.go similarity index 97% rename from src/vendor/github.com/beego/beego/orm/models_info_m.go rename to src/vendor/github.com/beego/beego/v2/client/orm/models_info_m.go index a4d733b6c..b94480ca0 100644 --- a/src/vendor/github.com/beego/beego/orm/models_info_m.go +++ b/src/vendor/github.com/beego/beego/v2/client/orm/models_info_m.go @@ -22,16 +22,16 @@ import ( // single model info type modelInfo struct { + manual bool + isThrough bool pkg string name string fullName string table string model interface{} fields *fields - manual bool - addrField reflect.Value //store the original struct value + addrField reflect.Value // store the original struct value uniques []string - isThrough bool } // new model info @@ -74,7 +74,7 @@ func addModelFields(mi *modelInfo, ind reflect.Value, mName string, index []int) } else if err != nil { break } - //record current field index + // record current field index fi.fieldIndex = append(fi.fieldIndex, index...) fi.fieldIndex = append(fi.fieldIndex, i) fi.mi = mi diff --git a/src/vendor/github.com/beego/beego/orm/models_utils.go b/src/vendor/github.com/beego/beego/v2/client/orm/models_utils.go similarity index 93% rename from src/vendor/github.com/beego/beego/orm/models_utils.go rename to src/vendor/github.com/beego/beego/v2/client/orm/models_utils.go index 71127a6ba..b2e5760ee 100644 --- a/src/vendor/github.com/beego/beego/orm/models_utils.go +++ b/src/vendor/github.com/beego/beego/v2/client/orm/models_utils.go @@ -45,6 +45,7 @@ var supportTag = map[string]int{ "on_delete": 2, "type": 2, "description": 2, + "precision": 2, } // get reflect.Type name with package path. @@ -106,6 +107,21 @@ func getTableUnique(val reflect.Value) [][]string { return nil } +// get whether the table needs to be created for the database alias +func isApplicableTableForDB(val reflect.Value, db string) bool { + if !val.IsValid() { + return true + } + fun := val.MethodByName("IsApplicableTableForDB") + if fun.IsValid() { + vals := fun.Call([]reflect.Value{reflect.ValueOf(db)}) + if len(vals) > 0 && vals[0].Kind() == reflect.Bool { + return vals[0].Bool() + } + } + return true +} + // get snaked column name func getColumnName(ft int, addrField reflect.Value, sf reflect.StructField, col string) string { column := col diff --git a/src/vendor/github.com/beego/beego/v2/client/orm/orm.go b/src/vendor/github.com/beego/beego/v2/client/orm/orm.go new file mode 100644 index 000000000..05614beb2 --- /dev/null +++ b/src/vendor/github.com/beego/beego/v2/client/orm/orm.go @@ -0,0 +1,661 @@ +// Copyright 2014 beego Author. All Rights Reserved. +// +// 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. + +//go:build go1.8 +// +build go1.8 + +// Package orm provide ORM for MySQL/PostgreSQL/sqlite +// Simple Usage +// +// package main +// +// import ( +// "fmt" +// "github.com/beego/beego/v2/client/orm" +// _ "github.com/go-sql-driver/mysql" // import your used driver +// ) +// +// // Model Struct +// type User struct { +// Id int `orm:"auto"` +// Name string `orm:"size(100)"` +// } +// +// func init() { +// orm.RegisterDataBase("default", "mysql", "root:root@/my_db?charset=utf8", 30) +// } +// +// func main() { +// o := orm.NewOrm() +// user := User{Name: "slene"} +// // insert +// id, err := o.Insert(&user) +// // update +// user.Name = "astaxie" +// num, err := o.Update(&user) +// // read one +// u := User{Id: user.Id} +// err = o.Read(&u) +// // delete +// num, err = o.Delete(&u) +// } +// +// more docs: http://beego.vip/docs/mvc/model/overview.md +package orm + +import ( + "context" + "database/sql" + "errors" + "fmt" + "os" + "reflect" + "time" + + "github.com/beego/beego/v2/client/orm/clauses/order_clause" + "github.com/beego/beego/v2/client/orm/hints" + "github.com/beego/beego/v2/core/logs" + "github.com/beego/beego/v2/core/utils" +) + +// DebugQueries define the debug +const ( + DebugQueries = iota +) + +// Define common vars +var ( + Debug = false + DebugLog = NewLog(os.Stdout) + DefaultRowsLimit = -1 + DefaultRelsDepth = 2 + DefaultTimeLoc = time.Local + ErrTxDone = errors.New(" transaction already done") + ErrMultiRows = errors.New(" return multi rows") + ErrNoRows = errors.New(" no row found") + ErrStmtClosed = errors.New(" stmt already closed") + ErrArgs = errors.New(" args error may be empty") + ErrNotImplement = errors.New("have not implement") + + ErrLastInsertIdUnavailable = errors.New(" last insert id is unavailable") +) + +// Params stores the Params +type Params map[string]interface{} + +// ParamsList stores paramslist +type ParamsList []interface{} + +type ormBase struct { + alias *alias + db dbQuerier +} + +var ( + _ DQL = new(ormBase) + _ DML = new(ormBase) + _ DriverGetter = new(ormBase) +) + +// get model info and model reflect value +func (*ormBase) getMi(md interface{}) (mi *modelInfo) { + val := reflect.ValueOf(md) + ind := reflect.Indirect(val) + typ := ind.Type() + mi = getTypeMi(typ) + return +} + +// get need ptr model info and model reflect value +func (*ormBase) getPtrMiInd(md interface{}) (mi *modelInfo, ind reflect.Value) { + val := reflect.ValueOf(md) + ind = reflect.Indirect(val) + typ := ind.Type() + if val.Kind() != reflect.Ptr { + panic(fmt.Errorf(" cannot use non-ptr model struct `%s`", getFullName(typ))) + } + mi = getTypeMi(typ) + return +} + +func getTypeMi(mdTyp reflect.Type) *modelInfo { + name := getFullName(mdTyp) + if mi, ok := defaultModelCache.getByFullName(name); ok { + return mi + } + panic(fmt.Errorf(" table: `%s` not found, make sure it was registered with `RegisterModel()`", name)) +} + +// get field info from model info by given field name +func (*ormBase) getFieldInfo(mi *modelInfo, name string) *fieldInfo { + fi, ok := mi.fields.GetByAny(name) + if !ok { + panic(fmt.Errorf(" cannot find field `%s` for model `%s`", name, mi.fullName)) + } + return fi +} + +// read data to model +func (o *ormBase) Read(md interface{}, cols ...string) error { + return o.ReadWithCtx(context.Background(), md, cols...) +} + +func (o *ormBase) ReadWithCtx(ctx context.Context, md interface{}, cols ...string) error { + mi, ind := o.getPtrMiInd(md) + return o.alias.DbBaser.Read(ctx, o.db, mi, ind, o.alias.TZ, cols, false) +} + +// read data to model, like Read(), but use "SELECT FOR UPDATE" form +func (o *ormBase) ReadForUpdate(md interface{}, cols ...string) error { + return o.ReadForUpdateWithCtx(context.Background(), md, cols...) +} + +func (o *ormBase) ReadForUpdateWithCtx(ctx context.Context, md interface{}, cols ...string) error { + mi, ind := o.getPtrMiInd(md) + return o.alias.DbBaser.Read(ctx, o.db, mi, ind, o.alias.TZ, cols, true) +} + +// Try to read a row from the database, or insert one if it doesn't exist +func (o *ormBase) ReadOrCreate(md interface{}, col1 string, cols ...string) (bool, int64, error) { + return o.ReadOrCreateWithCtx(context.Background(), md, col1, cols...) +} + +func (o *ormBase) ReadOrCreateWithCtx(ctx context.Context, md interface{}, col1 string, cols ...string) (bool, int64, error) { + cols = append([]string{col1}, cols...) + mi, ind := o.getPtrMiInd(md) + err := o.alias.DbBaser.Read(ctx, o.db, mi, ind, o.alias.TZ, cols, false) + if err == ErrNoRows { + // Create + id, err := o.InsertWithCtx(ctx, md) + return err == nil, id, err + } + + id, vid := int64(0), ind.FieldByIndex(mi.fields.pk.fieldIndex) + if mi.fields.pk.fieldType&IsPositiveIntegerField > 0 { + id = int64(vid.Uint()) + } else if mi.fields.pk.rel { + return o.ReadOrCreateWithCtx(ctx, vid.Interface(), mi.fields.pk.relModelInfo.fields.pk.name) + } else { + id = vid.Int() + } + + return false, id, err +} + +// insert model data to database +func (o *ormBase) Insert(md interface{}) (int64, error) { + return o.InsertWithCtx(context.Background(), md) +} + +func (o *ormBase) InsertWithCtx(ctx context.Context, md interface{}) (int64, error) { + mi, ind := o.getPtrMiInd(md) + id, err := o.alias.DbBaser.Insert(ctx, o.db, mi, ind, o.alias.TZ) + if err != nil { + return id, err + } + + o.setPk(mi, ind, id) + + return id, nil +} + +// set auto pk field +func (*ormBase) setPk(mi *modelInfo, ind reflect.Value, id int64) { + if mi.fields.pk.auto { + if mi.fields.pk.fieldType&IsPositiveIntegerField > 0 { + ind.FieldByIndex(mi.fields.pk.fieldIndex).SetUint(uint64(id)) + } else { + ind.FieldByIndex(mi.fields.pk.fieldIndex).SetInt(id) + } + } +} + +// insert some models to database +func (o *ormBase) InsertMulti(bulk int, mds interface{}) (int64, error) { + return o.InsertMultiWithCtx(context.Background(), bulk, mds) +} + +func (o *ormBase) InsertMultiWithCtx(ctx context.Context, bulk int, mds interface{}) (int64, error) { + var cnt int64 + + sind := reflect.Indirect(reflect.ValueOf(mds)) + + switch sind.Kind() { + case reflect.Array, reflect.Slice: + if sind.Len() == 0 { + return cnt, ErrArgs + } + default: + return cnt, ErrArgs + } + + if bulk <= 1 { + for i := 0; i < sind.Len(); i++ { + ind := reflect.Indirect(sind.Index(i)) + mi := o.getMi(ind.Interface()) + id, err := o.alias.DbBaser.Insert(ctx, o.db, mi, ind, o.alias.TZ) + if err != nil { + return cnt, err + } + + o.setPk(mi, ind, id) + + cnt++ + } + } else { + mi := o.getMi(sind.Index(0).Interface()) + return o.alias.DbBaser.InsertMulti(ctx, o.db, mi, sind, bulk, o.alias.TZ) + } + return cnt, nil +} + +// InsertOrUpdate data to database +func (o *ormBase) InsertOrUpdate(md interface{}, colConflictAndArgs ...string) (int64, error) { + return o.InsertOrUpdateWithCtx(context.Background(), md, colConflictAndArgs...) +} + +func (o *ormBase) InsertOrUpdateWithCtx(ctx context.Context, md interface{}, colConflitAndArgs ...string) (int64, error) { + mi, ind := o.getPtrMiInd(md) + id, err := o.alias.DbBaser.InsertOrUpdate(ctx, o.db, mi, ind, o.alias, colConflitAndArgs...) + if err != nil { + return id, err + } + + o.setPk(mi, ind, id) + + return id, nil +} + +// update model to database. +// cols set the columns those want to update. +func (o *ormBase) Update(md interface{}, cols ...string) (int64, error) { + return o.UpdateWithCtx(context.Background(), md, cols...) +} + +func (o *ormBase) UpdateWithCtx(ctx context.Context, md interface{}, cols ...string) (int64, error) { + mi, ind := o.getPtrMiInd(md) + return o.alias.DbBaser.Update(ctx, o.db, mi, ind, o.alias.TZ, cols) +} + +// delete model in database +// cols shows the delete conditions values read from. default is pk +func (o *ormBase) Delete(md interface{}, cols ...string) (int64, error) { + return o.DeleteWithCtx(context.Background(), md, cols...) +} + +func (o *ormBase) DeleteWithCtx(ctx context.Context, md interface{}, cols ...string) (int64, error) { + mi, ind := o.getPtrMiInd(md) + num, err := o.alias.DbBaser.Delete(ctx, o.db, mi, ind, o.alias.TZ, cols) + return num, err +} + +// create a models to models queryer +func (o *ormBase) QueryM2M(md interface{}, name string) QueryM2Mer { + mi, ind := o.getPtrMiInd(md) + fi := o.getFieldInfo(mi, name) + + switch { + case fi.fieldType == RelManyToMany: + case fi.fieldType == RelReverseMany && fi.reverseFieldInfo.mi.isThrough: + default: + panic(fmt.Errorf(" model `%s` . name `%s` is not a m2m field", fi.name, mi.fullName)) + } + + return newQueryM2M(md, o, mi, fi, ind) +} + +// NOTE: this method is deprecated, context parameter will not take effect. +func (o *ormBase) QueryM2MWithCtx(_ context.Context, md interface{}, name string) QueryM2Mer { + logs.Warn("QueryM2MWithCtx is DEPRECATED. Use methods with `WithCtx` suffix on QueryM2M as replacement please.") + return o.QueryM2M(md, name) +} + +// load related models to md model. +// args are limit, offset int and order string. +// +// example: +// orm.LoadRelated(post,"Tags") +// for _,tag := range post.Tags{...} +// +// make sure the relation is defined in model struct tags. +func (o *ormBase) LoadRelated(md interface{}, name string, args ...utils.KV) (int64, error) { + return o.LoadRelatedWithCtx(context.Background(), md, name, args...) +} + +func (o *ormBase) LoadRelatedWithCtx(_ context.Context, md interface{}, name string, args ...utils.KV) (int64, error) { + _, fi, ind, qs := o.queryRelated(md, name) + + var relDepth int + var limit, offset int64 + var order string + + kvs := utils.NewKVs(args...) + kvs.IfContains(hints.KeyRelDepth, func(value interface{}) { + if v, ok := value.(bool); ok { + if v { + relDepth = DefaultRelsDepth + } + } else if v, ok := value.(int); ok { + relDepth = v + } + }).IfContains(hints.KeyLimit, func(value interface{}) { + if v, ok := value.(int64); ok { + limit = v + } + }).IfContains(hints.KeyOffset, func(value interface{}) { + if v, ok := value.(int64); ok { + offset = v + } + }).IfContains(hints.KeyOrderBy, func(value interface{}) { + if v, ok := value.(string); ok { + order = v + } + }) + + switch fi.fieldType { + case RelOneToOne, RelForeignKey, RelReverseOne: + limit = 1 + offset = 0 + } + + qs.limit = limit + qs.offset = offset + qs.relDepth = relDepth + + if len(order) > 0 { + qs.orders = order_clause.ParseOrder(order) + } + + find := ind.FieldByIndex(fi.fieldIndex) + + var nums int64 + var err error + switch fi.fieldType { + case RelOneToOne, RelForeignKey, RelReverseOne: + val := reflect.New(find.Type().Elem()) + container := val.Interface() + err = qs.One(container) + if err == nil { + find.Set(val) + nums = 1 + } + default: + nums, err = qs.All(find.Addr().Interface()) + } + + return nums, err +} + +// get QuerySeter for related models to md model +func (o *ormBase) queryRelated(md interface{}, name string) (*modelInfo, *fieldInfo, reflect.Value, *querySet) { + mi, ind := o.getPtrMiInd(md) + fi := o.getFieldInfo(mi, name) + + _, _, exist := getExistPk(mi, ind) + if !exist { + panic(ErrMissPK) + } + + var qs *querySet + + switch fi.fieldType { + case RelOneToOne, RelForeignKey, RelManyToMany: + if !fi.inModel { + break + } + qs = o.getRelQs(md, mi, fi) + case RelReverseOne, RelReverseMany: + if !fi.inModel { + break + } + qs = o.getReverseQs(md, mi, fi) + } + + if qs == nil { + panic(fmt.Errorf(" name `%s` for model `%s` is not an available rel/reverse field", md, name)) + } + + return mi, fi, ind, qs +} + +// get reverse relation QuerySeter +func (o *ormBase) getReverseQs(md interface{}, mi *modelInfo, fi *fieldInfo) *querySet { + switch fi.fieldType { + case RelReverseOne, RelReverseMany: + default: + panic(fmt.Errorf(" name `%s` for model `%s` is not an available reverse field", fi.name, mi.fullName)) + } + + var q *querySet + + if fi.fieldType == RelReverseMany && fi.reverseFieldInfo.mi.isThrough { + q = newQuerySet(o, fi.relModelInfo).(*querySet) + q.cond = NewCondition().And(fi.reverseFieldInfoM2M.column+ExprSep+fi.reverseFieldInfo.column, md) + } else { + q = newQuerySet(o, fi.reverseFieldInfo.mi).(*querySet) + q.cond = NewCondition().And(fi.reverseFieldInfo.column, md) + } + + return q +} + +// get relation QuerySeter +func (o *ormBase) getRelQs(md interface{}, mi *modelInfo, fi *fieldInfo) *querySet { + switch fi.fieldType { + case RelOneToOne, RelForeignKey, RelManyToMany: + default: + panic(fmt.Errorf(" name `%s` for model `%s` is not an available rel field", fi.name, mi.fullName)) + } + + q := newQuerySet(o, fi.relModelInfo).(*querySet) + q.cond = NewCondition() + + if fi.fieldType == RelManyToMany { + q.cond = q.cond.And(fi.reverseFieldInfoM2M.column+ExprSep+fi.reverseFieldInfo.column, md) + } else { + q.cond = q.cond.And(fi.reverseFieldInfo.column, md) + } + + return q +} + +// return a QuerySeter for table operations. +// table name can be string or struct. +// e.g. QueryTable("user"), QueryTable(&user{}) or QueryTable((*User)(nil)), +func (o *ormBase) QueryTable(ptrStructOrTableName interface{}) (qs QuerySeter) { + var name string + if table, ok := ptrStructOrTableName.(string); ok { + name = nameStrategyMap[defaultNameStrategy](table) + if mi, ok := defaultModelCache.get(name); ok { + qs = newQuerySet(o, mi) + } + } else { + name = getFullName(indirectType(reflect.TypeOf(ptrStructOrTableName))) + if mi, ok := defaultModelCache.getByFullName(name); ok { + qs = newQuerySet(o, mi) + } + } + if qs == nil { + panic(fmt.Errorf(" table name: `%s` not exists", name)) + } + return qs +} + +// NOTE: this method is deprecated, context parameter will not take effect. +func (o *ormBase) QueryTableWithCtx(_ context.Context, ptrStructOrTableName interface{}) (qs QuerySeter) { + logs.Warn("QueryTableWithCtx is DEPRECATED. Use methods with `WithCtx` suffix on QuerySeter as replacement please.") + return o.QueryTable(ptrStructOrTableName) +} + +// return a raw query seter for raw sql string. +func (o *ormBase) Raw(query string, args ...interface{}) RawSeter { + return o.RawWithCtx(context.Background(), query, args...) +} + +func (o *ormBase) RawWithCtx(_ context.Context, query string, args ...interface{}) RawSeter { + return newRawSet(o, query, args) +} + +// return current using database Driver +func (o *ormBase) Driver() Driver { + return driver(o.alias.Name) +} + +// return sql.DBStats for current database +func (o *ormBase) DBStats() *sql.DBStats { + if o.alias != nil && o.alias.DB != nil { + stats := o.alias.DB.DB.Stats() + return &stats + } + return nil +} + +type orm struct { + ormBase +} + +var _ Ormer = new(orm) + +func (o *orm) Begin() (TxOrmer, error) { + return o.BeginWithCtx(context.Background()) +} + +func (o *orm) BeginWithCtx(ctx context.Context) (TxOrmer, error) { + return o.BeginWithCtxAndOpts(ctx, nil) +} + +func (o *orm) BeginWithOpts(opts *sql.TxOptions) (TxOrmer, error) { + return o.BeginWithCtxAndOpts(context.Background(), opts) +} + +func (o *orm) BeginWithCtxAndOpts(ctx context.Context, opts *sql.TxOptions) (TxOrmer, error) { + tx, err := o.db.(txer).BeginTx(ctx, opts) + if err != nil { + return nil, err + } + + _txOrm := &txOrm{ + ormBase: ormBase{ + alias: o.alias, + db: &TxDB{tx: tx}, + }, + } + + if Debug { + _txOrm.db = newDbQueryLog(o.alias, _txOrm.db) + } + + var taskTxOrm TxOrmer = _txOrm + return taskTxOrm, nil +} + +func (o *orm) DoTx(task func(ctx context.Context, txOrm TxOrmer) error) error { + return o.DoTxWithCtx(context.Background(), task) +} + +func (o *orm) DoTxWithCtx(ctx context.Context, task func(ctx context.Context, txOrm TxOrmer) error) error { + return o.DoTxWithCtxAndOpts(ctx, nil, task) +} + +func (o *orm) DoTxWithOpts(opts *sql.TxOptions, task func(ctx context.Context, txOrm TxOrmer) error) error { + return o.DoTxWithCtxAndOpts(context.Background(), opts, task) +} + +func (o *orm) DoTxWithCtxAndOpts(ctx context.Context, opts *sql.TxOptions, task func(ctx context.Context, txOrm TxOrmer) error) error { + return doTxTemplate(ctx, o, opts, task) +} + +func doTxTemplate(ctx context.Context, o TxBeginner, opts *sql.TxOptions, + task func(ctx context.Context, txOrm TxOrmer) error) error { + _txOrm, err := o.BeginWithCtxAndOpts(ctx, opts) + if err != nil { + return err + } + panicked := true + defer func() { + if panicked || err != nil { + e := _txOrm.Rollback() + if e != nil { + logs.Error("rollback transaction failed: %v,%v", e, panicked) + } + } else { + e := _txOrm.Commit() + if e != nil { + logs.Error("commit transaction failed: %v,%v", e, panicked) + } + } + }() + taskTxOrm := _txOrm + err = task(ctx, taskTxOrm) + panicked = false + return err +} + +type txOrm struct { + ormBase +} + +var _ TxOrmer = new(txOrm) + +func (t *txOrm) Commit() error { + return t.db.(txEnder).Commit() +} + +func (t *txOrm) Rollback() error { + return t.db.(txEnder).Rollback() +} + +func (t *txOrm) RollbackUnlessCommit() error { + return t.db.(txEnder).RollbackUnlessCommit() +} + +// NewOrm create new orm +func NewOrm() Ormer { + BootStrap() // execute only once + return NewOrmUsingDB(`default`) +} + +// NewOrmUsingDB create new orm with the name +func NewOrmUsingDB(aliasName string) Ormer { + if al, ok := dataBaseCache.get(aliasName); ok { + return newDBWithAlias(al) + } + panic(fmt.Errorf(" unknown db alias name `%s`", aliasName)) +} + +// NewOrmWithDB create a new ormer object with specify *sql.DB for query +func NewOrmWithDB(driverName, aliasName string, db *sql.DB, params ...DBOption) (Ormer, error) { + al, err := newAliasWithDb(aliasName, driverName, db, params...) + if err != nil { + return nil, err + } + + return newDBWithAlias(al), nil +} + +func newDBWithAlias(al *alias) Ormer { + o := new(orm) + o.alias = al + + if Debug { + o.db = newDbQueryLog(al, al.DB) + } else { + o.db = al.DB + } + + if len(globalFilterChains) > 0 { + return NewFilterOrmDecorator(o, globalFilterChains...) + } + return o +} diff --git a/src/vendor/github.com/beego/beego/orm/orm_conds.go b/src/vendor/github.com/beego/beego/v2/client/orm/orm_conds.go similarity index 92% rename from src/vendor/github.com/beego/beego/orm/orm_conds.go rename to src/vendor/github.com/beego/beego/v2/client/orm/orm_conds.go index f3fd66f0b..9946e595a 100644 --- a/src/vendor/github.com/beego/beego/orm/orm_conds.go +++ b/src/vendor/github.com/beego/beego/v2/client/orm/orm_conds.go @@ -17,11 +17,13 @@ package orm import ( "fmt" "strings" + + "github.com/beego/beego/v2/client/orm/clauses" ) // ExprSep define the expression separation const ( - ExprSep = "__" + ExprSep = clauses.ExprSep ) type condValue struct { @@ -76,17 +78,19 @@ func (c Condition) AndNot(expr string, args ...interface{}) *Condition { // AndCond combine a condition to current condition func (c *Condition) AndCond(cond *Condition) *Condition { - c = c.clone() if c == cond { panic(fmt.Errorf(" cannot use self as sub cond")) } + + c = c.clone() + if cond != nil { c.params = append(c.params, condValue{cond: cond, isCond: true}) } return c } -// AndNotCond combine a AND NOT condition to current condition +// AndNotCond combine an AND NOT condition to current condition func (c *Condition) AndNotCond(cond *Condition) *Condition { c = c.clone() if c == cond { @@ -117,7 +121,7 @@ func (c Condition) OrNot(expr string, args ...interface{}) *Condition { return &c } -// OrCond combine a OR condition to current condition +// OrCond combine an OR condition to current condition func (c *Condition) OrCond(cond *Condition) *Condition { c = c.clone() if c == cond { @@ -129,7 +133,7 @@ func (c *Condition) OrCond(cond *Condition) *Condition { return c } -// OrNotCond combine a OR NOT condition to current condition +// OrNotCond combine an OR NOT condition to current condition func (c *Condition) OrNotCond(cond *Condition) *Condition { c = c.clone() if c == cond { @@ -149,5 +153,8 @@ func (c *Condition) IsEmpty() bool { // clone clone a condition func (c Condition) clone() *Condition { + params := make([]condValue, len(c.params)) + copy(params, c.params) + c.params = params return &c } diff --git a/src/vendor/github.com/beego/beego/orm/orm_log.go b/src/vendor/github.com/beego/beego/v2/client/orm/orm_log.go similarity index 80% rename from src/vendor/github.com/beego/beego/orm/orm_log.go rename to src/vendor/github.com/beego/beego/v2/client/orm/orm_log.go index 5bb3a24f8..e6f8bc83c 100644 --- a/src/vendor/github.com/beego/beego/orm/orm_log.go +++ b/src/vendor/github.com/beego/beego/v2/client/orm/orm_log.go @@ -29,7 +29,7 @@ type Log struct { *log.Logger } -//costomer log func +// costomer log func var LogFunc func(query map[string]interface{}) // NewLog set io.Writer to create a Logger. @@ -40,8 +40,8 @@ func NewLog(out io.Writer) *Log { } func debugLogQueies(alias *alias, operaton, query string, t time.Time, err error, args ...interface{}) { - var logMap = make(map[string]interface{}) - sub := time.Now().Sub(t) / 1e5 + logMap := make(map[string]interface{}) + sub := time.Since(t) / 1e5 elsp := float64(int(sub)) / 10.0 logMap["cost_time"] = elsp flag := " OK" @@ -85,20 +85,32 @@ func (d *stmtQueryLog) Close() error { } func (d *stmtQueryLog) Exec(args ...interface{}) (sql.Result, error) { + return d.ExecContext(context.Background(), args...) +} + +func (d *stmtQueryLog) ExecContext(ctx context.Context, args ...interface{}) (sql.Result, error) { a := time.Now() - res, err := d.stmt.Exec(args...) + res, err := d.stmt.ExecContext(ctx, args...) debugLogQueies(d.alias, "st.Exec", d.query, a, err, args...) return res, err } func (d *stmtQueryLog) Query(args ...interface{}) (*sql.Rows, error) { + return d.QueryContext(context.Background(), args...) +} + +func (d *stmtQueryLog) QueryContext(ctx context.Context, args ...interface{}) (*sql.Rows, error) { a := time.Now() - res, err := d.stmt.Query(args...) + res, err := d.stmt.QueryContext(ctx, args...) debugLogQueies(d.alias, "st.Query", d.query, a, err, args...) return res, err } func (d *stmtQueryLog) QueryRow(args ...interface{}) *sql.Row { + return d.QueryRowContext(context.Background(), args...) +} + +func (d *stmtQueryLog) QueryRowContext(ctx context.Context, args ...interface{}) *sql.Row { a := time.Now() res := d.stmt.QueryRow(args...) debugLogQueies(d.alias, "st.QueryRow", d.query, a, nil, args...) @@ -122,15 +134,14 @@ type dbQueryLog struct { txe txEnder } -var _ dbQuerier = new(dbQueryLog) -var _ txer = new(dbQueryLog) -var _ txEnder = new(dbQueryLog) +var ( + _ dbQuerier = new(dbQueryLog) + _ txer = new(dbQueryLog) + _ txEnder = new(dbQueryLog) +) func (d *dbQueryLog) Prepare(query string) (*sql.Stmt, error) { - a := time.Now() - stmt, err := d.db.Prepare(query) - debugLogQueies(d.alias, "db.Prepare", query, a, err) - return stmt, err + return d.PrepareContext(context.Background(), query) } func (d *dbQueryLog) PrepareContext(ctx context.Context, query string) (*sql.Stmt, error) { @@ -141,10 +152,7 @@ func (d *dbQueryLog) PrepareContext(ctx context.Context, query string) (*sql.Stm } func (d *dbQueryLog) Exec(query string, args ...interface{}) (sql.Result, error) { - a := time.Now() - res, err := d.db.Exec(query, args...) - debugLogQueies(d.alias, "db.Exec", query, a, err, args...) - return res, err + return d.ExecContext(context.Background(), query, args...) } func (d *dbQueryLog) ExecContext(ctx context.Context, query string, args ...interface{}) (sql.Result, error) { @@ -155,10 +163,7 @@ func (d *dbQueryLog) ExecContext(ctx context.Context, query string, args ...inte } func (d *dbQueryLog) Query(query string, args ...interface{}) (*sql.Rows, error) { - a := time.Now() - res, err := d.db.Query(query, args...) - debugLogQueies(d.alias, "db.Query", query, a, err, args...) - return res, err + return d.QueryContext(context.Background(), query, args...) } func (d *dbQueryLog) QueryContext(ctx context.Context, query string, args ...interface{}) (*sql.Rows, error) { @@ -169,10 +174,7 @@ func (d *dbQueryLog) QueryContext(ctx context.Context, query string, args ...int } func (d *dbQueryLog) QueryRow(query string, args ...interface{}) *sql.Row { - a := time.Now() - res := d.db.QueryRow(query, args...) - debugLogQueies(d.alias, "db.QueryRow", query, a, nil, args...) - return res + return d.QueryRowContext(context.Background(), query, args...) } func (d *dbQueryLog) QueryRowContext(ctx context.Context, query string, args ...interface{}) *sql.Row { @@ -183,10 +185,7 @@ func (d *dbQueryLog) QueryRowContext(ctx context.Context, query string, args ... } func (d *dbQueryLog) Begin() (*sql.Tx, error) { - a := time.Now() - tx, err := d.db.(txer).Begin() - debugLogQueies(d.alias, "db.Begin", "START TRANSACTION", a, err) - return tx, err + return d.BeginTx(context.Background(), nil) } func (d *dbQueryLog) BeginTx(ctx context.Context, opts *sql.TxOptions) (*sql.Tx, error) { @@ -210,6 +209,13 @@ func (d *dbQueryLog) Rollback() error { return err } +func (d *dbQueryLog) RollbackUnlessCommit() error { + a := time.Now() + err := d.db.(txEnder).RollbackUnlessCommit() + debugLogQueies(d.alias, "tx.RollbackUnlessCommit", "ROLLBACK UNLESS COMMIT", a, err) + return err +} + func (d *dbQueryLog) SetDB(db dbQuerier) { d.db = db } diff --git a/src/vendor/github.com/beego/beego/orm/orm_object.go b/src/vendor/github.com/beego/beego/v2/client/orm/orm_object.go similarity index 82% rename from src/vendor/github.com/beego/beego/orm/orm_object.go rename to src/vendor/github.com/beego/beego/v2/client/orm/orm_object.go index de3181ce2..50c1ca416 100644 --- a/src/vendor/github.com/beego/beego/orm/orm_object.go +++ b/src/vendor/github.com/beego/beego/v2/client/orm/orm_object.go @@ -15,6 +15,7 @@ package orm import ( + "context" "fmt" "reflect" ) @@ -22,7 +23,7 @@ import ( // an insert queryer struct type insertSet struct { mi *modelInfo - orm *orm + orm *ormBase stmt stmtQuerier closed bool } @@ -31,6 +32,10 @@ var _ Inserter = new(insertSet) // insert model ignore it's registered or not. func (o *insertSet) Insert(md interface{}) (int64, error) { + return o.InsertWithCtx(context.Background(), md) +} + +func (o *insertSet) InsertWithCtx(ctx context.Context, md interface{}) (int64, error) { if o.closed { return 0, ErrStmtClosed } @@ -44,7 +49,7 @@ func (o *insertSet) Insert(md interface{}) (int64, error) { if name != o.mi.fullName { panic(fmt.Errorf(" need model `%s` but found `%s`", o.mi.fullName, name)) } - id, err := o.orm.alias.DbBaser.InsertStmt(o.stmt, o.mi, ind, o.orm.alias.TZ) + id, err := o.orm.alias.DbBaser.InsertStmt(ctx, o.stmt, o.mi, ind, o.orm.alias.TZ) if err != nil { return id, err } @@ -70,11 +75,11 @@ func (o *insertSet) Close() error { } // create new insert queryer. -func newInsertSet(orm *orm, mi *modelInfo) (Inserter, error) { +func newInsertSet(ctx context.Context, orm *ormBase, mi *modelInfo) (Inserter, error) { bi := new(insertSet) bi.orm = orm bi.mi = mi - st, query, err := orm.alias.DbBaser.PrepareInsert(orm.db, mi) + st, query, err := orm.alias.DbBaser.PrepareInsert(ctx, orm.db, mi) if err != nil { return nil, err } diff --git a/src/vendor/github.com/beego/beego/orm/orm_querym2m.go b/src/vendor/github.com/beego/beego/v2/client/orm/orm_querym2m.go similarity index 76% rename from src/vendor/github.com/beego/beego/orm/orm_querym2m.go rename to src/vendor/github.com/beego/beego/v2/client/orm/orm_querym2m.go index 6a270a0d8..9da49bba9 100644 --- a/src/vendor/github.com/beego/beego/orm/orm_querym2m.go +++ b/src/vendor/github.com/beego/beego/v2/client/orm/orm_querym2m.go @@ -14,7 +14,10 @@ package orm -import "reflect" +import ( + "context" + "reflect" +) // model to model struct type queryM2M struct { @@ -33,6 +36,10 @@ type queryM2M struct { // // make sure the relation is defined in post model struct tag. func (o *queryM2M) Add(mds ...interface{}) (int64, error) { + return o.AddWithCtx(context.Background(), mds...) +} + +func (o *queryM2M) AddWithCtx(ctx context.Context, mds ...interface{}) (int64, error) { fi := o.fi mi := fi.relThroughModelInfo mfi := fi.reverseFieldInfo @@ -96,11 +103,15 @@ func (o *queryM2M) Add(mds ...interface{}) (int64, error) { } names = append(names, otherNames...) values = append(values, otherValues...) - return dbase.InsertValue(orm.db, mi, true, names, values) + return dbase.InsertValue(ctx, orm.db, mi, true, names, values) } // remove models following the origin model relationship func (o *queryM2M) Remove(mds ...interface{}) (int64, error) { + return o.RemoveWithCtx(context.Background(), mds...) +} + +func (o *queryM2M) RemoveWithCtx(ctx context.Context, mds ...interface{}) (int64, error) { fi := o.fi qs := o.qs.Filter(fi.reverseFieldInfo.name, o.md) @@ -109,27 +120,39 @@ func (o *queryM2M) Remove(mds ...interface{}) (int64, error) { // check model is existed in relationship of origin model func (o *queryM2M) Exist(md interface{}) bool { + return o.ExistWithCtx(context.Background(), md) +} + +func (o *queryM2M) ExistWithCtx(ctx context.Context, md interface{}) bool { fi := o.fi return o.qs.Filter(fi.reverseFieldInfo.name, o.md). - Filter(fi.reverseFieldInfoTwo.name, md).Exist() + Filter(fi.reverseFieldInfoTwo.name, md).ExistWithCtx(ctx) } // clean all models in related of origin model func (o *queryM2M) Clear() (int64, error) { + return o.ClearWithCtx(context.Background()) +} + +func (o *queryM2M) ClearWithCtx(ctx context.Context) (int64, error) { fi := o.fi - return o.qs.Filter(fi.reverseFieldInfo.name, o.md).Delete() + return o.qs.Filter(fi.reverseFieldInfo.name, o.md).DeleteWithCtx(ctx) } // count all related models of origin model func (o *queryM2M) Count() (int64, error) { + return o.CountWithCtx(context.Background()) +} + +func (o *queryM2M) CountWithCtx(ctx context.Context) (int64, error) { fi := o.fi - return o.qs.Filter(fi.reverseFieldInfo.name, o.md).Count() + return o.qs.Filter(fi.reverseFieldInfo.name, o.md).CountWithCtx(ctx) } var _ QueryM2Mer = new(queryM2M) // create new M2M queryer. -func newQueryM2M(md interface{}, o *orm, mi *modelInfo, fi *fieldInfo, ind reflect.Value) QueryM2Mer { +func newQueryM2M(md interface{}, o *ormBase, mi *modelInfo, fi *fieldInfo, ind reflect.Value) QueryM2Mer { qm2m := new(queryM2M) qm2m.md = md qm2m.mi = mi diff --git a/src/vendor/github.com/beego/beego/orm/orm_queryset.go b/src/vendor/github.com/beego/beego/v2/client/orm/orm_queryset.go similarity index 61% rename from src/vendor/github.com/beego/beego/orm/orm_queryset.go rename to src/vendor/github.com/beego/beego/v2/client/orm/orm_queryset.go index 878b836b8..823258990 100644 --- a/src/vendor/github.com/beego/beego/orm/orm_queryset.go +++ b/src/vendor/github.com/beego/beego/v2/client/orm/orm_queryset.go @@ -17,6 +17,9 @@ package orm import ( "context" "fmt" + + "github.com/beego/beego/v2/client/orm/clauses/order_clause" + "github.com/beego/beego/v2/client/orm/hints" ) type colValue struct { @@ -62,19 +65,20 @@ func ColValue(opt operator, value interface{}) interface{} { // real query struct type querySet struct { - mi *modelInfo - cond *Condition - related []string - relDepth int - limit int64 - offset int64 - groups []string - orders []string - distinct bool - forupdate bool - orm *orm - ctx context.Context - forContext bool + mi *modelInfo + cond *Condition + related []string + relDepth int + limit int64 + offset int64 + groups []string + orders []*order_clause.Order + distinct bool + forUpdate bool + useIndex int + indexes []string + orm *ormBase + aggregate string } var _ QuerySeter = new(querySet) @@ -135,8 +139,20 @@ func (o querySet) GroupBy(exprs ...string) QuerySeter { // add ORDER expression. // "column" means ASC, "-column" means DESC. -func (o querySet) OrderBy(exprs ...string) QuerySeter { - o.orders = exprs +func (o querySet) OrderBy(expressions ...string) QuerySeter { + if len(expressions) <= 0 { + return &o + } + o.orders = order_clause.ParseOrder(expressions...) + return &o +} + +// add ORDER expression. +func (o querySet) OrderClauses(orders ...*order_clause.Order) QuerySeter { + if len(orders) <= 0 { + return &o + } + o.orders = orders return &o } @@ -148,7 +164,28 @@ func (o querySet) Distinct() QuerySeter { // add FOR UPDATE to SELECT func (o querySet) ForUpdate() QuerySeter { - o.forupdate = true + o.forUpdate = true + return &o +} + +// ForceIndex force index for query +func (o querySet) ForceIndex(indexes ...string) QuerySeter { + o.useIndex = hints.KeyForceIndex + o.indexes = indexes + return &o +} + +// UseIndex use index for query +func (o querySet) UseIndex(indexes ...string) QuerySeter { + o.useIndex = hints.KeyUseIndex + o.indexes = indexes + return &o +} + +// IgnoreIndex ignore index for query +func (o querySet) IgnoreIndex(indexes ...string) QuerySeter { + o.useIndex = hints.KeyIgnoreIndex + o.indexes = indexes return &o } @@ -185,45 +222,73 @@ func (o querySet) GetCond() *Condition { // return QuerySeter execution result number func (o *querySet) Count() (int64, error) { - return o.orm.alias.DbBaser.Count(o.orm.db, o, o.mi, o.cond, o.orm.alias.TZ) + return o.CountWithCtx(context.Background()) +} + +func (o *querySet) CountWithCtx(ctx context.Context) (int64, error) { + return o.orm.alias.DbBaser.Count(ctx, o.orm.db, o, o.mi, o.cond, o.orm.alias.TZ) } // check result empty or not after QuerySeter executed func (o *querySet) Exist() bool { - cnt, _ := o.orm.alias.DbBaser.Count(o.orm.db, o, o.mi, o.cond, o.orm.alias.TZ) + return o.ExistWithCtx(context.Background()) +} + +func (o *querySet) ExistWithCtx(ctx context.Context) bool { + cnt, _ := o.orm.alias.DbBaser.Count(ctx, o.orm.db, o, o.mi, o.cond, o.orm.alias.TZ) return cnt > 0 } // execute update with parameters func (o *querySet) Update(values Params) (int64, error) { - return o.orm.alias.DbBaser.UpdateBatch(o.orm.db, o, o.mi, o.cond, values, o.orm.alias.TZ) + return o.UpdateWithCtx(context.Background(), values) +} + +func (o *querySet) UpdateWithCtx(ctx context.Context, values Params) (int64, error) { + return o.orm.alias.DbBaser.UpdateBatch(ctx, o.orm.db, o, o.mi, o.cond, values, o.orm.alias.TZ) } // execute delete func (o *querySet) Delete() (int64, error) { - return o.orm.alias.DbBaser.DeleteBatch(o.orm.db, o, o.mi, o.cond, o.orm.alias.TZ) + return o.DeleteWithCtx(context.Background()) } -// return a insert queryer. +func (o *querySet) DeleteWithCtx(ctx context.Context) (int64, error) { + return o.orm.alias.DbBaser.DeleteBatch(ctx, o.orm.db, o, o.mi, o.cond, o.orm.alias.TZ) +} + +// return an insert queryer. // it can be used in times. // example: // i,err := sq.PrepareInsert() // i.Add(&user1{},&user2{}) func (o *querySet) PrepareInsert() (Inserter, error) { - return newInsertSet(o.orm, o.mi) + return o.PrepareInsertWithCtx(context.Background()) +} + +func (o *querySet) PrepareInsertWithCtx(ctx context.Context) (Inserter, error) { + return newInsertSet(ctx, o.orm, o.mi) } // query all data and map to containers. // cols means the columns when querying. func (o *querySet) All(container interface{}, cols ...string) (int64, error) { - return o.orm.alias.DbBaser.ReadBatch(o.orm.db, o, o.mi, o.cond, container, o.orm.alias.TZ, cols) + return o.AllWithCtx(context.Background(), container, cols...) +} + +func (o *querySet) AllWithCtx(ctx context.Context, container interface{}, cols ...string) (int64, error) { + return o.orm.alias.DbBaser.ReadBatch(ctx, o.orm.db, o, o.mi, o.cond, container, o.orm.alias.TZ, cols) } // query one row data and map to containers. // cols means the columns when querying. func (o *querySet) One(container interface{}, cols ...string) error { + return o.OneWithCtx(context.Background(), container, cols...) +} + +func (o *querySet) OneWithCtx(ctx context.Context, container interface{}, cols ...string) error { o.limit = 1 - num, err := o.orm.alias.DbBaser.ReadBatch(o.orm.db, o, o.mi, o.cond, container, o.orm.alias.TZ, cols) + num, err := o.orm.alias.DbBaser.ReadBatch(ctx, o.orm.db, o, o.mi, o.cond, container, o.orm.alias.TZ, cols) if err != nil { return err } @@ -241,19 +306,31 @@ func (o *querySet) One(container interface{}, cols ...string) error { // expres means condition expression. // it converts data to []map[column]value. func (o *querySet) Values(results *[]Params, exprs ...string) (int64, error) { - return o.orm.alias.DbBaser.ReadValues(o.orm.db, o, o.mi, o.cond, exprs, results, o.orm.alias.TZ) + return o.ValuesWithCtx(context.Background(), results, exprs...) +} + +func (o *querySet) ValuesWithCtx(ctx context.Context, results *[]Params, exprs ...string) (int64, error) { + return o.orm.alias.DbBaser.ReadValues(ctx, o.orm.db, o, o.mi, o.cond, exprs, results, o.orm.alias.TZ) } // query all data and map to [][]interface // it converts data to [][column_index]value func (o *querySet) ValuesList(results *[]ParamsList, exprs ...string) (int64, error) { - return o.orm.alias.DbBaser.ReadValues(o.orm.db, o, o.mi, o.cond, exprs, results, o.orm.alias.TZ) + return o.ValuesListWithCtx(context.Background(), results, exprs...) +} + +func (o *querySet) ValuesListWithCtx(ctx context.Context, results *[]ParamsList, exprs ...string) (int64, error) { + return o.orm.alias.DbBaser.ReadValues(ctx, o.orm.db, o, o.mi, o.cond, exprs, results, o.orm.alias.TZ) } // query all data and map to []interface. // it's designed for one row record set, auto change to []value, not [][column]value. func (o *querySet) ValuesFlat(result *ParamsList, expr string) (int64, error) { - return o.orm.alias.DbBaser.ReadValues(o.orm.db, o, o.mi, o.cond, []string{expr}, result, o.orm.alias.TZ) + return o.ValuesFlatWithCtx(context.Background(), result, expr) +} + +func (o *querySet) ValuesFlatWithCtx(ctx context.Context, result *ParamsList, expr string) (int64, error) { + return o.orm.alias.DbBaser.ReadValues(ctx, o.orm.db, o, o.mi, o.cond, []string{expr}, result, o.orm.alias.TZ) } // query all rows into map[string]interface with specify key and value column name. @@ -284,17 +361,16 @@ func (o *querySet) RowsToStruct(ptrStruct interface{}, keyCol, valueCol string) panic(ErrNotImplement) } -// set context to QuerySeter. -func (o querySet) WithContext(ctx context.Context) QuerySeter { - o.ctx = ctx - o.forContext = true - return &o -} - // create new QuerySeter. -func newQuerySet(orm *orm, mi *modelInfo) QuerySeter { +func newQuerySet(orm *ormBase, mi *modelInfo) QuerySeter { o := new(querySet) o.mi = mi o.orm = orm return o } + +// aggregate func +func (o querySet) Aggregate(s string) QuerySeter { + o.aggregate = s + return &o +} diff --git a/src/vendor/github.com/beego/beego/orm/orm_raw.go b/src/vendor/github.com/beego/beego/v2/client/orm/orm_raw.go similarity index 92% rename from src/vendor/github.com/beego/beego/orm/orm_raw.go rename to src/vendor/github.com/beego/beego/v2/client/orm/orm_raw.go index 1bdefc789..f4f3a62e8 100644 --- a/src/vendor/github.com/beego/beego/orm/orm_raw.go +++ b/src/vendor/github.com/beego/beego/v2/client/orm/orm_raw.go @@ -34,7 +34,8 @@ func (o *rawPrepare) Exec(args ...interface{}) (sql.Result, error) { if o.closed { return nil, ErrStmtClosed } - return o.stmt.Exec(args...) + flatParams := getFlatParams(nil, args, o.rs.orm.alias.TZ) + return o.stmt.Exec(flatParams...) } func (o *rawPrepare) Close() error { @@ -65,7 +66,7 @@ func newRawPreparer(rs *rawSet) (RawPreparer, error) { type rawSet struct { query string args []interface{} - orm *orm + orm *ormBase } var _ RawSeter = new(rawSet) @@ -180,6 +181,12 @@ func (o *rawSet) setFieldValue(ind reflect.Value, value interface{}) { if err == nil { ind.Set(reflect.ValueOf(t)) } + } else if len(str) >= 8 { + str = str[:8] + t, err := time.ParseInLocation(formatTime, str, DefaultTimeLoc) + if err == nil { + ind.Set(reflect.ValueOf(t)) + } } } case sql.NullString, sql.NullInt64, sql.NullFloat64, sql.NullBool: @@ -246,7 +253,6 @@ func (o *rawSet) loopSetRefs(refs []interface{}, sInds []reflect.Value, nIndsPtr } cur++ } - } else { value := reflect.ValueOf(refs[cur]).Elem().Interface() if isPtr && value == nil { @@ -308,7 +314,7 @@ func (o *rawSet) QueryRow(containers ...interface{}) error { structMode = true fn := getFullName(typ) - if mi, ok := modelCache.getByFullName(fn); ok { + if mi, ok := defaultModelCache.getByFullName(fn); ok { sMi = mi } } else { @@ -329,6 +335,8 @@ func (o *rawSet) QueryRow(containers ...interface{}) error { return err } + structTagMap := make(map[reflect.StructTag]map[string]string) + defer rows.Close() if rows.Next() { @@ -382,19 +390,38 @@ func (o *rawSet) QueryRow(containers ...interface{}) error { } } } else { - for i := 0; i < ind.NumField(); i++ { - f := ind.Field(i) - fe := ind.Type().Field(i) - _, tags := parseStructTag(fe.Tag.Get(defaultStructTagName)) - var col string - if col = tags["column"]; col == "" { - col = nameStrategyMap[nameStrategy](fe.Name) - } - if v, ok := columnsMp[col]; ok { - value := reflect.ValueOf(v).Elem().Interface() - o.setFieldValue(f, value) + // define recursive function + var recursiveSetField func(rv reflect.Value) + recursiveSetField = func(rv reflect.Value) { + for i := 0; i < rv.NumField(); i++ { + f := rv.Field(i) + fe := rv.Type().Field(i) + + // check if the field is a Struct + // recursive the Struct type + if fe.Type.Kind() == reflect.Struct { + recursiveSetField(f) + } + + // thanks @Gazeboxu. + tags := structTagMap[fe.Tag] + if tags == nil { + _, tags = parseStructTag(fe.Tag.Get(defaultStructTagName)) + structTagMap[fe.Tag] = tags + } + var col string + if col = tags["column"]; col == "" { + col = nameStrategyMap[nameStrategy](fe.Name) + } + if v, ok := columnsMp[col]; ok { + value := reflect.ValueOf(v).Elem().Interface() + o.setFieldValue(f, value) + } } } + + // init call the recursive function + recursiveSetField(ind) } } else { @@ -409,7 +436,6 @@ func (o *rawSet) QueryRow(containers ...interface{}) error { sInd.Set(nInd) } } - } else { return ErrNoRows } @@ -449,7 +475,7 @@ func (o *rawSet) QueryRows(containers ...interface{}) (int64, error) { structMode = true fn := getFullName(typ) - if mi, ok := modelCache.getByFullName(fn); ok { + if mi, ok := defaultModelCache.getByFullName(fn); ok { sMi = mi } } else { @@ -578,7 +604,6 @@ func (o *rawSet) QueryRows(containers ...interface{}) (int64, error) { } if cnt > 0 { - if structMode { sInds[0].Set(sInd) } else { @@ -876,7 +901,7 @@ func (o *rawSet) Prepare() (RawPreparer, error) { return newRawPreparer(o) } -func newRawSet(orm *orm, query string, args []interface{}) RawSeter { +func newRawSet(orm *ormBase, query string, args []interface{}) RawSeter { o := new(rawSet) o.query = query o.args = args diff --git a/src/vendor/github.com/beego/beego/orm/qb.go b/src/vendor/github.com/beego/beego/v2/client/orm/qb.go similarity index 96% rename from src/vendor/github.com/beego/beego/orm/qb.go rename to src/vendor/github.com/beego/beego/v2/client/orm/qb.go index e0655a178..c82d2255d 100644 --- a/src/vendor/github.com/beego/beego/orm/qb.go +++ b/src/vendor/github.com/beego/beego/v2/client/orm/qb.go @@ -52,7 +52,7 @@ func NewQueryBuilder(driver string) (qb QueryBuilder, err error) { } else if driver == "tidb" { qb = new(TiDBQueryBuilder) } else if driver == "postgres" { - err = errors.New("postgres query builder is not supported yet") + qb = new(PostgresQueryBuilder) } else if driver == "sqlite" { err = errors.New("sqlite query builder is not supported yet") } else { diff --git a/src/vendor/github.com/beego/beego/orm/qb_mysql.go b/src/vendor/github.com/beego/beego/v2/client/orm/qb_mysql.go similarity index 71% rename from src/vendor/github.com/beego/beego/orm/qb_mysql.go rename to src/vendor/github.com/beego/beego/v2/client/orm/qb_mysql.go index 23bdc9eef..191304967 100644 --- a/src/vendor/github.com/beego/beego/orm/qb_mysql.go +++ b/src/vendor/github.com/beego/beego/v2/client/orm/qb_mysql.go @@ -25,144 +25,144 @@ const CommaSpace = ", " // MySQLQueryBuilder is the SQL build type MySQLQueryBuilder struct { - Tokens []string + tokens []string } // Select will join the fields func (qb *MySQLQueryBuilder) Select(fields ...string) QueryBuilder { - qb.Tokens = append(qb.Tokens, "SELECT", strings.Join(fields, CommaSpace)) + qb.tokens = append(qb.tokens, "SELECT", strings.Join(fields, CommaSpace)) return qb } // ForUpdate add the FOR UPDATE clause func (qb *MySQLQueryBuilder) ForUpdate() QueryBuilder { - qb.Tokens = append(qb.Tokens, "FOR UPDATE") + qb.tokens = append(qb.tokens, "FOR UPDATE") return qb } // From join the tables func (qb *MySQLQueryBuilder) From(tables ...string) QueryBuilder { - qb.Tokens = append(qb.Tokens, "FROM", strings.Join(tables, CommaSpace)) + qb.tokens = append(qb.tokens, "FROM", strings.Join(tables, CommaSpace)) return qb } // InnerJoin INNER JOIN the table func (qb *MySQLQueryBuilder) InnerJoin(table string) QueryBuilder { - qb.Tokens = append(qb.Tokens, "INNER JOIN", table) + qb.tokens = append(qb.tokens, "INNER JOIN", table) return qb } // LeftJoin LEFT JOIN the table func (qb *MySQLQueryBuilder) LeftJoin(table string) QueryBuilder { - qb.Tokens = append(qb.Tokens, "LEFT JOIN", table) + qb.tokens = append(qb.tokens, "LEFT JOIN", table) return qb } // RightJoin RIGHT JOIN the table func (qb *MySQLQueryBuilder) RightJoin(table string) QueryBuilder { - qb.Tokens = append(qb.Tokens, "RIGHT JOIN", table) + qb.tokens = append(qb.tokens, "RIGHT JOIN", table) return qb } // On join with on cond func (qb *MySQLQueryBuilder) On(cond string) QueryBuilder { - qb.Tokens = append(qb.Tokens, "ON", cond) + qb.tokens = append(qb.tokens, "ON", cond) return qb } // Where join the Where cond func (qb *MySQLQueryBuilder) Where(cond string) QueryBuilder { - qb.Tokens = append(qb.Tokens, "WHERE", cond) + qb.tokens = append(qb.tokens, "WHERE", cond) return qb } // And join the and cond func (qb *MySQLQueryBuilder) And(cond string) QueryBuilder { - qb.Tokens = append(qb.Tokens, "AND", cond) + qb.tokens = append(qb.tokens, "AND", cond) return qb } // Or join the or cond func (qb *MySQLQueryBuilder) Or(cond string) QueryBuilder { - qb.Tokens = append(qb.Tokens, "OR", cond) + qb.tokens = append(qb.tokens, "OR", cond) return qb } // In join the IN (vals) func (qb *MySQLQueryBuilder) In(vals ...string) QueryBuilder { - qb.Tokens = append(qb.Tokens, "IN", "(", strings.Join(vals, CommaSpace), ")") + qb.tokens = append(qb.tokens, "IN", "(", strings.Join(vals, CommaSpace), ")") return qb } // OrderBy join the Order by fields func (qb *MySQLQueryBuilder) OrderBy(fields ...string) QueryBuilder { - qb.Tokens = append(qb.Tokens, "ORDER BY", strings.Join(fields, CommaSpace)) + qb.tokens = append(qb.tokens, "ORDER BY", strings.Join(fields, CommaSpace)) return qb } // Asc join the asc func (qb *MySQLQueryBuilder) Asc() QueryBuilder { - qb.Tokens = append(qb.Tokens, "ASC") + qb.tokens = append(qb.tokens, "ASC") return qb } // Desc join the desc func (qb *MySQLQueryBuilder) Desc() QueryBuilder { - qb.Tokens = append(qb.Tokens, "DESC") + qb.tokens = append(qb.tokens, "DESC") return qb } // Limit join the limit num func (qb *MySQLQueryBuilder) Limit(limit int) QueryBuilder { - qb.Tokens = append(qb.Tokens, "LIMIT", strconv.Itoa(limit)) + qb.tokens = append(qb.tokens, "LIMIT", strconv.Itoa(limit)) return qb } // Offset join the offset num func (qb *MySQLQueryBuilder) Offset(offset int) QueryBuilder { - qb.Tokens = append(qb.Tokens, "OFFSET", strconv.Itoa(offset)) + qb.tokens = append(qb.tokens, "OFFSET", strconv.Itoa(offset)) return qb } // GroupBy join the Group by fields func (qb *MySQLQueryBuilder) GroupBy(fields ...string) QueryBuilder { - qb.Tokens = append(qb.Tokens, "GROUP BY", strings.Join(fields, CommaSpace)) + qb.tokens = append(qb.tokens, "GROUP BY", strings.Join(fields, CommaSpace)) return qb } // Having join the Having cond func (qb *MySQLQueryBuilder) Having(cond string) QueryBuilder { - qb.Tokens = append(qb.Tokens, "HAVING", cond) + qb.tokens = append(qb.tokens, "HAVING", cond) return qb } // Update join the update table func (qb *MySQLQueryBuilder) Update(tables ...string) QueryBuilder { - qb.Tokens = append(qb.Tokens, "UPDATE", strings.Join(tables, CommaSpace)) + qb.tokens = append(qb.tokens, "UPDATE", strings.Join(tables, CommaSpace)) return qb } // Set join the set kv func (qb *MySQLQueryBuilder) Set(kv ...string) QueryBuilder { - qb.Tokens = append(qb.Tokens, "SET", strings.Join(kv, CommaSpace)) + qb.tokens = append(qb.tokens, "SET", strings.Join(kv, CommaSpace)) return qb } // Delete join the Delete tables func (qb *MySQLQueryBuilder) Delete(tables ...string) QueryBuilder { - qb.Tokens = append(qb.Tokens, "DELETE") + qb.tokens = append(qb.tokens, "DELETE") if len(tables) != 0 { - qb.Tokens = append(qb.Tokens, strings.Join(tables, CommaSpace)) + qb.tokens = append(qb.tokens, strings.Join(tables, CommaSpace)) } return qb } // InsertInto join the insert SQL func (qb *MySQLQueryBuilder) InsertInto(table string, fields ...string) QueryBuilder { - qb.Tokens = append(qb.Tokens, "INSERT INTO", table) + qb.tokens = append(qb.tokens, "INSERT INTO", table) if len(fields) != 0 { fieldsStr := strings.Join(fields, CommaSpace) - qb.Tokens = append(qb.Tokens, "(", fieldsStr, ")") + qb.tokens = append(qb.tokens, "(", fieldsStr, ")") } return qb } @@ -170,7 +170,7 @@ func (qb *MySQLQueryBuilder) InsertInto(table string, fields ...string) QueryBui // Values join the Values(vals) func (qb *MySQLQueryBuilder) Values(vals ...string) QueryBuilder { valsStr := strings.Join(vals, CommaSpace) - qb.Tokens = append(qb.Tokens, "VALUES", "(", valsStr, ")") + qb.tokens = append(qb.tokens, "VALUES", "(", valsStr, ")") return qb } @@ -179,7 +179,9 @@ func (qb *MySQLQueryBuilder) Subquery(sub string, alias string) string { return fmt.Sprintf("(%s) AS %s", sub, alias) } -// String join all Tokens +// String join all tokens func (qb *MySQLQueryBuilder) String() string { - return strings.Join(qb.Tokens, " ") + s := strings.Join(qb.tokens, " ") + qb.tokens = qb.tokens[:0] + return s } diff --git a/src/vendor/github.com/beego/beego/v2/client/orm/qb_postgres.go b/src/vendor/github.com/beego/beego/v2/client/orm/qb_postgres.go new file mode 100644 index 000000000..d7f216921 --- /dev/null +++ b/src/vendor/github.com/beego/beego/v2/client/orm/qb_postgres.go @@ -0,0 +1,219 @@ +package orm + +import ( + "fmt" + "strconv" + "strings" +) + +var quote string = `"` + +// PostgresQueryBuilder is the SQL build +type PostgresQueryBuilder struct { + tokens []string +} + +func processingStr(str []string) string { + s := strings.Join(str, `","`) + s = fmt.Sprintf("%s%s%s", quote, s, quote) + return s +} + +// Select will join the fields +func (qb *PostgresQueryBuilder) Select(fields ...string) QueryBuilder { + var str string + n := len(fields) + + if fields[0] == "*" { + str = "*" + } else { + for i := 0; i < n; i++ { + sli := strings.Split(fields[i], ".") + s := strings.Join(sli, `"."`) + s = fmt.Sprintf("%s%s%s", quote, s, quote) + if n == 1 || i == n-1 { + str += s + } else { + str += s + "," + } + } + } + + qb.tokens = append(qb.tokens, "SELECT", str) + return qb +} + +// ForUpdate add the FOR UPDATE clause +func (qb *PostgresQueryBuilder) ForUpdate() QueryBuilder { + qb.tokens = append(qb.tokens, "FOR UPDATE") + return qb +} + +// From join the tables +func (qb *PostgresQueryBuilder) From(tables ...string) QueryBuilder { + str := processingStr(tables) + qb.tokens = append(qb.tokens, "FROM", str) + return qb +} + +// InnerJoin INNER JOIN the table +func (qb *PostgresQueryBuilder) InnerJoin(table string) QueryBuilder { + str := fmt.Sprintf("%s%s%s", quote, table, quote) + qb.tokens = append(qb.tokens, "INNER JOIN", str) + return qb +} + +// LeftJoin LEFT JOIN the table +func (qb *PostgresQueryBuilder) LeftJoin(table string) QueryBuilder { + str := fmt.Sprintf("%s%s%s", quote, table, quote) + qb.tokens = append(qb.tokens, "LEFT JOIN", str) + return qb +} + +// RightJoin RIGHT JOIN the table +func (qb *PostgresQueryBuilder) RightJoin(table string) QueryBuilder { + str := fmt.Sprintf("%s%s%s", quote, table, quote) + qb.tokens = append(qb.tokens, "RIGHT JOIN", str) + return qb +} + +// On join with on cond +func (qb *PostgresQueryBuilder) On(cond string) QueryBuilder { + var str string + cond = strings.Replace(cond, " ", "", -1) + slice := strings.Split(cond, "=") + for i := 0; i < len(slice); i++ { + sli := strings.Split(slice[i], ".") + s := strings.Join(sli, `"."`) + s = fmt.Sprintf("%s%s%s", quote, s, quote) + if i == 0 { + str = s + " =" + " " + } else { + str += s + } + } + + qb.tokens = append(qb.tokens, "ON", str) + return qb +} + +// Where join the Where cond +func (qb *PostgresQueryBuilder) Where(cond string) QueryBuilder { + qb.tokens = append(qb.tokens, "WHERE", cond) + return qb +} + +// And join the and cond +func (qb *PostgresQueryBuilder) And(cond string) QueryBuilder { + qb.tokens = append(qb.tokens, "AND", cond) + return qb +} + +// Or join the or cond +func (qb *PostgresQueryBuilder) Or(cond string) QueryBuilder { + qb.tokens = append(qb.tokens, "OR", cond) + return qb +} + +// In join the IN (vals) +func (qb *PostgresQueryBuilder) In(vals ...string) QueryBuilder { + qb.tokens = append(qb.tokens, "IN", "(", strings.Join(vals, CommaSpace), ")") + return qb +} + +// OrderBy join the Order by fields +func (qb *PostgresQueryBuilder) OrderBy(fields ...string) QueryBuilder { + str := processingStr(fields) + qb.tokens = append(qb.tokens, "ORDER BY", str) + return qb +} + +// Asc join the asc +func (qb *PostgresQueryBuilder) Asc() QueryBuilder { + qb.tokens = append(qb.tokens, "ASC") + return qb +} + +// Desc join the desc +func (qb *PostgresQueryBuilder) Desc() QueryBuilder { + qb.tokens = append(qb.tokens, "DESC") + return qb +} + +// Limit join the limit num +func (qb *PostgresQueryBuilder) Limit(limit int) QueryBuilder { + qb.tokens = append(qb.tokens, "LIMIT", strconv.Itoa(limit)) + return qb +} + +// Offset join the offset num +func (qb *PostgresQueryBuilder) Offset(offset int) QueryBuilder { + qb.tokens = append(qb.tokens, "OFFSET", strconv.Itoa(offset)) + return qb +} + +// GroupBy join the Group by fields +func (qb *PostgresQueryBuilder) GroupBy(fields ...string) QueryBuilder { + str := processingStr(fields) + qb.tokens = append(qb.tokens, "GROUP BY", str) + return qb +} + +// Having join the Having cond +func (qb *PostgresQueryBuilder) Having(cond string) QueryBuilder { + qb.tokens = append(qb.tokens, "HAVING", cond) + return qb +} + +// Update join the update table +func (qb *PostgresQueryBuilder) Update(tables ...string) QueryBuilder { + str := processingStr(tables) + qb.tokens = append(qb.tokens, "UPDATE", str) + return qb +} + +// Set join the set kv +func (qb *PostgresQueryBuilder) Set(kv ...string) QueryBuilder { + qb.tokens = append(qb.tokens, "SET", strings.Join(kv, CommaSpace)) + return qb +} + +// Delete join the Delete tables +func (qb *PostgresQueryBuilder) Delete(tables ...string) QueryBuilder { + qb.tokens = append(qb.tokens, "DELETE") + if len(tables) != 0 { + str := processingStr(tables) + qb.tokens = append(qb.tokens, str) + } + return qb +} + +// InsertInto join the insert SQL +func (qb *PostgresQueryBuilder) InsertInto(table string, fields ...string) QueryBuilder { + str := fmt.Sprintf("%s%s%s", quote, table, quote) + qb.tokens = append(qb.tokens, "INSERT INTO", str) + if len(fields) != 0 { + fieldsStr := strings.Join(fields, CommaSpace) + qb.tokens = append(qb.tokens, "(", fieldsStr, ")") + } + return qb +} + +// Values join the Values(vals) +func (qb *PostgresQueryBuilder) Values(vals ...string) QueryBuilder { + valsStr := strings.Join(vals, CommaSpace) + qb.tokens = append(qb.tokens, "VALUES", "(", valsStr, ")") + return qb +} + +// Subquery join the sub as alias +func (qb *PostgresQueryBuilder) Subquery(sub string, alias string) string { + return fmt.Sprintf("(%s) AS %s", sub, alias) +} + +// String join all tokens +func (qb *PostgresQueryBuilder) String() string { + s := strings.Join(qb.tokens, " ") + qb.tokens = qb.tokens[:0] + return s +} diff --git a/src/vendor/github.com/beego/beego/v2/client/orm/qb_tidb.go b/src/vendor/github.com/beego/beego/v2/client/orm/qb_tidb.go new file mode 100644 index 000000000..772edb5d5 --- /dev/null +++ b/src/vendor/github.com/beego/beego/v2/client/orm/qb_tidb.go @@ -0,0 +1,21 @@ +// Copyright 2015 TiDB Author. All Rights Reserved. +// +// 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 orm + +// TiDBQueryBuilder is the SQL build +type TiDBQueryBuilder struct { + MySQLQueryBuilder + tokens []string +} diff --git a/src/vendor/github.com/beego/beego/orm/types.go b/src/vendor/github.com/beego/beego/v2/client/orm/types.go similarity index 61% rename from src/vendor/github.com/beego/beego/orm/types.go rename to src/vendor/github.com/beego/beego/v2/client/orm/types.go index 2fd10774f..df50a500d 100644 --- a/src/vendor/github.com/beego/beego/orm/types.go +++ b/src/vendor/github.com/beego/beego/v2/client/orm/types.go @@ -19,8 +19,68 @@ import ( "database/sql" "reflect" "time" + + "github.com/beego/beego/v2/client/orm/clauses/order_clause" + "github.com/beego/beego/v2/core/utils" ) +// TableNaming is usually used by model +// when you custom your table name, please implement this interfaces +// for example: +// type User struct { +// ... +// } +// func (u *User) TableName() string { +// return "USER_TABLE" +// } +type TableNameI interface { + TableName() string +} + +// TableEngineI is usually used by model +// when you want to use specific engine, like myisam, you can implement this interface +// for example: +// type User struct { +// ... +// } +// func (u *User) TableEngine() string { +// return "myisam" +// } +type TableEngineI interface { + TableEngine() string +} + +// TableIndexI is usually used by model +// when you want to create indexes, you can implement this interface +// for example: +// type User struct { +// ... +// } +// func (u *User) TableIndex() [][]string { +// return [][]string{{"Name"}} +// } +type TableIndexI interface { + TableIndex() [][]string +} + +// TableUniqueI is usually used by model +// when you want to create unique indexes, you can implement this interface +// for example: +// type User struct { +// ... +// } +// func (u *User) TableUnique() [][]string { +// return [][]string{{"Email"}} +// } +type TableUniqueI interface { + TableUnique() [][]string +} + +// IsApplicableTableForDB if return false, we won't create table to this db +type IsApplicableTableForDB interface { + IsApplicableTableForDB(db string) bool +} + // Driver define database driver type Driver interface { Name() string @@ -35,35 +95,67 @@ type Fielder interface { RawValue() interface{} } -// Ormer define the orm interface -type Ormer interface { - // read data to model - // for example: - // this will find User by Id field - // u = &User{Id: user.Id} - // err = Ormer.Read(u) - // this will find User by UserName field - // u = &User{UserName: "astaxie", Password: "pass"} - // err = Ormer.Read(u, "UserName") - Read(md interface{}, cols ...string) error - // Like Read(), but with "FOR UPDATE" clause, useful in transaction. - // Some databases are not support this feature. - ReadForUpdate(md interface{}, cols ...string) error - // Try to read a row from the database, or insert one if it doesn't exist - ReadOrCreate(md interface{}, col1 string, cols ...string) (bool, int64, error) +type TxBeginner interface { + // self control transaction + Begin() (TxOrmer, error) + BeginWithCtx(ctx context.Context) (TxOrmer, error) + BeginWithOpts(opts *sql.TxOptions) (TxOrmer, error) + BeginWithCtxAndOpts(ctx context.Context, opts *sql.TxOptions) (TxOrmer, error) + + // closure control transaction + DoTx(task func(ctx context.Context, txOrm TxOrmer) error) error + DoTxWithCtx(ctx context.Context, task func(ctx context.Context, txOrm TxOrmer) error) error + DoTxWithOpts(opts *sql.TxOptions, task func(ctx context.Context, txOrm TxOrmer) error) error + DoTxWithCtxAndOpts(ctx context.Context, opts *sql.TxOptions, task func(ctx context.Context, txOrm TxOrmer) error) error +} + +type TxCommitter interface { + txEnder +} + +// transaction beginner +type txer interface { + Begin() (*sql.Tx, error) + BeginTx(ctx context.Context, opts *sql.TxOptions) (*sql.Tx, error) +} + +// transaction ending +type txEnder interface { + Commit() error + Rollback() error + + // RollbackUnlessCommit if the transaction has been committed, do nothing, or transaction will be rollback + // For example: + // ```go + // txOrm := orm.Begin() + // defer txOrm.RollbackUnlessCommit() + // err := txOrm.Insert() // do something + // if err != nil { + // return err + // } + // txOrm.Commit() + // ``` + RollbackUnlessCommit() error +} + +// Data Manipulation Language +type DML interface { // insert model data to database // for example: // user := new(User) // id, err = Ormer.Insert(user) // user must be a pointer and Insert will set user's pk field - Insert(interface{}) (int64, error) + Insert(md interface{}) (int64, error) + InsertWithCtx(ctx context.Context, md interface{}) (int64, error) // mysql:InsertOrUpdate(model) or InsertOrUpdate(model,"colu=colu+value") // if colu type is integer : can use(+-*/), string : convert(colu,"value") // postgres: InsertOrUpdate(model,"conflictColumnName") or InsertOrUpdate(model,"conflictColumnName","colu=colu+value") // if colu type is integer : can use(+-*/), string : colu || "value" InsertOrUpdate(md interface{}, colConflitAndArgs ...string) (int64, error) + InsertOrUpdateWithCtx(ctx context.Context, md interface{}, colConflitAndArgs ...string) (int64, error) // insert some models to database InsertMulti(bulk int, mds interface{}) (int64, error) + InsertMultiWithCtx(ctx context.Context, bulk int, mds interface{}) (int64, error) // update model to database. // cols set the columns those want to update. // find model by Id(pk) field and update columns specified by fields, if cols is null then update all columns @@ -74,66 +166,105 @@ type Ormer interface { // user.Extra.Data = "orm" // num, err = Ormer.Update(&user, "Langs", "Extra") Update(md interface{}, cols ...string) (int64, error) + UpdateWithCtx(ctx context.Context, md interface{}, cols ...string) (int64, error) // delete model in database Delete(md interface{}, cols ...string) (int64, error) + DeleteWithCtx(ctx context.Context, md interface{}, cols ...string) (int64, error) + + // return a raw query seter for raw sql string. + // for example: + // ormer.Raw("UPDATE `user` SET `user_name` = ? WHERE `user_name` = ?", "slene", "testing").Exec() + // // update user testing's name to slene + Raw(query string, args ...interface{}) RawSeter + RawWithCtx(ctx context.Context, query string, args ...interface{}) RawSeter +} + +// Data Query Language +type DQL interface { + // read data to model + // for example: + // this will find User by Id field + // u = &User{Id: user.Id} + // err = Ormer.Read(u) + // this will find User by UserName field + // u = &User{UserName: "astaxie", Password: "pass"} + // err = Ormer.Read(u, "UserName") + Read(md interface{}, cols ...string) error + ReadWithCtx(ctx context.Context, md interface{}, cols ...string) error + + // Like Read(), but with "FOR UPDATE" clause, useful in transaction. + // Some databases are not support this feature. + ReadForUpdate(md interface{}, cols ...string) error + ReadForUpdateWithCtx(ctx context.Context, md interface{}, cols ...string) error + + // Try to read a row from the database, or insert one if it doesn't exist + ReadOrCreate(md interface{}, col1 string, cols ...string) (bool, int64, error) + ReadOrCreateWithCtx(ctx context.Context, md interface{}, col1 string, cols ...string) (bool, int64, error) + // load related models to md model. // args are limit, offset int and order string. // // example: // Ormer.LoadRelated(post,"Tags") // for _,tag := range post.Tags{...} - //args[0] bool true useDefaultRelsDepth ; false depth 0 - //args[0] int loadRelationDepth - //args[1] int limit default limit 1000 - //args[2] int offset default offset 0 - //args[3] string order for example : "-Id" + // hints.DefaultRelDepth useDefaultRelsDepth ; or depth 0 + // hints.RelDepth loadRelationDepth + // hints.Limit limit default limit 1000 + // hints.Offset int offset default offset 0 + // hints.OrderBy string order for example : "-Id" // make sure the relation is defined in model struct tags. - LoadRelated(md interface{}, name string, args ...interface{}) (int64, error) + LoadRelated(md interface{}, name string, args ...utils.KV) (int64, error) + LoadRelatedWithCtx(ctx context.Context, md interface{}, name string, args ...utils.KV) (int64, error) + // create a models to models queryer // for example: // post := Post{Id: 4} // m2m := Ormer.QueryM2M(&post, "Tags") QueryM2M(md interface{}, name string) QueryM2Mer + // NOTE: this method is deprecated, context parameter will not take effect. + // Use context.Context directly on methods with `WithCtx` suffix such as InsertWithCtx/UpdateWithCtx + QueryM2MWithCtx(ctx context.Context, md interface{}, name string) QueryM2Mer + // return a QuerySeter for table operations. // table name can be string or struct. // e.g. QueryTable("user"), QueryTable(&user{}) or QueryTable((*User)(nil)), QueryTable(ptrStructOrTableName interface{}) QuerySeter - // switch to another registered database driver by given name. - Using(name string) error - // begin transaction - // for example: - // o := NewOrm() - // err := o.Begin() - // ... - // err = o.Rollback() - Begin() error - // begin transaction with provided context and option - // the provided context is used until the transaction is committed or rolled back. - // if the context is canceled, the transaction will be rolled back. - // the provided TxOptions is optional and may be nil if defaults should be used. - // if a non-default isolation level is used that the driver doesn't support, an error will be returned. - // for example: - // o := NewOrm() - // err := o.BeginTx(context.Background(), &sql.TxOptions{Isolation: sql.LevelRepeatableRead}) - // ... - // err = o.Rollback() - BeginTx(ctx context.Context, opts *sql.TxOptions) error - // commit transaction - Commit() error - // rollback transaction - Rollback() error - // return a raw query seter for raw sql string. - // for example: - // ormer.Raw("UPDATE `user` SET `user_name` = ? WHERE `user_name` = ?", "slene", "testing").Exec() - // // update user testing's name to slene - Raw(query string, args ...interface{}) RawSeter - Driver() Driver + // NOTE: this method is deprecated, context parameter will not take effect. + // Use context.Context directly on methods with `WithCtx` suffix such as InsertWithCtx/UpdateWithCtx + QueryTableWithCtx(ctx context.Context, ptrStructOrTableName interface{}) QuerySeter + DBStats() *sql.DBStats } +type DriverGetter interface { + Driver() Driver +} + +type ormer interface { + DQL + DML + DriverGetter +} + +// QueryExecutor wrapping for ormer +type QueryExecutor interface { + ormer +} + +type Ormer interface { + QueryExecutor + TxBeginner +} + +type TxOrmer interface { + QueryExecutor + TxCommitter +} + // Inserter insert prepared statement type Inserter interface { Insert(interface{}) (int64, error) + InsertWithCtx(context.Context, interface{}) (int64, error) Close() error } @@ -193,6 +324,43 @@ type QuerySeter interface { // for example: // qs.OrderBy("-status") OrderBy(exprs ...string) QuerySeter + // add ORDER expression by order clauses + // for example: + // OrderClauses( + // order_clause.Clause( + // order.Column("Id"), + // order.SortAscending(), + // ), + // order_clause.Clause( + // order.Column("status"), + // order.SortDescending(), + // ), + // ) + // OrderClauses(order_clause.Clause( + // order_clause.Column(`user__status`), + // order_clause.SortDescending(),//default None + // )) + // OrderClauses(order_clause.Clause( + // order_clause.Column(`random()`), + // order_clause.SortNone(),//default None + // order_clause.Raw(),//default false.if true, do not check field is valid or not + // )) + OrderClauses(orders ...*order_clause.Order) QuerySeter + // add FORCE INDEX expression. + // for example: + // qs.ForceIndex(`idx_name1`,`idx_name2`) + // ForceIndex, UseIndex , IgnoreIndex are mutually exclusive + ForceIndex(indexes ...string) QuerySeter + // add USE INDEX expression. + // for example: + // qs.UseIndex(`idx_name1`,`idx_name2`) + // ForceIndex, UseIndex , IgnoreIndex are mutually exclusive + UseIndex(indexes ...string) QuerySeter + // add IGNORE INDEX expression. + // for example: + // qs.IgnoreIndex(`idx_name1`,`idx_name2`) + // ForceIndex, UseIndex , IgnoreIndex are mutually exclusive + IgnoreIndex(indexes ...string) QuerySeter // set relation model to query together. // it will query relation models and assign to parent model. // for example: @@ -216,9 +384,11 @@ type QuerySeter interface { // for example: // num, err = qs.Filter("profile__age__gt", 28).Count() Count() (int64, error) + CountWithCtx(context.Context) (int64, error) // check result empty or not after QuerySeter executed // the same as QuerySeter.Count > 0 Exist() bool + ExistWithCtx(context.Context) bool // execute update with parameters // for example: // num, err = qs.Filter("user_name", "slene").Update(Params{ @@ -228,12 +398,14 @@ type QuerySeter interface { // "user_name": "slene2" // }) // user slene's name will change to slene2 Update(values Params) (int64, error) + UpdateWithCtx(ctx context.Context, values Params) (int64, error) // delete from table - //for example: + // for example: // num ,err = qs.Filter("user_name__in", "testing1", "testing2").Delete() // //delete two user who's name is testing1 or testing2 Delete() (int64, error) - // return a insert queryer. + DeleteWithCtx(context.Context) (int64, error) + // return an insert queryer. // it can be used in times. // example: // i,err := sq.PrepareInsert() @@ -241,18 +413,21 @@ type QuerySeter interface { // num, err = i.Insert(&user2) // user table will add one record user2 at once // err = i.Close() //don't forget call Close PrepareInsert() (Inserter, error) + PrepareInsertWithCtx(context.Context) (Inserter, error) // query all data and map to containers. // cols means the columns when querying. // for example: // var users []*User // qs.All(&users) // users[0],users[1],users[2] ... All(container interface{}, cols ...string) (int64, error) + AllWithCtx(ctx context.Context, container interface{}, cols ...string) (int64, error) // query one row data and map to containers. // cols means the columns when querying. // for example: // var user User // qs.One(&user) //user.UserName == "slene" One(container interface{}, cols ...string) error + OneWithCtx(ctx context.Context, container interface{}, cols ...string) error // query all data and map to []map[string]interface. // expres means condition expression. // it converts data to []map[column]value. @@ -260,18 +435,21 @@ type QuerySeter interface { // var maps []Params // qs.Values(&maps) //maps[0]["UserName"]=="slene" Values(results *[]Params, exprs ...string) (int64, error) + ValuesWithCtx(ctx context.Context, results *[]Params, exprs ...string) (int64, error) // query all data and map to [][]interface // it converts data to [][column_index]value // for example: // var list []ParamsList // qs.ValuesList(&list) // list[0][1] == "slene" ValuesList(results *[]ParamsList, exprs ...string) (int64, error) + ValuesListWithCtx(ctx context.Context, results *[]ParamsList, exprs ...string) (int64, error) // query all data and map to []interface. // it's designed for one column record set, auto change to []value, not [][column]value. // for example: // var list ParamsList // qs.ValuesFlat(&list, "UserName") // list[0] == "slene" ValuesFlat(result *ParamsList, expr string) (int64, error) + ValuesFlatWithCtx(ctx context.Context, result *ParamsList, expr string) (int64, error) // query all rows into map[string]interface with specify key and value column name. // keyCol = "name", valueCol = "value" // table data @@ -294,6 +472,15 @@ type QuerySeter interface { // Found int // } RowsToStruct(ptrStruct interface{}, keyCol, valueCol string) (int64, error) + // aggregate func. + // for example: + // type result struct { + // DeptName string + // Total int + // } + // var res []result + // o.QueryTable("dept_info").Aggregate("dept_name,sum(salary) as total").GroupBy("dept_name").All(&res) + Aggregate(s string) QuerySeter } // QueryM2Mer model to model query struct @@ -311,18 +498,23 @@ type QueryM2Mer interface { // insert one or more rows to m2m table // make sure the relation is defined in post model struct tag. Add(...interface{}) (int64, error) + AddWithCtx(context.Context, ...interface{}) (int64, error) // remove models following the origin model relationship // only delete rows from m2m table // for example: - //tag3 := &Tag{Id:5,Name: "TestTag3"} - //num, err = m2m.Remove(tag3) + // tag3 := &Tag{Id:5,Name: "TestTag3"} + // num, err = m2m.Remove(tag3) Remove(...interface{}) (int64, error) + RemoveWithCtx(context.Context, ...interface{}) (int64, error) // check model is existed in relationship of origin model Exist(interface{}) bool + ExistWithCtx(context.Context, interface{}) bool // clean all models in related of origin model Clear() (int64, error) + ClearWithCtx(context.Context) (int64, error) // count all related models of origin model Count() (int64, error) + CountWithCtx(context.Context) (int64, error) } // RawPreparer raw query statement @@ -337,10 +529,10 @@ type RawPreparer interface { // sql := fmt.Sprintf("SELECT %sid%s,%sname%s FROM %suser%s WHERE id = ?",Q,Q,Q,Q,Q,Q) // rs := Ormer.Raw(sql, 1) type RawSeter interface { - //execute sql and get result + // execute sql and get result Exec() (sql.Result, error) - //query data and map to container - //for example: + // query data and map to container + // for example: // var name string // var id int // rs.QueryRow(&id,&name) // id==2 name=="slene" @@ -396,11 +588,11 @@ type RawSeter interface { type stmtQuerier interface { Close() error Exec(args ...interface{}) (sql.Result, error) - //ExecContext(ctx context.Context, args ...interface{}) (sql.Result, error) + ExecContext(ctx context.Context, args ...interface{}) (sql.Result, error) Query(args ...interface{}) (*sql.Rows, error) - //QueryContext(args ...interface{}) (*sql.Rows, error) + QueryContext(ctx context.Context, args ...interface{}) (*sql.Rows, error) QueryRow(args ...interface{}) *sql.Row - //QueryRowContext(ctx context.Context, args ...interface{}) *sql.Row + QueryRowContext(ctx context.Context, args ...interface{}) *sql.Row } // db querier @@ -423,39 +615,30 @@ type dbQuerier interface { // QueryRow(query string, args ...interface{}) *sql.Row // } -// transaction beginner -type txer interface { - Begin() (*sql.Tx, error) - BeginTx(ctx context.Context, opts *sql.TxOptions) (*sql.Tx, error) -} - -// transaction ending -type txEnder interface { - Commit() error - Rollback() error -} - // base database struct type dbBaser interface { - Read(dbQuerier, *modelInfo, reflect.Value, *time.Location, []string, bool) error - Insert(dbQuerier, *modelInfo, reflect.Value, *time.Location) (int64, error) - InsertOrUpdate(dbQuerier, *modelInfo, reflect.Value, *alias, ...string) (int64, error) - InsertMulti(dbQuerier, *modelInfo, reflect.Value, int, *time.Location) (int64, error) - InsertValue(dbQuerier, *modelInfo, bool, []string, []interface{}) (int64, error) - InsertStmt(stmtQuerier, *modelInfo, reflect.Value, *time.Location) (int64, error) - Update(dbQuerier, *modelInfo, reflect.Value, *time.Location, []string) (int64, error) - Delete(dbQuerier, *modelInfo, reflect.Value, *time.Location, []string) (int64, error) - ReadBatch(dbQuerier, *querySet, *modelInfo, *Condition, interface{}, *time.Location, []string) (int64, error) + Read(context.Context, dbQuerier, *modelInfo, reflect.Value, *time.Location, []string, bool) error + ReadBatch(context.Context, dbQuerier, *querySet, *modelInfo, *Condition, interface{}, *time.Location, []string) (int64, error) + Count(context.Context, dbQuerier, *querySet, *modelInfo, *Condition, *time.Location) (int64, error) + ReadValues(context.Context, dbQuerier, *querySet, *modelInfo, *Condition, []string, interface{}, *time.Location) (int64, error) + + Insert(context.Context, dbQuerier, *modelInfo, reflect.Value, *time.Location) (int64, error) + InsertOrUpdate(context.Context, dbQuerier, *modelInfo, reflect.Value, *alias, ...string) (int64, error) + InsertMulti(context.Context, dbQuerier, *modelInfo, reflect.Value, int, *time.Location) (int64, error) + InsertValue(context.Context, dbQuerier, *modelInfo, bool, []string, []interface{}) (int64, error) + InsertStmt(context.Context, stmtQuerier, *modelInfo, reflect.Value, *time.Location) (int64, error) + + Update(context.Context, dbQuerier, *modelInfo, reflect.Value, *time.Location, []string) (int64, error) + UpdateBatch(context.Context, dbQuerier, *querySet, *modelInfo, *Condition, Params, *time.Location) (int64, error) + + Delete(context.Context, dbQuerier, *modelInfo, reflect.Value, *time.Location, []string) (int64, error) + DeleteBatch(context.Context, dbQuerier, *querySet, *modelInfo, *Condition, *time.Location) (int64, error) + SupportUpdateJoin() bool - UpdateBatch(dbQuerier, *querySet, *modelInfo, *Condition, Params, *time.Location) (int64, error) - DeleteBatch(dbQuerier, *querySet, *modelInfo, *Condition, *time.Location) (int64, error) - Count(dbQuerier, *querySet, *modelInfo, *Condition, *time.Location) (int64, error) OperatorSQL(string) string GenerateOperatorSQL(*modelInfo, *fieldInfo, string, []interface{}, *time.Location) (string, []interface{}) GenerateOperatorLeftCol(*fieldInfo, string, *string) - PrepareInsert(dbQuerier, *modelInfo) (stmtQuerier, string, error) - ReadValues(dbQuerier, *querySet, *modelInfo, *Condition, []string, interface{}, *time.Location) (int64, error) - RowsTo(dbQuerier, *querySet, *modelInfo, *Condition, interface{}, string, string, *time.Location) (int64, error) + PrepareInsert(context.Context, dbQuerier, *modelInfo) (stmtQuerier, string, error) MaxLimit() uint64 TableQuote() string ReplaceMarks(*string) @@ -464,10 +647,12 @@ type dbBaser interface { TimeToDB(*time.Time, *time.Location) DbTypes() map[string]string GetTables(dbQuerier) (map[string]bool, error) - GetColumns(dbQuerier, string) (map[string][3]string, error) + GetColumns(context.Context, dbQuerier, string) (map[string][3]string, error) ShowTablesQuery() string ShowColumnsQuery(string) string - IndexExists(dbQuerier, string, string) bool + IndexExists(context.Context, dbQuerier, string, string) bool collectFieldValue(*modelInfo, *fieldInfo, reflect.Value, bool, *time.Location) (interface{}, error) - setval(dbQuerier, *modelInfo, []string) error + setval(context.Context, dbQuerier, *modelInfo, []string) error + + GenerateSpecifyIndex(tableName string, useIndex int, indexes []string) string } diff --git a/src/vendor/github.com/beego/beego/orm/utils.go b/src/vendor/github.com/beego/beego/v2/client/orm/utils.go similarity index 97% rename from src/vendor/github.com/beego/beego/orm/utils.go rename to src/vendor/github.com/beego/beego/v2/client/orm/utils.go index 3ff76772e..8d05c0801 100644 --- a/src/vendor/github.com/beego/beego/orm/utils.go +++ b/src/vendor/github.com/beego/beego/v2/client/orm/utils.go @@ -49,12 +49,12 @@ func (f *StrTo) Set(v string) { // Clear string func (f *StrTo) Clear() { - *f = StrTo(0x1E) + *f = StrTo(rune(0x1E)) } // Exist check string exist func (f StrTo) Exist() bool { - return string(f) != string(0x1E) + return string(f) != string(rune(0x1E)) } // Bool string to bool @@ -228,7 +228,7 @@ func snakeStringWithAcronym(s string) string { } data = append(data, d) } - return strings.ToLower(string(data[:])) + return strings.ToLower(string(data)) } // snake string, XxYy to xx_yy , XxYY to xx_y_y @@ -246,7 +246,7 @@ func snakeString(s string) string { } data = append(data, d) } - return strings.ToLower(string(data[:])) + return strings.ToLower(string(data)) } // SetNameStrategy set different name strategy @@ -274,7 +274,7 @@ func camelString(s string) string { } data = append(data, d) } - return string(data[:]) + return string(data) } type argString []string diff --git a/src/vendor/github.com/beego/beego/v2/core/admin/command.go b/src/vendor/github.com/beego/beego/v2/core/admin/command.go new file mode 100644 index 000000000..f65d27501 --- /dev/null +++ b/src/vendor/github.com/beego/beego/v2/core/admin/command.go @@ -0,0 +1,87 @@ +// Copyright 2020 +// +// 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 admin + +import ( + "github.com/pkg/errors" +) + +// Command is an experimental interface +// We try to use this to decouple modules +// All other modules depends on this, and they register the command they support +// We may change the API in the future, so be careful about this. +type Command interface { + Execute(params ...interface{}) *Result +} + +var CommandNotFound = errors.New("Command not found") + +type Result struct { + // Status is the same as http.Status + Status int + Error error + Content interface{} +} + +func (r *Result) IsSuccess() bool { + return r.Status >= 200 && r.Status < 300 +} + +// CommandRegistry stores all commands +// name => command +type moduleCommands map[string]Command + +// Get returns command with the name +func (m moduleCommands) Get(name string) Command { + c, ok := m[name] + if ok { + return c + } + return &doNothingCommand{} +} + +// module name => moduleCommand +type commandRegistry map[string]moduleCommands + +// Get returns module's commands +func (c commandRegistry) Get(moduleName string) moduleCommands { + if mcs, ok := c[moduleName]; ok { + return mcs + } + res := make(moduleCommands) + c[moduleName] = res + return res +} + +var cmdRegistry = make(commandRegistry) + +// RegisterCommand is not thread-safe +// do not use it in concurrent case +func RegisterCommand(module string, commandName string, command Command) { + cmdRegistry.Get(module)[commandName] = command +} + +func GetCommand(module string, cmdName string) Command { + return cmdRegistry.Get(module).Get(cmdName) +} + +type doNothingCommand struct{} + +func (d *doNothingCommand) Execute(params ...interface{}) *Result { + return &Result{ + Status: 404, + Error: CommandNotFound, + } +} diff --git a/src/vendor/github.com/beego/beego/toolbox/healthcheck.go b/src/vendor/github.com/beego/beego/v2/core/admin/healthcheck.go similarity index 92% rename from src/vendor/github.com/beego/beego/toolbox/healthcheck.go rename to src/vendor/github.com/beego/beego/v2/core/admin/healthcheck.go index e3544b3ad..73a6cb089 100644 --- a/src/vendor/github.com/beego/beego/toolbox/healthcheck.go +++ b/src/vendor/github.com/beego/beego/v2/core/admin/healthcheck.go @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -// Package toolbox healthcheck +// Package admin healthcheck // // type DatabaseCheck struct { // } @@ -27,8 +27,8 @@ // // AddHealthCheck("database",&DatabaseCheck{}) // -// more docs: http://beego.me/docs/module/toolbox.md -package toolbox +// more docs: http://beego.vip/docs/module/toolbox.md +package admin // AdminCheckList holds health checker map var AdminCheckList map[string]HealthChecker diff --git a/src/vendor/github.com/beego/beego/toolbox/profile.go b/src/vendor/github.com/beego/beego/v2/core/admin/profile.go similarity index 79% rename from src/vendor/github.com/beego/beego/toolbox/profile.go rename to src/vendor/github.com/beego/beego/v2/core/admin/profile.go index 06e40ede7..f85afaa2c 100644 --- a/src/vendor/github.com/beego/beego/toolbox/profile.go +++ b/src/vendor/github.com/beego/beego/v2/core/admin/profile.go @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -package toolbox +package admin import ( "fmt" @@ -25,10 +25,14 @@ import ( "runtime/pprof" "strconv" "time" + + "github.com/beego/beego/v2/core/utils" ) -var startTime = time.Now() -var pid int +var ( + startTime = time.Now() + pid int +) func init() { pid = os.Getpid() @@ -103,27 +107,26 @@ func PrintGCSummary(w io.Writer) { } func printGC(memStats *runtime.MemStats, gcstats *debug.GCStats, w io.Writer) { - if gcstats.NumGC > 0 { lastPause := gcstats.Pause[0] - elapsed := time.Now().Sub(startTime) + elapsed := time.Since(startTime) overhead := float64(gcstats.PauseTotal) / float64(elapsed) * 100 allocatedRate := float64(memStats.TotalAlloc) / elapsed.Seconds() fmt.Fprintf(w, "NumGC:%d Pause:%s Pause(Avg):%s Overhead:%3.2f%% Alloc:%s Sys:%s Alloc(Rate):%s/s Histogram:%s %s %s \n", gcstats.NumGC, - toS(lastPause), - toS(avg(gcstats.Pause)), + utils.ToShortTimeFormat(lastPause), + utils.ToShortTimeFormat(avg(gcstats.Pause)), overhead, toH(memStats.Alloc), toH(memStats.Sys), toH(uint64(allocatedRate)), - toS(gcstats.PauseQuantiles[94]), - toS(gcstats.PauseQuantiles[98]), - toS(gcstats.PauseQuantiles[99])) + utils.ToShortTimeFormat(gcstats.PauseQuantiles[94]), + utils.ToShortTimeFormat(gcstats.PauseQuantiles[98]), + utils.ToShortTimeFormat(gcstats.PauseQuantiles[99])) } else { // while GC has disabled - elapsed := time.Now().Sub(startTime) + elapsed := time.Since(startTime) allocatedRate := float64(memStats.TotalAlloc) / elapsed.Seconds() fmt.Fprintf(w, "Alloc:%s Sys:%s Alloc(Rate):%s/s\n", @@ -141,7 +144,7 @@ func avg(items []time.Duration) time.Duration { return time.Duration(int64(sum) / int64(len(items))) } -// format bytes number friendly +// toH format bytes number friendly func toH(bytes uint64) string { switch { case bytes < 1024: @@ -154,31 +157,3 @@ func toH(bytes uint64) string { return fmt.Sprintf("%.2fG", float64(bytes)/1024/1024/1024) } } - -// short string format -func toS(d time.Duration) string { - - u := uint64(d) - if u < uint64(time.Second) { - switch { - case u == 0: - return "0" - case u < uint64(time.Microsecond): - return fmt.Sprintf("%.2fns", float64(u)) - case u < uint64(time.Millisecond): - return fmt.Sprintf("%.2fus", float64(u)/1000) - default: - return fmt.Sprintf("%.2fms", float64(u)/1000/1000) - } - } else { - switch { - case u < uint64(time.Minute): - return fmt.Sprintf("%.2fs", float64(u)/1000/1000/1000) - case u < uint64(time.Hour): - return fmt.Sprintf("%.2fm", float64(u)/1000/1000/1000/60) - default: - return fmt.Sprintf("%.2fh", float64(u)/1000/1000/1000/60/60) - } - } - -} diff --git a/src/vendor/github.com/beego/beego/v2/core/berror/codes.go b/src/vendor/github.com/beego/beego/v2/core/berror/codes.go new file mode 100644 index 000000000..b6712a847 --- /dev/null +++ b/src/vendor/github.com/beego/beego/v2/core/berror/codes.go @@ -0,0 +1,86 @@ +// Copyright 2020 beego +// +// 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 berror + +import ( + "fmt" + "sync" +) + +// A Code is an unsigned 32-bit error code as defined in the beego spec. +type Code interface { + Code() uint32 + Module() string + Desc() string + Name() string +} + +var defaultCodeRegistry = &codeRegistry{ + codes: make(map[uint32]*codeDefinition, 127), +} + +// DefineCode defining a new Code +// Before defining a new code, please read Beego specification. +// desc could be markdown doc +func DefineCode(code uint32, module string, name string, desc string) Code { + res := &codeDefinition{ + code: code, + module: module, + desc: desc, + } + defaultCodeRegistry.lock.Lock() + defer defaultCodeRegistry.lock.Unlock() + + if _, ok := defaultCodeRegistry.codes[code]; ok { + panic(fmt.Sprintf("duplicate code, code %d has been registered", code)) + } + defaultCodeRegistry.codes[code] = res + return res +} + +type codeRegistry struct { + lock sync.RWMutex + codes map[uint32]*codeDefinition +} + +func (cr *codeRegistry) Get(code uint32) (Code, bool) { + cr.lock.RLock() + defer cr.lock.RUnlock() + c, ok := cr.codes[code] + return c, ok +} + +type codeDefinition struct { + code uint32 + module string + desc string + name string +} + +func (c *codeDefinition) Name() string { + return c.name +} + +func (c *codeDefinition) Code() uint32 { + return c.code +} + +func (c *codeDefinition) Module() string { + return c.module +} + +func (c *codeDefinition) Desc() string { + return c.desc +} diff --git a/src/vendor/github.com/beego/beego/v2/core/berror/error.go b/src/vendor/github.com/beego/beego/v2/core/berror/error.go new file mode 100644 index 000000000..c40009c68 --- /dev/null +++ b/src/vendor/github.com/beego/beego/v2/core/berror/error.go @@ -0,0 +1,69 @@ +// Copyright 2020 beego +// +// 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 berror + +import ( + "fmt" + "strconv" + "strings" + + "github.com/pkg/errors" +) + +// code, msg +const errFmt = "ERROR-%d, %s" + +// Error returns an error representing c and msg. If c is OK, returns nil. +func Error(c Code, msg string) error { + return fmt.Errorf(errFmt, c.Code(), msg) +} + +// Errorf returns error +func Errorf(c Code, format string, a ...interface{}) error { + return Error(c, fmt.Sprintf(format, a...)) +} + +func Wrap(err error, c Code, msg string) error { + if err == nil { + return nil + } + return errors.Wrap(err, fmt.Sprintf(errFmt, c.Code(), msg)) +} + +func Wrapf(err error, c Code, format string, a ...interface{}) error { + return Wrap(err, c, fmt.Sprintf(format, a...)) +} + +// FromError is very simple. It just parse error msg and check whether code has been register +// if code not being register, return unknown +// if err.Error() is not valid beego error code, return unknown +func FromError(err error) (Code, bool) { + msg := err.Error() + codeSeg := strings.SplitN(msg, ",", 2) + if strings.HasPrefix(codeSeg[0], "ERROR-") { + codeStr := strings.SplitN(codeSeg[0], "-", 2) + if len(codeStr) < 2 { + return Unknown, false + } + codeInt, e := strconv.ParseUint(codeStr[1], 10, 32) + if e != nil { + return Unknown, false + } + if code, ok := defaultCodeRegistry.Get(uint32(codeInt)); ok { + return code, true + } + } + return Unknown, false +} diff --git a/src/vendor/github.com/beego/beego/v2/core/berror/pre_define_code.go b/src/vendor/github.com/beego/beego/v2/core/berror/pre_define_code.go new file mode 100644 index 000000000..ff8eb46b6 --- /dev/null +++ b/src/vendor/github.com/beego/beego/v2/core/berror/pre_define_code.go @@ -0,0 +1,52 @@ +// Copyright 2021 beego +// +// 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 berror + +import ( + "fmt" +) + +// pre define code + +// Unknown indicates got some error which is not defined +var Unknown = DefineCode(5000001, "error", "Unknown", fmt.Sprintf(` +Unknown error code. Usually you will see this code in three cases: +1. You forget to define Code or function DefineCode not being executed; +2. This is not Beego's error but you call FromError(); +3. Beego got unexpected error and don't know how to handle it, and then return Unknown error + +A common practice to DefineCode looks like: +%s + +In this way, you may forget to import this package, and got Unknown error. + +Sometimes, you believe you got Beego error, but actually you don't, and then you call FromError(err) + +`, goCodeBlock(` +import your_package + +func init() { + DefineCode(5100100, "your_module", "detail") + // ... +} +`))) + +func goCodeBlock(code string) string { + return codeBlock("go", code) +} + +func codeBlock(lan string, code string) string { + return fmt.Sprintf("```%s\n%s\n```", lan, code) +} diff --git a/src/vendor/github.com/beego/beego/config/config.go b/src/vendor/github.com/beego/beego/v2/core/config/config.go similarity index 63% rename from src/vendor/github.com/beego/beego/config/config.go rename to src/vendor/github.com/beego/beego/v2/core/config/config.go index b115f0c95..9e84f059c 100644 --- a/src/vendor/github.com/beego/beego/config/config.go +++ b/src/vendor/github.com/beego/beego/v2/core/config/config.go @@ -14,8 +14,8 @@ // Package config is used to parse config. // Usage: -// import "github.com/beego/beego/config" -//Examples. +// import "github.com/beego/beego/v2/core/config" +// Examples. // // cnf, err := config.NewConfig("ini", "config.conf") // @@ -37,36 +37,164 @@ // cnf.DIY(key string) (interface{}, error) // cnf.GetSection(section string) (map[string]string, error) // cnf.SaveConfigFile(filename string) error -//More docs http://beego.me/docs/module/config.md +// More docs http://beego.vip/docs/module/config.md package config import ( + "context" + "errors" "fmt" "os" "reflect" + "strconv" + "strings" "time" ) // Configer defines how to get and set value from configuration raw data. type Configer interface { - Set(key, val string) error //support section::key type in given key when using ini type. - String(key string) string //support section::key type in key string when using ini and json type; Int,Int64,Bool,Float,DIY are same. - Strings(key string) []string //get string slice + // Set support section::key type in given key when using ini type. + Set(key, val string) error + + // String support section::key type in key string when using ini and json type; Int,Int64,Bool,Float,DIY are same. + String(key string) (string, error) + // Strings get string slice + Strings(key string) ([]string, error) Int(key string) (int, error) Int64(key string) (int64, error) Bool(key string) (bool, error) Float(key string) (float64, error) - DefaultString(key string, defaultVal string) string // support section::key type in key string when using ini and json type; Int,Int64,Bool,Float,DIY are same. - DefaultStrings(key string, defaultVal []string) []string //get string slice + // DefaultString support section::key type in key string when using ini and json type; Int,Int64,Bool,Float,DIY are same. + DefaultString(key string, defaultVal string) string + // DefaultStrings get string slice + DefaultStrings(key string, defaultVal []string) []string DefaultInt(key string, defaultVal int) int DefaultInt64(key string, defaultVal int64) int64 DefaultBool(key string, defaultVal bool) bool DefaultFloat(key string, defaultVal float64) float64 + + // DIY return the original value DIY(key string) (interface{}, error) + GetSection(section string) (map[string]string, error) + + Unmarshaler(prefix string, obj interface{}, opt ...DecodeOption) error + Sub(key string) (Configer, error) + OnChange(key string, fn func(value string)) SaveConfigFile(filename string) error } +type BaseConfiger struct { + // The reader should support key like "a.b.c" + reader func(ctx context.Context, key string) (string, error) +} + +func NewBaseConfiger(reader func(ctx context.Context, key string) (string, error)) BaseConfiger { + return BaseConfiger{ + reader: reader, + } +} + +func (c *BaseConfiger) Int(key string) (int, error) { + res, err := c.reader(context.TODO(), key) + if err != nil { + return 0, err + } + return strconv.Atoi(res) +} + +func (c *BaseConfiger) Int64(key string) (int64, error) { + res, err := c.reader(context.TODO(), key) + if err != nil { + return 0, err + } + return strconv.ParseInt(res, 10, 64) +} + +func (c *BaseConfiger) Bool(key string) (bool, error) { + res, err := c.reader(context.TODO(), key) + if err != nil { + return false, err + } + return ParseBool(res) +} + +func (c *BaseConfiger) Float(key string) (float64, error) { + res, err := c.reader(context.TODO(), key) + if err != nil { + return 0, err + } + return strconv.ParseFloat(res, 64) +} + +// DefaultString returns the string value for a given key. +// if err != nil or value is empty return defaultval +func (c *BaseConfiger) DefaultString(key string, defaultVal string) string { + if res, err := c.String(key); res != "" && err == nil { + return res + } + return defaultVal +} + +// DefaultStrings returns the []string value for a given key. +// if err != nil return defaultval +func (c *BaseConfiger) DefaultStrings(key string, defaultVal []string) []string { + if res, err := c.Strings(key); len(res) > 0 && err == nil { + return res + } + return defaultVal +} + +func (c *BaseConfiger) DefaultInt(key string, defaultVal int) int { + if res, err := c.Int(key); err == nil { + return res + } + return defaultVal +} + +func (c *BaseConfiger) DefaultInt64(key string, defaultVal int64) int64 { + if res, err := c.Int64(key); err == nil { + return res + } + return defaultVal +} + +func (c *BaseConfiger) DefaultBool(key string, defaultVal bool) bool { + if res, err := c.Bool(key); err == nil { + return res + } + return defaultVal +} + +func (c *BaseConfiger) DefaultFloat(key string, defaultVal float64) float64 { + if res, err := c.Float(key); err == nil { + return res + } + return defaultVal +} + +func (c *BaseConfiger) String(key string) (string, error) { + return c.reader(context.TODO(), key) +} + +// Strings returns the []string value for a given key. +// Return nil if config value does not exist or is empty. +func (c *BaseConfiger) Strings(key string) ([]string, error) { + res, err := c.String(key) + if err != nil || res == "" { + return nil, err + } + return strings.Split(res, ";"), nil +} + +func (*BaseConfiger) Sub(string) (Configer, error) { + return nil, errors.New("unsupported operation") +} + +func (*BaseConfiger) OnChange(_ string, _ func(value string)) { + // do nothing +} + // Config is the adapter interface for parsing config file to get raw data to Configer. type Config interface { Parse(key string) (Configer, error) @@ -121,6 +249,12 @@ func ExpandValueEnvForMap(m map[string]interface{}) map[string]interface{} { value[k2] = ExpandValueEnv(v2) } m[k] = value + case map[interface{}]interface{}: + tmp := make(map[string]interface{}, len(value)) + for k2, v2 := range value { + tmp[k2.(string)] = v2 + } + m[k] = ExpandValueEnvForMap(tmp) } } return m @@ -240,3 +374,7 @@ func ToString(x interface{}) string { // Fallback to fmt package for anything else like numeric types return fmt.Sprint(x) } + +type DecodeOption func(options decodeOptions) + +type decodeOptions struct{} diff --git a/src/vendor/github.com/beego/beego/v2/core/config/error.go b/src/vendor/github.com/beego/beego/v2/core/config/error.go new file mode 100644 index 000000000..e4636c452 --- /dev/null +++ b/src/vendor/github.com/beego/beego/v2/core/config/error.go @@ -0,0 +1,25 @@ +// Copyright 2020 +// +// 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 ( + "github.com/pkg/errors" +) + +// now not all implementation return those error codes +var ( + KeyNotFoundError = errors.New("the key is not found") + InvalidValueTypeError = errors.New("the value is not expected type") +) diff --git a/src/vendor/github.com/beego/beego/config/fake.go b/src/vendor/github.com/beego/beego/v2/core/config/fake.go similarity index 71% rename from src/vendor/github.com/beego/beego/config/fake.go rename to src/vendor/github.com/beego/beego/v2/core/config/fake.go index d21ab820d..3f6f46827 100644 --- a/src/vendor/github.com/beego/beego/config/fake.go +++ b/src/vendor/github.com/beego/beego/v2/core/config/fake.go @@ -15,12 +15,14 @@ package config import ( + "context" "errors" "strconv" "strings" ) type fakeConfigContainer struct { + BaseConfiger data map[string]string } @@ -33,42 +35,14 @@ func (c *fakeConfigContainer) Set(key, val string) error { return nil } -func (c *fakeConfigContainer) String(key string) string { - return c.getData(key) -} - -func (c *fakeConfigContainer) DefaultString(key string, defaultval string) string { - v := c.String(key) - if v == "" { - return defaultval - } - return v -} - -func (c *fakeConfigContainer) Strings(key string) []string { - v := c.String(key) - if v == "" { - return nil - } - return strings.Split(v, ";") -} - -func (c *fakeConfigContainer) DefaultStrings(key string, defaultval []string) []string { - v := c.Strings(key) - if v == nil { - return defaultval - } - return v -} - func (c *fakeConfigContainer) Int(key string) (int, error) { return strconv.Atoi(c.getData(key)) } -func (c *fakeConfigContainer) DefaultInt(key string, defaultval int) int { +func (c *fakeConfigContainer) DefaultInt(key string, defaultVal int) int { v, err := c.Int(key) if err != nil { - return defaultval + return defaultVal } return v } @@ -77,10 +51,10 @@ func (c *fakeConfigContainer) Int64(key string) (int64, error) { return strconv.ParseInt(c.getData(key), 10, 64) } -func (c *fakeConfigContainer) DefaultInt64(key string, defaultval int64) int64 { +func (c *fakeConfigContainer) DefaultInt64(key string, defaultVal int64) int64 { v, err := c.Int64(key) if err != nil { - return defaultval + return defaultVal } return v } @@ -89,10 +63,10 @@ func (c *fakeConfigContainer) Bool(key string) (bool, error) { return ParseBool(c.getData(key)) } -func (c *fakeConfigContainer) DefaultBool(key string, defaultval bool) bool { +func (c *fakeConfigContainer) DefaultBool(key string, defaultVal bool) bool { v, err := c.Bool(key) if err != nil { - return defaultval + return defaultVal } return v } @@ -101,10 +75,10 @@ func (c *fakeConfigContainer) Float(key string) (float64, error) { return strconv.ParseFloat(c.getData(key), 64) } -func (c *fakeConfigContainer) DefaultFloat(key string, defaultval float64) float64 { +func (c *fakeConfigContainer) DefaultFloat(key string, defaultVal float64) float64 { v, err := c.Float(key) if err != nil { - return defaultval + return defaultVal } return v } @@ -124,11 +98,19 @@ func (c *fakeConfigContainer) SaveConfigFile(filename string) error { return errors.New("not implement in the fakeConfigContainer") } +func (c *fakeConfigContainer) Unmarshaler(prefix string, obj interface{}, opt ...DecodeOption) error { + return errors.New("unsupported operation") +} + var _ Configer = new(fakeConfigContainer) // NewFakeConfig return a fake Configer func NewFakeConfig() Configer { - return &fakeConfigContainer{ + res := &fakeConfigContainer{ data: make(map[string]string), } + res.BaseConfiger = NewBaseConfiger(func(ctx context.Context, key string) (string, error) { + return res.getData(key), nil + }) + return res } diff --git a/src/vendor/github.com/beego/beego/v2/core/config/global.go b/src/vendor/github.com/beego/beego/v2/core/config/global.go new file mode 100644 index 000000000..6f692fce5 --- /dev/null +++ b/src/vendor/github.com/beego/beego/v2/core/config/global.go @@ -0,0 +1,111 @@ +// Copyright 2020 +// +// 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 + +// We use this to simply application's development +// for most users, they only need to use those methods +var globalInstance Configer + +// InitGlobalInstance will ini the global instance +// If you want to use specific implementation, don't forget to import it. +// e.g. _ import "github.com/beego/beego/v2/core/config/etcd" +// err := InitGlobalInstance("etcd", "someconfig") +func InitGlobalInstance(name string, cfg string) error { + var err error + globalInstance, err = NewConfig(name, cfg) + return err +} + +// Set support section::key type in given key when using ini type. +func Set(key, val string) error { + return globalInstance.Set(key, val) +} + +// String support section::key type in key string when using ini and json type; Int,Int64,Bool,Float,DIY are same. +func String(key string) (string, error) { + return globalInstance.String(key) +} + +// Strings will get string slice +func Strings(key string) ([]string, error) { + return globalInstance.Strings(key) +} + +func Int(key string) (int, error) { + return globalInstance.Int(key) +} + +func Int64(key string) (int64, error) { + return globalInstance.Int64(key) +} + +func Bool(key string) (bool, error) { + return globalInstance.Bool(key) +} + +func Float(key string) (float64, error) { + return globalInstance.Float(key) +} + +// DefaultString support section::key type in key string when using ini and json type; Int,Int64,Bool,Float,DIY are same. +func DefaultString(key string, defaultVal string) string { + return globalInstance.DefaultString(key, defaultVal) +} + +// DefaultStrings will get string slice +func DefaultStrings(key string, defaultVal []string) []string { + return globalInstance.DefaultStrings(key, defaultVal) +} + +func DefaultInt(key string, defaultVal int) int { + return globalInstance.DefaultInt(key, defaultVal) +} + +func DefaultInt64(key string, defaultVal int64) int64 { + return globalInstance.DefaultInt64(key, defaultVal) +} + +func DefaultBool(key string, defaultVal bool) bool { + return globalInstance.DefaultBool(key, defaultVal) +} + +func DefaultFloat(key string, defaultVal float64) float64 { + return globalInstance.DefaultFloat(key, defaultVal) +} + +// DIY return the original value +func DIY(key string) (interface{}, error) { + return globalInstance.DIY(key) +} + +func GetSection(section string) (map[string]string, error) { + return globalInstance.GetSection(section) +} + +func Unmarshaler(prefix string, obj interface{}, opt ...DecodeOption) error { + return globalInstance.Unmarshaler(prefix, obj, opt...) +} + +func Sub(key string) (Configer, error) { + return globalInstance.Sub(key) +} + +func OnChange(key string, fn func(value string)) { + globalInstance.OnChange(key, fn) +} + +func SaveConfigFile(filename string) error { + return globalInstance.SaveConfigFile(filename) +} diff --git a/src/vendor/github.com/beego/beego/config/ini.go b/src/vendor/github.com/beego/beego/v2/core/config/ini.go similarity index 84% rename from src/vendor/github.com/beego/beego/config/ini.go rename to src/vendor/github.com/beego/beego/v2/core/config/ini.go index 002e5e056..d4dea2e39 100644 --- a/src/vendor/github.com/beego/beego/config/ini.go +++ b/src/vendor/github.com/beego/beego/v2/core/config/ini.go @@ -17,6 +17,7 @@ package config import ( "bufio" "bytes" + "context" "errors" "io" "io/ioutil" @@ -26,6 +27,10 @@ import ( "strconv" "strings" "sync" + + "github.com/mitchellh/mapstructure" + + "github.com/beego/beego/v2/core/logs" ) var ( @@ -41,8 +46,7 @@ var ( ) // IniConfig implements Config to parse ini file. -type IniConfig struct { -} +type IniConfig struct{} // Parse creates a new Config and parses the file configuration from the named file. func (ini *IniConfig) Parse(name string) (Configer, error) { @@ -65,6 +69,10 @@ func (ini *IniConfig) parseData(dir string, data []byte) (*IniConfigContainer, e keyComment: make(map[string]string), RWMutex: sync.RWMutex{}, } + + cfg.BaseConfiger = NewBaseConfiger(func(ctx context.Context, key string) (string, error) { + return cfg.getdata(key), nil + }) cfg.Lock() defer cfg.Unlock() @@ -90,7 +98,7 @@ func (ini *IniConfig) parseData(dir string, data []byte) (*IniConfigContainer, e break } - //It might be a good idea to throw a error on all unknonw errors? + // It might be a good idea to throw an error on all unknonw errors? if _, ok := err.(*os.PathError); ok { return nil, err } @@ -222,9 +230,10 @@ func (ini *IniConfig) ParseData(data []byte) (Configer, error) { return ini.parseData(dir, data) } -// IniConfigContainer A Config represents the ini configuration. +// IniConfigContainer is a config which represents the ini configuration. // When set and get value, support key as section:name type. type IniConfigContainer struct { + BaseConfiger data map[string]map[string]string // section=> key:val sectionComment map[string]string // section : comment keyComment map[string]string // id: []{comment, key...}; id 1 is for main comment. @@ -237,11 +246,11 @@ func (c *IniConfigContainer) Bool(key string) (bool, error) { } // DefaultBool returns the boolean value for a given key. -// if err != nil return defaultval -func (c *IniConfigContainer) DefaultBool(key string, defaultval bool) bool { +// if err != nil return defaultVal +func (c *IniConfigContainer) DefaultBool(key string, defaultVal bool) bool { v, err := c.Bool(key) if err != nil { - return defaultval + return defaultVal } return v } @@ -252,11 +261,11 @@ func (c *IniConfigContainer) Int(key string) (int, error) { } // DefaultInt returns the integer value for a given key. -// if err != nil return defaultval -func (c *IniConfigContainer) DefaultInt(key string, defaultval int) int { +// if err != nil return defaultVal +func (c *IniConfigContainer) DefaultInt(key string, defaultVal int) int { v, err := c.Int(key) if err != nil { - return defaultval + return defaultVal } return v } @@ -267,11 +276,11 @@ func (c *IniConfigContainer) Int64(key string) (int64, error) { } // DefaultInt64 returns the int64 value for a given key. -// if err != nil return defaultval -func (c *IniConfigContainer) DefaultInt64(key string, defaultval int64) int64 { +// if err != nil return defaultVal +func (c *IniConfigContainer) DefaultInt64(key string, defaultVal int64) int64 { v, err := c.Int64(key) if err != nil { - return defaultval + return defaultVal } return v } @@ -282,46 +291,46 @@ func (c *IniConfigContainer) Float(key string) (float64, error) { } // DefaultFloat returns the float64 value for a given key. -// if err != nil return defaultval -func (c *IniConfigContainer) DefaultFloat(key string, defaultval float64) float64 { +// if err != nil return defaultVal +func (c *IniConfigContainer) DefaultFloat(key string, defaultVal float64) float64 { v, err := c.Float(key) if err != nil { - return defaultval + return defaultVal } return v } // String returns the string value for a given key. -func (c *IniConfigContainer) String(key string) string { - return c.getdata(key) +func (c *IniConfigContainer) String(key string) (string, error) { + return c.getdata(key), nil } // DefaultString returns the string value for a given key. -// if err != nil return defaultval -func (c *IniConfigContainer) DefaultString(key string, defaultval string) string { - v := c.String(key) - if v == "" { - return defaultval +// if err != nil return defaultVal +func (c *IniConfigContainer) DefaultString(key string, defaultVal string) string { + v, err := c.String(key) + if v == "" || err != nil { + return defaultVal } return v } // Strings returns the []string value for a given key. // Return nil if config value does not exist or is empty. -func (c *IniConfigContainer) Strings(key string) []string { - v := c.String(key) - if v == "" { - return nil +func (c *IniConfigContainer) Strings(key string) ([]string, error) { + v, err := c.String(key) + if v == "" || err != nil { + return nil, err } - return strings.Split(v, ";") + return strings.Split(v, ";"), nil } // DefaultStrings returns the []string value for a given key. -// if err != nil return defaultval -func (c *IniConfigContainer) DefaultStrings(key string, defaultval []string) []string { - v := c.Strings(key) - if v == nil { - return defaultval +// if err != nil return defaultVal +func (c *IniConfigContainer) DefaultStrings(key string, defaultVal []string) []string { + v, err := c.Strings(key) + if v == nil || err != nil { + return defaultVal } return v } @@ -437,7 +446,7 @@ func (c *IniConfigContainer) SaveConfigFile(filename string) (err error) { // Set writes a new value for key. // if write to one section, the key need be "section::key". // if the section is not existed, it panics. -func (c *IniConfigContainer) Set(key, value string) error { +func (c *IniConfigContainer) Set(key, val string) error { c.Lock() defer c.Unlock() if len(key) == 0 { @@ -460,7 +469,7 @@ func (c *IniConfigContainer) Set(key, value string) error { if _, ok := c.data[section]; !ok { c.data[section] = make(map[string]string) } - c.data[section][k] = value + c.data[section][k] = val return nil } @@ -469,7 +478,7 @@ func (c *IniConfigContainer) DIY(key string) (v interface{}, err error) { if v, ok := c.data[strings.ToLower(key)]; ok { return v, nil } - return v, errors.New("key not find") + return v, errors.New("key not found") } // section.key or key @@ -499,6 +508,20 @@ func (c *IniConfigContainer) getdata(key string) string { return "" } +func (c *IniConfigContainer) Unmarshaler(prefix string, obj interface{}, opt ...DecodeOption) error { + if len(prefix) > 0 { + return errors.New("unsupported prefix params") + } + return mapstructure.Decode(c.data, obj) +} + func init() { Register("ini", &IniConfig{}) + + err := InitGlobalInstance("ini", "conf/app.conf") + if err != nil { + logs.Debug("init global config instance failed. If you do not use this, just ignore it. ", err) + } } + +// Ignore this error diff --git a/src/vendor/github.com/beego/beego/logs/README.md b/src/vendor/github.com/beego/beego/v2/core/logs/README.md similarity index 93% rename from src/vendor/github.com/beego/beego/logs/README.md rename to src/vendor/github.com/beego/beego/v2/core/logs/README.md index 660b1fe19..b2c405ffb 100644 --- a/src/vendor/github.com/beego/beego/logs/README.md +++ b/src/vendor/github.com/beego/beego/v2/core/logs/README.md @@ -1,24 +1,22 @@ ## logs -logs is a Go logs manager. It can use many logs adapters. The repo is inspired by `database/sql` . +logs is a Go logs manager. It can use many logs adapters. The repo is inspired by `database/sql` . ## How to install? - go get github.com/beego/beego/logs - + go get github.com/beego/beego/v2/core/logs ## What adapters are supported? As of now this logs support console, file,smtp and conn. - ## How to use it? First you must import it ```golang import ( - "github.com/beego/beego/logs" + "github.com/beego/beego/v2/core/logs" ) ``` diff --git a/src/vendor/github.com/beego/beego/logs/accesslog.go b/src/vendor/github.com/beego/beego/v2/core/logs/access_log.go similarity index 89% rename from src/vendor/github.com/beego/beego/logs/accesslog.go rename to src/vendor/github.com/beego/beego/v2/core/logs/access_log.go index 9011b6022..10455fe92 100644 --- a/src/vendor/github.com/beego/beego/logs/accesslog.go +++ b/src/vendor/github.com/beego/beego/v2/core/logs/access_log.go @@ -28,7 +28,7 @@ const ( jsonFormat = "JSON_FORMAT" ) -// AccessLogRecord struct for holding access log data. +// AccessLogRecord is astruct for holding access log data. type AccessLogRecord struct { RemoteAddr string `json:"remote_addr"` RequestTime time.Time `json:"request_time"` @@ -63,7 +63,17 @@ func disableEscapeHTML(i interface{}) { // AccessLog - Format and print access log. func AccessLog(r *AccessLogRecord, format string) { - var msg string + msg := r.format(format) + lm := &LogMsg{ + Msg: strings.TrimSpace(msg), + When: time.Now(), + Level: levelLoggerImpl, + } + beeLogger.writeMsg(lm) +} + +func (r *AccessLogRecord) format(format string) string { + msg := "" switch format { case apacheFormat: timeFormatted := r.RequestTime.Format("02/Jan/2006 03:04:05") @@ -79,5 +89,5 @@ func AccessLog(r *AccessLogRecord, format string) { msg = string(jsonData) } } - beeLogger.writeMsg(levelLoggerImpl, strings.TrimSpace(msg)) + return msg } diff --git a/src/vendor/github.com/beego/beego/logs/conn.go b/src/vendor/github.com/beego/beego/v2/core/logs/conn.go similarity index 66% rename from src/vendor/github.com/beego/beego/logs/conn.go rename to src/vendor/github.com/beego/beego/v2/core/logs/conn.go index 74c458ab8..cfeb3f916 100644 --- a/src/vendor/github.com/beego/beego/logs/conn.go +++ b/src/vendor/github.com/beego/beego/v2/core/logs/conn.go @@ -16,16 +16,20 @@ package logs import ( "encoding/json" + "fmt" "io" "net" - "time" + + "github.com/pkg/errors" ) // connWriter implements LoggerInterface. -// it writes messages in keep-live tcp connection. +// Writes messages in keep-live tcp connection. type connWriter struct { lg *logWriter innerWriter io.WriteCloser + formatter LogFormatter + Formatter string `json:"formatter"` ReconnectOnMsg bool `json:"reconnectOnMsg"` Reconnect bool `json:"reconnect"` Net string `json:"net"` @@ -33,23 +37,40 @@ type connWriter struct { Level int `json:"level"` } -// NewConn create new ConnWrite returning as LoggerInterface. +// NewConn creates new ConnWrite returning as LoggerInterface. func NewConn() Logger { conn := new(connWriter) conn.Level = LevelTrace + conn.formatter = conn return conn } -// Init init connection writer with json config. -// json config only need key "level". -func (c *connWriter) Init(jsonConfig string) error { - return json.Unmarshal([]byte(jsonConfig), c) +func (c *connWriter) Format(lm *LogMsg) string { + return lm.OldStyleFormat() } -// WriteMsg write message in connection. -// if connection is down, try to re-connect. -func (c *connWriter) WriteMsg(when time.Time, msg string, level int) error { - if level > c.Level { +// Init initializes a connection writer with json config. +// json config only needs they "level" key +func (c *connWriter) Init(config string) error { + res := json.Unmarshal([]byte(config), c) + if res == nil && len(c.Formatter) > 0 { + fmtr, ok := GetFormatter(c.Formatter) + if !ok { + return errors.New(fmt.Sprintf("the formatter with name: %s not found", c.Formatter)) + } + c.formatter = fmtr + } + return res +} + +func (c *connWriter) SetFormatter(f LogFormatter) { + c.formatter = f +} + +// WriteMsg writes message in connection. +// If connection is down, try to re-connect. +func (c *connWriter) WriteMsg(lm *LogMsg) error { + if lm.Level > c.Level { return nil } if c.needToConnectOnMsg() { @@ -63,7 +84,9 @@ func (c *connWriter) WriteMsg(when time.Time, msg string, level int) error { defer c.innerWriter.Close() } - _, err := c.lg.writeln(when, msg) + msg := c.formatter.Format(lm) + + _, err := c.lg.writeln(msg) if err != nil { return err } @@ -72,7 +95,6 @@ func (c *connWriter) WriteMsg(when time.Time, msg string, level int) error { // Flush implementing method. empty. func (c *connWriter) Flush() { - } // Destroy destroy connection writer and close tcp listener. diff --git a/src/vendor/github.com/beego/beego/logs/console.go b/src/vendor/github.com/beego/beego/v2/core/logs/console.go similarity index 58% rename from src/vendor/github.com/beego/beego/logs/console.go rename to src/vendor/github.com/beego/beego/v2/core/logs/console.go index 3dcaee1df..ff4fcf468 100644 --- a/src/vendor/github.com/beego/beego/logs/console.go +++ b/src/vendor/github.com/beego/beego/v2/core/logs/console.go @@ -16,17 +16,18 @@ package logs import ( "encoding/json" + "fmt" "os" "strings" - "time" + "github.com/pkg/errors" "github.com/shiena/ansicolor" ) // brush is a color join function type brush func(string) string -// newBrush return a fix color Brush +// newBrush returns a fix color Brush func newBrush(color string) brush { pre := "\033[" reset := "\033[0m" @@ -48,50 +49,75 @@ var colors = []brush{ // consoleWriter implements LoggerInterface and writes messages to terminal. type consoleWriter struct { - lg *logWriter - Level int `json:"level"` - Colorful bool `json:"color"` //this filed is useful only when system's terminal supports color + lg *logWriter + formatter LogFormatter + Formatter string `json:"formatter"` + Level int `json:"level"` + Colorful bool `json:"color"` // this filed is useful only when system's terminal supports color } -// NewConsole create ConsoleWriter returning as LoggerInterface. +func (c *consoleWriter) Format(lm *LogMsg) string { + msg := lm.OldStyleFormat() + if c.Colorful { + msg = strings.Replace(msg, levelPrefix[lm.Level], colors[lm.Level](levelPrefix[lm.Level]), 1) + } + h, _, _ := formatTimeHeader(lm.When) + return string(append(h, msg...)) +} + +func (c *consoleWriter) SetFormatter(f LogFormatter) { + c.formatter = f +} + +// NewConsole creates ConsoleWriter returning as LoggerInterface. func NewConsole() Logger { + return newConsole() +} + +func newConsole() *consoleWriter { cw := &consoleWriter{ lg: newLogWriter(ansicolor.NewAnsiColorWriter(os.Stdout)), Level: LevelDebug, Colorful: true, } + cw.formatter = cw return cw } -// Init init console logger. -// jsonConfig like '{"level":LevelTrace}'. -func (c *consoleWriter) Init(jsonConfig string) error { - if len(jsonConfig) == 0 { +// Init initianlizes the console logger. +// jsonConfig must be in the format '{"level":LevelTrace}' +func (c *consoleWriter) Init(config string) error { + if len(config) == 0 { return nil } - return json.Unmarshal([]byte(jsonConfig), c) + + res := json.Unmarshal([]byte(config), c) + if res == nil && len(c.Formatter) > 0 { + fmtr, ok := GetFormatter(c.Formatter) + if !ok { + return errors.New(fmt.Sprintf("the formatter with name: %s not found", c.Formatter)) + } + c.formatter = fmtr + } + return res } -// WriteMsg write message in console. -func (c *consoleWriter) WriteMsg(when time.Time, msg string, level int) error { - if level > c.Level { +// WriteMsg writes message in console. +func (c *consoleWriter) WriteMsg(lm *LogMsg) error { + if lm.Level > c.Level { return nil } - if c.Colorful { - msg = strings.Replace(msg, levelPrefix[level], colors[level](levelPrefix[level]), 1) - } - c.lg.writeln(when, msg) + msg := c.formatter.Format(lm) + c.lg.writeln(msg) return nil } // Destroy implementing method. empty. func (c *consoleWriter) Destroy() { - } // Flush implementing method. empty. func (c *consoleWriter) Flush() { - } func init() { diff --git a/src/vendor/github.com/beego/beego/logs/file.go b/src/vendor/github.com/beego/beego/v2/core/logs/file.go similarity index 82% rename from src/vendor/github.com/beego/beego/logs/file.go rename to src/vendor/github.com/beego/beego/v2/core/logs/file.go index 40a3572a0..3234a674c 100644 --- a/src/vendor/github.com/beego/beego/logs/file.go +++ b/src/vendor/github.com/beego/beego/v2/core/logs/file.go @@ -30,9 +30,14 @@ import ( ) // fileLogWriter implements LoggerInterface. -// It writes messages by lines limit, file size limit, or time frequency. +// Writes messages by lines limit, file size limit, or time frequency. type fileLogWriter struct { sync.RWMutex // write log order by order and atomic incr maxLinesCurLines and maxSizeCurSize + + Rotate bool `json:"rotate"` + Daily bool `json:"daily"` + Hourly bool `json:"hourly"` + // The opened file Filename string `json:"filename"` fileWriter *os.File @@ -49,29 +54,30 @@ type fileLogWriter struct { maxSizeCurSize int // Rotate daily - Daily bool `json:"daily"` MaxDays int64 `json:"maxdays"` dailyOpenDate int dailyOpenTime time.Time // Rotate hourly - Hourly bool `json:"hourly"` MaxHours int64 `json:"maxhours"` hourlyOpenDate int hourlyOpenTime time.Time - Rotate bool `json:"rotate"` - Level int `json:"level"` - + // Permissions for log file Perm string `json:"perm"` + // Permissions for directory if it is specified in FileName + DirPerm string `json:"dirperm"` RotatePerm string `json:"rotateperm"` fileNameOnly, suffix string // like "project.log", project is fileNameOnly and .log is suffix + + logFormatter LogFormatter + Formatter string `json:"formatter"` } -// newFileWriter create a FileLogWriter returning as LoggerInterface. +// newFileWriter creates a FileLogWriter returning as LoggerInterface. func newFileWriter() Logger { w := &fileLogWriter{ Daily: true, @@ -82,13 +88,26 @@ func newFileWriter() Logger { RotatePerm: "0440", Level: LevelTrace, Perm: "0660", + DirPerm: "0770", MaxLines: 10000000, MaxFiles: 999, MaxSize: 1 << 28, } + w.logFormatter = w return w } +func (*fileLogWriter) Format(lm *LogMsg) string { + msg := lm.OldStyleFormat() + hd, _, _ := formatTimeHeader(lm.When) + msg = fmt.Sprintf("%s %s\n", string(hd), msg) + return msg +} + +func (w *fileLogWriter) SetFormatter(f LogFormatter) { + w.logFormatter = f +} + // Init file logger with json config. // jsonConfig like: // { @@ -100,12 +119,12 @@ func newFileWriter() Logger { // "rotate":true, // "perm":"0600" // } -func (w *fileLogWriter) Init(jsonConfig string) error { - err := json.Unmarshal([]byte(jsonConfig), w) +func (w *fileLogWriter) Init(config string) error { + err := json.Unmarshal([]byte(config), w) if err != nil { return err } - if len(w.Filename) == 0 { + if w.Filename == "" { return errors.New("jsonconfig must have filename") } w.suffix = filepath.Ext(w.Filename) @@ -113,6 +132,14 @@ func (w *fileLogWriter) Init(jsonConfig string) error { if w.suffix == "" { w.suffix = ".log" } + + if len(w.Formatter) > 0 { + fmtr, ok := GetFormatter(w.Formatter) + if !ok { + return fmt.Errorf("the formatter with name: %s not found", w.Formatter) + } + w.logFormatter = fmtr + } err = w.startLogger() return err } @@ -130,42 +157,43 @@ func (w *fileLogWriter) startLogger() error { return w.initFd() } -func (w *fileLogWriter) needRotateDaily(size int, day int) bool { +func (w *fileLogWriter) needRotateDaily(day int) bool { return (w.MaxLines > 0 && w.maxLinesCurLines >= w.MaxLines) || (w.MaxSize > 0 && w.maxSizeCurSize >= w.MaxSize) || (w.Daily && day != w.dailyOpenDate) } -func (w *fileLogWriter) needRotateHourly(size int, hour int) bool { +func (w *fileLogWriter) needRotateHourly(hour int) bool { return (w.MaxLines > 0 && w.maxLinesCurLines >= w.MaxLines) || (w.MaxSize > 0 && w.maxSizeCurSize >= w.MaxSize) || (w.Hourly && hour != w.hourlyOpenDate) - } -// WriteMsg write logger message into file. -func (w *fileLogWriter) WriteMsg(when time.Time, msg string, level int) error { - if level > w.Level { +// WriteMsg writes logger message into file. +func (w *fileLogWriter) WriteMsg(lm *LogMsg) error { + if lm.Level > w.Level { return nil } - hd, d, h := formatTimeHeader(when) - msg = string(hd) + msg + "\n" + + _, d, h := formatTimeHeader(lm.When) + + msg := w.logFormatter.Format(lm) if w.Rotate { w.RLock() - if w.needRotateHourly(len(msg), h) { + if w.needRotateHourly(h) { w.RUnlock() w.Lock() - if w.needRotateHourly(len(msg), h) { - if err := w.doRotate(when); err != nil { + if w.needRotateHourly(h) { + if err := w.doRotate(lm.When); err != nil { fmt.Fprintf(os.Stderr, "FileLogWriter(%q): %s\n", w.Filename, err) } } w.Unlock() - } else if w.needRotateDaily(len(msg), d) { + } else if w.needRotateDaily(d) { w.RUnlock() w.Lock() - if w.needRotateDaily(len(msg), d) { - if err := w.doRotate(when); err != nil { + if w.needRotateDaily(d) { + if err := w.doRotate(lm.When); err != nil { fmt.Fprintf(os.Stderr, "FileLogWriter(%q): %s\n", w.Filename, err) } } @@ -192,8 +220,13 @@ func (w *fileLogWriter) createLogFile() (*os.File, error) { return nil, err } + dirperm, err := strconv.ParseInt(w.DirPerm, 8, 64) + if err != nil { + return nil, err + } + filepath := path.Dir(w.Filename) - os.MkdirAll(filepath, os.FileMode(perm)) + os.MkdirAll(filepath, os.FileMode(dirperm)) fd, err := os.OpenFile(w.Filename, os.O_WRONLY|os.O_APPEND|os.O_CREATE, os.FileMode(perm)) if err == nil { @@ -236,7 +269,7 @@ func (w *fileLogWriter) dailyRotate(openTime time.Time) { tm := time.NewTimer(time.Duration(nextDay.UnixNano() - openTime.UnixNano() + 100)) <-tm.C w.Lock() - if w.needRotateDaily(0, time.Now().Day()) { + if w.needRotateDaily(time.Now().Day()) { if err := w.doRotate(time.Now()); err != nil { fmt.Fprintf(os.Stderr, "FileLogWriter(%q): %s\n", w.Filename, err) } @@ -251,7 +284,7 @@ func (w *fileLogWriter) hourlyRotate(openTime time.Time) { tm := time.NewTimer(time.Duration(nextHour.UnixNano() - openTime.UnixNano() + 100)) <-tm.C w.Lock() - if w.needRotateHourly(0, time.Now().Hour()) { + if w.needRotateHourly(time.Now().Hour()) { if err := w.doRotate(time.Now()); err != nil { fmt.Fprintf(os.Stderr, "FileLogWriter(%q): %s\n", w.Filename, err) } @@ -286,7 +319,7 @@ func (w *fileLogWriter) lines() (int, error) { return count, nil } -// DoRotate means it need to write file in new file. +// DoRotate means it needs to write logs into a new file. // new file name like xx.2013-01-01.log (daily) or xx.001.log (by line or size) func (w *fileLogWriter) doRotate(logTime time.Time) error { // file exists @@ -302,7 +335,7 @@ func (w *fileLogWriter) doRotate(logTime time.Time) error { _, err = os.Lstat(w.Filename) if err != nil { - //even if the file is not exist or other ,we should RESTART the logger + // even if the file is not exist or other ,we should RESTART the logger goto RESTART_LOGGER } @@ -397,7 +430,7 @@ func (w *fileLogWriter) Destroy() { w.fileWriter.Close() } -// Flush flush file logger. +// Flush flushes file logger. // there are no buffering messages in file logger in memory. // flush file means sync file from disk. func (w *fileLogWriter) Flush() { diff --git a/src/vendor/github.com/beego/beego/v2/core/logs/formatter.go b/src/vendor/github.com/beego/beego/v2/core/logs/formatter.go new file mode 100644 index 000000000..6fa4f70c5 --- /dev/null +++ b/src/vendor/github.com/beego/beego/v2/core/logs/formatter.go @@ -0,0 +1,91 @@ +// Copyright 2020 +// +// 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 logs + +import ( + "fmt" + "path" + "strconv" +) + +var formatterMap = make(map[string]LogFormatter, 4) + +type LogFormatter interface { + Format(lm *LogMsg) string +} + +// PatternLogFormatter provides a quick format method +// for example: +// tes := &PatternLogFormatter{Pattern: "%F:%n|%w %t>> %m", WhenFormat: "2006-01-02"} +// RegisterFormatter("tes", tes) +// SetGlobalFormatter("tes") +type PatternLogFormatter struct { + Pattern string + WhenFormat string +} + +func (p *PatternLogFormatter) getWhenFormatter() string { + s := p.WhenFormat + if s == "" { + s = "2006/01/02 15:04:05.123" // default style + } + return s +} + +func (p *PatternLogFormatter) Format(lm *LogMsg) string { + return p.ToString(lm) +} + +// RegisterFormatter register an formatter. Usually you should use this to extend your custom formatter +// for example: +// RegisterFormatter("my-fmt", &MyFormatter{}) +// logs.SetFormatter(Console, `{"formatter": "my-fmt"}`) +func RegisterFormatter(name string, fmtr LogFormatter) { + formatterMap[name] = fmtr +} + +func GetFormatter(name string) (LogFormatter, bool) { + res, ok := formatterMap[name] + return res, ok +} + +// ToString 'w' when, 'm' msg,'f' filename,'F' full path,'n' line number +// 'l' level number, 't' prefix of level type, 'T' full name of level type +func (p *PatternLogFormatter) ToString(lm *LogMsg) string { + s := []rune(p.Pattern) + msg := fmt.Sprintf(lm.Msg, lm.Args...) + m := map[rune]string{ + 'w': lm.When.Format(p.getWhenFormatter()), + 'm': msg, + 'n': strconv.Itoa(lm.LineNumber), + 'l': strconv.Itoa(lm.Level), + 't': levelPrefix[lm.Level], + 'T': levelNames[lm.Level], + 'F': lm.FilePath, + } + _, m['f'] = path.Split(lm.FilePath) + res := "" + for i := 0; i < len(s)-1; i++ { + if s[i] == '%' { + if k, ok := m[s[i+1]]; ok { + res += k + i++ + continue + } + } + res += string(s[i]) + } + return res +} diff --git a/src/vendor/github.com/beego/beego/logs/jianliao.go b/src/vendor/github.com/beego/beego/v2/core/logs/jianliao.go similarity index 56% rename from src/vendor/github.com/beego/beego/logs/jianliao.go rename to src/vendor/github.com/beego/beego/v2/core/logs/jianliao.go index 88ba0f9af..95835c068 100644 --- a/src/vendor/github.com/beego/beego/logs/jianliao.go +++ b/src/vendor/github.com/beego/beego/v2/core/logs/jianliao.go @@ -5,7 +5,8 @@ import ( "fmt" "net/http" "net/url" - "time" + + "github.com/pkg/errors" ) // JLWriter implements beego LoggerInterface and is used to send jiaoliao webhook @@ -16,26 +17,49 @@ type JLWriter struct { RedirectURL string `json:"redirecturl,omitempty"` ImageURL string `json:"imageurl,omitempty"` Level int `json:"level"` + + formatter LogFormatter + Formatter string `json:"formatter"` } -// newJLWriter create jiaoliao writer. +// newJLWriter creates jiaoliao writer. func newJLWriter() Logger { - return &JLWriter{Level: LevelTrace} + res := &JLWriter{Level: LevelTrace} + res.formatter = res + return res } // Init JLWriter with json config string -func (s *JLWriter) Init(jsonconfig string) error { - return json.Unmarshal([]byte(jsonconfig), s) +func (s *JLWriter) Init(config string) error { + res := json.Unmarshal([]byte(config), s) + if res == nil && len(s.Formatter) > 0 { + fmtr, ok := GetFormatter(s.Formatter) + if !ok { + return errors.New(fmt.Sprintf("the formatter with name: %s not found", s.Formatter)) + } + s.formatter = fmtr + } + return res } -// WriteMsg write message in smtp writer. -// it will send an email with subject and only this message. -func (s *JLWriter) WriteMsg(when time.Time, msg string, level int) error { - if level > s.Level { +func (s *JLWriter) Format(lm *LogMsg) string { + msg := lm.OldStyleFormat() + msg = fmt.Sprintf("%s %s", lm.When.Format("2006-01-02 15:04:05"), msg) + return msg +} + +func (s *JLWriter) SetFormatter(f LogFormatter) { + s.formatter = f +} + +// WriteMsg writes message in smtp writer. +// Sends an email with subject and only this message. +func (s *JLWriter) WriteMsg(lm *LogMsg) error { + if lm.Level > s.Level { return nil } - text := fmt.Sprintf("%s %s", when.Format("2006-01-02 15:04:05"), msg) + text := s.formatter.Format(lm) form := url.Values{} form.Add("authorName", s.AuthorName) diff --git a/src/vendor/github.com/beego/beego/logs/log.go b/src/vendor/github.com/beego/beego/v2/core/logs/log.go similarity index 73% rename from src/vendor/github.com/beego/beego/logs/log.go rename to src/vendor/github.com/beego/beego/v2/core/logs/log.go index 17bdc8d61..e5e736cae 100644 --- a/src/vendor/github.com/beego/beego/logs/log.go +++ b/src/vendor/github.com/beego/beego/v2/core/logs/log.go @@ -15,7 +15,7 @@ // Package logs provide a general log interface // Usage: // -// import "github.com/beego/beego/logs" +// import "github.com/beego/beego/v2/core/logs" // // log := NewLogger(10000) // log.SetLogger("console", "") @@ -30,19 +30,19 @@ // log.Debug("debug") // log.Critical("critical") // -// more docs http://beego.me/docs/module/logs.md +// more docs http://beego.vip/docs/module/logs.md package logs import ( "fmt" "log" "os" - "path" "runtime" - "strconv" "strings" "sync" "time" + + "github.com/pkg/errors" ) // RFC5424 log message levels. @@ -86,13 +86,16 @@ type newLoggerFunc func() Logger // Logger defines the behavior of a log provider. type Logger interface { Init(config string) error - WriteMsg(when time.Time, msg string, level int) error + WriteMsg(lm *LogMsg) error Destroy() Flush() + SetFormatter(f LogFormatter) } -var adapters = make(map[string]newLoggerFunc) -var levelPrefix = [LevelDebug + 1]string{"[M]", "[A]", "[C]", "[E]", "[W]", "[N]", "[I]", "[D]"} +var ( + adapters = make(map[string]newLoggerFunc) + levelPrefix = [LevelDebug + 1]string{"[M]", "[A]", "[C]", "[E]", "[W]", "[N]", "[I]", "[D]"} +) // Register makes a log provide available by the provided name. // If Register is called twice with the same name or if driver is nil, @@ -108,20 +111,22 @@ func Register(name string, log newLoggerFunc) { } // BeeLogger is default logger in beego application. -// it can contain several providers and log message into all providers. +// Can contain several providers and log message into all providers. type BeeLogger struct { lock sync.Mutex - level int init bool enableFuncCallDepth bool - loggerFuncCallDepth int + enableFullFilePath bool asynchronous bool + wg sync.WaitGroup + level int + loggerFuncCallDepth int prefix string msgChanLen int64 - msgChan chan *logMsg + msgChan chan *LogMsg signalChan chan string - wg sync.WaitGroup outputs []*nameLogger + globalFormatter string } const defaultAsyncMsgLen = 1e3 @@ -131,21 +136,15 @@ type nameLogger struct { name string } -type logMsg struct { - level int - msg string - when time.Time -} - var logMsgPool *sync.Pool // NewLogger returns a new BeeLogger. -// channelLen means the number of messages in chan(used where asynchronous is true). +// channelLen: the number of messages in chan(used where asynchronous is true). // if the buffering chan is full, logger adapters write to file or other way. func NewLogger(channelLens ...int64) *BeeLogger { bl := new(BeeLogger) bl.level = LevelDebug - bl.loggerFuncCallDepth = 2 + bl.loggerFuncCallDepth = 3 bl.msgChanLen = append(channelLens, 0)[0] if bl.msgChanLen <= 0 { bl.msgChanLen = defaultAsyncMsgLen @@ -155,7 +154,7 @@ func NewLogger(channelLens ...int64) *BeeLogger { return bl } -// Async set the log to asynchronous and start the goroutine +// Async sets the log to asynchronous and start the goroutine func (bl *BeeLogger) Async(msgLen ...int64) *BeeLogger { bl.lock.Lock() defer bl.lock.Unlock() @@ -166,10 +165,10 @@ func (bl *BeeLogger) Async(msgLen ...int64) *BeeLogger { if len(msgLen) > 0 && msgLen[0] > 0 { bl.msgChanLen = msgLen[0] } - bl.msgChan = make(chan *logMsg, bl.msgChanLen) + bl.msgChan = make(chan *LogMsg, bl.msgChanLen) logMsgPool = &sync.Pool{ New: func() interface{} { - return &logMsg{} + return &LogMsg{} }, } bl.wg.Add(1) @@ -178,7 +177,7 @@ func (bl *BeeLogger) Async(msgLen ...int64) *BeeLogger { } // SetLogger provides a given logger adapter into BeeLogger with config string. -// config need to be correct JSON as string: {"interval":360}. +// config must in in JSON format like {"interval":360}} func (bl *BeeLogger) setLogger(adapterName string, configs ...string) error { config := append(configs, "{}")[0] for _, l := range bl.outputs { @@ -193,6 +192,16 @@ func (bl *BeeLogger) setLogger(adapterName string, configs ...string) error { } lg := logAdapter() + + // Global formatter overrides the default set formatter + if len(bl.globalFormatter) > 0 { + fmtr, ok := GetFormatter(bl.globalFormatter) + if !ok { + return errors.New(fmt.Sprintf("the formatter with name: %s not found", bl.globalFormatter)) + } + lg.SetFormatter(fmtr) + } + err := lg.Init(config) if err != nil { fmt.Fprintln(os.Stderr, "logs.BeeLogger.SetLogger: "+err.Error()) @@ -203,7 +212,7 @@ func (bl *BeeLogger) setLogger(adapterName string, configs ...string) error { } // SetLogger provides a given logger adapter into BeeLogger with config string. -// config need to be correct JSON as string: {"interval":360}. +// config must in in JSON format like {"interval":360}} func (bl *BeeLogger) SetLogger(adapterName string, configs ...string) error { bl.lock.Lock() defer bl.lock.Unlock() @@ -214,7 +223,7 @@ func (bl *BeeLogger) SetLogger(adapterName string, configs ...string) error { return bl.setLogger(adapterName, configs...) } -// DelLogger remove a logger adapter in BeeLogger. +// DelLogger removes a logger adapter in BeeLogger. func (bl *BeeLogger) DelLogger(adapterName string) error { bl.lock.Lock() defer bl.lock.Unlock() @@ -233,9 +242,9 @@ func (bl *BeeLogger) DelLogger(adapterName string) error { return nil } -func (bl *BeeLogger) writeToLoggers(when time.Time, msg string, level int) { +func (bl *BeeLogger) writeToLoggers(lm *LogMsg) { for _, l := range bl.outputs { - err := l.WriteMsg(when, msg, level) + err := l.WriteMsg(lm) if err != nil { fmt.Fprintf(os.Stderr, "unable to WriteMsg to adapter:%v,error:%v\n", l.name, err) } @@ -250,65 +259,74 @@ func (bl *BeeLogger) Write(p []byte) (n int, err error) { if p[len(p)-1] == '\n' { p = p[0 : len(p)-1] } + lm := &LogMsg{ + Msg: string(p), + Level: levelLoggerImpl, + When: time.Now(), + } + // set levelLoggerImpl to ensure all log message will be write out - err = bl.writeMsg(levelLoggerImpl, string(p)) + err = bl.writeMsg(lm) if err == nil { - return len(p), err + return len(p), nil } return 0, err } -func (bl *BeeLogger) writeMsg(logLevel int, msg string, v ...interface{}) error { +func (bl *BeeLogger) writeMsg(lm *LogMsg) error { if !bl.init { bl.lock.Lock() bl.setLogger(AdapterConsole) bl.lock.Unlock() } - if len(v) > 0 { - msg = fmt.Sprintf(msg, v...) + var ( + file string + line int + ok bool + ) + + _, file, line, ok = runtime.Caller(bl.loggerFuncCallDepth) + if !ok { + file = "???" + line = 0 } + lm.FilePath = file + lm.LineNumber = line + lm.Prefix = bl.prefix - msg = bl.prefix + " " + msg + lm.enableFullFilePath = bl.enableFullFilePath + lm.enableFuncCallDepth = bl.enableFuncCallDepth - when := time.Now() - if bl.enableFuncCallDepth { - _, file, line, ok := runtime.Caller(bl.loggerFuncCallDepth) - if !ok { - file = "???" - line = 0 - } - _, filename := path.Split(file) - msg = "[" + filename + ":" + strconv.Itoa(line) + "] " + msg - } - - //set level info in front of filename info - if logLevel == levelLoggerImpl { + // set level info in front of filename info + if lm.Level == levelLoggerImpl { // set to emergency to ensure all log will be print out correctly - logLevel = LevelEmergency - } else { - msg = levelPrefix[logLevel] + " " + msg + lm.Level = LevelEmergency } if bl.asynchronous { - lm := logMsgPool.Get().(*logMsg) - lm.level = logLevel - lm.msg = msg - lm.when = when + logM := logMsgPool.Get().(*LogMsg) + logM.Level = lm.Level + logM.Msg = lm.Msg + logM.When = lm.When + logM.Args = lm.Args + logM.FilePath = lm.FilePath + logM.LineNumber = lm.LineNumber + logM.Prefix = lm.Prefix if bl.outputs != nil { bl.msgChan <- lm } else { logMsgPool.Put(lm) } } else { - bl.writeToLoggers(when, msg, logLevel) + bl.writeToLoggers(lm) } return nil } -// SetLevel Set log message level. +// SetLevel sets log message level. // If message level (such as LevelDebug) is higher than logger level (such as LevelWarning), -// log providers will not even be sent the message. +// log providers will not be sent the message. func (bl *BeeLogger) SetLevel(l int) { bl.level = l } @@ -345,7 +363,7 @@ func (bl *BeeLogger) startLogger() { for { select { case bm := <-bl.msgChan: - bl.writeToLoggers(bm.when, bm.msg, bm.level) + bl.writeToLoggers(bm) logMsgPool.Put(bm) case sg := <-bl.signalChan: // Now should only send "flush" or "close" to bl.signalChan @@ -365,12 +383,33 @@ func (bl *BeeLogger) startLogger() { } } +func (bl *BeeLogger) setGlobalFormatter(fmtter string) error { + bl.globalFormatter = fmtter + return nil +} + +// SetGlobalFormatter sets the global formatter for all log adapters +// don't forget to register the formatter by invoking RegisterFormatter +func SetGlobalFormatter(fmtter string) error { + return beeLogger.setGlobalFormatter(fmtter) +} + // Emergency Log EMERGENCY level message. func (bl *BeeLogger) Emergency(format string, v ...interface{}) { if LevelEmergency > bl.level { return } - bl.writeMsg(LevelEmergency, format, v...) + + lm := &LogMsg{ + Level: LevelEmergency, + Msg: format, + When: time.Now(), + } + if len(v) > 0 { + lm.Msg = fmt.Sprintf(lm.Msg, v...) + } + + bl.writeMsg(lm) } // Alert Log ALERT level message. @@ -378,7 +417,14 @@ func (bl *BeeLogger) Alert(format string, v ...interface{}) { if LevelAlert > bl.level { return } - bl.writeMsg(LevelAlert, format, v...) + + lm := &LogMsg{ + Level: LevelAlert, + Msg: format, + When: time.Now(), + Args: v, + } + bl.writeMsg(lm) } // Critical Log CRITICAL level message. @@ -386,7 +432,14 @@ func (bl *BeeLogger) Critical(format string, v ...interface{}) { if LevelCritical > bl.level { return } - bl.writeMsg(LevelCritical, format, v...) + lm := &LogMsg{ + Level: LevelCritical, + Msg: format, + When: time.Now(), + Args: v, + } + + bl.writeMsg(lm) } // Error Log ERROR level message. @@ -394,7 +447,14 @@ func (bl *BeeLogger) Error(format string, v ...interface{}) { if LevelError > bl.level { return } - bl.writeMsg(LevelError, format, v...) + lm := &LogMsg{ + Level: LevelError, + Msg: format, + When: time.Now(), + Args: v, + } + + bl.writeMsg(lm) } // Warning Log WARNING level message. @@ -402,7 +462,14 @@ func (bl *BeeLogger) Warning(format string, v ...interface{}) { if LevelWarn > bl.level { return } - bl.writeMsg(LevelWarn, format, v...) + lm := &LogMsg{ + Level: LevelWarn, + Msg: format, + When: time.Now(), + Args: v, + } + + bl.writeMsg(lm) } // Notice Log NOTICE level message. @@ -410,7 +477,14 @@ func (bl *BeeLogger) Notice(format string, v ...interface{}) { if LevelNotice > bl.level { return } - bl.writeMsg(LevelNotice, format, v...) + lm := &LogMsg{ + Level: LevelNotice, + Msg: format, + When: time.Now(), + Args: v, + } + + bl.writeMsg(lm) } // Informational Log INFORMATIONAL level message. @@ -418,7 +492,14 @@ func (bl *BeeLogger) Informational(format string, v ...interface{}) { if LevelInfo > bl.level { return } - bl.writeMsg(LevelInfo, format, v...) + lm := &LogMsg{ + Level: LevelInfo, + Msg: format, + When: time.Now(), + Args: v, + } + + bl.writeMsg(lm) } // Debug Log DEBUG level message. @@ -426,7 +507,14 @@ func (bl *BeeLogger) Debug(format string, v ...interface{}) { if LevelDebug > bl.level { return } - bl.writeMsg(LevelDebug, format, v...) + lm := &LogMsg{ + Level: LevelDebug, + Msg: format, + When: time.Now(), + Args: v, + } + + bl.writeMsg(lm) } // Warn Log WARN level message. @@ -435,7 +523,14 @@ func (bl *BeeLogger) Warn(format string, v ...interface{}) { if LevelWarn > bl.level { return } - bl.writeMsg(LevelWarn, format, v...) + lm := &LogMsg{ + Level: LevelWarn, + Msg: format, + When: time.Now(), + Args: v, + } + + bl.writeMsg(lm) } // Info Log INFO level message. @@ -444,7 +539,14 @@ func (bl *BeeLogger) Info(format string, v ...interface{}) { if LevelInfo > bl.level { return } - bl.writeMsg(LevelInfo, format, v...) + lm := &LogMsg{ + Level: LevelInfo, + Msg: format, + When: time.Now(), + Args: v, + } + + bl.writeMsg(lm) } // Trace Log TRACE level message. @@ -453,7 +555,14 @@ func (bl *BeeLogger) Trace(format string, v ...interface{}) { if LevelDebug > bl.level { return } - bl.writeMsg(LevelDebug, format, v...) + lm := &LogMsg{ + Level: LevelDebug, + Msg: format, + When: time.Now(), + Args: v, + } + + bl.writeMsg(lm) } // Flush flush all chan data. @@ -497,7 +606,7 @@ func (bl *BeeLogger) flush() { for { if len(bl.msgChan) > 0 { bm := <-bl.msgChan - bl.writeToLoggers(bm.when, bm.msg, bm.level) + bl.writeToLoggers(bm) logMsgPool.Put(bm) continue } @@ -547,6 +656,12 @@ func GetLogger(prefixes ...string) *log.Logger { return l } +// EnableFullFilePath enables full file path logging. Disabled by default +// e.g "/home/Documents/GitHub/beego/mainapp/" instead of "mainapp" +func EnableFullFilePath(b bool) { + beeLogger.enableFullFilePath = b +} + // Reset will remove all the adapter func Reset() { beeLogger.Reset() @@ -575,7 +690,7 @@ func EnableFuncCallDepth(b bool) { // SetLogFuncCall set the CallDepth, default is 4 func SetLogFuncCall(b bool) { beeLogger.EnableFuncCallDepth(b) - beeLogger.SetLogFuncCallDepth(4) + beeLogger.SetLogFuncCallDepth(3) } // SetLogFuncCallDepth set log funcCallDepth @@ -590,61 +705,61 @@ func SetLogger(adapter string, config ...string) error { // Emergency logs a message at emergency level. func Emergency(f interface{}, v ...interface{}) { - beeLogger.Emergency(formatLog(f, v...)) + beeLogger.Emergency(formatPattern(f, v...), v...) } // Alert logs a message at alert level. func Alert(f interface{}, v ...interface{}) { - beeLogger.Alert(formatLog(f, v...)) + beeLogger.Alert(formatPattern(f, v...), v...) } // Critical logs a message at critical level. func Critical(f interface{}, v ...interface{}) { - beeLogger.Critical(formatLog(f, v...)) + beeLogger.Critical(formatPattern(f, v...), v...) } // Error logs a message at error level. func Error(f interface{}, v ...interface{}) { - beeLogger.Error(formatLog(f, v...)) + beeLogger.Error(formatPattern(f, v...), v...) } // Warning logs a message at warning level. func Warning(f interface{}, v ...interface{}) { - beeLogger.Warn(formatLog(f, v...)) + beeLogger.Warn(formatPattern(f, v...), v...) } // Warn compatibility alias for Warning() func Warn(f interface{}, v ...interface{}) { - beeLogger.Warn(formatLog(f, v...)) + beeLogger.Warn(formatPattern(f, v...), v...) } // Notice logs a message at notice level. func Notice(f interface{}, v ...interface{}) { - beeLogger.Notice(formatLog(f, v...)) + beeLogger.Notice(formatPattern(f, v...), v...) } // Informational logs a message at info level. func Informational(f interface{}, v ...interface{}) { - beeLogger.Info(formatLog(f, v...)) + beeLogger.Info(formatPattern(f, v...), v...) } // Info compatibility alias for Warning() func Info(f interface{}, v ...interface{}) { - beeLogger.Info(formatLog(f, v...)) + beeLogger.Info(formatPattern(f, v...), v...) } // Debug logs a message at debug level. func Debug(f interface{}, v ...interface{}) { - beeLogger.Debug(formatLog(f, v...)) + beeLogger.Debug(formatPattern(f, v...), v...) } // Trace logs a message at trace level. // compatibility alias for Warning() func Trace(f interface{}, v ...interface{}) { - beeLogger.Trace(formatLog(f, v...)) + beeLogger.Trace(formatPattern(f, v...), v...) } -func formatLog(f interface{}, v ...interface{}) string { +func formatPattern(f interface{}, v ...interface{}) string { var msg string switch f.(type) { case string: @@ -652,10 +767,8 @@ func formatLog(f interface{}, v ...interface{}) string { if len(v) == 0 { return msg } - if strings.Contains(msg, "%") && !strings.Contains(msg, "%%") { - //format string - } else { - //do not contain format char + if !strings.Contains(msg, "%") { + // do not contain format char msg += strings.Repeat(" %v", len(v)) } default: @@ -665,5 +778,5 @@ func formatLog(f interface{}, v ...interface{}) string { } msg += strings.Repeat(" %v", len(v)) } - return fmt.Sprintf(msg, v...) + return msg } diff --git a/src/vendor/github.com/beego/beego/v2/core/logs/log_msg.go b/src/vendor/github.com/beego/beego/v2/core/logs/log_msg.go new file mode 100644 index 000000000..f1a0b559c --- /dev/null +++ b/src/vendor/github.com/beego/beego/v2/core/logs/log_msg.go @@ -0,0 +1,55 @@ +// Copyright 2020 +// +// 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 logs + +import ( + "fmt" + "path" + "time" +) + +type LogMsg struct { + Level int + Msg string + When time.Time + FilePath string + LineNumber int + Args []interface{} + Prefix string + enableFullFilePath bool + enableFuncCallDepth bool +} + +// OldStyleFormat you should never invoke this +func (lm *LogMsg) OldStyleFormat() string { + msg := lm.Msg + + if len(lm.Args) > 0 { + msg = fmt.Sprintf(lm.Msg, lm.Args...) + } + + msg = lm.Prefix + " " + msg + + if lm.enableFuncCallDepth { + filePath := lm.FilePath + if !lm.enableFullFilePath { + _, filePath = path.Split(filePath) + } + msg = fmt.Sprintf("[%s:%d] %s", filePath, lm.LineNumber, msg) + } + + msg = levelPrefix[lm.Level] + " " + msg + return msg +} diff --git a/src/vendor/github.com/beego/beego/logs/logger.go b/src/vendor/github.com/beego/beego/v2/core/logs/logger.go similarity index 93% rename from src/vendor/github.com/beego/beego/logs/logger.go rename to src/vendor/github.com/beego/beego/v2/core/logs/logger.go index a28bff6f8..29e33a826 100644 --- a/src/vendor/github.com/beego/beego/logs/logger.go +++ b/src/vendor/github.com/beego/beego/v2/core/logs/logger.go @@ -30,10 +30,10 @@ func newLogWriter(wr io.Writer) *logWriter { return &logWriter{writer: wr} } -func (lg *logWriter) writeln(when time.Time, msg string) (int, error) { +func (lg *logWriter) writeln(msg string) (int, error) { lg.Lock() - h, _, _ := formatTimeHeader(when) - n, err := lg.writer.Write(append(append(h, msg...), '\n')) + msg += "\n" + n, err := lg.writer.Write([]byte(msg)) lg.Unlock() return n, err } @@ -60,7 +60,7 @@ func formatTimeHeader(when time.Time) ([]byte, int, int) { y, mo, d := when.Date() h, mi, s := when.Clock() ns := when.Nanosecond() / 1000000 - //len("2006/01/02 15:04:05.123 ")==24 + // len("2006/01/02 15:04:05.123 ")==24 var buf [24]byte buf[0] = y1[y/1000%10] @@ -112,8 +112,10 @@ var ( reset = string([]byte{27, 91, 48, 109}) ) -var once sync.Once -var colorMap map[string]string +var ( + once sync.Once + colorMap map[string]string +) func initColor() { if runtime.GOOS == "windows" { @@ -126,12 +128,12 @@ func initColor() { cyan = w32Cyan } colorMap = map[string]string{ - //by color + // by color "green": green, "white": white, "yellow": yellow, "red": red, - //by method + // by method "GET": blue, "POST": cyan, "PUT": yellow, diff --git a/src/vendor/github.com/beego/beego/logs/multifile.go b/src/vendor/github.com/beego/beego/v2/core/logs/multifile.go similarity index 83% rename from src/vendor/github.com/beego/beego/logs/multifile.go rename to src/vendor/github.com/beego/beego/v2/core/logs/multifile.go index 901682743..009bd32f7 100644 --- a/src/vendor/github.com/beego/beego/logs/multifile.go +++ b/src/vendor/github.com/beego/beego/v2/core/logs/multifile.go @@ -16,7 +16,6 @@ package logs import ( "encoding/json" - "time" ) // A filesLogWriter manages several fileLogWriter @@ -54,11 +53,17 @@ func (f *multiFileLogWriter) Init(config string) error { f.fullLogWriter = writer f.writers[LevelDebug+1] = writer - //unmarshal "separate" field to f.Separate - json.Unmarshal([]byte(config), f) + // unmarshal "separate" field to f.Separate + err = json.Unmarshal([]byte(config), f) + if err != nil { + return err + } jsonMap := map[string]interface{}{} - json.Unmarshal([]byte(config), &jsonMap) + err = json.Unmarshal([]byte(config), &jsonMap) + if err != nil { + return err + } for i := LevelEmergency; i < LevelDebug+1; i++ { for _, v := range f.Separate { @@ -75,10 +80,17 @@ func (f *multiFileLogWriter) Init(config string) error { } } } - return nil } +func (*multiFileLogWriter) Format(lm *LogMsg) string { + return lm.OldStyleFormat() +} + +func (f *multiFileLogWriter) SetFormatter(fmt LogFormatter) { + f.fullLogWriter.SetFormatter(fmt) +} + func (f *multiFileLogWriter) Destroy() { for i := 0; i < len(f.writers); i++ { if f.writers[i] != nil { @@ -87,14 +99,14 @@ func (f *multiFileLogWriter) Destroy() { } } -func (f *multiFileLogWriter) WriteMsg(when time.Time, msg string, level int) error { +func (f *multiFileLogWriter) WriteMsg(lm *LogMsg) error { if f.fullLogWriter != nil { - f.fullLogWriter.WriteMsg(when, msg, level) + f.fullLogWriter.WriteMsg(lm) } for i := 0; i < len(f.writers)-1; i++ { if f.writers[i] != nil { - if level == f.writers[i].Level { - f.writers[i].WriteMsg(when, msg, level) + if lm.Level == f.writers[i].Level { + f.writers[i].WriteMsg(lm) } } } @@ -111,7 +123,8 @@ func (f *multiFileLogWriter) Flush() { // newFilesWriter create a FileLogWriter returning as LoggerInterface. func newFilesWriter() Logger { - return &multiFileLogWriter{} + res := &multiFileLogWriter{} + return res } func init() { diff --git a/src/vendor/github.com/beego/beego/v2/core/logs/slack.go b/src/vendor/github.com/beego/beego/v2/core/logs/slack.go new file mode 100644 index 000000000..ce892a1bc --- /dev/null +++ b/src/vendor/github.com/beego/beego/v2/core/logs/slack.go @@ -0,0 +1,84 @@ +package logs + +import ( + "bytes" + "encoding/json" + "fmt" + "net/http" + + "github.com/pkg/errors" +) + +// SLACKWriter implements beego LoggerInterface and is used to send jiaoliao webhook +type SLACKWriter struct { + WebhookURL string `json:"webhookurl"` + Level int `json:"level"` + formatter LogFormatter + Formatter string `json:"formatter"` +} + +// newSLACKWriter creates jiaoliao writer. +func newSLACKWriter() Logger { + res := &SLACKWriter{Level: LevelTrace} + res.formatter = res + return res +} + +func (s *SLACKWriter) Format(lm *LogMsg) string { + // text := fmt.Sprintf("{\"text\": \"%s\"}", msg) + return lm.When.Format("2006-01-02 15:04:05") + " " + lm.OldStyleFormat() +} + +func (s *SLACKWriter) SetFormatter(f LogFormatter) { + s.formatter = f +} + +// Init SLACKWriter with json config string +func (s *SLACKWriter) Init(config string) error { + res := json.Unmarshal([]byte(config), s) + + if res == nil && len(s.Formatter) > 0 { + fmtr, ok := GetFormatter(s.Formatter) + if !ok { + return errors.New(fmt.Sprintf("the formatter with name: %s not found", s.Formatter)) + } + s.formatter = fmtr + } + + return res +} + +// WriteMsg write message in smtp writer. +// Sends an email with subject and only this message. +func (s *SLACKWriter) WriteMsg(lm *LogMsg) error { + if lm.Level > s.Level { + return nil + } + msg := s.Format(lm) + m := make(map[string]string, 1) + m["text"] = msg + + body, _ := json.Marshal(m) + // resp, err := http.PostForm(s.WebhookURL, form) + resp, err := http.Post(s.WebhookURL, "application/json", bytes.NewReader(body)) + if err != nil { + return err + } + defer resp.Body.Close() + if resp.StatusCode != http.StatusOK { + return fmt.Errorf("Post webhook failed %s %d", resp.Status, resp.StatusCode) + } + return nil +} + +// Flush implementing method. empty. +func (s *SLACKWriter) Flush() { +} + +// Destroy implementing method. empty. +func (s *SLACKWriter) Destroy() { +} + +func init() { + Register(AdapterSlack, newSLACKWriter) +} diff --git a/src/vendor/github.com/beego/beego/logs/smtp.go b/src/vendor/github.com/beego/beego/v2/core/logs/smtp.go similarity index 77% rename from src/vendor/github.com/beego/beego/logs/smtp.go rename to src/vendor/github.com/beego/beego/v2/core/logs/smtp.go index 6208d7b85..40891a7c8 100644 --- a/src/vendor/github.com/beego/beego/logs/smtp.go +++ b/src/vendor/github.com/beego/beego/v2/core/logs/smtp.go @@ -21,7 +21,8 @@ import ( "net" "net/smtp" "strings" - "time" + + "github.com/pkg/errors" ) // SMTPWriter implements LoggerInterface and is used to send emails via given SMTP-server. @@ -33,11 +34,15 @@ type SMTPWriter struct { FromAddress string `json:"fromAddress"` RecipientAddresses []string `json:"sendTos"` Level int `json:"level"` + formatter LogFormatter + Formatter string `json:"formatter"` } -// NewSMTPWriter create smtp writer. +// NewSMTPWriter creates the smtp writer. func newSMTPWriter() Logger { - return &SMTPWriter{Level: LevelTrace} + res := &SMTPWriter{Level: LevelTrace} + res.formatter = res + return res } // Init smtp writer with json config. @@ -51,8 +56,16 @@ func newSMTPWriter() Logger { // "sendTos":["email1","email2"], // "level":LevelError // } -func (s *SMTPWriter) Init(jsonconfig string) error { - return json.Unmarshal([]byte(jsonconfig), s) +func (s *SMTPWriter) Init(config string) error { + res := json.Unmarshal([]byte(config), s) + if res == nil && len(s.Formatter) > 0 { + fmtr, ok := GetFormatter(s.Formatter) + if !ok { + return errors.New(fmt.Sprintf("the formatter with name: %s not found", s.Formatter)) + } + s.formatter = fmtr + } + return res } func (s *SMTPWriter) getSMTPAuth(host string) smtp.Auth { @@ -67,6 +80,10 @@ func (s *SMTPWriter) getSMTPAuth(host string) smtp.Auth { ) } +func (s *SMTPWriter) SetFormatter(f LogFormatter) { + s.formatter = f +} + func (s *SMTPWriter) sendMail(hostAddressWithPort string, auth smtp.Auth, fromAddress string, recipients []string, msgContent []byte) error { client, err := smtp.Dial(hostAddressWithPort) if err != nil { @@ -115,10 +132,14 @@ func (s *SMTPWriter) sendMail(hostAddressWithPort string, auth smtp.Auth, fromAd return client.Quit() } -// WriteMsg write message in smtp writer. -// it will send an email with subject and only this message. -func (s *SMTPWriter) WriteMsg(when time.Time, msg string, level int) error { - if level > s.Level { +func (s *SMTPWriter) Format(lm *LogMsg) string { + return lm.OldStyleFormat() +} + +// WriteMsg writes message in smtp writer. +// Sends an email with subject and only this message. +func (s *SMTPWriter) WriteMsg(lm *LogMsg) error { + if lm.Level > s.Level { return nil } @@ -127,11 +148,13 @@ func (s *SMTPWriter) WriteMsg(when time.Time, msg string, level int) error { // Set up authentication information. auth := s.getSMTPAuth(hp[0]) + msg := s.Format(lm) + // Connect to the server, authenticate, set the sender and recipient, // and send the email all in one step. contentType := "Content-Type: text/plain" + "; charset=UTF-8" mailmsg := []byte("To: " + strings.Join(s.RecipientAddresses, ";") + "\r\nFrom: " + s.FromAddress + "<" + s.FromAddress + - ">\r\nSubject: " + s.Subject + "\r\n" + contentType + "\r\n\r\n" + fmt.Sprintf(".%s", when.Format("2006-01-02 15:04:05")) + msg) + ">\r\nSubject: " + s.Subject + "\r\n" + contentType + "\r\n\r\n" + fmt.Sprintf(".%s", lm.When.Format("2006-01-02 15:04:05")) + msg) return s.sendMail(s.Host, auth, s.FromAddress, s.RecipientAddresses, mailmsg) } diff --git a/src/vendor/github.com/beego/beego/utils/caller.go b/src/vendor/github.com/beego/beego/v2/core/utils/caller.go similarity index 100% rename from src/vendor/github.com/beego/beego/utils/caller.go rename to src/vendor/github.com/beego/beego/v2/core/utils/caller.go diff --git a/src/vendor/github.com/beego/beego/utils/debug.go b/src/vendor/github.com/beego/beego/v2/core/utils/debug.go similarity index 90% rename from src/vendor/github.com/beego/beego/utils/debug.go rename to src/vendor/github.com/beego/beego/v2/core/utils/debug.go index 93c27b70d..93279181d 100644 --- a/src/vendor/github.com/beego/beego/utils/debug.go +++ b/src/vendor/github.com/beego/beego/v2/core/utils/debug.go @@ -47,20 +47,20 @@ func GetDisplayString(data ...interface{}) string { } func display(displayed bool, data ...interface{}) string { - var pc, file, line, ok = runtime.Caller(2) + pc, file, line, ok := runtime.Caller(2) if !ok { return "" } - var buf = new(bytes.Buffer) + buf := new(bytes.Buffer) fmt.Fprintf(buf, "[Debug] at %s() [%s:%d]\n", function(pc), file, line) fmt.Fprintf(buf, "\n[Variables]\n") for i := 0; i < len(data); i += 2 { - var output = fomateinfo(len(data[i].(string))+3, data[i+1]) + output := fomateinfo(len(data[i].(string))+3, data[i+1]) fmt.Fprintf(buf, "%s = %s", data[i], output) } @@ -72,7 +72,7 @@ func display(displayed bool, data ...interface{}) string { // return data dump and format bytes func fomateinfo(headlen int, data ...interface{}) []byte { - var buf = new(bytes.Buffer) + buf := new(bytes.Buffer) if len(data) > 1 { fmt.Fprint(buf, " ") @@ -83,9 +83,9 @@ func fomateinfo(headlen int, data ...interface{}) []byte { } for k, v := range data { - var buf2 = new(bytes.Buffer) + buf2 := new(bytes.Buffer) var pointers *pointerInfo - var interfaces = make([]reflect.Value, 0, 10) + interfaces := make([]reflect.Value, 0, 10) printKeyValue(buf2, reflect.ValueOf(v), &pointers, &interfaces, nil, true, " ", 1) @@ -140,13 +140,13 @@ func isSimpleType(val reflect.Value, kind reflect.Kind, pointers **pointerInfo, return true } - var elem = val.Elem() + elem := val.Elem() if isSimpleType(elem, elem.Kind(), pointers, interfaces) { return true } - var addr = val.Elem().UnsafeAddr() + addr := val.Elem().UnsafeAddr() for p := *pointers; p != nil; p = p.prev { if addr == p.addr { @@ -162,7 +162,7 @@ func isSimpleType(val reflect.Value, kind reflect.Kind, pointers **pointerInfo, // dump value func printKeyValue(buf *bytes.Buffer, val reflect.Value, pointers **pointerInfo, interfaces *[]reflect.Value, structFilter func(string, string) bool, formatOutput bool, indent string, level int) { - var t = val.Kind() + t := val.Kind() switch t { case reflect.Bool: @@ -183,7 +183,7 @@ func printKeyValue(buf *bytes.Buffer, val reflect.Value, pointers **pointerInfo, return } - var addr = val.Elem().UnsafeAddr() + addr := val.Elem().UnsafeAddr() for p := *pointers; p != nil; p = p.prev { if addr == p.addr { @@ -206,7 +206,7 @@ func printKeyValue(buf *bytes.Buffer, val reflect.Value, pointers **pointerInfo, case reflect.String: fmt.Fprint(buf, "\"", val.String(), "\"") case reflect.Interface: - var value = val.Elem() + value := val.Elem() if !value.IsValid() { fmt.Fprint(buf, "nil") @@ -223,7 +223,7 @@ func printKeyValue(buf *bytes.Buffer, val reflect.Value, pointers **pointerInfo, printKeyValue(buf, value, pointers, interfaces, structFilter, formatOutput, indent, level+1) } case reflect.Struct: - var t = val.Type() + t := val.Type() fmt.Fprint(buf, t) fmt.Fprint(buf, "{") @@ -235,7 +235,7 @@ func printKeyValue(buf *bytes.Buffer, val reflect.Value, pointers **pointerInfo, fmt.Fprint(buf, " ") } - var name = t.Field(i).Name + name := t.Field(i).Name if formatOutput { for ind := 0; ind < level; ind++ { @@ -270,12 +270,12 @@ func printKeyValue(buf *bytes.Buffer, val reflect.Value, pointers **pointerInfo, fmt.Fprint(buf, val.Type()) fmt.Fprint(buf, "{") - var allSimple = true + allSimple := true for i := 0; i < val.Len(); i++ { - var elem = val.Index(i) + elem := val.Index(i) - var isSimple = isSimpleType(elem, elem.Kind(), pointers, interfaces) + isSimple := isSimpleType(elem, elem.Kind(), pointers, interfaces) if !isSimple { allSimple = false @@ -312,18 +312,18 @@ func printKeyValue(buf *bytes.Buffer, val reflect.Value, pointers **pointerInfo, fmt.Fprint(buf, "}") case reflect.Map: - var t = val.Type() - var keys = val.MapKeys() + t := val.Type() + keys := val.MapKeys() fmt.Fprint(buf, t) fmt.Fprint(buf, "{") - var allSimple = true + allSimple := true for i := 0; i < len(keys); i++ { - var elem = val.MapIndex(keys[i]) + elem := val.MapIndex(keys[i]) - var isSimple = isSimpleType(elem, elem.Kind(), pointers, interfaces) + isSimple := isSimpleType(elem, elem.Kind(), pointers, interfaces) if !isSimple { allSimple = false @@ -372,8 +372,8 @@ func printKeyValue(buf *bytes.Buffer, val reflect.Value, pointers **pointerInfo, // PrintPointerInfo dump pointer value func PrintPointerInfo(buf *bytes.Buffer, headlen int, pointers *pointerInfo) { - var anyused = false - var pointerNum = 0 + anyused := false + pointerNum := 0 for p := pointers; p != nil; p = p.prev { if len(p.used) > 0 { @@ -384,10 +384,10 @@ func PrintPointerInfo(buf *bytes.Buffer, headlen int, pointers *pointerInfo) { } if anyused { - var pointerBufs = make([][]rune, pointerNum+1) + pointerBufs := make([][]rune, pointerNum+1) for i := 0; i < len(pointerBufs); i++ { - var pointerBuf = make([]rune, buf.Len()+headlen) + pointerBuf := make([]rune, buf.Len()+headlen) for j := 0; j < len(pointerBuf); j++ { pointerBuf[j] = ' ' @@ -402,7 +402,7 @@ func PrintPointerInfo(buf *bytes.Buffer, headlen int, pointers *pointerInfo) { if pn == p.n { pointerBufs[pn][p.pos+headlen] = '└' - var maxpos = 0 + maxpos := 0 for i, pos := range p.used { if i < len(p.used)-1 { @@ -440,10 +440,10 @@ func PrintPointerInfo(buf *bytes.Buffer, headlen int, pointers *pointerInfo) { // Stack get stack bytes func Stack(skip int, indent string) []byte { - var buf = new(bytes.Buffer) + buf := new(bytes.Buffer) for i := skip; ; i++ { - var pc, file, line, ok = runtime.Caller(i) + pc, file, line, ok := runtime.Caller(i) if !ok { break diff --git a/src/vendor/github.com/beego/beego/utils/file.go b/src/vendor/github.com/beego/beego/v2/core/utils/file.go similarity index 100% rename from src/vendor/github.com/beego/beego/utils/file.go rename to src/vendor/github.com/beego/beego/v2/core/utils/file.go diff --git a/src/vendor/github.com/beego/beego/v2/core/utils/kv.go b/src/vendor/github.com/beego/beego/v2/core/utils/kv.go new file mode 100644 index 000000000..7fc5cee93 --- /dev/null +++ b/src/vendor/github.com/beego/beego/v2/core/utils/kv.go @@ -0,0 +1,87 @@ +// Copyright 2020 beego-dev +// +// 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 utils + +type KV interface { + GetKey() interface{} + GetValue() interface{} +} + +// SimpleKV is common structure to store key-value pairs. +// When you need something like Pair, you can use this +type SimpleKV struct { + Key interface{} + Value interface{} +} + +var _ KV = new(SimpleKV) + +func (s *SimpleKV) GetKey() interface{} { + return s.Key +} + +func (s *SimpleKV) GetValue() interface{} { + return s.Value +} + +// KVs interface +type KVs interface { + GetValueOr(key interface{}, defValue interface{}) interface{} + Contains(key interface{}) bool + IfContains(key interface{}, action func(value interface{})) KVs +} + +// SimpleKVs will store SimpleKV collection as map +type SimpleKVs struct { + kvs map[interface{}]interface{} +} + +var _ KVs = new(SimpleKVs) + +// GetValueOr returns the value for a given key, if non-existent +// it returns defValue +func (kvs *SimpleKVs) GetValueOr(key interface{}, defValue interface{}) interface{} { + v, ok := kvs.kvs[key] + if ok { + return v + } + return defValue +} + +// Contains checks if a key exists +func (kvs *SimpleKVs) Contains(key interface{}) bool { + _, ok := kvs.kvs[key] + return ok +} + +// IfContains invokes the action on a key if it exists +func (kvs *SimpleKVs) IfContains(key interface{}, action func(value interface{})) KVs { + v, ok := kvs.kvs[key] + if ok { + action(v) + } + return kvs +} + +// NewKVs creates the *KVs instance +func NewKVs(kvs ...KV) KVs { + res := &SimpleKVs{ + kvs: make(map[interface{}]interface{}, len(kvs)), + } + for _, kv := range kvs { + res.kvs[kv.GetKey()] = kv.GetValue() + } + return res +} diff --git a/src/vendor/github.com/beego/beego/utils/mail.go b/src/vendor/github.com/beego/beego/v2/core/utils/mail.go similarity index 99% rename from src/vendor/github.com/beego/beego/utils/mail.go rename to src/vendor/github.com/beego/beego/v2/core/utils/mail.go index 80a366cae..e685c4496 100644 --- a/src/vendor/github.com/beego/beego/utils/mail.go +++ b/src/vendor/github.com/beego/beego/v2/core/utils/mail.go @@ -188,10 +188,10 @@ func (e *Email) Attach(r io.Reader, filename string, args ...string) (a *Attachm err = errors.New("Must specify the file type and number of parameters can not exceed at least two") return } - c := args[0] //Content-Type + c := args[0] // Content-Type id := "" if len(args) > 1 { - id = args[1] //Content-ID + id = args[1] // Content-ID } var buffer bytes.Buffer if _, err = io.Copy(&buffer, r); err != nil { diff --git a/src/vendor/github.com/beego/beego/utils/rand.go b/src/vendor/github.com/beego/beego/v2/core/utils/rand.go similarity index 97% rename from src/vendor/github.com/beego/beego/utils/rand.go rename to src/vendor/github.com/beego/beego/v2/core/utils/rand.go index 344d1cd53..f80dad7df 100644 --- a/src/vendor/github.com/beego/beego/utils/rand.go +++ b/src/vendor/github.com/beego/beego/v2/core/utils/rand.go @@ -27,7 +27,7 @@ func RandomCreateBytes(n int, alphabets ...byte) []byte { if len(alphabets) == 0 { alphabets = alphaNum } - var bytes = make([]byte, n) + bytes := make([]byte, n) var randBy bool if num, err := rand.Read(bytes); num != n || err != nil { r.Seed(time.Now().UnixNano()) diff --git a/src/vendor/github.com/beego/beego/utils/safemap.go b/src/vendor/github.com/beego/beego/v2/core/utils/safemap.go similarity index 98% rename from src/vendor/github.com/beego/beego/utils/safemap.go rename to src/vendor/github.com/beego/beego/v2/core/utils/safemap.go index 1793030a5..8c48cb5b6 100644 --- a/src/vendor/github.com/beego/beego/utils/safemap.go +++ b/src/vendor/github.com/beego/beego/v2/core/utils/safemap.go @@ -18,7 +18,7 @@ import ( "sync" ) -// BeeMap is a map with lock +// Deprecated: using sync.Map type BeeMap struct { lock *sync.RWMutex bm map[interface{}]interface{} diff --git a/src/vendor/github.com/beego/beego/utils/slice.go b/src/vendor/github.com/beego/beego/v2/core/utils/slice.go similarity index 99% rename from src/vendor/github.com/beego/beego/utils/slice.go rename to src/vendor/github.com/beego/beego/v2/core/utils/slice.go index 8f2cef980..0eda46703 100644 --- a/src/vendor/github.com/beego/beego/utils/slice.go +++ b/src/vendor/github.com/beego/beego/v2/core/utils/slice.go @@ -20,6 +20,7 @@ import ( ) type reducetype func(interface{}) interface{} + type filtertype func(interface{}) bool // InSlice checks given string in string slice or not. diff --git a/src/vendor/github.com/beego/beego/v2/core/utils/time.go b/src/vendor/github.com/beego/beego/v2/core/utils/time.go new file mode 100644 index 000000000..2f813a057 --- /dev/null +++ b/src/vendor/github.com/beego/beego/v2/core/utils/time.go @@ -0,0 +1,46 @@ +// Copyright 2020 +// +// 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 utils + +import ( + "fmt" + "time" +) + +// ToShortTimeFormat short string format +func ToShortTimeFormat(d time.Duration) string { + u := uint64(d) + if u < uint64(time.Second) { + switch { + case u == 0: + return "0" + case u < uint64(time.Microsecond): + return fmt.Sprintf("%.2fns", float64(u)) + case u < uint64(time.Millisecond): + return fmt.Sprintf("%.2fus", float64(u)/1000) + default: + return fmt.Sprintf("%.2fms", float64(u)/1000/1000) + } + } else { + switch { + case u < uint64(time.Minute): + return fmt.Sprintf("%.2fs", float64(u)/1000/1000/1000) + case u < uint64(time.Hour): + return fmt.Sprintf("%.2fm", float64(u)/1000/1000/1000/60) + default: + return fmt.Sprintf("%.2fh", float64(u)/1000/1000/1000/60/60) + } + } +} diff --git a/src/vendor/github.com/beego/beego/utils/utils.go b/src/vendor/github.com/beego/beego/v2/core/utils/utils.go similarity index 100% rename from src/vendor/github.com/beego/beego/utils/utils.go rename to src/vendor/github.com/beego/beego/v2/core/utils/utils.go diff --git a/src/vendor/github.com/beego/beego/validation/README.md b/src/vendor/github.com/beego/beego/v2/core/validation/README.md similarity index 90% rename from src/vendor/github.com/beego/beego/validation/README.md rename to src/vendor/github.com/beego/beego/v2/core/validation/README.md index 80085f2cc..dee5a7b1b 100644 --- a/src/vendor/github.com/beego/beego/validation/README.md +++ b/src/vendor/github.com/beego/beego/v2/core/validation/README.md @@ -7,18 +7,18 @@ validation is a form validation for a data validation and error collecting using Install: - go get github.com/beego/beego/validation + go get github.com/beego/beego/v2/core/validation Test: - go test github.com/beego/beego/validation + go test github.com/beego/beego/v2/core/validation ## Example Direct Use: import ( - "github.com/beego/beego/validation" + "github.com/beego/beego/v2/core/validation" "log" ) @@ -49,7 +49,7 @@ Direct Use: Struct Tag Use: import ( - "github.com/beego/beego/validation" + "github.com/beego/beego/v2/core/validation" ) // validation function follow with "valid" tag @@ -81,7 +81,7 @@ Struct Tag Use: Use custom function: import ( - "github.com/beego/beego/validation" + "github.com/beego/beego/v2/core/validation" ) type user struct { @@ -141,7 +141,6 @@ Struct Tag Functions: Phone ZipCode - ## LICENSE BSD License http://creativecommons.org/licenses/BSD/ diff --git a/src/vendor/github.com/beego/beego/validation/util.go b/src/vendor/github.com/beego/beego/v2/core/validation/util.go similarity index 100% rename from src/vendor/github.com/beego/beego/validation/util.go rename to src/vendor/github.com/beego/beego/v2/core/validation/util.go diff --git a/src/vendor/github.com/beego/beego/validation/validation.go b/src/vendor/github.com/beego/beego/v2/core/validation/validation.go similarity index 95% rename from src/vendor/github.com/beego/beego/validation/validation.go rename to src/vendor/github.com/beego/beego/v2/core/validation/validation.go index 18dbf62fb..7605d22f4 100644 --- a/src/vendor/github.com/beego/beego/validation/validation.go +++ b/src/vendor/github.com/beego/beego/v2/core/validation/validation.go @@ -15,7 +15,7 @@ // Package validation for validations // // import ( -// "github.com/beego/beego/validation" +// "github.com/beego/beego/v2/core/validation" // "log" // ) // @@ -43,7 +43,7 @@ // } // } // -// more info: http://beego.me/docs/mvc/controller/validation.md +// more info: http://beego.vip/docs/mvc/controller/validation.md package validation import ( @@ -107,7 +107,7 @@ func (r *Result) Message(message string, args ...interface{}) *Result { // A Validation context manages data validation and error messages. type Validation struct { // if this field set true, in struct tag valid - // if the struct field vale is empty + // if the struct field value is empty // it will skip those valid functions, see CanSkipFuncs RequiredFirst bool @@ -121,7 +121,7 @@ func (v *Validation) Clear() { v.ErrorsMap = nil } -// HasErrors Has ValidationError nor not. +// HasErrors Has ValidationError or not. func (v *Validation) HasErrors() bool { return len(v.Errors) > 0 } @@ -158,7 +158,7 @@ func (v *Validation) Max(obj interface{}, max int, key string) *Result { return v.apply(Max{max, key}, obj) } -// Range Test that the obj is between mni and max if obj's type is int +// Range Test that the obj is between min and max if obj's type is int func (v *Validation) Range(obj interface{}, min, max int, key string) *Result { return v.apply(Range{Min{Min: min}, Max{Max: max}, key}, obj) } @@ -235,8 +235,11 @@ func (v *Validation) Tel(obj interface{}, key string) *Result { // Phone Test that the obj is chinese mobile or telephone number if type is string func (v *Validation) Phone(obj interface{}, key string) *Result { - return v.apply(Phone{Mobile{Match: Match{Regexp: mobilePattern}}, - Tel{Match: Match{Regexp: telPattern}}, key}, obj) + return v.apply(Phone{ + Mobile{Match: Match{Regexp: mobilePattern}}, + Tel{Match: Match{Regexp: telPattern}}, + key, + }, obj) } // ZipCode Test that the obj is chinese zip code if type is string @@ -269,6 +272,11 @@ func (v *Validation) apply(chk Validator, obj interface{}) *Result { Field := "" Label := "" parts := strings.Split(key, ".") + if len(parts) == 2 { + Field = parts[0] + Name = parts[1] + Label = Field + } if len(parts) == 3 { Field = parts[0] Name = parts[1] @@ -418,7 +426,7 @@ func (v *Validation) Valid(obj interface{}) (b bool, err error) { // Step2: If pass on step1, then reflect obj's fields // Step3: Do the Recursively validation to all struct or struct pointer fields func (v *Validation) RecursiveValid(objc interface{}) (bool, error) { - //Step 1: validate obj itself firstly + // Step 1: validate obj itself firstly // fails if objc is not struct pass, err := v.Valid(objc) if err != nil || !pass { diff --git a/src/vendor/github.com/beego/beego/validation/validators.go b/src/vendor/github.com/beego/beego/v2/core/validation/validators.go similarity index 99% rename from src/vendor/github.com/beego/beego/validation/validators.go rename to src/vendor/github.com/beego/beego/v2/core/validation/validators.go index 3fbcc6b29..c4ea1f512 100644 --- a/src/vendor/github.com/beego/beego/validation/validators.go +++ b/src/vendor/github.com/beego/beego/v2/core/validation/validators.go @@ -23,7 +23,7 @@ import ( "time" "unicode/utf8" - "github.com/beego/beego/logs" + "github.com/beego/beego/v2/core/logs" ) // CanSkipFuncs will skip valid if RequiredFirst is true and the struct field's value is empty diff --git a/src/vendor/github.com/beego/beego/v2/doc.go b/src/vendor/github.com/beego/beego/v2/doc.go new file mode 100644 index 000000000..6975885ab --- /dev/null +++ b/src/vendor/github.com/beego/beego/v2/doc.go @@ -0,0 +1,15 @@ +// Copyright 2020 +// +// 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 beego diff --git a/src/vendor/github.com/beego/beego/v2/server/web/LICENSE b/src/vendor/github.com/beego/beego/v2/server/web/LICENSE new file mode 100644 index 000000000..26050108e --- /dev/null +++ b/src/vendor/github.com/beego/beego/v2/server/web/LICENSE @@ -0,0 +1,13 @@ +Copyright 2014 astaxie + +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. diff --git a/src/vendor/github.com/beego/beego/v2/server/web/admin.go b/src/vendor/github.com/beego/beego/v2/server/web/admin.go new file mode 100644 index 000000000..56d2906f2 --- /dev/null +++ b/src/vendor/github.com/beego/beego/v2/server/web/admin.go @@ -0,0 +1,122 @@ +// Copyright 2014 beego Author. All Rights Reserved. +// +// 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 web + +import ( + "fmt" + "net" + "net/http" + "reflect" + "time" + + "github.com/beego/beego/v2/core/logs" +) + +// BeeAdminApp is the default adminApp used by admin module. +var beeAdminApp *adminApp + +// FilterMonitorFunc is default monitor filter when admin module is enable. +// if this func returns, admin module records qps for this request by condition of this function logic. +// usage: +// func MyFilterMonitor(method, requestPath string, t time.Duration, pattern string, statusCode int) bool { +// if method == "POST" { +// return false +// } +// if t.Nanoseconds() < 100 { +// return false +// } +// if strings.HasPrefix(requestPath, "/astaxie") { +// return false +// } +// return true +// } +// beego.FilterMonitorFunc = MyFilterMonitor. +var FilterMonitorFunc func(string, string, time.Duration, string, int) bool + +func init() { + FilterMonitorFunc = func(string, string, time.Duration, string, int) bool { return true } +} + +func list(root string, p interface{}, m M) { + pt := reflect.TypeOf(p) + pv := reflect.ValueOf(p) + if pt.Kind() == reflect.Ptr { + pt = pt.Elem() + pv = pv.Elem() + } + for i := 0; i < pv.NumField(); i++ { + var key string + if root == "" { + key = pt.Field(i).Name + } else { + key = root + "." + pt.Field(i).Name + } + if pv.Field(i).Kind() == reflect.Struct { + list(key, pv.Field(i).Interface(), m) + } else { + m[key] = pv.Field(i).Interface() + } + } +} + +func writeJSON(rw http.ResponseWriter, jsonData []byte) { + rw.Header().Set("Content-Type", "application/json") + rw.Write(jsonData) +} + +// adminApp is an http.HandlerFunc map used as beeAdminApp. +type adminApp struct { + *HttpServer +} + +// Run start Beego admin +func (admin *adminApp) Run() { + logs.Debug("now we don't start tasks here, if you use task module," + + " please invoke task.StartTask, or task will not be executed") + addr := BConfig.Listen.AdminAddr + if BConfig.Listen.AdminPort != 0 { + addr = net.JoinHostPort(BConfig.Listen.AdminAddr, fmt.Sprintf("%d", BConfig.Listen.AdminPort)) + } + logs.Info("Admin server Running on %s", addr) + admin.HttpServer.Run(addr) +} + +func registerAdmin() error { + if BConfig.Listen.EnableAdmin { + + c := &adminController{ + servers: make([]*HttpServer, 0, 2), + } + + // copy config to avoid conflict + adminCfg := *BConfig + adminCfg.Listen.EnableHTTPS = false + adminCfg.Listen.EnableMutualHTTPS = false + beeAdminApp = &adminApp{ + HttpServer: NewHttpServerWithCfg(&adminCfg), + } + // keep in mind that all data should be html escaped to avoid XSS attack + beeAdminApp.Router("/", c, "get:AdminIndex") + beeAdminApp.Router("/qps", c, "get:QpsIndex") + beeAdminApp.Router("/prof", c, "get:ProfIndex") + beeAdminApp.Router("/healthcheck", c, "get:Healthcheck") + beeAdminApp.Router("/task", c, "get:TaskStatus") + beeAdminApp.Router("/listconf", c, "get:ListConf") + beeAdminApp.Router("/metrics", c, "get:PrometheusMetrics") + + go beeAdminApp.Run() + } + return nil +} diff --git a/src/vendor/github.com/beego/beego/v2/server/web/admin_controller.go b/src/vendor/github.com/beego/beego/v2/server/web/admin_controller.go new file mode 100644 index 000000000..ce77ecdb9 --- /dev/null +++ b/src/vendor/github.com/beego/beego/v2/server/web/admin_controller.go @@ -0,0 +1,293 @@ +// Copyright 2020 +// +// 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 web + +import ( + "bytes" + "encoding/json" + "fmt" + "net/http" + "strconv" + "text/template" + + "github.com/prometheus/client_golang/prometheus/promhttp" + + "github.com/beego/beego/v2/core/admin" +) + +type adminController struct { + Controller + servers []*HttpServer +} + +func (a *adminController) registerHttpServer(svr *HttpServer) { + a.servers = append(a.servers, svr) +} + +// ProfIndex is a http.Handler for showing profile command. +// it's in url pattern "/prof" in admin module. +func (a *adminController) ProfIndex() { + rw, r := a.Ctx.ResponseWriter, a.Ctx.Request + r.ParseForm() + command := r.Form.Get("command") + if command == "" { + return + } + + var ( + format = r.Form.Get("format") + data = make(map[interface{}]interface{}) + result bytes.Buffer + ) + admin.ProcessInput(command, &result) + data["Content"] = template.HTMLEscapeString(result.String()) + + if format == "json" && command == "gc summary" { + dataJSON, err := json.Marshal(data) + if err != nil { + http.Error(rw, err.Error(), http.StatusInternalServerError) + return + } + writeJSON(rw, dataJSON) + return + } + + data["Title"] = template.HTMLEscapeString(command) + defaultTpl := defaultScriptsTpl + if command == "gc summary" { + defaultTpl = gcAjaxTpl + } + writeTemplate(rw, data, profillingTpl, defaultTpl) +} + +func (a *adminController) PrometheusMetrics() { + promhttp.Handler().ServeHTTP(a.Ctx.ResponseWriter, a.Ctx.Request) +} + +// TaskStatus is a http.Handler with running task status (task name, status and the last execution). +// it's in "/task" pattern in admin module. +func (a *adminController) TaskStatus() { + rw, req := a.Ctx.ResponseWriter, a.Ctx.Request + + data := make(map[interface{}]interface{}) + + // Run Task + req.ParseForm() + taskname := req.Form.Get("taskname") + if taskname != "" { + cmd := admin.GetCommand("task", "run") + res := cmd.Execute(taskname) + if res.IsSuccess() { + data["Message"] = []string{ + "success", + template.HTMLEscapeString(fmt.Sprintf("%s run success,Now the Status is
%s", + taskname, res.Content.(string))), + } + } else { + data["Message"] = []string{"error", template.HTMLEscapeString(fmt.Sprintf("%s", res.Error))} + } + } + + // List Tasks + content := make(M) + resultList := admin.GetCommand("task", "list").Execute().Content.([][]string) + fields := []string{ + "Task Name", + "Task Spec", + "Task Status", + "Last Time", + "", + } + + content["Fields"] = fields + content["Data"] = resultList + data["Content"] = content + data["Title"] = "Tasks" + writeTemplate(rw, data, tasksTpl, defaultScriptsTpl) +} + +func (a *adminController) AdminIndex() { + // AdminIndex is the default http.Handler for admin module. + // it matches url pattern "/". + writeTemplate(a.Ctx.ResponseWriter, map[interface{}]interface{}{}, indexTpl, defaultScriptsTpl) +} + +// Healthcheck is a http.Handler calling health checking and showing the result. +// it's in "/healthcheck" pattern in admin module. +func (a *adminController) Healthcheck() { + heathCheck(a.Ctx.ResponseWriter, a.Ctx.Request) +} + +func heathCheck(rw http.ResponseWriter, r *http.Request) { + var ( + result []string + data = make(map[interface{}]interface{}) + resultList = new([][]string) + content = M{ + "Fields": []string{"Name", "Message", "Status"}, + } + ) + + for name, h := range admin.AdminCheckList { + if err := h.Check(); err != nil { + result = []string{ + "error", + template.HTMLEscapeString(name), + template.HTMLEscapeString(err.Error()), + } + } else { + result = []string{ + "success", + template.HTMLEscapeString(name), + "OK", + } + } + *resultList = append(*resultList, result) + } + + queryParams := r.URL.Query() + jsonFlag := queryParams.Get("json") + shouldReturnJSON, _ := strconv.ParseBool(jsonFlag) + + if shouldReturnJSON { + response := buildHealthCheckResponseList(resultList) + jsonResponse, err := json.Marshal(response) + + if err != nil { + http.Error(rw, err.Error(), http.StatusInternalServerError) + } else { + writeJSON(rw, jsonResponse) + } + return + } + + content["Data"] = resultList + data["Content"] = content + data["Title"] = "Health Check" + + writeTemplate(rw, data, healthCheckTpl, defaultScriptsTpl) +} + +// QpsIndex is the http.Handler for writing qps statistics map result info in http.ResponseWriter. +// it's registered with url pattern "/qps" in admin module. +func (a *adminController) QpsIndex() { + data := make(map[interface{}]interface{}) + data["Content"] = StatisticsMap.GetMap() + + // do html escape before display path, avoid xss + if content, ok := (data["Content"]).(M); ok { + if resultLists, ok := (content["Data"]).([][]string); ok { + for i := range resultLists { + if len(resultLists[i]) > 0 { + resultLists[i][0] = template.HTMLEscapeString(resultLists[i][0]) + } + } + } + } + writeTemplate(a.Ctx.ResponseWriter, data, qpsTpl, defaultScriptsTpl) +} + +// ListConf is the http.Handler of displaying all beego configuration values as key/value pair. +// it's registered with url pattern "/listconf" in admin module. +func (a *adminController) ListConf() { + rw := a.Ctx.ResponseWriter + r := a.Ctx.Request + r.ParseForm() + command := r.Form.Get("command") + if command == "" { + rw.Write([]byte("command not support")) + return + } + + data := make(map[interface{}]interface{}) + switch command { + case "conf": + m := make(M) + list("BConfig", BConfig, m) + m["appConfigPath"] = template.HTMLEscapeString(appConfigPath) + m["appConfigProvider"] = template.HTMLEscapeString(appConfigProvider) + tmpl := template.Must(template.New("dashboard").Parse(dashboardTpl)) + tmpl = template.Must(tmpl.Parse(configTpl)) + tmpl = template.Must(tmpl.Parse(defaultScriptsTpl)) + + data["Content"] = m + + tmpl.Execute(rw, data) + + case "router": + content := BeeApp.PrintTree() + content["Fields"] = []string{ + "Router Pattern", + "Methods", + "Controller", + } + data["Content"] = content + data["Title"] = "Routers" + writeTemplate(rw, data, routerAndFilterTpl, defaultScriptsTpl) + case "filter": + content := M{ + "Fields": []string{ + "Router Pattern", + "Filter Function", + }, + } + + filterTypeData := BeeApp.reportFilter() + + filterTypes := make([]string, 0, len(filterTypeData)) + for k := range filterTypeData { + filterTypes = append(filterTypes, k) + } + + content["Data"] = filterTypeData + content["Methods"] = filterTypes + + data["Content"] = content + data["Title"] = "Filters" + writeTemplate(rw, data, routerAndFilterTpl, defaultScriptsTpl) + default: + rw.Write([]byte("command not support")) + } +} + +func writeTemplate(rw http.ResponseWriter, data map[interface{}]interface{}, tpls ...string) { + tmpl := template.Must(template.New("dashboard").Parse(dashboardTpl)) + for _, tpl := range tpls { + tmpl = template.Must(tmpl.Parse(tpl)) + } + tmpl.Execute(rw, data) +} + +func buildHealthCheckResponseList(healthCheckResults *[][]string) []map[string]interface{} { + response := make([]map[string]interface{}, len(*healthCheckResults)) + + for i, healthCheckResult := range *healthCheckResults { + currentResultMap := make(map[string]interface{}) + + currentResultMap["name"] = healthCheckResult[0] + currentResultMap["message"] = healthCheckResult[1] + currentResultMap["status"] = healthCheckResult[2] + + response[i] = currentResultMap + } + + return response +} + +// PrintTree print all routers +// Deprecated using BeeApp directly +func PrintTree() M { + return BeeApp.PrintTree() +} diff --git a/src/vendor/github.com/beego/beego/adminui.go b/src/vendor/github.com/beego/beego/v2/server/web/adminui.go similarity index 98% rename from src/vendor/github.com/beego/beego/adminui.go rename to src/vendor/github.com/beego/beego/v2/server/web/adminui.go index cdcdef33f..75a8badc8 100644 --- a/src/vendor/github.com/beego/beego/adminui.go +++ b/src/vendor/github.com/beego/beego/v2/server/web/adminui.go @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -package beego +package web var indexTpl = ` {{define "content"}} @@ -21,10 +21,10 @@ var indexTpl = ` For detail usage please check our document:

-Toolbox +Toolbox

-Live Monitor +Live Monitor

{{.Content}} {{end}}` diff --git a/src/vendor/github.com/beego/beego/beego.go b/src/vendor/github.com/beego/beego/v2/server/web/beego.go similarity index 65% rename from src/vendor/github.com/beego/beego/beego.go rename to src/vendor/github.com/beego/beego/v2/server/web/beego.go index 44184c25c..cfe9a5ddc 100644 --- a/src/vendor/github.com/beego/beego/beego.go +++ b/src/vendor/github.com/beego/beego/v2/server/web/beego.go @@ -12,19 +12,15 @@ // See the License for the specific language governing permissions and // limitations under the License. -package beego +package web import ( "os" "path/filepath" - "strconv" - "strings" + "sync" ) const ( - // VERSION represent beego web framework version. - VERSION = "1.12.3" - // DEV is for develop DEV = "dev" // PROD is for production @@ -37,9 +33,7 @@ type M map[string]interface{} // Hook function to run type hookfunc func() error -var ( - hooks = make([]hookfunc, 0) //hook function slice to store the hookfunc -) +var hooks = make([]hookfunc, 0) // hook function slice to store the hookfunc // AddAPPStartHook is used to register the hookfunc // The hookfuncs will run in beego.Run() @@ -54,56 +48,39 @@ func AddAPPStartHook(hf ...hookfunc) { // beego.Run(":8089") // beego.Run("127.0.0.1:8089") func Run(params ...string) { - - initBeforeHTTPRun() - if len(params) > 0 && params[0] != "" { - strs := strings.Split(params[0], ":") - if len(strs) > 0 && strs[0] != "" { - BConfig.Listen.HTTPAddr = strs[0] - } - if len(strs) > 1 && strs[1] != "" { - BConfig.Listen.HTTPPort, _ = strconv.Atoi(strs[1]) - } - - BConfig.Listen.Domains = params + BeeApp.Run(params[0]) } - - BeeApp.Run() + BeeApp.Run("") } // RunWithMiddleWares Run beego application with middlewares. func RunWithMiddleWares(addr string, mws ...MiddleWare) { - initBeforeHTTPRun() - - strs := strings.Split(addr, ":") - if len(strs) > 0 && strs[0] != "" { - BConfig.Listen.HTTPAddr = strs[0] - BConfig.Listen.Domains = []string{strs[0]} - } - if len(strs) > 1 && strs[1] != "" { - BConfig.Listen.HTTPPort, _ = strconv.Atoi(strs[1]) - } - - BeeApp.Run(mws...) + BeeApp.Run(addr, mws...) } -func initBeforeHTTPRun() { - //init hooks - AddAPPStartHook( - registerMime, - registerDefaultErrorHandler, - registerSession, - registerTemplate, - registerAdmin, - registerGzip, - ) +var initHttpOnce sync.Once - for _, hk := range hooks { - if err := hk(); err != nil { - panic(err) +// TODO move to module init function +func initBeforeHTTPRun() { + initHttpOnce.Do(func() { + // init hooks + AddAPPStartHook( + registerMime, + registerDefaultErrorHandler, + registerSession, + registerTemplate, + registerAdmin, + registerGzip, + // registerCommentRouter, + ) + + for _, hk := range hooks { + if err := hk(); err != nil { + panic(err) + } } - } + }) } // TestBeegoInit is for test package init diff --git a/src/vendor/github.com/beego/beego/v2/server/web/config.go b/src/vendor/github.com/beego/beego/v2/server/web/config.go new file mode 100644 index 000000000..006201a63 --- /dev/null +++ b/src/vendor/github.com/beego/beego/v2/server/web/config.go @@ -0,0 +1,872 @@ +// Copyright 2014 beego Author. All Rights Reserved. +// +// 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 web + +import ( + "crypto/tls" + "fmt" + "net/http" + "os" + "path/filepath" + "reflect" + "runtime" + "strings" + + "github.com/beego/beego/v2" + "github.com/beego/beego/v2/core/config" + "github.com/beego/beego/v2/core/logs" + "github.com/beego/beego/v2/core/utils" + "github.com/beego/beego/v2/server/web/context" + "github.com/beego/beego/v2/server/web/session" +) + +// Config is the main struct for BConfig +// TODO after supporting multiple servers, remove common config to somewhere else +type Config struct { + // AppName + // @Description Application's name. You'd better set it because we use it to do some logging and tracing + // @Default beego + AppName string // Application name + // RunMode + // @Description it's the same as environment. In general, we have different run modes. + // For example, the most common case is using dev, test, prod three environments + // when you are developing the application, you should set it as dev + // when you completed coding and want QA to test your code, you should deploy your application to test environment + // and the RunMode should be set as test + // when you completed all tests, you want to deploy it to prod, you should set it to prod + // You should never set RunMode="dev" when you deploy the application to prod + // because Beego will do more things which need Go SDK and other tools when it found out the RunMode="dev" + // @Default dev + RunMode string // Running Mode: dev | prod + + // RouterCaseSensitive + // @Description If it was true, it means that the router is case sensitive. + // For example, when you register a router with pattern "/hello", + // 1. If this is true, and the request URL is "/Hello", it won't match this pattern + // 2. If this is false and the request URL is "/Hello", it will match this pattern + // @Default true + RouterCaseSensitive bool + // RecoverPanic + // @Description if it was true, Beego will try to recover from panic when it serves your http request + // So you should notice that it doesn't mean that Beego will recover all panic cases. + // @Default true + RecoverPanic bool + // CopyRequestBody + // @Description if it's true, Beego will copy the request body. But if the request body's size > MaxMemory, + // Beego will return 413 as http status + // If you are building RESTful API, please set it to true. + // And if you want to read data from request Body multiple times, please set it to true + // In general, if you don't meet any performance issue, you could set it to true + // @Default false + CopyRequestBody bool + // EnableGzip + // @Description If it was true, Beego will try to compress data by using zip algorithm. + // But there are two points: + // 1. Only static resources will be compressed + // 2. Only those static resource which has the extension specified by StaticExtensionsToGzip will be compressed + // @Default false + EnableGzip bool + // EnableErrorsShow + // @Description If it's true, Beego will show error message to page + // it will work with ErrorMaps which allows you register some error handler + // You may want to set it to false when application was deploy to prod environment + // because you may not want to expose your internal error msg to your users + // it's a little bit unsafe + // @Default true + EnableErrorsShow bool + // EnableErrorsRender + // @Description If it's true, it will output the error msg as a page. It's similar to EnableErrorsShow + // And this configure item only work in dev run mode (see RunMode) + // @Default true + EnableErrorsRender bool + // ServerName + // @Description server name. For example, in large scale system, + // you may want to deploy your application to several machines, so that each of them has a server name + // we suggest you'd better set value because Beego use this to output some DEBUG msg, + // or integrated with other tools such as tracing, metrics + // @Default + ServerName string + + // RecoverFunc + // @Description when Beego want to recover from panic, it will use this func as callback + // see RecoverPanic + // @Default defaultRecoverPanic + RecoverFunc func(*context.Context, *Config) + // @Description MaxMemory and MaxUploadSize are used to limit the request body + // if the request is not uploading file, MaxMemory is the max size of request body + // if the request is uploading file, MaxUploadSize is the max size of request body + // if CopyRequestBody is true, this value will be used as the threshold of request body + // see CopyRequestBody + // the default value is 1 << 26 (64MB) + // @Default 67108864 + MaxMemory int64 + // MaxUploadSize + // @Description MaxMemory and MaxUploadSize are used to limit the request body + // if the request is not uploading file, MaxMemory is the max size of request body + // if the request is uploading file, MaxUploadSize is the max size of request body + // the default value is 1 << 30 (1GB) + // @Default 1073741824 + MaxUploadSize int64 + // Listen + // @Description the configuration about socket or http protocol + Listen Listen + // WebConfig + // @Description the configuration about Web + WebConfig WebConfig + // LogConfig + // @Description log configuration + Log LogConfig +} + +// Listen holds for http and https related config +type Listen struct { + // Graceful + // @Description means use graceful module to start the server + // @Default false + Graceful bool + // ListenTCP4 + // @Description if it's true, means that Beego only work for TCP4 + // please check net.Listen function + // In general, you should not set it to true + // @Default false + ListenTCP4 bool + // EnableHTTP + // @Description if it's true, Beego will accept HTTP request. + // But if you want to use HTTPS only, please set it to false + // see EnableHTTPS + // @Default true + EnableHTTP bool + // AutoTLS + // @Description If it's true, Beego will use default value to initialize the TLS configure + // But those values could be override if you have custom value. + // see Domains, TLSCacheDir + // @Default false + AutoTLS bool + // EnableHTTPS + // @Description If it's true, Beego will accept HTTPS request. + // Now, you'd better use HTTPS protocol on prod environment to get better security + // In prod, the best option is EnableHTTPS=true and EnableHTTP=false + // see EnableHTTP + // @Default false + EnableHTTPS bool + // EnableMutualHTTPS + // @Description if it's true, Beego will handle requests on incoming mutual TLS connections + // see Server.ListenAndServeMutualTLS + // @Default false + EnableMutualHTTPS bool + // EnableAdmin + // @Description if it's true, Beego will provide admin service. + // You can visit the admin service via browser. + // The default port is 8088 + // see AdminPort + // @Default false + EnableAdmin bool + // EnableFcgi + // @Description + // @Default false + EnableFcgi bool + // EnableStdIo + // @Description EnableStdIo works with EnableFcgi Use FCGI via standard I/O + // @Default false + EnableStdIo bool + // ServerTimeOut + // @Description Beego use this as ReadTimeout and WriteTimeout + // The unit is second. + // see http.Server.ReadTimeout, WriteTimeout + // @Default 0 + ServerTimeOut int64 + // HTTPAddr + // @Description Beego listen to this address when the application start up. + // @Default "" + HTTPAddr string + // HTTPPort + // @Description Beego listen to this port + // you'd better change this value when you deploy to prod environment + // @Default 8080 + HTTPPort int + // Domains + // @Description Beego use this to configure TLS. Those domains are "white list" domain + // @Default [] + Domains []string + // TLSCacheDir + // @Description Beego use this as cache dir to store TLS cert data + // @Default "" + TLSCacheDir string + // HTTPSAddr + // @Description Beego will listen to this address to accept HTTPS request + // see EnableHTTPS + // @Default "" + HTTPSAddr string + // HTTPSPort + // @Description Beego will listen to this port to accept HTTPS request + // @Default 10443 + HTTPSPort int + // HTTPSCertFile + // @Description Beego read this file as cert file + // When you are using HTTPS protocol, please configure it + // see HTTPSKeyFile + // @Default "" + HTTPSCertFile string + // HTTPSKeyFile + // @Description Beego read this file as key file + // When you are using HTTPS protocol, please configure it + // see HTTPSCertFile + // @Default "" + HTTPSKeyFile string + // TrustCaFile + // @Description Beego read this file as CA file + // @Default "" + TrustCaFile string + // AdminAddr + // @Description Beego will listen to this address to provide admin service + // In general, it should be the same with your application address, HTTPAddr or HTTPSAddr + // @Default "" + AdminAddr string + // AdminPort + // @Description Beego will listen to this port to provide admin service + // @Default 8088 + AdminPort int + // @Description Beego use this tls.ClientAuthType to initialize TLS connection + // The default value is tls.RequireAndVerifyClientCert + // @Default 4 + ClientAuth int +} + +// WebConfig holds web related config +type WebConfig struct { + // AutoRender + // @Description If it's true, Beego will render the page based on your template and data + // In general, keep it as true. + // But if you are building RESTFul API and you don't have any page, + // you can set it to false + // @Default true + AutoRender bool + // Deprecated: Beego didn't use it anymore + EnableDocs bool + // EnableXSRF + // @Description If it's true, Beego will help to provide XSRF support + // But you should notice that, now Beego only work for HTTPS protocol with XSRF + // because it's not safe if using HTTP protocol + // And, the cookie storing XSRF token has two more flags HttpOnly and Secure + // It means that you must use HTTPS protocol and you can not read the token from JS script + // This is completed different from Beego 1.x because we got many security reports + // And if you are in dev environment, you could set it to false + // @Default false + EnableXSRF bool + // DirectoryIndex + // @Description When Beego serves static resources request, it will look up the file. + // If the file is directory, Beego will try to find the index.html as the response + // But if the index.html is not exist or it's a directory, + // Beego will return 403 response if DirectoryIndex is **false** + // @Default false + DirectoryIndex bool + // FlashName + // @Description the cookie's name when Beego try to store the flash data into cookie + // @Default BEEGO_FLASH + FlashName string + // FlashSeparator + // @Description When Beego read flash data from request, it uses this as the separator + // @Default BEEGOFLASH + FlashSeparator string + // StaticDir + // @Description Beego uses this as static resources' root directory. + // It means that Beego will try to search static resource from this start point + // It's a map, the key is the path and the value is the directory + // For example, the default value is /static => static, + // which means that when Beego got a request with path /static/xxx + // Beego will try to find the resource from static directory + // @Default /static => static + StaticDir map[string]string + // StaticExtensionsToGzip + // @Description The static resources with those extension will be compressed if EnableGzip is true + // @Default [".css", ".js" ] + StaticExtensionsToGzip []string + // StaticCacheFileSize + // @Description If the size of static resource < StaticCacheFileSize, Beego will try to handle it by itself, + // it means that Beego will compressed the file data (if enable) and cache this file. + // But if the file size > StaticCacheFileSize, Beego just simply delegate the request to http.ServeFile + // the default value is 100KB. + // the max memory size of caching static files is StaticCacheFileSize * StaticCacheFileNum + // see StaticCacheFileNum + // @Default 102400 + StaticCacheFileSize int + // StaticCacheFileNum + // @Description Beego use it to control the memory usage of caching static resource file + // If the caching files > StaticCacheFileNum, Beego use LRU algorithm to remove caching file + // the max memory size of caching static files is StaticCacheFileSize * StaticCacheFileNum + // see StaticCacheFileSize + // @Default 1000 + StaticCacheFileNum int + // TemplateLeft + // @Description Beego use this to render page + // see TemplateRight + // @Default {{ + TemplateLeft string + // TemplateRight + // @Description Beego use this to render page + // see TemplateLeft + // @Default }} + TemplateRight string + // ViewsPath + // @Description The directory of Beego application storing template + // @Default views + ViewsPath string + // CommentRouterPath + // @Description Beego scans this directory and its sub directory to generate router + // Beego only scans this directory when it's in dev environment + // @Default controllers + CommentRouterPath string + // XSRFKey + // @Description the name of cookie storing XSRF token + // see EnableXSRF + // @Default beegoxsrf + XSRFKey string + // XSRFExpire + // @Description the expiration time of XSRF token cookie + // second + // @Default 0 + XSRFExpire int + // @Description session related config + Session SessionConfig +} + +// SessionConfig holds session related config +type SessionConfig struct { + // SessionOn + // @Description if it's true, Beego will auto manage session + // @Default false + SessionOn bool + // SessionAutoSetCookie + // @Description if it's true, Beego will put the session token into cookie too + // @Default true + SessionAutoSetCookie bool + // SessionDisableHTTPOnly + // @Description used to allow for cross domain cookies/javascript cookies + // In general, you should not set it to true unless you understand the risk + // @Default false + SessionDisableHTTPOnly bool + // SessionEnableSidInHTTPHeader + // @Description enable store/get the sessionId into/from http headers + // @Default false + SessionEnableSidInHTTPHeader bool + // SessionEnableSidInURLQuery + // @Description enable get the sessionId from Url Query params + // @Default false + SessionEnableSidInURLQuery bool + // SessionProvider + // @Description session provider's name. + // You should confirm that this provider has been register via session.Register method + // the default value is memory. This is not suitable for distributed system + // @Default memory + SessionProvider string + // SessionName + // @Description If SessionAutoSetCookie is true, we use this value as the cookie's name + // @Default beegosessionID + SessionName string + // SessionGCMaxLifetime + // @Description Beego will GC session to clean useless session. + // unit: second + // @Default 3600 + SessionGCMaxLifetime int64 + // SessionProviderConfig + // @Description the config of session provider + // see SessionProvider + // you should read the document of session provider to learn how to set this value + // @Default "" + SessionProviderConfig string + // SessionCookieLifeTime + // @Description If SessionAutoSetCookie is true, + // we use this value as the expiration time and max age of the cookie + // unit second + // @Default 0 + SessionCookieLifeTime int + // SessionDomain + // @Description If SessionAutoSetCookie is true, we use this value as the cookie's domain + // @Default "" + SessionDomain string + // SessionNameInHTTPHeader + // @Description if SessionEnableSidInHTTPHeader is true, this value will be used as the http header + // @Default Beegosessionid + SessionNameInHTTPHeader string + // SessionCookieSameSite + // @Description If SessionAutoSetCookie is true, we use this value as the cookie's same site policy + // the default value is http.SameSiteDefaultMode + // @Default 1 + SessionCookieSameSite http.SameSite + + // SessionIDPrefix + // @Description session id's prefix + // @Default "" + SessionIDPrefix string +} + +// LogConfig holds Log related config +type LogConfig struct { + // AccessLogs + // @Description If it's true, Beego will log the HTTP request info + // @Default false + AccessLogs bool + // EnableStaticLogs + // @Description log static files requests + // @Default false + EnableStaticLogs bool + // FileLineNum + // @Description if it's true, it will log the line number + // @Default true + FileLineNum bool + // AccessLogsFormat + // @Description access log format: JSON_FORMAT, APACHE_FORMAT or empty string + // @Default APACHE_FORMAT + AccessLogsFormat string + // Outputs + // @Description the destination of access log + // the key is log adapter and the value is adapter's configure + // @Default "console" => "" + Outputs map[string]string // Store Adaptor : config +} + +var ( + // BConfig is the default config for Application + BConfig *Config + // AppConfig is the instance of Config, store the config information from file + AppConfig *beegoAppConfig + // AppPath is the absolute path to the app + AppPath string + // GlobalSessions is the instance for the session manager + GlobalSessions *session.Manager + + // appConfigPath is the path to the config files + appConfigPath string + // appConfigProvider is the provider for the config, default is ini + appConfigProvider = "ini" + // WorkPath is the absolute path to project root directory + WorkPath string +) + +func init() { + BConfig = newBConfig() + var err error + if AppPath, err = filepath.Abs(filepath.Dir(os.Args[0])); err != nil { + panic(err) + } + WorkPath, err = os.Getwd() + if err != nil { + panic(err) + } + filename := "app.conf" + if os.Getenv("BEEGO_RUNMODE") != "" { + filename = os.Getenv("BEEGO_RUNMODE") + ".app.conf" + } + appConfigPath = filepath.Join(WorkPath, "conf", filename) + if !utils.FileExists(appConfigPath) { + appConfigPath = filepath.Join(AppPath, "conf", filename) + if !utils.FileExists(appConfigPath) { + AppConfig = &beegoAppConfig{innerConfig: config.NewFakeConfig()} + return + } + } + if err = parseConfig(appConfigPath); err != nil { + panic(err) + } +} + +func defaultRecoverPanic(ctx *context.Context, cfg *Config) { + if err := recover(); err != nil { + if err == ErrAbort { + return + } + if !cfg.RecoverPanic { + panic(err) + } + if cfg.EnableErrorsShow { + if _, ok := ErrorMaps[fmt.Sprint(err)]; ok { + exception(fmt.Sprint(err), ctx) + return + } + } + var stack string + logs.Critical("the request url is ", ctx.Input.URL()) + logs.Critical("Handler crashed with error", err) + for i := 1; ; i++ { + _, file, line, ok := runtime.Caller(i) + if !ok { + break + } + logs.Critical(fmt.Sprintf("%s:%d", file, line)) + stack = stack + fmt.Sprintln(fmt.Sprintf("%s:%d", file, line)) + } + + if ctx.Output.Status != 0 { + ctx.ResponseWriter.WriteHeader(ctx.Output.Status) + } else { + ctx.ResponseWriter.WriteHeader(500) + } + + if cfg.RunMode == DEV && cfg.EnableErrorsRender { + showErr(err, ctx, stack) + } + } +} + +func newBConfig() *Config { + res := &Config{ + AppName: "beego", + RunMode: PROD, + RouterCaseSensitive: true, + ServerName: "beegoServer:" + beego.VERSION, + RecoverPanic: true, + + CopyRequestBody: false, + EnableGzip: false, + MaxMemory: 1 << 26, // 64MB + MaxUploadSize: 1 << 30, // 1GB + EnableErrorsShow: true, + EnableErrorsRender: true, + Listen: Listen{ + Graceful: false, + ServerTimeOut: 0, + ListenTCP4: false, + EnableHTTP: true, + AutoTLS: false, + Domains: []string{}, + TLSCacheDir: ".", + HTTPAddr: "", + HTTPPort: 8080, + EnableHTTPS: false, + HTTPSAddr: "", + HTTPSPort: 10443, + HTTPSCertFile: "", + HTTPSKeyFile: "", + EnableAdmin: false, + AdminAddr: "", + AdminPort: 8088, + EnableFcgi: false, + EnableStdIo: false, + ClientAuth: int(tls.RequireAndVerifyClientCert), + }, + WebConfig: WebConfig{ + AutoRender: true, + EnableDocs: false, + FlashName: "BEEGO_FLASH", + FlashSeparator: "BEEGOFLASH", + DirectoryIndex: false, + StaticDir: map[string]string{"/static": "static"}, + StaticExtensionsToGzip: []string{".css", ".js"}, + StaticCacheFileSize: 1024 * 100, + StaticCacheFileNum: 1000, + TemplateLeft: "{{", + TemplateRight: "}}", + ViewsPath: "views", + CommentRouterPath: "controllers", + EnableXSRF: false, + XSRFKey: "beegoxsrf", + XSRFExpire: 0, + Session: SessionConfig{ + SessionOn: false, + SessionProvider: "memory", + SessionName: "beegosessionID", + SessionGCMaxLifetime: 3600, + SessionProviderConfig: "", + SessionDisableHTTPOnly: false, + SessionCookieLifeTime: 0, // set cookie default is the browser life + SessionAutoSetCookie: true, + SessionDomain: "", + SessionEnableSidInHTTPHeader: false, // enable store/get the sessionId into/from http headers + SessionNameInHTTPHeader: "Beegosessionid", + SessionEnableSidInURLQuery: false, // enable get the sessionId from Url Query params + SessionCookieSameSite: http.SameSiteDefaultMode, + }, + }, + Log: LogConfig{ + AccessLogs: false, + EnableStaticLogs: false, + AccessLogsFormat: "APACHE_FORMAT", + FileLineNum: true, + Outputs: map[string]string{"console": ""}, + }, + } + + res.RecoverFunc = defaultRecoverPanic + return res +} + +// now only support ini, next will support json. +func parseConfig(appConfigPath string) (err error) { + AppConfig, err = newAppConfig(appConfigProvider, appConfigPath) + if err != nil { + return err + } + return assignConfig(AppConfig) +} + +// assignConfig is tricky. +// For 1.x, it use assignSingleConfig to parse the file +// but for 2.x, we use Unmarshaler method +func assignConfig(ac config.Configer) error { + parseConfigForV1(ac) + + err := ac.Unmarshaler("", BConfig) + if err != nil { + _, _ = fmt.Fprintln(os.Stderr, fmt.Sprintf("Unmarshaler config file to BConfig failed. "+ + "And if you are working on v1.x config file, please ignore this, err: %s", err)) + return err + } + + // init log + logs.Reset() + for adaptor, cfg := range BConfig.Log.Outputs { + err := logs.SetLogger(adaptor, cfg) + if err != nil { + fmt.Fprintln(os.Stderr, fmt.Sprintf("%s with the config %q got err:%s", adaptor, cfg, err.Error())) + return err + } + } + logs.SetLogFuncCall(BConfig.Log.FileLineNum) + return nil +} + +func parseConfigForV1(ac config.Configer) { + for _, i := range []interface{}{BConfig, &BConfig.Listen, &BConfig.WebConfig, &BConfig.Log, &BConfig.WebConfig.Session} { + assignSingleConfig(i, ac) + } + + // set the run mode first + if envRunMode := os.Getenv("BEEGO_RUNMODE"); envRunMode != "" { + BConfig.RunMode = envRunMode + } else if runMode, err := ac.String("RunMode"); runMode != "" && err == nil { + BConfig.RunMode = runMode + } + + if sd, err := ac.String("StaticDir"); sd != "" && err == nil { + BConfig.WebConfig.StaticDir = map[string]string{} + sds := strings.Fields(sd) + for _, v := range sds { + if url2fsmap := strings.SplitN(v, ":", 2); len(url2fsmap) == 2 { + BConfig.WebConfig.StaticDir["/"+strings.Trim(url2fsmap[0], "/")] = url2fsmap[1] + } else { + BConfig.WebConfig.StaticDir["/"+strings.Trim(url2fsmap[0], "/")] = url2fsmap[0] + } + } + } + + if sgz, err := ac.String("StaticExtensionsToGzip"); sgz != "" && err == nil { + extensions := strings.Split(sgz, ",") + fileExts := []string{} + for _, ext := range extensions { + ext = strings.TrimSpace(ext) + if ext == "" { + continue + } + if !strings.HasPrefix(ext, ".") { + ext = "." + ext + } + fileExts = append(fileExts, ext) + } + if len(fileExts) > 0 { + BConfig.WebConfig.StaticExtensionsToGzip = fileExts + } + } + + if sfs, err := ac.Int("StaticCacheFileSize"); err == nil { + BConfig.WebConfig.StaticCacheFileSize = sfs + } + + if sfn, err := ac.Int("StaticCacheFileNum"); err == nil { + BConfig.WebConfig.StaticCacheFileNum = sfn + } + + if lo, err := ac.String("LogOutputs"); lo != "" && err == nil { + // if lo is not nil or empty + // means user has set his own LogOutputs + // clear the default setting to BConfig.Log.Outputs + BConfig.Log.Outputs = make(map[string]string) + los := strings.Split(lo, ";") + for _, v := range los { + if logType2Config := strings.SplitN(v, ",", 2); len(logType2Config) == 2 { + BConfig.Log.Outputs[logType2Config[0]] = logType2Config[1] + } else { + continue + } + } + } +} + +func assignSingleConfig(p interface{}, ac config.Configer) { + pt := reflect.TypeOf(p) + if pt.Kind() != reflect.Ptr { + return + } + pt = pt.Elem() + if pt.Kind() != reflect.Struct { + return + } + pv := reflect.ValueOf(p).Elem() + + for i := 0; i < pt.NumField(); i++ { + pf := pv.Field(i) + if !pf.CanSet() { + continue + } + name := pt.Field(i).Name + switch pf.Kind() { + case reflect.String: + pf.SetString(ac.DefaultString(name, pf.String())) + case reflect.Int, reflect.Int64: + pf.SetInt(ac.DefaultInt64(name, pf.Int())) + case reflect.Bool: + pf.SetBool(ac.DefaultBool(name, pf.Bool())) + case reflect.Struct: + default: + // do nothing here + } + } +} + +// LoadAppConfig allow developer to apply a config file +func LoadAppConfig(adapterName, configPath string) error { + absConfigPath, err := filepath.Abs(configPath) + if err != nil { + return err + } + + if !utils.FileExists(absConfigPath) { + return fmt.Errorf("the target config file: %s don't exist", configPath) + } + + appConfigPath = absConfigPath + appConfigProvider = adapterName + + return parseConfig(appConfigPath) +} + +type beegoAppConfig struct { + config.BaseConfiger + innerConfig config.Configer +} + +func newAppConfig(appConfigProvider, appConfigPath string) (*beegoAppConfig, error) { + ac, err := config.NewConfig(appConfigProvider, appConfigPath) + if err != nil { + return nil, err + } + return &beegoAppConfig{innerConfig: ac}, nil +} + +func (b *beegoAppConfig) Unmarshaler(prefix string, obj interface{}, opt ...config.DecodeOption) error { + return b.innerConfig.Unmarshaler(prefix, obj, opt...) +} + +func (b *beegoAppConfig) Set(key, val string) error { + if err := b.innerConfig.Set(BConfig.RunMode+"::"+key, val); err != nil { + return b.innerConfig.Set(key, val) + } + return nil +} + +func (b *beegoAppConfig) String(key string) (string, error) { + if v, err := b.innerConfig.String(BConfig.RunMode + "::" + key); v != "" && err == nil { + return v, nil + } + return b.innerConfig.String(key) +} + +func (b *beegoAppConfig) Strings(key string) ([]string, error) { + if v, err := b.innerConfig.Strings(BConfig.RunMode + "::" + key); len(v) > 0 && err == nil { + return v, nil + } + return b.innerConfig.Strings(key) +} + +func (b *beegoAppConfig) Int(key string) (int, error) { + if v, err := b.innerConfig.Int(BConfig.RunMode + "::" + key); err == nil { + return v, nil + } + return b.innerConfig.Int(key) +} + +func (b *beegoAppConfig) Int64(key string) (int64, error) { + if v, err := b.innerConfig.Int64(BConfig.RunMode + "::" + key); err == nil { + return v, nil + } + return b.innerConfig.Int64(key) +} + +func (b *beegoAppConfig) Bool(key string) (bool, error) { + if v, err := b.innerConfig.Bool(BConfig.RunMode + "::" + key); err == nil { + return v, nil + } + return b.innerConfig.Bool(key) +} + +func (b *beegoAppConfig) Float(key string) (float64, error) { + if v, err := b.innerConfig.Float(BConfig.RunMode + "::" + key); err == nil { + return v, nil + } + return b.innerConfig.Float(key) +} + +func (b *beegoAppConfig) DefaultString(key string, defaultVal string) string { + if v, err := b.String(key); v != "" && err == nil { + return v + } + return defaultVal +} + +func (b *beegoAppConfig) DefaultStrings(key string, defaultVal []string) []string { + if v, err := b.Strings(key); len(v) != 0 && err == nil { + return v + } + return defaultVal +} + +func (b *beegoAppConfig) DefaultInt(key string, defaultVal int) int { + if v, err := b.Int(key); err == nil { + return v + } + return defaultVal +} + +func (b *beegoAppConfig) DefaultInt64(key string, defaultVal int64) int64 { + if v, err := b.Int64(key); err == nil { + return v + } + return defaultVal +} + +func (b *beegoAppConfig) DefaultBool(key string, defaultVal bool) bool { + if v, err := b.Bool(key); err == nil { + return v + } + return defaultVal +} + +func (b *beegoAppConfig) DefaultFloat(key string, defaultVal float64) float64 { + if v, err := b.Float(key); err == nil { + return v + } + return defaultVal +} + +func (b *beegoAppConfig) DIY(key string) (interface{}, error) { + return b.innerConfig.DIY(key) +} + +func (b *beegoAppConfig) GetSection(section string) (map[string]string, error) { + return b.innerConfig.GetSection(section) +} + +func (b *beegoAppConfig) SaveConfigFile(filename string) error { + return b.innerConfig.SaveConfigFile(filename) +} diff --git a/src/vendor/github.com/beego/beego/context/acceptencoder.go b/src/vendor/github.com/beego/beego/v2/server/web/context/acceptencoder.go similarity index 81% rename from src/vendor/github.com/beego/beego/context/acceptencoder.go rename to src/vendor/github.com/beego/beego/v2/server/web/context/acceptencoder.go index b4e2492c0..4181bfee9 100644 --- a/src/vendor/github.com/beego/beego/context/acceptencoder.go +++ b/src/vendor/github.com/beego/beego/v2/server/web/context/acceptencoder.go @@ -28,18 +28,18 @@ import ( ) var ( - //Default size==20B same as nginx + // Default size==20B same as nginx defaultGzipMinLength = 20 - //Content will only be compressed if content length is either unknown or greater than gzipMinLength. + // Content will only be compressed if content length is either unknown or greater than gzipMinLength. gzipMinLength = defaultGzipMinLength - //The compression level used for deflate compression. (0-9). + // Compression level used for deflate compression. (0-9). gzipCompressLevel int - //List of HTTP methods to compress. If not set, only GET requests are compressed. + // List of HTTP methods to compress. If not set, only GET requests are compressed. includedMethods map[string]bool getMethodOnly bool ) -// InitGzip init the gzipcompress +// InitGzip initializes the gzipcompress func InitGzip(minLength, compressLevel int, methods []string) { if minLength >= 0 { gzipMinLength = minLength @@ -65,7 +65,7 @@ type nopResetWriter struct { } func (n nopResetWriter) Reset(w io.Writer) { - //do nothing + // do nothing } type acceptEncoder struct { @@ -98,9 +98,9 @@ func (ac acceptEncoder) put(wr resetWriter, level int) { } wr.Reset(nil) - //notice - //compressionLevel==BestCompression DOES NOT MATTER - //sync.Pool will not memory leak + // notice + // compressionLevel==BestCompression DOES NOT MATTER + // sync.Pool will not memory leak switch level { case gzipCompressLevel: @@ -119,10 +119,10 @@ var ( bestCompressionPool: &sync.Pool{New: func() interface{} { wr, _ := gzip.NewWriterLevel(nil, flate.BestCompression); return wr }}, } - //according to the sec :http://tools.ietf.org/html/rfc2616#section-3.5 ,the deflate compress in http is zlib indeed - //deflate - //The "zlib" format defined in RFC 1950 [31] in combination with - //the "deflate" compression mechanism described in RFC 1951 [29]. + // According to: http://tools.ietf.org/html/rfc2616#section-3.5 the deflate compress in http is zlib indeed + // deflate + // The "zlib" format defined in RFC 1950 [31] in combination with + // the "deflate" compression mechanism described in RFC 1951 [29]. deflateCompressEncoder = acceptEncoder{ name: "deflate", levelEncode: func(level int) resetWriter { wr, _ := zlib.NewWriterLevel(nil, level); return wr }, @@ -131,21 +131,19 @@ var ( } ) -var ( - encoderMap = map[string]acceptEncoder{ // all the other compress methods will ignore - "gzip": gzipCompressEncoder, - "deflate": deflateCompressEncoder, - "*": gzipCompressEncoder, // * means any compress will accept,we prefer gzip - "identity": noneCompressEncoder, // identity means none-compress - } -) +var encoderMap = map[string]acceptEncoder{ // all the other compress methods will ignore + "gzip": gzipCompressEncoder, + "deflate": deflateCompressEncoder, + "*": gzipCompressEncoder, // * means any compress will accept,we prefer gzip + "identity": noneCompressEncoder, // identity means none-compress +} // WriteFile reads from file and writes to writer by the specific encoding(gzip/deflate) func WriteFile(encoding string, writer io.Writer, file *os.File) (bool, string, error) { return writeLevel(encoding, writer, file, flate.BestCompression) } -// WriteBody reads writes content to writer by the specific encoding(gzip/deflate) +// WriteBody reads writes content to writer by the specific encoding(gzip/deflate) func WriteBody(encoding string, writer io.Writer, content []byte) (bool, string, error) { if encoding == "" || len(content) < gzipMinLength { _, err := writer.Write(content) @@ -154,12 +152,12 @@ func WriteBody(encoding string, writer io.Writer, content []byte) (bool, string, return writeLevel(encoding, writer, bytes.NewReader(content), gzipCompressLevel) } -// writeLevel reads from reader,writes to writer by specific encoding and compress level -// the compress level is defined by deflate package +// writeLevel reads from reader and writes to writer by specific encoding and compress level. +// The compress level is defined by deflate package func writeLevel(encoding string, writer io.Writer, reader io.Reader, level int) (bool, string, error) { var outputWriter resetWriter var err error - var ce = noneCompressEncoder + ce := noneCompressEncoder if cf, ok := encoderMap[encoding]; ok { ce = cf diff --git a/src/vendor/github.com/beego/beego/v2/server/web/context/context.go b/src/vendor/github.com/beego/beego/v2/server/web/context/context.go new file mode 100644 index 000000000..c85dc45b5 --- /dev/null +++ b/src/vendor/github.com/beego/beego/v2/server/web/context/context.go @@ -0,0 +1,399 @@ +// Copyright 2014 beego Author. All Rights Reserved. +// +// 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 context provide the context utils +// Usage: +// +// import "github.com/beego/beego/v2/server/web/context" +// +// ctx := context.Context{Request:req,ResponseWriter:rw} +// +// more docs http://beego.vip/docs/module/context.md +package context + +import ( + "bufio" + "crypto/hmac" + "crypto/sha256" + "encoding/base64" + "encoding/json" + "encoding/xml" + "errors" + "fmt" + "net" + "net/http" + "net/url" + "reflect" + "strconv" + "strings" + "time" + + "google.golang.org/protobuf/proto" + "gopkg.in/yaml.v3" + + "github.com/beego/beego/v2/core/utils" + "github.com/beego/beego/v2/server/web/session" +) + +// Commonly used mime-types +const ( + ApplicationJSON = "application/json" + ApplicationXML = "application/xml" + ApplicationForm = "application/x-www-form-urlencoded" + ApplicationProto = "application/x-protobuf" + ApplicationYAML = "application/x-yaml" + TextXML = "text/xml" + + formatTime = "15:04:05" + formatDate = "2006-01-02" + formatDateTime = "2006-01-02 15:04:05" + formatDateTimeT = "2006-01-02T15:04:05" +) + +// NewContext return the Context with Input and Output +func NewContext() *Context { + return &Context{ + Input: NewInput(), + Output: NewOutput(), + } +} + +// Context Http request context struct including BeegoInput, BeegoOutput, http.Request and http.ResponseWriter. +// BeegoInput and BeegoOutput provides an api to operate request and response more easily. +type Context struct { + Input *BeegoInput + Output *BeegoOutput + Request *http.Request + ResponseWriter *Response + _xsrfToken string +} + +func (ctx *Context) Bind(obj interface{}) error { + ct, exist := ctx.Request.Header["Content-Type"] + if !exist || len(ct) == 0 { + return ctx.BindJSON(obj) + } + i, l := 0, len(ct[0]) + for i < l && ct[0][i] != ';' { + i++ + } + switch ct[0][0:i] { + case ApplicationJSON: + return ctx.BindJSON(obj) + case ApplicationXML, TextXML: + return ctx.BindXML(obj) + case ApplicationForm: + return ctx.BindForm(obj) + case ApplicationProto: + return ctx.BindProtobuf(obj.(proto.Message)) + case ApplicationYAML: + return ctx.BindYAML(obj) + default: + return errors.New("Unsupported Content-Type:" + ct[0]) + } +} + +// Resp sends response based on the Accept Header +// By default response will be in JSON +func (ctx *Context) Resp(data interface{}) error { + accept := ctx.Input.Header("Accept") + switch accept { + case ApplicationYAML: + return ctx.YamlResp(data) + case ApplicationXML, TextXML: + return ctx.XMLResp(data) + case ApplicationProto: + return ctx.ProtoResp(data.(proto.Message)) + default: + return ctx.JSONResp(data) + } +} + +func (ctx *Context) JSONResp(data interface{}) error { + return ctx.Output.JSON(data, false, false) +} + +func (ctx *Context) XMLResp(data interface{}) error { + return ctx.Output.XML(data, false) +} + +func (ctx *Context) YamlResp(data interface{}) error { + return ctx.Output.YAML(data) +} + +func (ctx *Context) ProtoResp(data proto.Message) error { + return ctx.Output.Proto(data) +} + +// BindYAML only read data from http request body +func (ctx *Context) BindYAML(obj interface{}) error { + return yaml.Unmarshal(ctx.Input.RequestBody, obj) +} + +// BindForm will parse form values to struct via tag. +func (ctx *Context) BindForm(obj interface{}) error { + err := ctx.Request.ParseForm() + if err != nil { + return err + } + return ParseForm(ctx.Request.Form, obj) +} + +// BindJSON only read data from http request body +func (ctx *Context) BindJSON(obj interface{}) error { + return json.Unmarshal(ctx.Input.RequestBody, obj) +} + +// BindProtobuf only read data from http request body +func (ctx *Context) BindProtobuf(obj proto.Message) error { + return proto.Unmarshal(ctx.Input.RequestBody, obj) +} + +// BindXML only read data from http request body +func (ctx *Context) BindXML(obj interface{}) error { + return xml.Unmarshal(ctx.Input.RequestBody, obj) +} + +// ParseForm will parse form values to struct via tag. +func ParseForm(form url.Values, obj interface{}) error { + objT := reflect.TypeOf(obj) + objV := reflect.ValueOf(obj) + if !isStructPtr(objT) { + return fmt.Errorf("%v must be a struct pointer", obj) + } + objT = objT.Elem() + objV = objV.Elem() + return parseFormToStruct(form, objT, objV) +} + +func isStructPtr(t reflect.Type) bool { + return t.Kind() == reflect.Ptr && t.Elem().Kind() == reflect.Struct +} + +// Reset initializes Context, BeegoInput and BeegoOutput +func (ctx *Context) Reset(rw http.ResponseWriter, r *http.Request) { + ctx.Request = r + if ctx.ResponseWriter == nil { + ctx.ResponseWriter = &Response{} + } + ctx.ResponseWriter.reset(rw) + ctx.Input.Reset(ctx) + ctx.Output.Reset(ctx) + ctx._xsrfToken = "" +} + +// Redirect redirects to localurl with http header status code. +func (ctx *Context) Redirect(status int, localurl string) { + http.Redirect(ctx.ResponseWriter, ctx.Request, localurl, status) +} + +// Abort stops the request. +// If beego.ErrorMaps exists, panic body. +func (ctx *Context) Abort(status int, body string) { + ctx.Output.SetStatus(status) + panic(body) +} + +// WriteString writes a string to response body. +func (ctx *Context) WriteString(content string) { + _, _ = ctx.ResponseWriter.Write([]byte(content)) +} + +// GetCookie gets a cookie from a request for a given key. +// (Alias of BeegoInput.Cookie) +func (ctx *Context) GetCookie(key string) string { + return ctx.Input.Cookie(key) +} + +// SetCookie sets a cookie for a response. +// (Alias of BeegoOutput.Cookie) +func (ctx *Context) SetCookie(name string, value string, others ...interface{}) { + ctx.Output.Cookie(name, value, others...) +} + +// GetSecureCookie gets a secure cookie from a request for a given key. +func (ctx *Context) GetSecureCookie(Secret, key string) (string, bool) { + val := ctx.Input.Cookie(key) + if val == "" { + return "", false + } + + parts := strings.SplitN(val, "|", 3) + + if len(parts) != 3 { + return "", false + } + + vs := parts[0] + timestamp := parts[1] + sig := parts[2] + + h := hmac.New(sha256.New, []byte(Secret)) + _, err := fmt.Fprintf(h, "%s%s", vs, timestamp) + if err != nil { + return "", false + } + + if fmt.Sprintf("%02x", h.Sum(nil)) != sig { + return "", false + } + res, _ := base64.URLEncoding.DecodeString(vs) + return string(res), true +} + +// SetSecureCookie sets a secure cookie for a response. +func (ctx *Context) SetSecureCookie(Secret, name, value string, others ...interface{}) { + vs := base64.URLEncoding.EncodeToString([]byte(value)) + timestamp := strconv.FormatInt(time.Now().UnixNano(), 10) + h := hmac.New(sha256.New, []byte(Secret)) + _, _ = fmt.Fprintf(h, "%s%s", vs, timestamp) + sig := fmt.Sprintf("%02x", h.Sum(nil)) + cookie := strings.Join([]string{vs, timestamp, sig}, "|") + ctx.Output.Cookie(name, cookie, others...) +} + +// XSRFToken creates and returns an xsrf token string +func (ctx *Context) XSRFToken(key string, expire int64) string { + if ctx._xsrfToken == "" { + token, ok := ctx.GetSecureCookie(key, "_xsrf") + if !ok { + token = string(utils.RandomCreateBytes(32)) + // TODO make it configurable + ctx.SetSecureCookie(key, "_xsrf", token, expire, "/", "", true, true) + } + ctx._xsrfToken = token + } + return ctx._xsrfToken +} + +// CheckXSRFCookie checks if the XSRF token in this request is valid or not. +// The token can be provided in the request header in the form "X-Xsrftoken" or "X-CsrfToken" +// or in form field value named as "_xsrf". +func (ctx *Context) CheckXSRFCookie() bool { + token := ctx.Input.Query("_xsrf") + if token == "" { + token = ctx.Request.Header.Get("X-Xsrftoken") + } + if token == "" { + token = ctx.Request.Header.Get("X-Csrftoken") + } + if token == "" { + ctx.Abort(422, "422") + return false + } + if ctx._xsrfToken != token { + ctx.Abort(417, "417") + return false + } + return true +} + +// RenderMethodResult renders the return value of a controller method to the output +func (ctx *Context) RenderMethodResult(result interface{}) { + if result != nil { + renderer, ok := result.(Renderer) + if !ok { + err, ok := result.(error) + if ok { + renderer = errorRenderer(err) + } else { + renderer = jsonRenderer(result) + } + } + renderer.Render(ctx) + } +} + +// Session return session store of this context of request +func (ctx *Context) Session() (store session.Store, err error) { + if ctx.Input != nil { + if ctx.Input.CruSession != nil { + store = ctx.Input.CruSession + return + } else { + err = errors.New(`no valid session store(please initialize session)`) + return + } + } else { + err = errors.New(`no valid input`) + return + } +} + +// Response is a wrapper for the http.ResponseWriter +// Started: if true, response was already written to so the other handler will not be executed +type Response struct { + http.ResponseWriter + Started bool + Status int + Elapsed time.Duration +} + +func (r *Response) reset(rw http.ResponseWriter) { + r.ResponseWriter = rw + r.Status = 0 + r.Started = false +} + +// Write writes the data to the connection as part of a HTTP reply, +// and sets `Started` to true. +// Started: if true, the response was already sent +func (r *Response) Write(p []byte) (int, error) { + r.Started = true + return r.ResponseWriter.Write(p) +} + +// WriteHeader sends a HTTP response header with status code, +// and sets `Started` to true. +func (r *Response) WriteHeader(code int) { + if r.Status > 0 { + // prevent multiple response.WriteHeader calls + return + } + r.Status = code + r.Started = true + r.ResponseWriter.WriteHeader(code) +} + +// Hijack hijacker for http +func (r *Response) Hijack() (net.Conn, *bufio.ReadWriter, error) { + hj, ok := r.ResponseWriter.(http.Hijacker) + if !ok { + return nil, nil, errors.New("webserver doesn't support hijacking") + } + return hj.Hijack() +} + +// Flush http.Flusher +func (r *Response) Flush() { + if f, ok := r.ResponseWriter.(http.Flusher); ok { + f.Flush() + } +} + +// CloseNotify http.CloseNotifier +func (r *Response) CloseNotify() <-chan bool { + if cn, ok := r.ResponseWriter.(http.CloseNotifier); ok { + return cn.CloseNotify() + } + return nil +} + +// Pusher http.Pusher +func (r *Response) Pusher() (pusher http.Pusher) { + if pusher, ok := r.ResponseWriter.(http.Pusher); ok { + return pusher + } + return nil +} diff --git a/src/vendor/github.com/beego/beego/v2/server/web/context/form.go b/src/vendor/github.com/beego/beego/v2/server/web/context/form.go new file mode 100644 index 000000000..07a1b0b22 --- /dev/null +++ b/src/vendor/github.com/beego/beego/v2/server/web/context/form.go @@ -0,0 +1,189 @@ +// Copyright 2020 beego +// +// 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 context + +import ( + "net/url" + "reflect" + "strconv" + "strings" + "time" +) + +var ( + sliceOfInts = reflect.TypeOf([]int(nil)) + sliceOfStrings = reflect.TypeOf([]string(nil)) +) + +// ParseForm will parse form values to struct via tag. +// Support for anonymous struct. +func parseFormToStruct(form url.Values, objT reflect.Type, objV reflect.Value) error { + for i := 0; i < objT.NumField(); i++ { + fieldV := objV.Field(i) + if !fieldV.CanSet() { + continue + } + + fieldT := objT.Field(i) + if fieldT.Anonymous && fieldT.Type.Kind() == reflect.Struct { + err := parseFormToStruct(form, fieldT.Type, fieldV) + if err != nil { + return err + } + continue + } + + tag, ok := formTagName(fieldT) + if !ok { + continue + } + + value, ok := formValue(tag, form, fieldT) + if !ok { + continue + } + + switch fieldT.Type.Kind() { + case reflect.Bool: + b, err := parseFormBoolValue(value) + if err != nil { + return err + } + fieldV.SetBool(b) + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + x, err := strconv.ParseInt(value, 10, 64) + if err != nil { + return err + } + fieldV.SetInt(x) + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: + x, err := strconv.ParseUint(value, 10, 64) + if err != nil { + return err + } + fieldV.SetUint(x) + case reflect.Float32, reflect.Float64: + x, err := strconv.ParseFloat(value, 64) + if err != nil { + return err + } + fieldV.SetFloat(x) + case reflect.Interface: + fieldV.Set(reflect.ValueOf(value)) + case reflect.String: + fieldV.SetString(value) + case reflect.Struct: + if fieldT.Type.String() == "time.Time" { + t, err := parseFormTime(value) + if err != nil { + return err + } + fieldV.Set(reflect.ValueOf(t)) + } + case reflect.Slice: + if fieldT.Type == sliceOfInts { + formVals := form[tag] + fieldV.Set(reflect.MakeSlice(reflect.SliceOf(reflect.TypeOf(int(1))), len(formVals), len(formVals))) + for i := 0; i < len(formVals); i++ { + val, err := strconv.Atoi(formVals[i]) + if err != nil { + return err + } + fieldV.Index(i).SetInt(int64(val)) + } + } else if fieldT.Type == sliceOfStrings { + formVals := form[tag] + fieldV.Set(reflect.MakeSlice(reflect.SliceOf(reflect.TypeOf("")), len(formVals), len(formVals))) + for i := 0; i < len(formVals); i++ { + fieldV.Index(i).SetString(formVals[i]) + } + } + } + } + return nil +} + +// nolint +func parseFormTime(value string) (time.Time, error) { + var pattern string + if len(value) >= 25 { + value = value[:25] + pattern = time.RFC3339 + } else if strings.HasSuffix(strings.ToUpper(value), "Z") { + pattern = time.RFC3339 + } else if len(value) >= 19 { + if strings.Contains(value, "T") { + pattern = formatDateTimeT + } else { + pattern = formatDateTime + } + value = value[:19] + } else if len(value) >= 10 { + if len(value) > 10 { + value = value[:10] + } + pattern = formatDate + } else if len(value) >= 8 { + if len(value) > 8 { + value = value[:8] + } + pattern = formatTime + } + return time.ParseInLocation(pattern, value, time.Local) +} + +func parseFormBoolValue(value string) (bool, error) { + if strings.ToLower(value) == "on" || strings.ToLower(value) == "1" || strings.ToLower(value) == "yes" { + return true, nil + } + if strings.ToLower(value) == "off" || strings.ToLower(value) == "0" || strings.ToLower(value) == "no" { + return false, nil + } + return strconv.ParseBool(value) +} + +// nolint +func formTagName(fieldT reflect.StructField) (string, bool) { + tags := strings.Split(fieldT.Tag.Get("form"), ",") + var tag string + if len(tags) == 0 || tags[0] == "" { + tag = fieldT.Name + } else if tags[0] == "-" { + return "", false + } else { + tag = tags[0] + } + return tag, true +} + +func formValue(tag string, form url.Values, fieldT reflect.StructField) (string, bool) { + formValues := form[tag] + var value string + if len(formValues) == 0 { + defaultValue := fieldT.Tag.Get("default") + if defaultValue != "" { + value = defaultValue + } else { + return "", false + } + } + if len(formValues) == 1 { + value = formValues[0] + if value == "" { + return "", false + } + } + return value, true +} diff --git a/src/vendor/github.com/beego/beego/context/input.go b/src/vendor/github.com/beego/beego/v2/server/web/context/input.go similarity index 92% rename from src/vendor/github.com/beego/beego/context/input.go rename to src/vendor/github.com/beego/beego/v2/server/web/context/input.go index 385cc85ef..dfb14dfb9 100644 --- a/src/vendor/github.com/beego/beego/context/input.go +++ b/src/vendor/github.com/beego/beego/v2/server/web/context/input.go @@ -29,7 +29,7 @@ import ( "strings" "sync" - "github.com/beego/beego/session" + "github.com/beego/beego/v2/server/web/session" ) // Regexes for checking the accept headers @@ -43,7 +43,7 @@ var ( ) // BeegoInput operates the http request header, data, cookie and body. -// it also contains router params and current session. +// Contains router params and current session. type BeegoInput struct { Context *Context CruSession session.Store @@ -56,7 +56,7 @@ type BeegoInput struct { RunController reflect.Type } -// NewInput return BeegoInput generated by Context. +// NewInput returns the BeegoInput generated by context. func NewInput() *BeegoInput { return &BeegoInput{ pnames: make([]string, 0, maxParam), @@ -65,7 +65,7 @@ func NewInput() *BeegoInput { } } -// Reset init the BeegoInput +// Reset initializes the BeegoInput func (input *BeegoInput) Reset(ctx *Context) { input.Context = ctx input.CruSession = nil @@ -77,27 +77,27 @@ func (input *BeegoInput) Reset(ctx *Context) { input.RequestBody = []byte{} } -// Protocol returns request protocol name, such as HTTP/1.1 . +// Protocol returns the request protocol name, such as HTTP/1.1 . func (input *BeegoInput) Protocol() string { return input.Context.Request.Proto } -// URI returns full request url with query string, fragment. +// URI returns the full request url with query, string and fragment. func (input *BeegoInput) URI() string { return input.Context.Request.RequestURI } -// URL returns request url path (without query string, fragment). +// URL returns the request url path (without query, string and fragment). func (input *BeegoInput) URL() string { - return input.Context.Request.URL.EscapedPath() + return input.Context.Request.URL.Path } -// Site returns base site url as scheme://domain type. +// Site returns the base site url as scheme://domain type. func (input *BeegoInput) Site() string { return input.Scheme() + "://" + input.Domain() } -// Scheme returns request scheme as "http" or "https". +// Scheme returns the request scheme as "http" or "https". func (input *BeegoInput) Scheme() string { if scheme := input.Header("X-Forwarded-Proto"); scheme != "" { return scheme @@ -111,14 +111,13 @@ func (input *BeegoInput) Scheme() string { return "https" } -// Domain returns host name. -// Alias of Host method. +// Domain returns the host name (alias of host method) func (input *BeegoInput) Domain() string { return input.Host() } -// Host returns host name. -// if no host info in request, return localhost. +// Host returns the host name. +// If no host info in request, return localhost. func (input *BeegoInput) Host() string { if input.Context.Request.Host != "" { if hostPart, _, err := net.SplitHostPort(input.Context.Request.Host); err == nil { @@ -134,7 +133,7 @@ func (input *BeegoInput) Method() string { return input.Context.Request.Method } -// Is returns boolean of this request is on given method, such as Is("POST"). +// Is returns the boolean value of this request is on given method, such as Is("POST"). func (input *BeegoInput) Is(method string) bool { return input.Method() == method } @@ -154,7 +153,7 @@ func (input *BeegoInput) IsHead() bool { return input.Is("HEAD") } -// IsOptions Is this a OPTIONS method request? +// IsOptions Is this an OPTIONS method request? func (input *BeegoInput) IsOptions() bool { return input.Is("OPTIONS") } @@ -174,7 +173,7 @@ func (input *BeegoInput) IsPatch() bool { return input.Is("PATCH") } -// IsAjax returns boolean of this request is generated by ajax. +// IsAjax returns boolean of is this request generated by ajax. func (input *BeegoInput) IsAjax() bool { return input.Header("X-Requested-With") == "XMLHttpRequest" } @@ -251,7 +250,7 @@ func (input *BeegoInput) Refer() string { } // SubDomains returns sub domain string. -// if aa.bb.domain.com, returns aa.bb . +// if aa.bb.domain.com, returns aa.bb func (input *BeegoInput) SubDomains() string { parts := strings.Split(input.Host(), ".") if len(parts) >= 3 { @@ -306,7 +305,7 @@ func (input *BeegoInput) Params() map[string]string { return m } -// SetParam will set the param with key and value +// SetParam sets the param with key and value func (input *BeegoInput) SetParam(key, val string) { // check if already exists for i, v := range input.pnames { @@ -319,9 +318,8 @@ func (input *BeegoInput) SetParam(key, val string) { input.pnames = append(input.pnames, key) } -// ResetParams clears any of the input's Params -// This function is used to clear parameters so they may be reset between filter -// passes. +// ResetParams clears any of the input's params +// Used to clear parameters so they may be reset between filter passes. func (input *BeegoInput) ResetParams() { input.pnames = input.pnames[:0] input.pvalues = input.pvalues[:0] @@ -363,7 +361,7 @@ func (input *BeegoInput) Cookie(key string) string { // Session returns current session item value by a given key. // if non-existed, return nil. func (input *BeegoInput) Session(key interface{}) interface{} { - return input.CruSession.Get(key) + return input.CruSession.Get(nil, key) } // CopyBody returns the raw request body data as bytes. @@ -391,7 +389,7 @@ func (input *BeegoInput) CopyBody(MaxMemory int64) []byte { return requestbody } -// Data return the implicit data in the input +// Data returns the implicit data in the input func (input *BeegoInput) Data() map[interface{}]interface{} { input.dataLock.Lock() defer input.dataLock.Unlock() @@ -412,7 +410,7 @@ func (input *BeegoInput) GetData(key interface{}) interface{} { } // SetData stores data with given key in this context. -// This data are only available in this context. +// This data is only available in this context. func (input *BeegoInput) SetData(key, val interface{}) { input.dataLock.Lock() defer input.dataLock.Unlock() @@ -422,10 +420,10 @@ func (input *BeegoInput) SetData(key, val interface{}) { input.data[key] = val } -// ParseFormOrMulitForm parseForm or parseMultiForm based on Content-type -func (input *BeegoInput) ParseFormOrMulitForm(maxMemory int64) error { +// ParseFormOrMultiForm parseForm or parseMultiForm based on Content-type +func (input *BeegoInput) ParseFormOrMultiForm(maxMemory int64) error { // Parse the body depending on the content type. - if strings.Contains(input.Header("Content-Type"), "multipart/form-data") { + if input.IsUpload() { if err := input.Context.Request.ParseMultipartForm(maxMemory); err != nil { return errors.New("Error parsing request body:" + err.Error()) } diff --git a/src/vendor/github.com/beego/beego/context/output.go b/src/vendor/github.com/beego/beego/v2/server/web/context/output.go similarity index 81% rename from src/vendor/github.com/beego/beego/context/output.go rename to src/vendor/github.com/beego/beego/v2/server/web/context/output.go index 41b95d968..f52eac9d8 100644 --- a/src/vendor/github.com/beego/beego/context/output.go +++ b/src/vendor/github.com/beego/beego/v2/server/web/context/output.go @@ -31,7 +31,8 @@ import ( "strings" "time" - yaml "gopkg.in/yaml.v2" + "google.golang.org/protobuf/proto" + "gopkg.in/yaml.v2" ) // BeegoOutput does work for sending response header. @@ -42,12 +43,12 @@ type BeegoOutput struct { } // NewOutput returns new BeegoOutput. -// it contains nothing now. +// Empty when initialized func NewOutput() *BeegoOutput { return &BeegoOutput{} } -// Reset init BeegoOutput +// Reset initializes BeegoOutput func (output *BeegoOutput) Reset(ctx *Context) { output.Context = ctx output.Status = 0 @@ -58,12 +59,12 @@ func (output *BeegoOutput) Header(key, val string) { output.Context.ResponseWriter.Header().Set(key, val) } -// Body sets response body content. -// if EnableGzip, compress content string. -// it sends out response body directly. +// Body sets the response body content. +// if EnableGzip, content is compressed. +// Sends out response body directly. func (output *BeegoOutput) Body(content []byte) error { var encoding string - var buf = &bytes.Buffer{} + buf := &bytes.Buffer{} if output.EnableGzip { encoding = ParseEncoding(output.Context.Request) } @@ -85,13 +86,13 @@ func (output *BeegoOutput) Body(content []byte) error { return nil } -// Cookie sets cookie value via given key. -// others are ordered as cookie's max age time, path,domain, secure and httponly. +// Cookie sets a cookie value via given key. +// others: used to set a cookie's max age time, path,domain, secure and httponly. func (output *BeegoOutput) Cookie(name string, value string, others ...interface{}) { var b bytes.Buffer fmt.Fprintf(&b, "%s=%s", sanitizeName(name), sanitizeValue(value)) - //fix cookie not work in IE + // fix cookie not work in IE if len(others) > 0 { var maxAge int64 @@ -154,6 +155,14 @@ func (output *BeegoOutput) Cookie(name string, value string, others ...interface fmt.Fprintf(&b, "; HttpOnly") } } + + // default empty + if len(others) > 5 { + if v, ok := others[5].(string); ok && len(v) > 0 { + fmt.Fprintf(&b, "; SameSite=%s", sanitizeValue(v)) + } + } + output.Context.ResponseWriter.Header().Add("Set-Cookie", b.String()) } @@ -182,7 +191,7 @@ func errorRenderer(err error) Renderer { }) } -// JSON writes json to response body. +// JSON writes json to the response body. // if encoding is true, it converts utf-8 to \u0000 type. func (output *BeegoOutput) JSON(data interface{}, hasIndent bool, encoding bool) error { output.Header("Content-Type", "application/json; charset=utf-8") @@ -203,7 +212,7 @@ func (output *BeegoOutput) JSON(data interface{}, hasIndent bool, encoding bool) return output.Body(content) } -// YAML writes yaml to response body. +// YAML writes yaml to the response body. func (output *BeegoOutput) YAML(data interface{}) error { output.Header("Content-Type", "application/x-yaml; charset=utf-8") var content []byte @@ -216,7 +225,20 @@ func (output *BeegoOutput) YAML(data interface{}) error { return output.Body(content) } -// JSONP writes jsonp to response body. +// Proto writes protobuf to the response body. +func (output *BeegoOutput) Proto(data proto.Message) error { + output.Header("Content-Type", "application/x-protobuf; charset=utf-8") + var content []byte + var err error + content, err = proto.Marshal(data) + if err != nil { + http.Error(output.Context.ResponseWriter, err.Error(), http.StatusInternalServerError) + return err + } + return output.Body(content) +} + +// JSONP writes jsonp to the response body. func (output *BeegoOutput) JSONP(data interface{}, hasIndent bool) error { output.Header("Content-Type", "application/javascript; charset=utf-8") var content []byte @@ -242,7 +264,7 @@ func (output *BeegoOutput) JSONP(data interface{}, hasIndent bool) error { return output.Body(callbackContent.Bytes()) } -// XML writes xml string to response body. +// XML writes xml string to the response body. func (output *BeegoOutput) XML(data interface{}, hasIndent bool) error { output.Header("Content-Type", "application/xml; charset=utf-8") var content []byte @@ -259,21 +281,21 @@ func (output *BeegoOutput) XML(data interface{}, hasIndent bool) error { return output.Body(content) } -// ServeFormatted serve YAML, XML OR JSON, depending on the value of the Accept header -func (output *BeegoOutput) ServeFormatted(data interface{}, hasIndent bool, hasEncode ...bool) { +// ServeFormatted serves YAML, XML or JSON, depending on the value of the Accept header +func (output *BeegoOutput) ServeFormatted(data interface{}, hasIndent bool, hasEncode ...bool) error { accept := output.Context.Input.Header("Accept") switch accept { case ApplicationYAML: - output.YAML(data) + return output.YAML(data) case ApplicationXML, TextXML: - output.XML(data, hasIndent) + return output.XML(data, hasIndent) default: - output.JSON(data, hasIndent, len(hasEncode) > 0 && hasEncode[0]) + return output.JSON(data, hasIndent, len(hasEncode) > 0 && hasEncode[0]) } } // Download forces response for download file. -// it prepares the download response header automatically. +// Prepares the download response header automatically. func (output *BeegoOutput) Download(file string, filename ...string) { // check get file error, file not found or other error. if _, err := os.Stat(file); err != nil { @@ -287,7 +309,7 @@ func (output *BeegoOutput) Download(file string, filename ...string) { } else { fName = filepath.Base(file) } - //https://tools.ietf.org/html/rfc6266#section-4.3 + // https://tools.ietf.org/html/rfc6266#section-4.3 fn := url.PathEscape(fName) if fName == fn { fn = "filename=" + fn @@ -322,61 +344,61 @@ func (output *BeegoOutput) ContentType(ext string) { } } -// SetStatus sets response status code. -// It writes response header directly. +// SetStatus sets the response status code. +// Writes response header directly. func (output *BeegoOutput) SetStatus(status int) { output.Status = status } -// IsCachable returns boolean of this request is cached. +// IsCachable returns boolean of if this request is cached. // HTTP 304 means cached. func (output *BeegoOutput) IsCachable() bool { return output.Status >= 200 && output.Status < 300 || output.Status == 304 } -// IsEmpty returns boolean of this request is empty. +// IsEmpty returns boolean of if this request is empty. // HTTP 201,204 and 304 means empty. func (output *BeegoOutput) IsEmpty() bool { return output.Status == 201 || output.Status == 204 || output.Status == 304 } -// IsOk returns boolean of this request runs well. +// IsOk returns boolean of if this request was ok. // HTTP 200 means ok. func (output *BeegoOutput) IsOk() bool { return output.Status == 200 } -// IsSuccessful returns boolean of this request runs successfully. +// IsSuccessful returns boolean of this request was successful. // HTTP 2xx means ok. func (output *BeegoOutput) IsSuccessful() bool { return output.Status >= 200 && output.Status < 300 } -// IsRedirect returns boolean of this request is redirection header. +// IsRedirect returns boolean of if this request is redirected. // HTTP 301,302,307 means redirection. func (output *BeegoOutput) IsRedirect() bool { return output.Status == 301 || output.Status == 302 || output.Status == 303 || output.Status == 307 } -// IsForbidden returns boolean of this request is forbidden. +// IsForbidden returns boolean of if this request is forbidden. // HTTP 403 means forbidden. func (output *BeegoOutput) IsForbidden() bool { return output.Status == 403 } -// IsNotFound returns boolean of this request is not found. +// IsNotFound returns boolean of if this request is not found. // HTTP 404 means not found. func (output *BeegoOutput) IsNotFound() bool { return output.Status == 404 } -// IsClientError returns boolean of this request client sends error data. +// IsClientError returns boolean of if this request client sends error data. // HTTP 4xx means client error. func (output *BeegoOutput) IsClientError() bool { return output.Status >= 400 && output.Status < 500 } -// IsServerError returns boolean of this server handler errors. +// IsServerError returns boolean of if this server handler errors. // HTTP 5xx means server internal error. func (output *BeegoOutput) IsServerError() bool { return output.Status >= 500 && output.Status < 600 @@ -403,5 +425,5 @@ func stringsToJSON(str string) string { // Session sets session item value with given key. func (output *BeegoOutput) Session(name interface{}, value interface{}) { - output.Context.Input.CruSession.Set(name, value) + output.Context.Input.CruSession.Set(nil, name, value) } diff --git a/src/vendor/github.com/beego/beego/context/param/conv.go b/src/vendor/github.com/beego/beego/v2/server/web/context/param/conv.go similarity index 95% rename from src/vendor/github.com/beego/beego/context/param/conv.go rename to src/vendor/github.com/beego/beego/v2/server/web/context/param/conv.go index c12420425..eecb6acbd 100644 --- a/src/vendor/github.com/beego/beego/context/param/conv.go +++ b/src/vendor/github.com/beego/beego/v2/server/web/context/param/conv.go @@ -4,8 +4,8 @@ import ( "fmt" "reflect" - beecontext "github.com/beego/beego/context" - "github.com/beego/beego/logs" + "github.com/beego/beego/v2/core/logs" + beecontext "github.com/beego/beego/v2/server/web/context" ) // ConvertParams converts http method params to values that will be passed to the method controller as arguments diff --git a/src/vendor/github.com/beego/beego/context/param/methodparams.go b/src/vendor/github.com/beego/beego/v2/server/web/context/param/methodparams.go similarity index 85% rename from src/vendor/github.com/beego/beego/context/param/methodparams.go rename to src/vendor/github.com/beego/beego/v2/server/web/context/param/methodparams.go index cd6708a27..22ff0e436 100644 --- a/src/vendor/github.com/beego/beego/context/param/methodparams.go +++ b/src/vendor/github.com/beego/beego/v2/server/web/context/param/methodparams.go @@ -5,7 +5,7 @@ import ( "strings" ) -//MethodParam keeps param information to be auto passed to controller methods +// MethodParam keeps param information to be auto passed to controller methods type MethodParam struct { name string in paramType @@ -22,7 +22,7 @@ const ( header ) -//New creates a new MethodParam with name and specific options +// New creates a new MethodParam with name and specific options func New(name string, opts ...MethodParamOption) *MethodParam { return newParam(name, nil, opts) } @@ -35,7 +35,7 @@ func newParam(name string, parser paramParser, opts []MethodParamOption) (param return } -//Make creates an array of MethodParmas or an empty array +// Make creates an array of MethodParmas or an empty array func Make(list ...*MethodParam) []*MethodParam { if len(list) > 0 { return list diff --git a/src/vendor/github.com/beego/beego/context/param/options.go b/src/vendor/github.com/beego/beego/v2/server/web/context/param/options.go similarity index 100% rename from src/vendor/github.com/beego/beego/context/param/options.go rename to src/vendor/github.com/beego/beego/v2/server/web/context/param/options.go diff --git a/src/vendor/github.com/beego/beego/context/param/parsers.go b/src/vendor/github.com/beego/beego/v2/server/web/context/param/parsers.go similarity index 93% rename from src/vendor/github.com/beego/beego/context/param/parsers.go rename to src/vendor/github.com/beego/beego/v2/server/web/context/param/parsers.go index 421aecf08..99ad9275e 100644 --- a/src/vendor/github.com/beego/beego/context/param/parsers.go +++ b/src/vendor/github.com/beego/beego/v2/server/web/context/param/parsers.go @@ -18,7 +18,7 @@ func getParser(param *MethodParam, t reflect.Type) paramParser { reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: return intParser{} case reflect.Slice: - if t.Elem().Kind() == reflect.Uint8 { //treat []byte as string + if t.Elem().Kind() == reflect.Uint8 { // treat []byte as string return stringParser{} } if param.in == body { @@ -55,29 +55,25 @@ func (f parserFunc) parse(value string, toType reflect.Type) (interface{}, error return f(value, toType) } -type boolParser struct { -} +type boolParser struct{} func (p boolParser) parse(value string, toType reflect.Type) (interface{}, error) { return strconv.ParseBool(value) } -type stringParser struct { -} +type stringParser struct{} func (p stringParser) parse(value string, toType reflect.Type) (interface{}, error) { return value, nil } -type intParser struct { -} +type intParser struct{} func (p intParser) parse(value string, toType reflect.Type) (interface{}, error) { return strconv.Atoi(value) } -type floatParser struct { -} +type floatParser struct{} func (p floatParser) parse(value string, toType reflect.Type) (interface{}, error) { if toType.Kind() == reflect.Float32 { @@ -90,8 +86,7 @@ func (p floatParser) parse(value string, toType reflect.Type) (interface{}, erro return strconv.ParseFloat(value, 64) } -type timeParser struct { -} +type timeParser struct{} func (p timeParser) parse(value string, toType reflect.Type) (result interface{}, err error) { result, err = time.Parse(time.RFC3339, value) @@ -101,8 +96,7 @@ func (p timeParser) parse(value string, toType reflect.Type) (result interface{} return } -type jsonParser struct { -} +type jsonParser struct{} func (p jsonParser) parse(value string, toType reflect.Type) (interface{}, error) { pResult := reflect.New(toType) diff --git a/src/vendor/github.com/beego/beego/context/renderer.go b/src/vendor/github.com/beego/beego/v2/server/web/context/renderer.go similarity index 77% rename from src/vendor/github.com/beego/beego/context/renderer.go rename to src/vendor/github.com/beego/beego/v2/server/web/context/renderer.go index 36a7cb53f..5a0783324 100644 --- a/src/vendor/github.com/beego/beego/context/renderer.go +++ b/src/vendor/github.com/beego/beego/v2/server/web/context/renderer.go @@ -1,6 +1,6 @@ package context -// Renderer defines an http response renderer +// Renderer defines a http response renderer type Renderer interface { Render(ctx *Context) } diff --git a/src/vendor/github.com/beego/beego/context/response.go b/src/vendor/github.com/beego/beego/v2/server/web/context/response.go similarity index 66% rename from src/vendor/github.com/beego/beego/context/response.go rename to src/vendor/github.com/beego/beego/v2/server/web/context/response.go index 9c3c715a2..86b2c0b88 100644 --- a/src/vendor/github.com/beego/beego/context/response.go +++ b/src/vendor/github.com/beego/beego/v2/server/web/context/response.go @@ -1,27 +1,26 @@ package context import ( - "strconv" - "net/http" + "strconv" ) const ( - //BadRequest indicates http error 400 + // BadRequest indicates HTTP error 400 BadRequest StatusCode = http.StatusBadRequest - //NotFound indicates http error 404 + // NotFound indicates HTTP error 404 NotFound StatusCode = http.StatusNotFound ) -// StatusCode sets the http response status code +// StatusCode sets the HTTP response status code type StatusCode int func (s StatusCode) Error() string { return strconv.Itoa(int(s)) } -// Render sets the http status code +// Render sets the HTTP status code func (s StatusCode) Render(ctx *Context) { ctx.Output.SetStatus(int(s)) } diff --git a/src/vendor/github.com/beego/beego/controller.go b/src/vendor/github.com/beego/beego/v2/server/web/controller.go similarity index 83% rename from src/vendor/github.com/beego/beego/controller.go rename to src/vendor/github.com/beego/beego/v2/server/web/controller.go index 27e6f133b..6bf061dda 100644 --- a/src/vendor/github.com/beego/beego/controller.go +++ b/src/vendor/github.com/beego/beego/v2/server/web/controller.go @@ -12,10 +12,11 @@ // See the License for the specific language governing permissions and // limitations under the License. -package beego +package web import ( "bytes" + context2 "context" "errors" "fmt" "html/template" @@ -27,10 +28,13 @@ import ( "reflect" "strconv" "strings" + "sync" - "github.com/beego/beego/context" - "github.com/beego/beego/context/param" - "github.com/beego/beego/session" + "google.golang.org/protobuf/proto" + + "github.com/beego/beego/v2/server/web/context" + "github.com/beego/beego/v2/server/web/context/param" + "github.com/beego/beego/v2/server/web/session" ) var ( @@ -38,8 +42,21 @@ var ( ErrAbort = errors.New("user stop run") // GlobalControllerRouter store comments with controller. pkgpath+controller:comments GlobalControllerRouter = make(map[string][]ControllerComments) + copyBufferPool sync.Pool ) +const ( + bytePerKb = 1024 + copyBufferKb = 32 + filePerm = 0o666 +) + +func init() { + copyBufferPool.New = func() interface{} { + return make([]byte, bytePerKb*copyBufferKb) + } +} + // ControllerFilter store the filter for controller type ControllerFilter struct { Pattern string @@ -106,9 +123,9 @@ type Controller struct { EnableRender bool // xsrf data + EnableXSRF bool _xsrfToken string XSRFExpire int - EnableXSRF bool // session CruSession session.Store @@ -224,6 +241,37 @@ func (c *Controller) HandlerFunc(fnname string) bool { // URLMapping register the internal Controller router. func (c *Controller) URLMapping() {} +// Bind if the content type is form, we read data from form +// otherwise, read data from request body +func (c *Controller) Bind(obj interface{}) error { + return c.Ctx.Bind(obj) +} + +// BindYAML only read data from http request body +func (c *Controller) BindYAML(obj interface{}) error { + return c.Ctx.BindYAML(obj) +} + +// BindForm read data from form +func (c *Controller) BindForm(obj interface{}) error { + return c.Ctx.BindForm(obj) +} + +// BindJSON only read data from http request body +func (c *Controller) BindJSON(obj interface{}) error { + return c.Ctx.BindJSON(obj) +} + +// BindProtobuf only read data from http request body +func (c *Controller) BindProtobuf(obj proto.Message) error { + return c.Ctx.BindProtobuf(obj) +} + +// BindXML only read data from http request body +func (c *Controller) BindXML(obj interface{}) error { + return c.Ctx.BindXML(obj) +} + // Mapping the method to function func (c *Controller) Mapping(method string, fn func()) { c.methodMapping[method] = fn @@ -249,6 +297,9 @@ func (c *Controller) Render() error { // RenderString returns the rendered template string. Do not send out response. func (c *Controller) RenderString() (string, error) { b, e := c.RenderBytes() + if e != nil { + return "", e + } return string(b), e } @@ -275,7 +326,7 @@ func (c *Controller) RenderBytes() ([]byte, error) { } buf.Reset() - ExecuteViewPathTemplate(&buf, c.Layout, c.viewPath(), c.Data) + err = ExecuteViewPathTemplate(&buf, c.Layout, c.viewPath(), c.Data) } return buf.Bytes(), err } @@ -343,9 +394,9 @@ func (c *Controller) Abort(code string) { // CustomAbort stops controller handler and show the error data, it's similar Aborts, but support status code and body. func (c *Controller) CustomAbort(status int, body string) { + c.Ctx.Output.Status = status // first panic from ErrorMaps, it is user defined error functions. if _, ok := ErrorMaps[body]; ok { - c.Ctx.Output.Status = status panic(body) } // last panic user string @@ -371,51 +422,74 @@ func (c *Controller) URLFor(endpoint string, values ...interface{}) string { return URLFor(endpoint, values...) } +func (c *Controller) JSONResp(data interface{}) error { + return c.Ctx.JSONResp(data) +} + +func (c *Controller) XMLResp(data interface{}) error { + return c.Ctx.XMLResp(data) +} + +func (c *Controller) YamlResp(data interface{}) error { + return c.Ctx.YamlResp(data) +} + +// Resp sends response based on the Accept Header +// By default response will be in JSON +// it's different from ServeXXX methods +// because we don't store the data to Data field +func (c *Controller) Resp(data interface{}) error { + return c.Ctx.Resp(data) +} + // ServeJSON sends a json response with encoding charset. -func (c *Controller) ServeJSON(encoding ...bool) { +func (c *Controller) ServeJSON(encoding ...bool) error { var ( hasIndent = BConfig.RunMode != PROD hasEncoding = len(encoding) > 0 && encoding[0] ) - c.Ctx.Output.JSON(c.Data["json"], hasIndent, hasEncoding) + return c.Ctx.Output.JSON(c.Data["json"], hasIndent, hasEncoding) } // ServeJSONP sends a jsonp response. -func (c *Controller) ServeJSONP() { +func (c *Controller) ServeJSONP() error { hasIndent := BConfig.RunMode != PROD - c.Ctx.Output.JSONP(c.Data["jsonp"], hasIndent) + return c.Ctx.Output.JSONP(c.Data["jsonp"], hasIndent) } // ServeXML sends xml response. -func (c *Controller) ServeXML() { +func (c *Controller) ServeXML() error { hasIndent := BConfig.RunMode != PROD - c.Ctx.Output.XML(c.Data["xml"], hasIndent) + return c.Ctx.Output.XML(c.Data["xml"], hasIndent) } // ServeYAML sends yaml response. -func (c *Controller) ServeYAML() { - c.Ctx.Output.YAML(c.Data["yaml"]) +func (c *Controller) ServeYAML() error { + return c.Ctx.Output.YAML(c.Data["yaml"]) } // ServeFormatted serve YAML, XML OR JSON, depending on the value of the Accept header -func (c *Controller) ServeFormatted(encoding ...bool) { +func (c *Controller) ServeFormatted(encoding ...bool) error { hasIndent := BConfig.RunMode != PROD hasEncoding := len(encoding) > 0 && encoding[0] - c.Ctx.Output.ServeFormatted(c.Data, hasIndent, hasEncoding) + return c.Ctx.Output.ServeFormatted(c.Data, hasIndent, hasEncoding) } // Input returns the input data map from POST or PUT request body and query string. -func (c *Controller) Input() url.Values { +func (c *Controller) Input() (url.Values, error) { if c.Ctx.Request.Form == nil { - c.Ctx.Request.ParseForm() + err := c.Ctx.Request.ParseForm() + if err != nil { + return nil, err + } } - return c.Ctx.Request.Form + return c.Ctx.Request.Form, nil } // ParseForm maps input data map to obj struct. func (c *Controller) ParseForm(obj interface{}) error { - return ParseForm(c.Input(), obj) + return c.Ctx.BindForm(obj) } // GetString returns the input value by key string or the default value while it's present and input is blank @@ -437,7 +511,7 @@ func (c *Controller) GetStrings(key string, def ...[]string) []string { defv = def[0] } - if f := c.Input(); f == nil { + if f, err := c.Input(); f == nil || err != nil { return defv } else if vs := f[key]; len(vs) > 0 { return vs @@ -593,19 +667,31 @@ func (c *Controller) GetFiles(key string) ([]*multipart.FileHeader, error) { // SaveToFile saves uploaded file to new path. // it only operates the first one of mutil-upload form file field. -func (c *Controller) SaveToFile(fromfile, tofile string) error { - file, _, err := c.Ctx.Request.FormFile(fromfile) +func (c *Controller) SaveToFile(fromFile, toFile string) error { + buf := copyBufferPool.Get().([]byte) + defer copyBufferPool.Put(buf) + return c.SaveToFileWithBuffer(fromFile, toFile, buf) +} + +type onlyWriter struct { + io.Writer +} + +func (c *Controller) SaveToFileWithBuffer(fromFile string, toFile string, buf []byte) error { + src, _, err := c.Ctx.Request.FormFile(fromFile) if err != nil { return err } - defer file.Close() - f, err := os.OpenFile(tofile, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0666) + defer src.Close() + + dst, err := os.OpenFile(toFile, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, filePerm) if err != nil { return err } - defer f.Close() - io.Copy(f, file) - return nil + defer dst.Close() + + _, err = io.CopyBuffer(onlyWriter{dst}, src, buf) + return err } // StartSession starts session and load old session data info this controller. @@ -617,11 +703,11 @@ func (c *Controller) StartSession() session.Store { } // SetSession puts value into session. -func (c *Controller) SetSession(name interface{}, value interface{}) { +func (c *Controller) SetSession(name interface{}, value interface{}) error { if c.CruSession == nil { c.StartSession() } - c.CruSession.Set(name, value) + return c.CruSession.Set(context2.Background(), name, value) } // GetSession gets value from session. @@ -629,33 +715,38 @@ func (c *Controller) GetSession(name interface{}) interface{} { if c.CruSession == nil { c.StartSession() } - return c.CruSession.Get(name) + return c.CruSession.Get(context2.Background(), name) } // DelSession removes value from session. -func (c *Controller) DelSession(name interface{}) { +func (c *Controller) DelSession(name interface{}) error { if c.CruSession == nil { c.StartSession() } - c.CruSession.Delete(name) + return c.CruSession.Delete(context2.Background(), name) } // SessionRegenerateID regenerates session id for this session. // the session data have no changes. -func (c *Controller) SessionRegenerateID() (err error) { +func (c *Controller) SessionRegenerateID() error { if c.CruSession != nil { - c.CruSession.SessionRelease(c.Ctx.ResponseWriter) + c.CruSession.SessionRelease(context2.Background(), c.Ctx.ResponseWriter) } + var err error c.CruSession, err = GlobalSessions.SessionRegenerateID(c.Ctx.ResponseWriter, c.Ctx.Request) c.Ctx.Input.CruSession = c.CruSession - return + return err } // DestroySession cleans session data and session cookie. -func (c *Controller) DestroySession() { - c.Ctx.Input.CruSession.Flush() +func (c *Controller) DestroySession() error { + err := c.Ctx.Input.CruSession.Flush(nil) + if err != nil { + return err + } c.Ctx.Input.CruSession = nil GlobalSessions.SessionDestroy(c.Ctx.ResponseWriter, c.Ctx.Request) + return nil } // IsAjax returns this request is ajax or not. diff --git a/src/vendor/github.com/beego/beego/doc.go b/src/vendor/github.com/beego/beego/v2/server/web/doc.go similarity index 82% rename from src/vendor/github.com/beego/beego/doc.go rename to src/vendor/github.com/beego/beego/v2/server/web/doc.go index cbbd2711c..0bde987e7 100644 --- a/src/vendor/github.com/beego/beego/doc.go +++ b/src/vendor/github.com/beego/beego/v2/server/web/doc.go @@ -6,12 +6,12 @@ It is used for rapid development of RESTful APIs, web apps and backend services beego is inspired by Tornado, Sinatra and Flask with the added benefit of some Go-specific features such as interfaces and struct embedding. package main - import "github.com/beego/beego" + import "github.com/beego/beego/v2" func main() { beego.Run() } -more information: http://beego.me +more information: http://beego.vip */ -package beego +package web diff --git a/src/vendor/github.com/beego/beego/error.go b/src/vendor/github.com/beego/beego/v2/server/web/error.go similarity index 95% rename from src/vendor/github.com/beego/beego/error.go rename to src/vendor/github.com/beego/beego/v2/server/web/error.go index 23e044d2e..118585dc4 100644 --- a/src/vendor/github.com/beego/beego/error.go +++ b/src/vendor/github.com/beego/beego/v2/server/web/error.go @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -package beego +package web import ( "fmt" @@ -23,8 +23,9 @@ import ( "strconv" "strings" - "github.com/beego/beego/context" - "github.com/beego/beego/utils" + "github.com/beego/beego/v2" + "github.com/beego/beego/v2/core/utils" + "github.com/beego/beego/v2/server/web/context" ) const ( @@ -90,7 +91,7 @@ func showErr(err interface{}, ctx *context.Context, stack string) { "RequestURL": ctx.Input.URI(), "RemoteAddr": ctx.Input.IP(), "Stack": stack, - "BeegoVersion": VERSION, + "BeegoVersion": beego.VERSION, "GoVersion": runtime.Version(), } t.Execute(ctx.ResponseWriter, data) @@ -377,7 +378,7 @@ func responseError(rw http.ResponseWriter, r *http.Request, errCode int, errCont t, _ := template.New("beegoerrortemp").Parse(errtpl) data := M{ "Title": http.StatusText(errCode), - "BeegoVersion": VERSION, + "BeegoVersion": beego.VERSION, "Content": template.HTML(errContent), } t.Execute(rw, data) @@ -387,7 +388,7 @@ func responseError(rw http.ResponseWriter, r *http.Request, errCode int, errCont // usage: // beego.ErrorHandler("404",NotFound) // beego.ErrorHandler("500",InternalServerError) -func ErrorHandler(code string, h http.HandlerFunc) *App { +func ErrorHandler(code string, h http.HandlerFunc) *HttpServer { ErrorMaps[code] = &errorInfo{ errorType: errorTypeHandler, handler: h, @@ -399,7 +400,7 @@ func ErrorHandler(code string, h http.HandlerFunc) *App { // ErrorController registers ControllerInterface to each http err code string. // usage: // beego.ErrorController(&controllers.ErrorController{}) -func ErrorController(c ControllerInterface) *App { +func ErrorController(c ControllerInterface) *HttpServer { reflectVal := reflect.ValueOf(c) rt := reflectVal.Type() ct := reflect.Indirect(reflectVal).Type() @@ -442,13 +443,13 @@ func exception(errCode string, ctx *context.Context) { return } } - //if 50x error has been removed from errorMap + // if 50x error has been removed from errorMap ctx.ResponseWriter.WriteHeader(atoi(errCode)) ctx.WriteString(errCode) } func executeError(err *errorInfo, ctx *context.Context, code int) { - //make sure to log the error in the access log + // make sure to log the error in the access log LogAccess(ctx, nil, code) if err.errorType == errorTypeHandler { @@ -458,16 +459,16 @@ func executeError(err *errorInfo, ctx *context.Context, code int) { } if err.errorType == errorTypeController { ctx.Output.SetStatus(code) - //Invoke the request handler + // Invoke the request handler vc := reflect.New(err.controllerType) execController, ok := vc.Interface().(ControllerInterface) if !ok { panic("controller is not ControllerInterface") } - //call the controller init function + // call the controller init function execController.Init(ctx, err.controllerType.Name(), err.method, vc.Interface()) - //call prepare function + // call prepare function execController.Prepare() execController.URLMapping() @@ -475,7 +476,7 @@ func executeError(err *errorInfo, ctx *context.Context, code int) { method := vc.MethodByName(err.method) method.Call([]reflect.Value{}) - //render template + // render template if BConfig.WebConfig.AutoRender { if err := execController.Render(); err != nil { panic(err) diff --git a/src/vendor/github.com/beego/beego/v2/server/web/filter.go b/src/vendor/github.com/beego/beego/v2/server/web/filter.go new file mode 100644 index 000000000..2237703d5 --- /dev/null +++ b/src/vendor/github.com/beego/beego/v2/server/web/filter.go @@ -0,0 +1,136 @@ +// Copyright 2014 beego Author. All Rights Reserved. +// +// 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 web + +import ( + "strings" + + "github.com/beego/beego/v2/server/web/context" +) + +// FilterChain is different from pure FilterFunc +// when you use this, you must invoke next(ctx) inside the FilterFunc which is returned +// And all those FilterChain will be invoked before other FilterFunc +type FilterChain func(next FilterFunc) FilterFunc + +// FilterFunc defines a filter function which is invoked before the controller handler is executed. +// It's a alias of HandleFunc +// In fact, the HandleFunc is the last Filter. This is the truth +type FilterFunc = HandleFunc + +// FilterRouter defines a filter operation which is invoked before the controller handler is executed. +// It can match the URL against a pattern, and execute a filter function +// when a request with a matching URL arrives. +type FilterRouter struct { + filterFunc FilterFunc + next *FilterRouter + tree *Tree + pattern string + returnOnOutput bool + resetParams bool +} + +// params is for: +// 1. setting the returnOnOutput value (false allows multiple filters to execute) +// 2. determining whether or not params need to be reset. +func newFilterRouter(pattern string, filter FilterFunc, opts ...FilterOpt) *FilterRouter { + mr := &FilterRouter{ + tree: NewTree(), + pattern: pattern, + filterFunc: filter, + } + + fos := &filterOpts{ + returnOnOutput: true, + } + + for _, o := range opts { + o(fos) + } + + if !fos.routerCaseSensitive { + mr.pattern = strings.ToLower(pattern) + } + + mr.returnOnOutput = fos.returnOnOutput + mr.resetParams = fos.resetParams + mr.tree.AddRouter(pattern, true) + return mr +} + +// filter will check whether we need to execute the filter logic +// return (started, done) +func (f *FilterRouter) filter(ctx *context.Context, urlPath string, preFilterParams map[string]string) (bool, bool) { + if f.returnOnOutput && ctx.ResponseWriter.Started { + return true, true + } + if f.resetParams { + preFilterParams = ctx.Input.Params() + } + if ok := f.ValidRouter(urlPath, ctx); ok { + f.filterFunc(ctx) + if f.resetParams { + ctx.Input.ResetParams() + for k, v := range preFilterParams { + ctx.Input.SetParam(k, v) + } + } + } else if f.next != nil { + return f.next.filter(ctx, urlPath, preFilterParams) + } + if f.returnOnOutput && ctx.ResponseWriter.Started { + return true, true + } + return false, false +} + +// ValidRouter checks if the current request is matched by this filter. +// If the request is matched, the values of the URL parameters defined +// by the filter pattern are also returned. +func (f *FilterRouter) ValidRouter(url string, ctx *context.Context) bool { + isOk := f.tree.Match(url, ctx) + if isOk != nil { + if b, ok := isOk.(bool); ok { + return b + } + } + return false +} + +type filterOpts struct { + returnOnOutput bool + resetParams bool + routerCaseSensitive bool +} + +type FilterOpt func(opts *filterOpts) + +func WithReturnOnOutput(ret bool) FilterOpt { + return func(opts *filterOpts) { + opts.returnOnOutput = ret + } +} + +func WithResetParams(reset bool) FilterOpt { + return func(opts *filterOpts) { + opts.resetParams = reset + } +} + +func WithCaseSensitive(sensitive bool) FilterOpt { + return func(opts *filterOpts) { + opts.routerCaseSensitive = sensitive + } +} diff --git a/src/vendor/github.com/beego/beego/flash.go b/src/vendor/github.com/beego/beego/v2/server/web/flash.go similarity index 98% rename from src/vendor/github.com/beego/beego/flash.go rename to src/vendor/github.com/beego/beego/v2/server/web/flash.go index a6485a17e..4b6567ac0 100644 --- a/src/vendor/github.com/beego/beego/flash.go +++ b/src/vendor/github.com/beego/beego/v2/server/web/flash.go @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -package beego +package web import ( "fmt" @@ -102,7 +102,7 @@ func ReadFromRequest(c *Controller) *FlashData { } } } - //read one time then delete it + // read one time then delete it c.Ctx.SetCookie(BConfig.WebConfig.FlashName, "", -1, "/") } c.Data["flash"] = flash.Data diff --git a/src/vendor/github.com/beego/beego/fs.go b/src/vendor/github.com/beego/beego/v2/server/web/fs.go similarity index 97% rename from src/vendor/github.com/beego/beego/fs.go rename to src/vendor/github.com/beego/beego/v2/server/web/fs.go index 41cc6f6e0..4d65630a3 100644 --- a/src/vendor/github.com/beego/beego/fs.go +++ b/src/vendor/github.com/beego/beego/v2/server/web/fs.go @@ -1,4 +1,4 @@ -package beego +package web import ( "net/http" @@ -6,8 +6,7 @@ import ( "path/filepath" ) -type FileSystem struct { -} +type FileSystem struct{} func (d FileSystem) Open(name string) (http.File, error) { return os.Open(name) @@ -17,7 +16,6 @@ func (d FileSystem) Open(name string) (http.File, error) { // directory in the tree, including root. All errors that arise visiting files // and directories are filtered by walkFn. func Walk(fs http.FileSystem, root string, walkFn filepath.WalkFunc) error { - f, err := fs.Open(root) if err != nil { return err diff --git a/src/vendor/github.com/beego/beego/grace/grace.go b/src/vendor/github.com/beego/beego/v2/server/web/grace/grace.go similarity index 89% rename from src/vendor/github.com/beego/beego/grace/grace.go rename to src/vendor/github.com/beego/beego/v2/server/web/grace/grace.go index 3e396ea88..bad7e61db 100644 --- a/src/vendor/github.com/beego/beego/grace/grace.go +++ b/src/vendor/github.com/beego/beego/v2/server/web/grace/grace.go @@ -22,7 +22,7 @@ // "net/http" // "os" // -// "github.com/beego/beego/grace" +// "github.com/beego/beego/v2/server/web/grace" // ) // // func handler(w http.ResponseWriter, r *http.Request) { @@ -105,8 +105,17 @@ func init() { } } +// ServerOption configures how we set up the connection. +type ServerOption func(*Server) + +func WithShutdownCallback(shutdownCallback func()) ServerOption { + return func(srv *Server) { + srv.shutdownCallbacks = append(srv.shutdownCallbacks, shutdownCallback) + } +} + // NewServer returns a new graceServer. -func NewServer(addr string, handler http.Handler) (srv *Server) { +func NewServer(addr string, handler http.Handler, opts ...ServerOption) (srv *Server) { regLock.Lock() defer regLock.Unlock() @@ -138,7 +147,7 @@ func NewServer(addr string, handler http.Handler) (srv *Server) { }, state: StateInit, Network: "tcp", - terminalChan: make(chan error), //no cache channel + terminalChan: make(chan error), // no cache channel } srv.Server = &http.Server{ Addr: addr, @@ -148,6 +157,10 @@ func NewServer(addr string, handler http.Handler) (srv *Server) { Handler: handler, } + for _, opt := range opts { + opt(srv) + } + runningServersOrder = append(runningServersOrder, addr) runningServers[addr] = srv return srv diff --git a/src/vendor/github.com/beego/beego/grace/server.go b/src/vendor/github.com/beego/beego/v2/server/web/grace/server.go similarity index 79% rename from src/vendor/github.com/beego/beego/grace/server.go rename to src/vendor/github.com/beego/beego/v2/server/web/grace/server.go index 008a61716..982849f36 100644 --- a/src/vendor/github.com/beego/beego/grace/server.go +++ b/src/vendor/github.com/beego/beego/v2/server/web/grace/server.go @@ -20,31 +20,42 @@ import ( // Server embedded http.Server type Server struct { *http.Server - ln net.Listener - SignalHooks map[int]map[os.Signal][]func() - sigChan chan os.Signal - isChild bool - state uint8 - Network string - terminalChan chan error + ln net.Listener + SignalHooks map[int]map[os.Signal][]func() + sigChan chan os.Signal + isChild bool + state uint8 + Network string + terminalChan chan error + shutdownCallbacks []func() } -// Serve accepts incoming connections on the Listener l, -// creating a new service goroutine for each. +// Serve accepts incoming connections on the Listener l +// and creates a new service goroutine for each. // The service goroutines read requests and then call srv.Handler to reply to them. func (srv *Server) Serve() (err error) { + return srv.internalServe(srv.ln) +} + +func (srv *Server) ServeWithListener(ln net.Listener) (err error) { + srv.ln = ln + go srv.handleSignals() + return srv.internalServe(ln) +} + +func (srv *Server) internalServe(ln net.Listener) (err error) { srv.state = StateRunning defer func() { srv.state = StateTerminate }() // When Shutdown is called, Serve, ListenAndServe, and ListenAndServeTLS // immediately return ErrServerClosed. Make sure the program doesn't exit // and waits instead for Shutdown to return. - if err = srv.Server.Serve(srv.ln); err != nil && err != http.ErrServerClosed { + if err = srv.Server.Serve(ln); err != nil && err != http.ErrServerClosed { log.Println(syscall.Getpid(), "Server.Serve() error:", err) return err } - log.Println(syscall.Getpid(), srv.ln.Addr(), "Listener closed.") + log.Println(syscall.Getpid(), ln.Addr(), "Listener closed.") // wait for Shutdown to return if shutdownErr := <-srv.terminalChan; shutdownErr != nil { return shutdownErr @@ -95,6 +106,15 @@ func (srv *Server) ListenAndServe() (err error) { // // If srv.Addr is blank, ":https" is used. func (srv *Server) ListenAndServeTLS(certFile, keyFile string) (err error) { + ln, err := srv.ListenTLS(certFile, keyFile) + if err != nil { + return err + } + + return srv.ServeTLS(ln) +} + +func (srv *Server) ListenTLS(certFile string, keyFile string) (net.Listener, error) { addr := srv.Addr if addr == "" { addr = ":https" @@ -108,20 +128,35 @@ func (srv *Server) ListenAndServeTLS(certFile, keyFile string) (err error) { } srv.TLSConfig.Certificates = make([]tls.Certificate, 1) - srv.TLSConfig.Certificates[0], err = tls.LoadX509KeyPair(certFile, keyFile) + cert, err := tls.LoadX509KeyPair(certFile, keyFile) if err != nil { - return + return nil, err } + srv.TLSConfig.Certificates[0] = cert go srv.handleSignals() ln, err := srv.getListener(addr) if err != nil { log.Println(err) + return nil, err + } + tlsListener := tls.NewListener(tcpKeepAliveListener{ln.(*net.TCPListener)}, srv.TLSConfig) + return tlsListener, nil +} + +// ListenAndServeMutualTLS listens on the TCP network address srv.Addr and then calls +// Serve to handle requests on incoming mutual TLS connections. +func (srv *Server) ListenAndServeMutualTLS(certFile, keyFile, trustFile string) (err error) { + ln, err := srv.ListenMutualTLS(certFile, keyFile, trustFile) + if err != nil { return err } - srv.ln = tls.NewListener(tcpKeepAliveListener{ln.(*net.TCPListener)}, srv.TLSConfig) + return srv.ServeTLS(ln) +} + +func (srv *Server) ServeTLS(ln net.Listener) error { if srv.isChild { process, err := os.FindProcess(os.Getppid()) if err != nil { @@ -134,13 +169,11 @@ func (srv *Server) ListenAndServeTLS(certFile, keyFile string) (err error) { } } - log.Println(os.Getpid(), srv.Addr) - return srv.Serve() + go srv.handleSignals() + return srv.internalServe(ln) } -// ListenAndServeMutualTLS listens on the TCP network address srv.Addr and then calls -// Serve to handle requests on incoming mutual TLS connections. -func (srv *Server) ListenAndServeMutualTLS(certFile, keyFile, trustFile string) (err error) { +func (srv *Server) ListenMutualTLS(certFile string, keyFile string, trustFile string) (net.Listener, error) { addr := srv.Addr if addr == "" { addr = ":https" @@ -154,16 +187,17 @@ func (srv *Server) ListenAndServeMutualTLS(certFile, keyFile, trustFile string) } srv.TLSConfig.Certificates = make([]tls.Certificate, 1) - srv.TLSConfig.Certificates[0], err = tls.LoadX509KeyPair(certFile, keyFile) + cert, err := tls.LoadX509KeyPair(certFile, keyFile) if err != nil { - return + return nil, err } + srv.TLSConfig.Certificates[0] = cert srv.TLSConfig.ClientAuth = tls.RequireAndVerifyClientCert pool := x509.NewCertPool() data, err := ioutil.ReadFile(trustFile) if err != nil { log.Println(err) - return err + return nil, err } pool.AppendCertsFromPEM(data) srv.TLSConfig.ClientCAs = pool @@ -173,24 +207,10 @@ func (srv *Server) ListenAndServeMutualTLS(certFile, keyFile, trustFile string) ln, err := srv.getListener(addr) if err != nil { log.Println(err) - return err + return nil, err } - srv.ln = tls.NewListener(tcpKeepAliveListener{ln.(*net.TCPListener)}, srv.TLSConfig) - - if srv.isChild { - process, err := os.FindProcess(os.Getppid()) - if err != nil { - log.Println(err) - return err - } - err = process.Signal(syscall.SIGTERM) - if err != nil { - return err - } - } - - log.Println(os.Getpid(), srv.Addr) - return srv.Serve() + tlsListener := tls.NewListener(tcpKeepAliveListener{ln.(*net.TCPListener)}, srv.TLSConfig) + return tlsListener, nil } // getListener either opens a new socket to listen on, or takes the acceptor socket @@ -292,6 +312,9 @@ func (srv *Server) shutdown() { ctx, cancel = context.WithTimeout(context.Background(), DefaultTimeout) defer cancel() } + for _, shutdownCallback := range srv.shutdownCallbacks { + shutdownCallback() + } srv.terminalChan <- srv.Server.Shutdown(ctx) } @@ -303,8 +326,8 @@ func (srv *Server) fork() (err error) { } runningServersForked = true - var files = make([]*os.File, len(runningServers)) - var orderArgs = make([]string, len(runningServers)) + files := make([]*os.File, len(runningServers)) + orderArgs := make([]string, len(runningServers)) for _, srvPtr := range runningServers { f, _ := srvPtr.ln.(*net.TCPListener).File() files[socketPtrOffsetMap[srvPtr.Server.Addr]] = f diff --git a/src/vendor/github.com/beego/beego/hooks.go b/src/vendor/github.com/beego/beego/v2/server/web/hooks.go similarity index 88% rename from src/vendor/github.com/beego/beego/hooks.go rename to src/vendor/github.com/beego/beego/v2/server/web/hooks.go index dd1cefa6e..68cc61a18 100644 --- a/src/vendor/github.com/beego/beego/hooks.go +++ b/src/vendor/github.com/beego/beego/v2/server/web/hooks.go @@ -1,4 +1,4 @@ -package beego +package web import ( "encoding/json" @@ -6,9 +6,9 @@ import ( "net/http" "path/filepath" - "github.com/beego/beego/context" - "github.com/beego/beego/logs" - "github.com/beego/beego/session" + "github.com/beego/beego/v2/core/logs" + "github.com/beego/beego/v2/server/web/context" + "github.com/beego/beego/v2/server/web/session" ) // register MIME type with content type @@ -47,9 +47,9 @@ func registerDefaultErrorHandler() error { func registerSession() error { if BConfig.WebConfig.Session.SessionOn { var err error - sessionConfig := AppConfig.String("sessionConfig") + sessionConfig, err := AppConfig.String("sessionConfig") conf := new(session.ManagerConfig) - if sessionConfig == "" { + if sessionConfig == "" || err != nil { conf.CookieName = BConfig.WebConfig.Session.SessionName conf.EnableSetCookie = BConfig.WebConfig.Session.SessionAutoSetCookie conf.Gclifetime = BConfig.WebConfig.Session.SessionGCMaxLifetime @@ -62,6 +62,7 @@ func registerSession() error { conf.SessionNameInHTTPHeader = BConfig.WebConfig.Session.SessionNameInHTTPHeader conf.EnableSidInURLQuery = BConfig.WebConfig.Session.SessionEnableSidInURLQuery conf.CookieSameSite = BConfig.WebConfig.Session.SessionCookieSameSite + conf.SessionIDPrefix = BConfig.WebConfig.Session.SessionIDPrefix } else { if err = json.Unmarshal([]byte(sessionConfig), conf); err != nil { return err @@ -86,13 +87,6 @@ func registerTemplate() error { return nil } -func registerAdmin() error { - if BConfig.Listen.EnableAdmin { - go beeAdminApp.Run() - } - return nil -} - func registerGzip() error { if BConfig.EnableGzip { context.InitGzip( diff --git a/src/vendor/github.com/beego/beego/mime.go b/src/vendor/github.com/beego/beego/v2/server/web/mime.go similarity index 99% rename from src/vendor/github.com/beego/beego/mime.go rename to src/vendor/github.com/beego/beego/v2/server/web/mime.go index ca2878ab2..9393e9c7b 100644 --- a/src/vendor/github.com/beego/beego/mime.go +++ b/src/vendor/github.com/beego/beego/v2/server/web/mime.go @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -package beego +package web var mimemaps = map[string]string{ ".3dm": "x-world/x-3dmf", diff --git a/src/vendor/github.com/beego/beego/namespace.go b/src/vendor/github.com/beego/beego/v2/server/web/namespace.go similarity index 63% rename from src/vendor/github.com/beego/beego/namespace.go rename to src/vendor/github.com/beego/beego/v2/server/web/namespace.go index 8f21dbb99..8da8a797b 100644 --- a/src/vendor/github.com/beego/beego/namespace.go +++ b/src/vendor/github.com/beego/beego/v2/server/web/namespace.go @@ -12,13 +12,13 @@ // See the License for the specific language governing permissions and // limitations under the License. -package beego +package web import ( "net/http" "strings" - beecontext "github.com/beego/beego/context" + beecontext "github.com/beego/beego/v2/server/web/context" ) type namespaceCond func(*beecontext.Context) bool @@ -48,7 +48,7 @@ func NewNamespace(prefix string, params ...LinkNamespace) *Namespace { // if cond return true can run this namespace, else can't // usage: // ns.Cond(func (ctx *context.Context) bool{ -// if ctx.Input.Domain() == "api.beego.me" { +// if ctx.Input.Domain() == "api.beego.vip" { // return true // } // return false @@ -91,106 +91,154 @@ func (n *Namespace) Filter(action string, filter ...FilterFunc) *Namespace { a = FinishRouter } for _, f := range filter { - n.handlers.InsertFilter("*", a, f) + n.handlers.InsertFilter("*", a, f, WithReturnOnOutput(true)) } return n } // Router same as beego.Rourer -// refer: https://godoc.org/github.com/beego/beego#Router +// refer: https://godoc.org/github.com/beego/beego/v2#Router func (n *Namespace) Router(rootpath string, c ControllerInterface, mappingMethods ...string) *Namespace { - n.handlers.Add(rootpath, c, mappingMethods...) + n.handlers.Add(rootpath, c, WithRouterMethods(c, mappingMethods...)) return n } // AutoRouter same as beego.AutoRouter -// refer: https://godoc.org/github.com/beego/beego#AutoRouter +// refer: https://godoc.org/github.com/beego/beego/v2#AutoRouter func (n *Namespace) AutoRouter(c ControllerInterface) *Namespace { n.handlers.AddAuto(c) return n } // AutoPrefix same as beego.AutoPrefix -// refer: https://godoc.org/github.com/beego/beego#AutoPrefix +// refer: https://godoc.org/github.com/beego/beego/v2#AutoPrefix func (n *Namespace) AutoPrefix(prefix string, c ControllerInterface) *Namespace { n.handlers.AddAutoPrefix(prefix, c) return n } // Get same as beego.Get -// refer: https://godoc.org/github.com/beego/beego#Get -func (n *Namespace) Get(rootpath string, f FilterFunc) *Namespace { +// refer: https://godoc.org/github.com/beego/beego/v2#Get +func (n *Namespace) Get(rootpath string, f HandleFunc) *Namespace { n.handlers.Get(rootpath, f) return n } // Post same as beego.Post -// refer: https://godoc.org/github.com/beego/beego#Post -func (n *Namespace) Post(rootpath string, f FilterFunc) *Namespace { +// refer: https://godoc.org/github.com/beego/beego/v2#Post +func (n *Namespace) Post(rootpath string, f HandleFunc) *Namespace { n.handlers.Post(rootpath, f) return n } // Delete same as beego.Delete -// refer: https://godoc.org/github.com/beego/beego#Delete -func (n *Namespace) Delete(rootpath string, f FilterFunc) *Namespace { +// refer: https://godoc.org/github.com/beego/beego/v2#Delete +func (n *Namespace) Delete(rootpath string, f HandleFunc) *Namespace { n.handlers.Delete(rootpath, f) return n } // Put same as beego.Put -// refer: https://godoc.org/github.com/beego/beego#Put -func (n *Namespace) Put(rootpath string, f FilterFunc) *Namespace { +// refer: https://godoc.org/github.com/beego/beego/v2#Put +func (n *Namespace) Put(rootpath string, f HandleFunc) *Namespace { n.handlers.Put(rootpath, f) return n } // Head same as beego.Head -// refer: https://godoc.org/github.com/beego/beego#Head -func (n *Namespace) Head(rootpath string, f FilterFunc) *Namespace { +// refer: https://godoc.org/github.com/beego/beego/v2#Head +func (n *Namespace) Head(rootpath string, f HandleFunc) *Namespace { n.handlers.Head(rootpath, f) return n } // Options same as beego.Options -// refer: https://godoc.org/github.com/beego/beego#Options -func (n *Namespace) Options(rootpath string, f FilterFunc) *Namespace { +// refer: https://godoc.org/github.com/beego/beego/v2#Options +func (n *Namespace) Options(rootpath string, f HandleFunc) *Namespace { n.handlers.Options(rootpath, f) return n } // Patch same as beego.Patch -// refer: https://godoc.org/github.com/beego/beego#Patch -func (n *Namespace) Patch(rootpath string, f FilterFunc) *Namespace { +// refer: https://godoc.org/github.com/beego/beego/v2#Patch +func (n *Namespace) Patch(rootpath string, f HandleFunc) *Namespace { n.handlers.Patch(rootpath, f) return n } // Any same as beego.Any -// refer: https://godoc.org/github.com/beego/beego#Any -func (n *Namespace) Any(rootpath string, f FilterFunc) *Namespace { +// refer: https://godoc.org/github.com/beego/beego/v2#Any +func (n *Namespace) Any(rootpath string, f HandleFunc) *Namespace { n.handlers.Any(rootpath, f) return n } // Handler same as beego.Handler -// refer: https://godoc.org/github.com/beego/beego#Handler +// refer: https://godoc.org/github.com/beego/beego/v2#Handler func (n *Namespace) Handler(rootpath string, h http.Handler) *Namespace { n.handlers.Handler(rootpath, h) return n } // Include add include class -// refer: https://godoc.org/github.com/beego/beego#Include +// refer: https://godoc.org/github.com/beego/beego/v2#Include func (n *Namespace) Include(cList ...ControllerInterface) *Namespace { n.handlers.Include(cList...) return n } +// CtrlGet same as beego.CtrlGet +func (n *Namespace) CtrlGet(rootpath string, f interface{}) *Namespace { + n.handlers.CtrlGet(rootpath, f) + return n +} + +// CtrlPost same as beego.CtrlPost +func (n *Namespace) CtrlPost(rootpath string, f interface{}) *Namespace { + n.handlers.CtrlPost(rootpath, f) + return n +} + +// CtrlDelete same as beego.CtrlDelete +func (n *Namespace) CtrlDelete(rootpath string, f interface{}) *Namespace { + n.handlers.CtrlDelete(rootpath, f) + return n +} + +// CtrlPut same as beego.CtrlPut +func (n *Namespace) CtrlPut(rootpath string, f interface{}) *Namespace { + n.handlers.CtrlPut(rootpath, f) + return n +} + +// CtrlHead same as beego.CtrlHead +func (n *Namespace) CtrlHead(rootpath string, f interface{}) *Namespace { + n.handlers.CtrlHead(rootpath, f) + return n +} + +// CtrlOptions same as beego.CtrlOptions +func (n *Namespace) CtrlOptions(rootpath string, f interface{}) *Namespace { + n.handlers.CtrlOptions(rootpath, f) + return n +} + +// CtrlPatch same as beego.CtrlPatch +func (n *Namespace) CtrlPatch(rootpath string, f interface{}) *Namespace { + n.handlers.CtrlPatch(rootpath, f) + return n +} + +// Any same as beego.CtrlAny +func (n *Namespace) CtrlAny(rootpath string, f interface{}) *Namespace { + n.handlers.CtrlAny(rootpath, f) + return n +} + // Namespace add nest Namespace // usage: -//ns := beego.NewNamespace(“/v1”). -//Namespace( +// ns := beego.NewNamespace(“/v1”). +// Namespace( // beego.NewNamespace("/shop"). // Get("/:id", func(ctx *context.Context) { // ctx.Output.Body([]byte("shopinfo")) @@ -203,7 +251,7 @@ func (n *Namespace) Include(cList ...ControllerInterface) *Namespace { // Get("/:id", func(ctx *context.Context) { // ctx.Output.Body([]byte("crminfo")) // }), -//) +// ) func (n *Namespace) Namespace(ns ...*Namespace) *Namespace { for _, ni := range ns { for k, v := range ni.handlers.routers { @@ -311,61 +359,117 @@ func NSRouter(rootpath string, c ControllerInterface, mappingMethods ...string) } // NSGet call Namespace Get -func NSGet(rootpath string, f FilterFunc) LinkNamespace { +func NSGet(rootpath string, f HandleFunc) LinkNamespace { return func(ns *Namespace) { ns.Get(rootpath, f) } } // NSPost call Namespace Post -func NSPost(rootpath string, f FilterFunc) LinkNamespace { +func NSPost(rootpath string, f HandleFunc) LinkNamespace { return func(ns *Namespace) { ns.Post(rootpath, f) } } // NSHead call Namespace Head -func NSHead(rootpath string, f FilterFunc) LinkNamespace { +func NSHead(rootpath string, f HandleFunc) LinkNamespace { return func(ns *Namespace) { ns.Head(rootpath, f) } } // NSPut call Namespace Put -func NSPut(rootpath string, f FilterFunc) LinkNamespace { +func NSPut(rootpath string, f HandleFunc) LinkNamespace { return func(ns *Namespace) { ns.Put(rootpath, f) } } // NSDelete call Namespace Delete -func NSDelete(rootpath string, f FilterFunc) LinkNamespace { +func NSDelete(rootpath string, f HandleFunc) LinkNamespace { return func(ns *Namespace) { ns.Delete(rootpath, f) } } // NSAny call Namespace Any -func NSAny(rootpath string, f FilterFunc) LinkNamespace { +func NSAny(rootpath string, f HandleFunc) LinkNamespace { return func(ns *Namespace) { ns.Any(rootpath, f) } } // NSOptions call Namespace Options -func NSOptions(rootpath string, f FilterFunc) LinkNamespace { +func NSOptions(rootpath string, f HandleFunc) LinkNamespace { return func(ns *Namespace) { ns.Options(rootpath, f) } } // NSPatch call Namespace Patch -func NSPatch(rootpath string, f FilterFunc) LinkNamespace { +func NSPatch(rootpath string, f HandleFunc) LinkNamespace { return func(ns *Namespace) { ns.Patch(rootpath, f) } } +// NSCtrlGet call Namespace CtrlGet +func NSCtrlGet(rootpath string, f interface{}) LinkNamespace { + return func(ns *Namespace) { + ns.CtrlGet(rootpath, f) + } +} + +// NSCtrlPost call Namespace CtrlPost +func NSCtrlPost(rootpath string, f interface{}) LinkNamespace { + return func(ns *Namespace) { + ns.CtrlPost(rootpath, f) + } +} + +// NSCtrlHead call Namespace CtrlHead +func NSCtrlHead(rootpath string, f interface{}) LinkNamespace { + return func(ns *Namespace) { + ns.CtrlHead(rootpath, f) + } +} + +// NSCtrlPut call Namespace CtrlPut +func NSCtrlPut(rootpath string, f interface{}) LinkNamespace { + return func(ns *Namespace) { + ns.CtrlPut(rootpath, f) + } +} + +// NSCtrlDelete call Namespace CtrlDelete +func NSCtrlDelete(rootpath string, f interface{}) LinkNamespace { + return func(ns *Namespace) { + ns.CtrlDelete(rootpath, f) + } +} + +// NSCtrlAny call Namespace CtrlAny +func NSCtrlAny(rootpath string, f interface{}) LinkNamespace { + return func(ns *Namespace) { + ns.CtrlAny(rootpath, f) + } +} + +// NSCtrlOptions call Namespace CtrlOptions +func NSCtrlOptions(rootpath string, f interface{}) LinkNamespace { + return func(ns *Namespace) { + ns.CtrlOptions(rootpath, f) + } +} + +// NSCtrlPatch call Namespace CtrlPatch +func NSCtrlPatch(rootpath string, f interface{}) LinkNamespace { + return func(ns *Namespace) { + ns.CtrlPatch(rootpath, f) + } +} + // NSAutoRouter call Namespace AutoRouter func NSAutoRouter(c ControllerInterface) LinkNamespace { return func(ns *Namespace) { diff --git a/src/vendor/github.com/beego/beego/policy.go b/src/vendor/github.com/beego/beego/v2/server/web/policy.go similarity index 96% rename from src/vendor/github.com/beego/beego/policy.go rename to src/vendor/github.com/beego/beego/v2/server/web/policy.go index 58222885a..41fb2669d 100644 --- a/src/vendor/github.com/beego/beego/policy.go +++ b/src/vendor/github.com/beego/beego/v2/server/web/policy.go @@ -12,12 +12,12 @@ // See the License for the specific language governing permissions and // limitations under the License. -package beego +package web import ( "strings" - "github.com/beego/beego/context" + "github.com/beego/beego/v2/server/web/context" ) // PolicyFunc defines a policy function which is invoked before the controller handler is executed. @@ -25,7 +25,7 @@ type PolicyFunc func(*context.Context) // FindPolicy Find Router info for URL func (p *ControllerRegister) FindPolicy(cont *context.Context) []PolicyFunc { - var urlPath = cont.Input.URL() + urlPath := cont.Input.URL() if !BConfig.RouterCaseSensitive { urlPath = strings.ToLower(urlPath) } diff --git a/src/vendor/github.com/beego/beego/v2/server/web/router.go b/src/vendor/github.com/beego/beego/v2/server/web/router.go new file mode 100644 index 000000000..1536bae4a --- /dev/null +++ b/src/vendor/github.com/beego/beego/v2/server/web/router.go @@ -0,0 +1,1391 @@ +// Copyright 2014 beego Author. All Rights Reserved. +// +// 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 web + +import ( + "bytes" + "errors" + "fmt" + "io" + "net/http" + "path" + "reflect" + "runtime" + "strconv" + "strings" + "sync" + "time" + + "github.com/beego/beego/v2/core/logs" + "github.com/beego/beego/v2/core/utils" + beecontext "github.com/beego/beego/v2/server/web/context" + "github.com/beego/beego/v2/server/web/context/param" +) + +// default filter execution points +const ( + BeforeStatic = iota + BeforeRouter + BeforeExec + AfterExec + FinishRouter +) + +const ( + routerTypeBeego = iota + routerTypeRESTFul + routerTypeHandler +) + +var ( + // HTTPMETHOD list the supported http methods. + HTTPMETHOD = map[string]bool{ + "GET": true, + "POST": true, + "PUT": true, + "DELETE": true, + "PATCH": true, + "OPTIONS": true, + "HEAD": true, + "TRACE": true, + "CONNECT": true, + "MKCOL": true, + "COPY": true, + "MOVE": true, + "PROPFIND": true, + "PROPPATCH": true, + "LOCK": true, + "UNLOCK": true, + } + // these web.Controller's methods shouldn't reflect to AutoRouter + // see registerControllerExceptMethods + exceptMethod = initExceptMethod() + + urlPlaceholder = "{{placeholder}}" + // DefaultAccessLogFilter will skip the accesslog if return true + DefaultAccessLogFilter FilterHandler = &logFilter{} +) + +// FilterHandler is an interface for +type FilterHandler interface { + Filter(*beecontext.Context) bool +} + +// default log filter static file will not show +type logFilter struct{} + +func (l *logFilter) Filter(ctx *beecontext.Context) bool { + requestPath := path.Clean(ctx.Request.URL.Path) + if requestPath == "/favicon.ico" || requestPath == "/robots.txt" { + return true + } + for prefix := range BConfig.WebConfig.StaticDir { + if strings.HasPrefix(requestPath, prefix) { + return true + } + } + return false +} + +// ExceptMethodAppend to append a slice's value into "exceptMethod", for controller's methods shouldn't reflect to AutoRouter +func ExceptMethodAppend(action string) { + exceptMethod = append(exceptMethod, action) +} + +func initExceptMethod() []string { + res := make([]string, 0, 32) + c := &Controller{} + t := reflect.TypeOf(c) + for i := 0; i < t.NumMethod(); i++ { + m := t.Method(i) + res = append(res, m.Name) + } + return res +} + +// ControllerInfo holds information about the controller. +type ControllerInfo struct { + pattern string + controllerType reflect.Type + methods map[string]string + handler http.Handler + runFunction HandleFunc + routerType int + initialize func() ControllerInterface + methodParams []*param.MethodParam + sessionOn bool +} + +type ControllerOption func(*ControllerInfo) + +func (c *ControllerInfo) GetPattern() string { + return c.pattern +} + +func (c *ControllerInfo) GetMethod() map[string]string { + return c.methods +} + +func WithRouterMethods(ctrlInterface ControllerInterface, mappingMethod ...string) ControllerOption { + return func(c *ControllerInfo) { + c.methods = parseMappingMethods(ctrlInterface, mappingMethod) + } +} + +func WithRouterSessionOn(sessionOn bool) ControllerOption { + return func(c *ControllerInfo) { + c.sessionOn = sessionOn + } +} + +type filterChainConfig struct { + pattern string + chain FilterChain + opts []FilterOpt +} + +// ControllerRegister containers registered router rules, controller handlers and filters. +type ControllerRegister struct { + routers map[string]*Tree + enablePolicy bool + enableFilter bool + policies map[string]*Tree + filters [FinishRouter + 1][]*FilterRouter + pool sync.Pool + + // the filter created by FilterChain + chainRoot *FilterRouter + + // keep registered chain and build it when serve http + filterChains []filterChainConfig + + cfg *Config +} + +// NewControllerRegister returns a new ControllerRegister. +// Usually you should not use this method +// please use NewControllerRegisterWithCfg +func NewControllerRegister() *ControllerRegister { + return NewControllerRegisterWithCfg(BeeApp.Cfg) +} + +func NewControllerRegisterWithCfg(cfg *Config) *ControllerRegister { + res := &ControllerRegister{ + routers: make(map[string]*Tree), + policies: make(map[string]*Tree), + pool: sync.Pool{ + New: func() interface{} { + return beecontext.NewContext() + }, + }, + cfg: cfg, + filterChains: make([]filterChainConfig, 0, 4), + } + res.chainRoot = newFilterRouter("/*", res.serveHttp, WithCaseSensitive(false)) + return res +} + +// Init will be executed when HttpServer start running +func (p *ControllerRegister) Init() { + for i := len(p.filterChains) - 1; i >= 0; i-- { + fc := p.filterChains[i] + root := p.chainRoot + filterFunc := fc.chain(func(ctx *beecontext.Context) { + var preFilterParams map[string]string + root.filter(ctx, p.getUrlPath(ctx), preFilterParams) + }) + p.chainRoot = newFilterRouter(fc.pattern, filterFunc, fc.opts...) + p.chainRoot.next = root + } +} + +// Add controller handler and pattern rules to ControllerRegister. +// usage: +// +// default methods is the same name as method +// Add("/user",&UserController{}) +// Add("/api/list",&RestController{},"*:ListFood") +// Add("/api/create",&RestController{},"post:CreateFood") +// Add("/api/update",&RestController{},"put:UpdateFood") +// Add("/api/delete",&RestController{},"delete:DeleteFood") +// Add("/api",&RestController{},"get,post:ApiFunc" +// Add("/simple",&SimpleController{},"get:GetFunc;post:PostFunc") +func (p *ControllerRegister) Add(pattern string, c ControllerInterface, opts ...ControllerOption) { + p.addWithMethodParams(pattern, c, nil, opts...) +} + +func parseMappingMethods(c ControllerInterface, mappingMethods []string) map[string]string { + reflectVal := reflect.ValueOf(c) + t := reflect.Indirect(reflectVal).Type() + methods := make(map[string]string) + + if len(mappingMethods) == 0 { + return methods + } + + semi := strings.Split(mappingMethods[0], ";") + for _, v := range semi { + colon := strings.Split(v, ":") + if len(colon) != 2 { + panic("method mapping format is invalid") + } + comma := strings.Split(colon[0], ",") + for _, m := range comma { + if m != "*" && !HTTPMETHOD[strings.ToUpper(m)] { + panic(v + " is an invalid method mapping. Method doesn't exist " + m) + } + if val := reflectVal.MethodByName(colon[1]); val.IsValid() { + methods[strings.ToUpper(m)] = colon[1] + continue + } + panic("'" + colon[1] + "' method doesn't exist in the controller " + t.Name()) + } + } + + return methods +} + +func (p *ControllerRegister) addRouterForMethod(route *ControllerInfo) { + if len(route.methods) == 0 { + for m := range HTTPMETHOD { + p.addToRouter(m, route.pattern, route) + } + return + } + for k := range route.methods { + if k != "*" { + p.addToRouter(k, route.pattern, route) + continue + } + for m := range HTTPMETHOD { + p.addToRouter(m, route.pattern, route) + } + } +} + +func (p *ControllerRegister) addWithMethodParams(pattern string, c ControllerInterface, methodParams []*param.MethodParam, opts ...ControllerOption) { + reflectVal := reflect.ValueOf(c) + t := reflect.Indirect(reflectVal).Type() + + route := p.createBeegoRouter(t, pattern) + route.initialize = func() ControllerInterface { + vc := reflect.New(route.controllerType) + execController, ok := vc.Interface().(ControllerInterface) + if !ok { + panic("controller is not ControllerInterface") + } + + elemVal := reflect.ValueOf(c).Elem() + elemType := reflect.TypeOf(c).Elem() + execElem := reflect.ValueOf(execController).Elem() + + numOfFields := elemVal.NumField() + for i := 0; i < numOfFields; i++ { + fieldType := elemType.Field(i) + elemField := execElem.FieldByName(fieldType.Name) + if elemField.CanSet() { + fieldVal := elemVal.Field(i) + elemField.Set(fieldVal) + } + } + + return execController + } + route.methodParams = methodParams + for i := range opts { + opts[i](route) + } + + globalSessionOn := p.cfg.WebConfig.Session.SessionOn + if !globalSessionOn && route.sessionOn { + logs.Warn("global sessionOn is false, sessionOn of router [%s] can't be set to true", route.pattern) + route.sessionOn = globalSessionOn + } + + p.addRouterForMethod(route) +} + +func (p *ControllerRegister) addToRouter(method, pattern string, r *ControllerInfo) { + if !p.cfg.RouterCaseSensitive { + pattern = strings.ToLower(pattern) + } + if t, ok := p.routers[method]; ok { + t.AddRouter(pattern, r) + } else { + t := NewTree() + t.AddRouter(pattern, r) + p.routers[method] = t + } +} + +// Include only when the Runmode is dev will generate router file in the router/auto.go from the controller +// Include(&BankAccount{}, &OrderController{},&RefundController{},&ReceiptController{}) +func (p *ControllerRegister) Include(cList ...ControllerInterface) { + for _, c := range cList { + reflectVal := reflect.ValueOf(c) + t := reflect.Indirect(reflectVal).Type() + key := t.PkgPath() + ":" + t.Name() + if comm, ok := GlobalControllerRouter[key]; ok { + for _, a := range comm { + for _, f := range a.Filters { + p.InsertFilter(f.Pattern, f.Pos, f.Filter, WithReturnOnOutput(f.ReturnOnOutput), WithResetParams(f.ResetParams)) + } + p.addWithMethodParams(a.Router, c, a.MethodParams, WithRouterMethods(c, strings.Join(a.AllowHTTPMethods, ",")+":"+a.Method)) + } + } + } +} + +// GetContext returns a context from pool, so usually you should remember to call Reset function to clean the context +// And don't forget to give back context to pool +// example: +// +// ctx := p.GetContext() +// ctx.Reset(w, q) +// defer p.GiveBackContext(ctx) +func (p *ControllerRegister) GetContext() *beecontext.Context { + return p.pool.Get().(*beecontext.Context) +} + +// GiveBackContext put the ctx into pool so that it could be reuse +func (p *ControllerRegister) GiveBackContext(ctx *beecontext.Context) { + p.pool.Put(ctx) +} + +// CtrlGet add get method +// usage: +// +// type MyController struct { +// web.Controller +// } +// func (m MyController) Ping() { +// m.Ctx.Output.Body([]byte("hello world")) +// } +// +// CtrlGet("/api/:id", MyController.Ping) +// +// If the receiver of function Ping is pointer, you should use CtrlGet("/api/:id", (*MyController).Ping) +func (p *ControllerRegister) CtrlGet(pattern string, f interface{}) { + p.AddRouterMethod(http.MethodGet, pattern, f) +} + +// CtrlPost add post method +// usage: +// +// type MyController struct { +// web.Controller +// } +// func (m MyController) Ping() { +// m.Ctx.Output.Body([]byte("hello world")) +// } +// +// CtrlPost("/api/:id", MyController.Ping) +// +// If the receiver of function Ping is pointer, you should use CtrlPost("/api/:id", (*MyController).Ping) +func (p *ControllerRegister) CtrlPost(pattern string, f interface{}) { + p.AddRouterMethod(http.MethodPost, pattern, f) +} + +// CtrlHead add head method +// usage: +// +// type MyController struct { +// web.Controller +// } +// func (m MyController) Ping() { +// m.Ctx.Output.Body([]byte("hello world")) +// } +// +// CtrlHead("/api/:id", MyController.Ping) +// +// If the receiver of function Ping is pointer, you should use CtrlHead("/api/:id", (*MyController).Ping) +func (p *ControllerRegister) CtrlHead(pattern string, f interface{}) { + p.AddRouterMethod(http.MethodHead, pattern, f) +} + +// CtrlPut add put method +// usage: +// type MyController struct { +// web.Controller +// } +// func (m MyController) Ping() { +// m.Ctx.Output.Body([]byte("hello world")) +// } +// +// CtrlPut("/api/:id", MyController.Ping) + +func (p *ControllerRegister) CtrlPut(pattern string, f interface{}) { + p.AddRouterMethod(http.MethodPut, pattern, f) +} + +// CtrlPatch add patch method +// usage: +// +// type MyController struct { +// web.Controller +// } +// func (m MyController) Ping() { +// m.Ctx.Output.Body([]byte("hello world")) +// } +// +// CtrlPatch("/api/:id", MyController.Ping) +func (p *ControllerRegister) CtrlPatch(pattern string, f interface{}) { + p.AddRouterMethod(http.MethodPatch, pattern, f) +} + +// CtrlDelete add delete method +// usage: +// +// type MyController struct { +// web.Controller +// } +// func (m MyController) Ping() { +// m.Ctx.Output.Body([]byte("hello world")) +// } +// +// CtrlDelete("/api/:id", MyController.Ping) +func (p *ControllerRegister) CtrlDelete(pattern string, f interface{}) { + p.AddRouterMethod(http.MethodDelete, pattern, f) +} + +// CtrlOptions add options method +// usage: +// +// type MyController struct { +// web.Controller +// } +// func (m MyController) Ping() { +// m.Ctx.Output.Body([]byte("hello world")) +// } +// +// CtrlOptions("/api/:id", MyController.Ping) +func (p *ControllerRegister) CtrlOptions(pattern string, f interface{}) { + p.AddRouterMethod(http.MethodOptions, pattern, f) +} + +// CtrlAny add all method +// usage: +// +// type MyController struct { +// web.Controller +// } +// func (m MyController) Ping() { +// m.Ctx.Output.Body([]byte("hello world")) +// } +// +// CtrlAny("/api/:id", MyController.Ping) +func (p *ControllerRegister) CtrlAny(pattern string, f interface{}) { + p.AddRouterMethod("*", pattern, f) +} + +// AddRouterMethod add http method router +// usage: +// +// type MyController struct { +// web.Controller +// } +// func (m MyController) Ping() { +// m.Ctx.Output.Body([]byte("hello world")) +// } +// +// AddRouterMethod("get","/api/:id", MyController.Ping) +func (p *ControllerRegister) AddRouterMethod(httpMethod, pattern string, f interface{}) { + httpMethod = p.getUpperMethodString(httpMethod) + ct, methodName := getReflectTypeAndMethod(f) + + p.addBeegoTypeRouter(ct, methodName, httpMethod, pattern) +} + +// addBeegoTypeRouter add beego type router +func (p *ControllerRegister) addBeegoTypeRouter(ct reflect.Type, ctMethod, httpMethod, pattern string) { + route := p.createBeegoRouter(ct, pattern) + methods := p.getHttpMethodMapMethod(httpMethod, ctMethod) + route.methods = methods + + p.addRouterForMethod(route) +} + +// createBeegoRouter create beego router base on reflect type and pattern +func (p *ControllerRegister) createBeegoRouter(ct reflect.Type, pattern string) *ControllerInfo { + route := &ControllerInfo{} + route.pattern = pattern + route.routerType = routerTypeBeego + route.sessionOn = p.cfg.WebConfig.Session.SessionOn + route.controllerType = ct + return route +} + +// createRestfulRouter create restful router with filter function and pattern +func (p *ControllerRegister) createRestfulRouter(f HandleFunc, pattern string) *ControllerInfo { + route := &ControllerInfo{} + route.pattern = pattern + route.routerType = routerTypeRESTFul + route.sessionOn = p.cfg.WebConfig.Session.SessionOn + route.runFunction = f + return route +} + +// createHandlerRouter create handler router with handler and pattern +func (p *ControllerRegister) createHandlerRouter(h http.Handler, pattern string) *ControllerInfo { + route := &ControllerInfo{} + route.pattern = pattern + route.routerType = routerTypeHandler + route.sessionOn = p.cfg.WebConfig.Session.SessionOn + route.handler = h + return route +} + +// getHttpMethodMapMethod based on http method and controller method, if ctMethod is empty, then it will +// use http method as the controller method +func (p *ControllerRegister) getHttpMethodMapMethod(httpMethod, ctMethod string) map[string]string { + methods := make(map[string]string) + // not match-all sign, only add for the http method + if httpMethod != "*" { + + if ctMethod == "" { + ctMethod = httpMethod + } + methods[httpMethod] = ctMethod + return methods + } + + // add all http method + for val := range HTTPMETHOD { + if ctMethod == "" { + methods[val] = val + } else { + methods[val] = ctMethod + } + } + return methods +} + +// getUpperMethodString get upper string of method, and panic if the method +// is not valid +func (p *ControllerRegister) getUpperMethodString(method string) string { + method = strings.ToUpper(method) + if method != "*" && !HTTPMETHOD[method] { + panic("not support http method: " + method) + } + return method +} + +// get reflect controller type and method by controller method expression +func getReflectTypeAndMethod(f interface{}) (controllerType reflect.Type, method string) { + // check f is a function + funcType := reflect.TypeOf(f) + if funcType.Kind() != reflect.Func { + panic("not a method") + } + + // get function name + funcObj := runtime.FuncForPC(reflect.ValueOf(f).Pointer()) + if funcObj == nil { + panic("cannot find the method") + } + funcNameSli := strings.Split(funcObj.Name(), ".") + lFuncSli := len(funcNameSli) + if lFuncSli == 0 { + panic("invalid method full name: " + funcObj.Name()) + } + + method = funcNameSli[lFuncSli-1] + if len(method) == 0 { + panic("method name is empty") + } else if method[0] > 96 || method[0] < 65 { + panic(fmt.Sprintf("%s is not a public method", method)) + } + + // check only one param which is the method receiver + if numIn := funcType.NumIn(); numIn != 1 { + panic("invalid number of param in") + } + + controllerType = funcType.In(0) + + // check controller has the method + _, exists := controllerType.MethodByName(method) + if !exists { + panic(controllerType.String() + " has no method " + method) + } + + // check the receiver implement ControllerInterface + if controllerType.Kind() == reflect.Ptr { + controllerType = controllerType.Elem() + } + controller := reflect.New(controllerType) + _, ok := controller.Interface().(ControllerInterface) + if !ok { + panic(controllerType.String() + " is not implemented ControllerInterface") + } + + return +} + +// HandleFunc define how to process the request +type HandleFunc func(ctx *beecontext.Context) + +// Get add get method +// usage: +// +// Get("/", func(ctx *context.Context){ +// ctx.Output.Body("hello world") +// }) +func (p *ControllerRegister) Get(pattern string, f HandleFunc) { + p.AddMethod("get", pattern, f) +} + +// Post add post method +// usage: +// +// Post("/api", func(ctx *context.Context){ +// ctx.Output.Body("hello world") +// }) +func (p *ControllerRegister) Post(pattern string, f HandleFunc) { + p.AddMethod("post", pattern, f) +} + +// Put add put method +// usage: +// +// Put("/api/:id", func(ctx *context.Context){ +// ctx.Output.Body("hello world") +// }) +func (p *ControllerRegister) Put(pattern string, f HandleFunc) { + p.AddMethod("put", pattern, f) +} + +// Delete add delete method +// usage: +// +// Delete("/api/:id", func(ctx *context.Context){ +// ctx.Output.Body("hello world") +// }) +func (p *ControllerRegister) Delete(pattern string, f HandleFunc) { + p.AddMethod("delete", pattern, f) +} + +// Head add head method +// usage: +// +// Head("/api/:id", func(ctx *context.Context){ +// ctx.Output.Body("hello world") +// }) +func (p *ControllerRegister) Head(pattern string, f HandleFunc) { + p.AddMethod("head", pattern, f) +} + +// Patch add patch method +// usage: +// +// Patch("/api/:id", func(ctx *context.Context){ +// ctx.Output.Body("hello world") +// }) +func (p *ControllerRegister) Patch(pattern string, f HandleFunc) { + p.AddMethod("patch", pattern, f) +} + +// Options add options method +// usage: +// +// Options("/api/:id", func(ctx *context.Context){ +// ctx.Output.Body("hello world") +// }) +func (p *ControllerRegister) Options(pattern string, f HandleFunc) { + p.AddMethod("options", pattern, f) +} + +// Any add all method +// usage: +// +// Any("/api/:id", func(ctx *context.Context){ +// ctx.Output.Body("hello world") +// }) +func (p *ControllerRegister) Any(pattern string, f HandleFunc) { + p.AddMethod("*", pattern, f) +} + +// AddMethod add http method router +// usage: +// +// AddMethod("get","/api/:id", func(ctx *context.Context){ +// ctx.Output.Body("hello world") +// }) +func (p *ControllerRegister) AddMethod(method, pattern string, f HandleFunc) { + method = p.getUpperMethodString(method) + + route := p.createRestfulRouter(f, pattern) + methods := p.getHttpMethodMapMethod(method, "") + route.methods = methods + + p.addRouterForMethod(route) +} + +// Handler add user defined Handler +func (p *ControllerRegister) Handler(pattern string, h http.Handler, options ...interface{}) { + route := p.createHandlerRouter(h, pattern) + if len(options) > 0 { + if _, ok := options[0].(bool); ok { + pattern = path.Join(pattern, "?:all(.*)") + } + } + for m := range HTTPMETHOD { + p.addToRouter(m, pattern, route) + } +} + +// AddAuto router to ControllerRegister. +// example beego.AddAuto(&MainController{}), +// MainController has method List and Page. +// visit the url /main/list to execute List function +// /main/page to execute Page function. +func (p *ControllerRegister) AddAuto(c ControllerInterface) { + p.AddAutoPrefix("/", c) +} + +// AddAutoPrefix Add auto router to ControllerRegister with prefix. +// example beego.AddAutoPrefix("/admin",&MainController{}), +// MainController has method List and Page. +// visit the url /admin/main/list to execute List function +// /admin/main/page to execute Page function. +func (p *ControllerRegister) AddAutoPrefix(prefix string, c ControllerInterface) { + reflectVal := reflect.ValueOf(c) + rt := reflectVal.Type() + ct := reflect.Indirect(reflectVal).Type() + controllerName := strings.TrimSuffix(ct.Name(), "Controller") + for i := 0; i < rt.NumMethod(); i++ { + methodName := rt.Method(i).Name + if !utils.InSlice(methodName, exceptMethod) { + p.addAutoPrefixMethod(prefix, controllerName, methodName, ct) + } + } +} + +func (p *ControllerRegister) addAutoPrefixMethod(prefix, controllerName, methodName string, ctrl reflect.Type) { + pattern := path.Join(prefix, strings.ToLower(controllerName), strings.ToLower(methodName), "*") + patternInit := path.Join(prefix, controllerName, methodName, "*") + patternFix := path.Join(prefix, strings.ToLower(controllerName), strings.ToLower(methodName)) + patternFixInit := path.Join(prefix, controllerName, methodName) + + route := p.createBeegoRouter(ctrl, pattern) + route.methods = map[string]string{"*": methodName} + for m := range HTTPMETHOD { + + p.addToRouter(m, pattern, route) + + // only case sensitive, we add three more routes + if p.cfg.RouterCaseSensitive { + p.addToRouter(m, patternInit, route) + p.addToRouter(m, patternFix, route) + p.addToRouter(m, patternFixInit, route) + } + } +} + +// InsertFilter Add a FilterFunc with pattern rule and action constant. +// params is for: +// 1. setting the returnOnOutput value (false allows multiple filters to execute) +// 2. determining whether or not params need to be reset. +func (p *ControllerRegister) InsertFilter(pattern string, pos int, filter FilterFunc, opts ...FilterOpt) error { + opts = append(opts, WithCaseSensitive(p.cfg.RouterCaseSensitive)) + mr := newFilterRouter(pattern, filter, opts...) + return p.insertFilterRouter(pos, mr) +} + +// InsertFilterChain is similar to InsertFilter, +// but it will using chainRoot.filterFunc as input to build a new filterFunc +// for example, assume that chainRoot is funcA +// and we add new FilterChain +// +// fc := func(next) { +// return func(ctx) { +// // do something +// next(ctx) +// // do something +// } +// } +func (p *ControllerRegister) InsertFilterChain(pattern string, chain FilterChain, opts ...FilterOpt) { + opts = append([]FilterOpt{WithCaseSensitive(p.cfg.RouterCaseSensitive)}, opts...) + p.filterChains = append(p.filterChains, filterChainConfig{ + pattern: pattern, + chain: chain, + opts: opts, + }) +} + +// add Filter into +func (p *ControllerRegister) insertFilterRouter(pos int, mr *FilterRouter) (err error) { + if pos < BeforeStatic || pos > FinishRouter { + return errors.New("can not find your filter position") + } + p.enableFilter = true + p.filters[pos] = append(p.filters[pos], mr) + return nil +} + +// URLFor does another controller handler in this request function. +// it can access any controller method. +func (p *ControllerRegister) URLFor(endpoint string, values ...interface{}) string { + paths := strings.Split(endpoint, ".") + if len(paths) <= 1 { + logs.Warn("urlfor endpoint must like path.controller.method") + return "" + } + if len(values)%2 != 0 { + logs.Warn("urlfor params must key-value pair") + return "" + } + params := make(map[string]string) + if len(values) > 0 { + key := "" + for k, v := range values { + if k%2 == 0 { + key = fmt.Sprint(v) + } else { + params[key] = fmt.Sprint(v) + } + } + } + controllerName := strings.Join(paths[:len(paths)-1], "/") + methodName := paths[len(paths)-1] + for m, t := range p.routers { + ok, url := p.getURL(t, "/", controllerName, methodName, params, m) + if ok { + return url + } + } + return "" +} + +func (p *ControllerRegister) getURL(t *Tree, url, controllerName, methodName string, params map[string]string, httpMethod string) (bool, string) { + for _, subtree := range t.fixrouters { + u := path.Join(url, subtree.prefix) + ok, u := p.getURL(subtree, u, controllerName, methodName, params, httpMethod) + if ok { + return ok, u + } + } + if t.wildcard != nil { + u := path.Join(url, urlPlaceholder) + ok, u := p.getURL(t.wildcard, u, controllerName, methodName, params, httpMethod) + if ok { + return ok, u + } + } + for _, l := range t.leaves { + if c, ok := l.runObject.(*ControllerInfo); ok { + if c.routerType == routerTypeBeego && + strings.HasSuffix(path.Join(c.controllerType.PkgPath(), c.controllerType.Name()), `/`+controllerName) { + find := false + if HTTPMETHOD[strings.ToUpper(methodName)] { + if len(c.methods) == 0 { + find = true + } else if m, ok := c.methods[strings.ToUpper(methodName)]; ok && m == strings.ToUpper(methodName) { + find = true + } else if m, ok = c.methods["*"]; ok && m == methodName { + find = true + } + } + if !find { + for m, md := range c.methods { + if (m == "*" || m == httpMethod) && md == methodName { + find = true + } + } + } + if find { + if l.regexps == nil { + if len(l.wildcards) == 0 { + return true, strings.Replace(url, "/"+urlPlaceholder, "", 1) + toURL(params) + } + if len(l.wildcards) == 1 { + if v, ok := params[l.wildcards[0]]; ok { + delete(params, l.wildcards[0]) + return true, strings.Replace(url, urlPlaceholder, v, 1) + toURL(params) + } + return false, "" + } + if len(l.wildcards) == 3 && l.wildcards[0] == "." { + if p, ok := params[":path"]; ok { + if e, isok := params[":ext"]; isok { + delete(params, ":path") + delete(params, ":ext") + return true, strings.Replace(url, urlPlaceholder, p+"."+e, -1) + toURL(params) + } + } + } + canSkip := false + for _, v := range l.wildcards { + if v == ":" { + canSkip = true + continue + } + if u, ok := params[v]; ok { + delete(params, v) + url = strings.Replace(url, urlPlaceholder, u, 1) + } else { + if canSkip { + canSkip = false + continue + } + return false, "" + } + } + return true, url + toURL(params) + } + var i int + var startReg bool + regURL := "" + for _, v := range strings.Trim(l.regexps.String(), "^$") { + if v == '(' { + startReg = true + continue + } else if v == ')' { + startReg = false + if v, ok := params[l.wildcards[i]]; ok { + delete(params, l.wildcards[i]) + regURL = regURL + v + i++ + } else { + break + } + } else if !startReg { + regURL = string(append([]rune(regURL), v)) + } + } + if l.regexps.MatchString(regURL) { + ps := strings.Split(regURL, "/") + for _, p := range ps { + url = strings.Replace(url, urlPlaceholder, p, 1) + } + return true, url + toURL(params) + } + } + } + } + } + + return false, "" +} + +func (p *ControllerRegister) execFilter(context *beecontext.Context, urlPath string, pos int) (started bool) { + var preFilterParams map[string]string + for _, filterR := range p.filters[pos] { + b, done := filterR.filter(context, urlPath, preFilterParams) + if done { + return b + } + } + return false +} + +// Implement http.Handler interface. +func (p *ControllerRegister) ServeHTTP(rw http.ResponseWriter, r *http.Request) { + ctx := p.GetContext() + + ctx.Reset(rw, r) + defer p.GiveBackContext(ctx) + + var preFilterParams map[string]string + p.chainRoot.filter(ctx, p.getUrlPath(ctx), preFilterParams) +} + +func (p *ControllerRegister) serveHttp(ctx *beecontext.Context) { + var err error + startTime := time.Now() + r := ctx.Request + rw := ctx.ResponseWriter.ResponseWriter + var ( + runRouter reflect.Type + findRouter bool + runMethod string + methodParams []*param.MethodParam + routerInfo *ControllerInfo + isRunnable bool + currentSessionOn bool + originRouterInfo *ControllerInfo + originFindRouter bool + ) + + if p.cfg.RecoverFunc != nil { + defer p.cfg.RecoverFunc(ctx, p.cfg) + } + + ctx.Output.EnableGzip = p.cfg.EnableGzip + + if p.cfg.RunMode == DEV { + ctx.Output.Header("Server", p.cfg.ServerName) + } + + urlPath := p.getUrlPath(ctx) + + // filter wrong http method + if !HTTPMETHOD[r.Method] { + exception("405", ctx) + goto Admin + } + + // filter for static file + if len(p.filters[BeforeStatic]) > 0 && p.execFilter(ctx, urlPath, BeforeStatic) { + goto Admin + } + + serverStaticRouter(ctx) + + if ctx.ResponseWriter.Started { + findRouter = true + goto Admin + } + + if r.Method != http.MethodGet && r.Method != http.MethodHead { + body := ctx.Input.Context.Request.Body + if body == nil { + body = io.NopCloser(bytes.NewReader([]byte{})) + } + + if ctx.Input.IsUpload() { + ctx.Input.Context.Request.Body = http.MaxBytesReader(ctx.Input.Context.ResponseWriter, + body, + p.cfg.MaxUploadSize) + } else if p.cfg.CopyRequestBody { + // connection will close if the incoming data are larger (RFC 7231, 6.5.11) + if r.ContentLength > p.cfg.MaxMemory { + logs.Error(errors.New("payload too large")) + exception("413", ctx) + goto Admin + } + ctx.Input.CopyBody(p.cfg.MaxMemory) + } else { + ctx.Input.Context.Request.Body = http.MaxBytesReader(ctx.Input.Context.ResponseWriter, + body, + p.cfg.MaxMemory) + } + + err = ctx.Input.ParseFormOrMultiForm(p.cfg.MaxMemory) + if err != nil { + logs.Error(err) + if strings.Contains(err.Error(), `http: request body too large`) { + exception("413", ctx) + } else { + exception("500", ctx) + } + goto Admin + } + } + + // session init + currentSessionOn = p.cfg.WebConfig.Session.SessionOn + originRouterInfo, originFindRouter = p.FindRouter(ctx) + if originFindRouter { + currentSessionOn = originRouterInfo.sessionOn + } + if currentSessionOn { + ctx.Input.CruSession, err = GlobalSessions.SessionStart(rw, r) + if err != nil { + logs.Error(err) + exception("503", ctx) + goto Admin + } + defer func() { + if ctx.Input.CruSession != nil { + ctx.Input.CruSession.SessionRelease(nil, rw) + } + }() + } + if len(p.filters[BeforeRouter]) > 0 && p.execFilter(ctx, urlPath, BeforeRouter) { + goto Admin + } + // User can define RunController and RunMethod in filter + if ctx.Input.RunController != nil && ctx.Input.RunMethod != "" { + findRouter = true + runMethod = ctx.Input.RunMethod + runRouter = ctx.Input.RunController + } else { + routerInfo, findRouter = p.FindRouter(ctx) + } + + // if no matches to url, throw a not found exception + if !findRouter { + exception("404", ctx) + goto Admin + } + if splat := ctx.Input.Param(":splat"); splat != "" { + for k, v := range strings.Split(splat, "/") { + ctx.Input.SetParam(strconv.Itoa(k), v) + } + } + + if routerInfo != nil { + // store router pattern into context + ctx.Input.SetData("RouterPattern", routerInfo.pattern) + } + + // execute middleware filters + if len(p.filters[BeforeExec]) > 0 && p.execFilter(ctx, urlPath, BeforeExec) { + goto Admin + } + + // check policies + if p.execPolicy(ctx, urlPath) { + goto Admin + } + + if routerInfo != nil { + if routerInfo.routerType == routerTypeRESTFul { + if _, ok := routerInfo.methods[r.Method]; ok { + isRunnable = true + routerInfo.runFunction(ctx) + } else { + exception("405", ctx) + goto Admin + } + } else if routerInfo.routerType == routerTypeHandler { + isRunnable = true + routerInfo.handler.ServeHTTP(ctx.ResponseWriter, ctx.Request) + } else { + runRouter = routerInfo.controllerType + methodParams = routerInfo.methodParams + method := r.Method + if r.Method == http.MethodPost && ctx.Input.Query("_method") == http.MethodPut { + method = http.MethodPut + } + if r.Method == http.MethodPost && ctx.Input.Query("_method") == http.MethodDelete { + method = http.MethodDelete + } + if m, ok := routerInfo.methods[method]; ok { + runMethod = m + } else if m, ok = routerInfo.methods["*"]; ok { + runMethod = m + } else { + runMethod = method + } + } + } + + // also defined runRouter & runMethod from filter + if !isRunnable { + // Invoke the request handler + var execController ControllerInterface + if routerInfo != nil && routerInfo.initialize != nil { + execController = routerInfo.initialize() + } else { + vc := reflect.New(runRouter) + var ok bool + execController, ok = vc.Interface().(ControllerInterface) + if !ok { + panic("controller is not ControllerInterface") + } + } + + // call the controller init function + execController.Init(ctx, runRouter.Name(), runMethod, execController) + + // call prepare function + execController.Prepare() + + // if XSRF is Enable then check cookie where there has any cookie in the request's cookie _csrf + if p.cfg.WebConfig.EnableXSRF { + execController.XSRFToken() + if r.Method == http.MethodPost || r.Method == http.MethodDelete || r.Method == http.MethodPut || + (r.Method == http.MethodPost && (ctx.Input.Query("_method") == http.MethodDelete || ctx.Input.Query("_method") == http.MethodPut)) { + execController.CheckXSRFCookie() + } + } + + execController.URLMapping() + + if !ctx.ResponseWriter.Started { + // exec main logic + switch runMethod { + case http.MethodGet: + execController.Get() + case http.MethodPost: + execController.Post() + case http.MethodDelete: + execController.Delete() + case http.MethodPut: + execController.Put() + case http.MethodHead: + execController.Head() + case http.MethodPatch: + execController.Patch() + case http.MethodOptions: + execController.Options() + case http.MethodTrace: + execController.Trace() + default: + if !execController.HandlerFunc(runMethod) { + vc := reflect.ValueOf(execController) + method := vc.MethodByName(runMethod) + in := param.ConvertParams(methodParams, method.Type(), ctx) + out := method.Call(in) + + // For backward compatibility we only handle response if we had incoming methodParams + if methodParams != nil { + p.handleParamResponse(ctx, execController, out) + } + } + } + + // render template + if !ctx.ResponseWriter.Started && ctx.Output.Status == 0 { + if p.cfg.WebConfig.AutoRender { + if err := execController.Render(); err != nil { + logs.Error(err) + } + } + } + } + + // finish all runRouter. release resource + execController.Finish() + } + + // execute middleware filters + if len(p.filters[AfterExec]) > 0 && p.execFilter(ctx, urlPath, AfterExec) { + goto Admin + } + + if len(p.filters[FinishRouter]) > 0 && p.execFilter(ctx, urlPath, FinishRouter) { + goto Admin + } + +Admin: + // admin module record QPS + + statusCode := ctx.ResponseWriter.Status + if statusCode == 0 { + statusCode = 200 + } + + LogAccess(ctx, &startTime, statusCode) + + timeDur := time.Since(startTime) + ctx.ResponseWriter.Elapsed = timeDur + if p.cfg.Listen.EnableAdmin { + pattern := "" + if routerInfo != nil { + pattern = routerInfo.pattern + } + + if FilterMonitorFunc(r.Method, r.URL.Path, timeDur, pattern, statusCode) { + routerName := "" + if runRouter != nil { + routerName = runRouter.Name() + } + go StatisticsMap.AddStatistics(r.Method, r.URL.Path, routerName, timeDur) + } + } + + if p.cfg.RunMode == DEV && !p.cfg.Log.AccessLogs { + match := map[bool]string{true: "match", false: "nomatch"} + devInfo := fmt.Sprintf("|%15s|%s %3d %s|%13s|%8s|%s %-7s %s %-3s", + ctx.Input.IP(), + logs.ColorByStatus(statusCode), statusCode, logs.ResetColor(), + timeDur.String(), + match[findRouter], + logs.ColorByMethod(r.Method), r.Method, logs.ResetColor(), + r.URL.Path) + if routerInfo != nil { + devInfo += fmt.Sprintf(" r:%s", routerInfo.pattern) + } + + logs.Debug(devInfo) + } + // Call WriteHeader if status code has been set changed + if ctx.Output.Status != 0 { + ctx.ResponseWriter.WriteHeader(ctx.Output.Status) + } +} + +func (p *ControllerRegister) getUrlPath(ctx *beecontext.Context) string { + urlPath := ctx.Request.URL.Path + if !p.cfg.RouterCaseSensitive { + urlPath = strings.ToLower(urlPath) + } + return urlPath +} + +func (p *ControllerRegister) handleParamResponse(context *beecontext.Context, execController ControllerInterface, results []reflect.Value) { + // looping in reverse order for the case when both error and value are returned and error sets the response status code + for i := len(results) - 1; i >= 0; i-- { + result := results[i] + if result.Kind() != reflect.Interface || !result.IsNil() { + resultValue := result.Interface() + context.RenderMethodResult(resultValue) + } + } + if !context.ResponseWriter.Started && len(results) > 0 && context.Output.Status == 0 { + context.Output.SetStatus(200) + } +} + +// FindRouter Find Router info for URL +func (p *ControllerRegister) FindRouter(context *beecontext.Context) (routerInfo *ControllerInfo, isFind bool) { + urlPath := context.Input.URL() + if !p.cfg.RouterCaseSensitive { + urlPath = strings.ToLower(urlPath) + } + httpMethod := context.Input.Method() + if t, ok := p.routers[httpMethod]; ok { + runObject := t.Match(urlPath, context) + if r, ok := runObject.(*ControllerInfo); ok { + return r, true + } + } + return +} + +// GetAllControllerInfo get all ControllerInfo +func (p *ControllerRegister) GetAllControllerInfo() (routerInfos []*ControllerInfo) { + for _, webTree := range p.routers { + composeControllerInfos(webTree, &routerInfos) + } + return +} + +func composeControllerInfos(tree *Tree, routerInfos *[]*ControllerInfo) { + if tree.fixrouters != nil { + for _, subTree := range tree.fixrouters { + composeControllerInfos(subTree, routerInfos) + } + } + if tree.wildcard != nil { + composeControllerInfos(tree.wildcard, routerInfos) + } + if tree.leaves != nil { + for _, l := range tree.leaves { + if c, ok := l.runObject.(*ControllerInfo); ok { + *routerInfos = append(*routerInfos, c) + } + } + } +} + +func toURL(params map[string]string) string { + if len(params) == 0 { + return "" + } + u := "?" + for k, v := range params { + u += k + "=" + v + "&" + } + return strings.TrimRight(u, "&") +} + +// LogAccess logging info HTTP Access +func LogAccess(ctx *beecontext.Context, startTime *time.Time, statusCode int) { + BeeApp.LogAccess(ctx, startTime, statusCode) +} diff --git a/src/vendor/github.com/beego/beego/v2/server/web/server.go b/src/vendor/github.com/beego/beego/v2/server/web/server.go new file mode 100644 index 000000000..fe4c6164b --- /dev/null +++ b/src/vendor/github.com/beego/beego/v2/server/web/server.go @@ -0,0 +1,959 @@ +// Copyright 2014 beego Author. All Rights Reserved. +// +// 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 web + +import ( + "crypto/tls" + "crypto/x509" + "fmt" + "io/ioutil" + "net" + "net/http" + "net/http/fcgi" + "os" + "path" + "strconv" + "strings" + "text/template" + "time" + + "golang.org/x/crypto/acme/autocert" + + "github.com/beego/beego/v2/core/logs" + "github.com/beego/beego/v2/core/utils" + beecontext "github.com/beego/beego/v2/server/web/context" + "github.com/beego/beego/v2/server/web/grace" +) + +// BeeApp is an application instance +// If you are using single server, you could use this +// But if you need multiple servers, do not use this +var BeeApp *HttpServer + +func init() { + // create beego application + BeeApp = NewHttpSever() +} + +// HttpServer defines beego application with a new PatternServeMux. +type HttpServer struct { + Handlers *ControllerRegister + Server *http.Server + Cfg *Config + LifeCycleCallbacks []LifeCycleCallback +} + +// NewHttpSever returns a new beego application. +// this method will use the BConfig as the configure to create HttpServer +// Be careful that when you update BConfig, the server's Cfg will be updated too +func NewHttpSever() *HttpServer { + return NewHttpServerWithCfg(BConfig) +} + +// NewHttpServerWithCfg will create an sever with specific cfg +func NewHttpServerWithCfg(cfg *Config) *HttpServer { + cr := NewControllerRegisterWithCfg(cfg) + app := &HttpServer{ + Handlers: cr, + Server: &http.Server{}, + Cfg: cfg, + } + + return app +} + +// MiddleWare function for http.Handler +type MiddleWare func(http.Handler) http.Handler + +// LifeCycleCallback configures callback. +// Developer can implement this interface to add custom logic to server lifecycle +type LifeCycleCallback interface { + AfterStart(app *HttpServer) + BeforeShutdown(app *HttpServer) +} + +// Run beego application. +func (app *HttpServer) Run(addr string, mws ...MiddleWare) { + initBeforeHTTPRun() + + // init... + app.initAddr(addr) + app.Handlers.Init() + + addr = app.Cfg.Listen.HTTPAddr + + if app.Cfg.Listen.HTTPPort != 0 { + addr = fmt.Sprintf("%s:%d", app.Cfg.Listen.HTTPAddr, app.Cfg.Listen.HTTPPort) + } + + var ( + err error + l net.Listener + endRunning = make(chan bool, 1) + ) + + // run cgi server + if app.Cfg.Listen.EnableFcgi { + for _, lifeCycleCallback := range app.LifeCycleCallbacks { + lifeCycleCallback.AfterStart(app) + } + if app.Cfg.Listen.EnableStdIo { + if err = fcgi.Serve(nil, app.Handlers); err == nil { // standard I/O + logs.Info("Use FCGI via standard I/O") + } else { + logs.Critical("Cannot use FCGI via standard I/O", err) + } + for _, lifeCycleCallback := range app.LifeCycleCallbacks { + lifeCycleCallback.BeforeShutdown(app) + } + return + } + if app.Cfg.Listen.HTTPPort == 0 { + // remove the Socket file before start + if utils.FileExists(addr) { + os.Remove(addr) + } + l, err = net.Listen("unix", addr) + } else { + l, err = net.Listen("tcp", addr) + } + if err != nil { + logs.Critical("Listen for Fcgi: ", err) + } + if err = fcgi.Serve(l, app.Handlers); err != nil { + logs.Critical("fcgi.Serve: ", err) + } + for _, lifeCycleCallback := range app.LifeCycleCallbacks { + lifeCycleCallback.BeforeShutdown(app) + } + return + } + + app.Server.Handler = app.Handlers + for i := len(mws) - 1; i >= 0; i-- { + if mws[i] == nil { + continue + } + app.Server.Handler = mws[i](app.Server.Handler) + } + app.Server.ReadTimeout = time.Duration(app.Cfg.Listen.ServerTimeOut) * time.Second + app.Server.WriteTimeout = time.Duration(app.Cfg.Listen.ServerTimeOut) * time.Second + app.Server.ErrorLog = logs.GetLogger("HTTP") + + // run graceful mode + if app.Cfg.Listen.Graceful { + var opts []grace.ServerOption + for _, lifeCycleCallback := range app.LifeCycleCallbacks { + lifeCycleCallbackDup := lifeCycleCallback + opts = append(opts, grace.WithShutdownCallback(func() { + lifeCycleCallbackDup.BeforeShutdown(app) + })) + } + + httpsAddr := app.Cfg.Listen.HTTPSAddr + app.Server.Addr = httpsAddr + if app.Cfg.Listen.EnableHTTPS || app.Cfg.Listen.EnableMutualHTTPS { + go func() { + time.Sleep(1000 * time.Microsecond) + if app.Cfg.Listen.HTTPSPort != 0 { + httpsAddr = fmt.Sprintf("%s:%d", app.Cfg.Listen.HTTPSAddr, app.Cfg.Listen.HTTPSPort) + app.Server.Addr = httpsAddr + } + server := grace.NewServer(httpsAddr, app.Server.Handler, opts...) + server.Server.ReadTimeout = app.Server.ReadTimeout + server.Server.WriteTimeout = app.Server.WriteTimeout + var ln net.Listener + if app.Cfg.Listen.EnableMutualHTTPS { + if ln, err = server.ListenMutualTLS(app.Cfg.Listen.HTTPSCertFile, + app.Cfg.Listen.HTTPSKeyFile, + app.Cfg.Listen.TrustCaFile); err != nil { + logs.Critical("ListenMutualTLS: ", err, fmt.Sprintf("%d", os.Getpid())) + return + } + } else { + if app.Cfg.Listen.AutoTLS { + m := autocert.Manager{ + Prompt: autocert.AcceptTOS, + HostPolicy: autocert.HostWhitelist(app.Cfg.Listen.Domains...), + Cache: autocert.DirCache(app.Cfg.Listen.TLSCacheDir), + } + app.Server.TLSConfig = &tls.Config{GetCertificate: m.GetCertificate} + app.Cfg.Listen.HTTPSCertFile, app.Cfg.Listen.HTTPSKeyFile = "", "" + } + if ln, err = server.ListenTLS(app.Cfg.Listen.HTTPSCertFile, app.Cfg.Listen.HTTPSKeyFile); err != nil { + logs.Critical("ListenTLS: ", err, fmt.Sprintf("%d", os.Getpid())) + return + } + } + for _, callback := range app.LifeCycleCallbacks { + callback.AfterStart(app) + } + if err = server.ServeTLS(ln); err != nil { + logs.Critical("ServeTLS: ", err, fmt.Sprintf("%d", os.Getpid())) + time.Sleep(100 * time.Microsecond) + } + endRunning <- true + }() + } + if app.Cfg.Listen.EnableHTTP { + go func() { + server := grace.NewServer(addr, app.Server.Handler, opts...) + server.Server.ReadTimeout = app.Server.ReadTimeout + server.Server.WriteTimeout = app.Server.WriteTimeout + if app.Cfg.Listen.ListenTCP4 { + server.Network = "tcp4" + } + ln, err := net.Listen(server.Network, server.Addr) + logs.Info("graceful http server Running on http://%s", server.Addr) + if err != nil { + logs.Critical("Listen for HTTP[graceful mode]: ", err) + endRunning <- true + return + } + for _, callback := range app.LifeCycleCallbacks { + callback.AfterStart(app) + } + if err := server.ServeWithListener(ln); err != nil { + logs.Critical("ServeWithListener: ", err, fmt.Sprintf("%d", os.Getpid())) + time.Sleep(100 * time.Microsecond) + } + endRunning <- true + }() + } + <-endRunning + return + } + + // run normal mode + if app.Cfg.Listen.EnableHTTPS || app.Cfg.Listen.EnableMutualHTTPS { + go func() { + time.Sleep(1000 * time.Microsecond) + if app.Cfg.Listen.HTTPSPort != 0 { + app.Server.Addr = fmt.Sprintf("%s:%d", app.Cfg.Listen.HTTPSAddr, app.Cfg.Listen.HTTPSPort) + } else if app.Cfg.Listen.EnableHTTP { + logs.Info("Start https server error, conflict with http. Please reset https port") + return + } + logs.Info("https server Running on https://%s", app.Server.Addr) + if app.Cfg.Listen.AutoTLS { + m := autocert.Manager{ + Prompt: autocert.AcceptTOS, + HostPolicy: autocert.HostWhitelist(app.Cfg.Listen.Domains...), + Cache: autocert.DirCache(app.Cfg.Listen.TLSCacheDir), + } + app.Server.TLSConfig = &tls.Config{GetCertificate: m.GetCertificate} + app.Cfg.Listen.HTTPSCertFile, app.Cfg.Listen.HTTPSKeyFile = "", "" + } else if app.Cfg.Listen.EnableMutualHTTPS { + pool := x509.NewCertPool() + data, err := ioutil.ReadFile(app.Cfg.Listen.TrustCaFile) + if err != nil { + logs.Info("MutualHTTPS should provide TrustCaFile") + return + } + pool.AppendCertsFromPEM(data) + app.Server.TLSConfig = &tls.Config{ + ClientCAs: pool, + ClientAuth: tls.ClientAuthType(app.Cfg.Listen.ClientAuth), + } + } + if err := app.Server.ListenAndServeTLS(app.Cfg.Listen.HTTPSCertFile, app.Cfg.Listen.HTTPSKeyFile); err != nil { + logs.Critical("ListenAndServeTLS: ", err) + time.Sleep(100 * time.Microsecond) + endRunning <- true + } + }() + } + if app.Cfg.Listen.EnableHTTP { + go func() { + app.Server.Addr = addr + logs.Info("http server Running on http://%s", app.Server.Addr) + if app.Cfg.Listen.ListenTCP4 { + ln, err := net.Listen("tcp4", app.Server.Addr) + if err != nil { + logs.Critical("Listen for HTTP[normal mode]: ", err) + time.Sleep(100 * time.Microsecond) + endRunning <- true + return + } + if err = app.Server.Serve(ln); err != nil { + logs.Critical("Serve: ", err) + time.Sleep(100 * time.Microsecond) + endRunning <- true + return + } + } else { + if err := app.Server.ListenAndServe(); err != nil { + logs.Critical("ListenAndServe: ", err) + time.Sleep(100 * time.Microsecond) + endRunning <- true + } + } + }() + } + <-endRunning +} + +// Router see HttpServer.Router +func Router(rootpath string, c ControllerInterface, mappingMethods ...string) *HttpServer { + return RouterWithOpts(rootpath, c, WithRouterMethods(c, mappingMethods...)) +} + +func RouterWithOpts(rootpath string, c ControllerInterface, opts ...ControllerOption) *HttpServer { + return BeeApp.RouterWithOpts(rootpath, c, opts...) +} + +// Router adds a patterned controller handler to BeeApp. +// it's an alias method of HttpServer.Router. +// usage: +// simple router +// beego.Router("/admin", &admin.UserController{}) +// beego.Router("/admin/index", &admin.ArticleController{}) +// +// regex router +// +// beego.Router("/api/:id([0-9]+)", &controllers.RController{}) +// +// custom rules +// beego.Router("/api/list",&RestController{},"*:ListFood") +// beego.Router("/api/create",&RestController{},"post:CreateFood") +// beego.Router("/api/update",&RestController{},"put:UpdateFood") +// beego.Router("/api/delete",&RestController{},"delete:DeleteFood") +func (app *HttpServer) Router(rootPath string, c ControllerInterface, mappingMethods ...string) *HttpServer { + return app.RouterWithOpts(rootPath, c, WithRouterMethods(c, mappingMethods...)) +} + +func (app *HttpServer) RouterWithOpts(rootPath string, c ControllerInterface, opts ...ControllerOption) *HttpServer { + app.Handlers.Add(rootPath, c, opts...) + return app +} + +// UnregisterFixedRoute see HttpServer.UnregisterFixedRoute +func UnregisterFixedRoute(fixedRoute string, method string) *HttpServer { + return BeeApp.UnregisterFixedRoute(fixedRoute, method) +} + +// UnregisterFixedRoute unregisters the route with the specified fixedRoute. It is particularly useful +// in web applications that inherit most routes from a base webapp via the underscore +// import, and aim to overwrite only certain paths. +// The method parameter can be empty or "*" for all HTTP methods, or a particular +// method type (e.g. "GET" or "POST") for selective removal. +// +// Usage (replace "GET" with "*" for all methods): +// beego.UnregisterFixedRoute("/yourpreviouspath", "GET") +// beego.Router("/yourpreviouspath", yourControllerAddress, "get:GetNewPage") +func (app *HttpServer) UnregisterFixedRoute(fixedRoute string, method string) *HttpServer { + subPaths := splitPath(fixedRoute) + if method == "" || method == "*" { + for m := range HTTPMETHOD { + if _, ok := app.Handlers.routers[m]; !ok { + continue + } + if app.Handlers.routers[m].prefix == strings.Trim(fixedRoute, "/ ") { + findAndRemoveSingleTree(app.Handlers.routers[m]) + continue + } + findAndRemoveTree(subPaths, app.Handlers.routers[m], m) + } + return app + } + // Single HTTP method + um := strings.ToUpper(method) + if _, ok := app.Handlers.routers[um]; ok { + if app.Handlers.routers[um].prefix == strings.Trim(fixedRoute, "/ ") { + findAndRemoveSingleTree(app.Handlers.routers[um]) + return app + } + findAndRemoveTree(subPaths, app.Handlers.routers[um], um) + } + return app +} + +func findAndRemoveTree(paths []string, entryPointTree *Tree, method string) { + for i := range entryPointTree.fixrouters { + if entryPointTree.fixrouters[i].prefix == paths[0] { + if len(paths) == 1 { + if len(entryPointTree.fixrouters[i].fixrouters) > 0 { + // If the route had children subtrees, remove just the functional leaf, + // to allow children to function as before + if len(entryPointTree.fixrouters[i].leaves) > 0 { + entryPointTree.fixrouters[i].leaves[0] = nil + entryPointTree.fixrouters[i].leaves = entryPointTree.fixrouters[i].leaves[1:] + } + } else { + // Remove the *Tree from the fixrouters slice + entryPointTree.fixrouters[i] = nil + + if i == len(entryPointTree.fixrouters)-1 { + entryPointTree.fixrouters = entryPointTree.fixrouters[:i] + } else { + entryPointTree.fixrouters = append(entryPointTree.fixrouters[:i], entryPointTree.fixrouters[i+1:len(entryPointTree.fixrouters)]...) + } + } + return + } + findAndRemoveTree(paths[1:], entryPointTree.fixrouters[i], method) + } + } +} + +func findAndRemoveSingleTree(entryPointTree *Tree) { + if entryPointTree == nil { + return + } + if len(entryPointTree.fixrouters) > 0 { + // If the route had children subtrees, remove just the functional leaf, + // to allow children to function as before + if len(entryPointTree.leaves) > 0 { + entryPointTree.leaves[0] = nil + entryPointTree.leaves = entryPointTree.leaves[1:] + } + } +} + +// Include see HttpServer.Include +func Include(cList ...ControllerInterface) *HttpServer { + return BeeApp.Include(cList...) +} + +// Include will generate router file in the router/xxx.go from the controller's comments +// usage: +// beego.Include(&BankAccount{}, &OrderController{},&RefundController{},&ReceiptController{}) +// type BankAccount struct{ +// beego.Controller +// } +// +// register the function +// func (b *BankAccount)Mapping(){ +// b.Mapping("ShowAccount" , b.ShowAccount) +// b.Mapping("ModifyAccount", b.ModifyAccount) +// } +// +// //@router /account/:id [get] +// func (b *BankAccount) ShowAccount(){ +// //logic +// } +// +// +// //@router /account/:id [post] +// func (b *BankAccount) ModifyAccount(){ +// //logic +// } +// +// the comments @router url methodlist +// url support all the function Router's pattern +// methodlist [get post head put delete options *] +func (app *HttpServer) Include(cList ...ControllerInterface) *HttpServer { + app.Handlers.Include(cList...) + return app +} + +// RESTRouter see HttpServer.RESTRouter +func RESTRouter(rootpath string, c ControllerInterface) *HttpServer { + return BeeApp.RESTRouter(rootpath, c) +} + +// RESTRouter adds a restful controller handler to BeeApp. +// its' controller implements beego.ControllerInterface and +// defines a param "pattern/:objectId" to visit each resource. +func (app *HttpServer) RESTRouter(rootpath string, c ControllerInterface) *HttpServer { + app.Router(rootpath, c) + app.Router(path.Join(rootpath, ":objectId"), c) + return app +} + +// AutoRouter see HttpServer.AutoRouter +func AutoRouter(c ControllerInterface) *HttpServer { + return BeeApp.AutoRouter(c) +} + +// AutoRouter adds defined controller handler to BeeApp. +// it's same to HttpServer.AutoRouter. +// if beego.AddAuto(&MainController{}) and MainController has methods List and Page, +// visit the url /main/list to exec List function or /main/page to exec Page function. +func (app *HttpServer) AutoRouter(c ControllerInterface) *HttpServer { + app.Handlers.AddAuto(c) + return app +} + +// AutoPrefix see HttpServer.AutoPrefix +func AutoPrefix(prefix string, c ControllerInterface) *HttpServer { + return BeeApp.AutoPrefix(prefix, c) +} + +// AutoPrefix adds controller handler to BeeApp with prefix. +// it's same to HttpServer.AutoRouterWithPrefix. +// if beego.AutoPrefix("/admin",&MainController{}) and MainController has methods List and Page, +// visit the url /admin/main/list to exec List function or /admin/main/page to exec Page function. +func (app *HttpServer) AutoPrefix(prefix string, c ControllerInterface) *HttpServer { + app.Handlers.AddAutoPrefix(prefix, c) + return app +} + +// CtrlGet see HttpServer.CtrlGet +func CtrlGet(rootpath string, f interface{}) { + BeeApp.CtrlGet(rootpath, f) +} + +// CtrlGet used to register router for CtrlGet method +// usage: +// type MyController struct { +// web.Controller +// } +// func (m MyController) Ping() { +// m.Ctx.Output.Body([]byte("hello world")) +// } +// +// CtrlGet("/api/:id", MyController.Ping) +func (app *HttpServer) CtrlGet(rootpath string, f interface{}) *HttpServer { + app.Handlers.CtrlGet(rootpath, f) + return app +} + +// CtrlPost see HttpServer.CtrlGet +func CtrlPost(rootpath string, f interface{}) { + BeeApp.CtrlPost(rootpath, f) +} + +// CtrlPost used to register router for CtrlPost method +// usage: +// type MyController struct { +// web.Controller +// } +// func (m MyController) Ping() { +// m.Ctx.Output.Body([]byte("hello world")) +// } +// +// CtrlPost("/api/:id", MyController.Ping) +func (app *HttpServer) CtrlPost(rootpath string, f interface{}) *HttpServer { + app.Handlers.CtrlPost(rootpath, f) + return app +} + +// CtrlHead see HttpServer.CtrlHead +func CtrlHead(rootpath string, f interface{}) { + BeeApp.CtrlHead(rootpath, f) +} + +// CtrlHead used to register router for CtrlHead method +// usage: +// type MyController struct { +// web.Controller +// } +// func (m MyController) Ping() { +// m.Ctx.Output.Body([]byte("hello world")) +// } +// +// CtrlHead("/api/:id", MyController.Ping) +func (app *HttpServer) CtrlHead(rootpath string, f interface{}) *HttpServer { + app.Handlers.CtrlHead(rootpath, f) + return app +} + +// CtrlPut see HttpServer.CtrlPut +func CtrlPut(rootpath string, f interface{}) { + BeeApp.CtrlPut(rootpath, f) +} + +// CtrlPut used to register router for CtrlPut method +// usage: +// type MyController struct { +// web.Controller +// } +// func (m MyController) Ping() { +// m.Ctx.Output.Body([]byte("hello world")) +// } +// +// CtrlPut("/api/:id", MyController.Ping) +func (app *HttpServer) CtrlPut(rootpath string, f interface{}) *HttpServer { + app.Handlers.CtrlPut(rootpath, f) + return app +} + +// CtrlPatch see HttpServer.CtrlPatch +func CtrlPatch(rootpath string, f interface{}) { + BeeApp.CtrlPatch(rootpath, f) +} + +// CtrlPatch used to register router for CtrlPatch method +// usage: +// type MyController struct { +// web.Controller +// } +// func (m MyController) Ping() { +// m.Ctx.Output.Body([]byte("hello world")) +// } +// +// CtrlPatch("/api/:id", MyController.Ping) +func (app *HttpServer) CtrlPatch(rootpath string, f interface{}) *HttpServer { + app.Handlers.CtrlPatch(rootpath, f) + return app +} + +// CtrlDelete see HttpServer.CtrlDelete +func CtrlDelete(rootpath string, f interface{}) { + BeeApp.CtrlDelete(rootpath, f) +} + +// CtrlDelete used to register router for CtrlDelete method +// usage: +// type MyController struct { +// web.Controller +// } +// func (m MyController) Ping() { +// m.Ctx.Output.Body([]byte("hello world")) +// } +// +// CtrlDelete("/api/:id", MyController.Ping) +func (app *HttpServer) CtrlDelete(rootpath string, f interface{}) *HttpServer { + app.Handlers.CtrlDelete(rootpath, f) + return app +} + +// CtrlOptions see HttpServer.CtrlOptions +func CtrlOptions(rootpath string, f interface{}) { + BeeApp.CtrlOptions(rootpath, f) +} + +// CtrlOptions used to register router for CtrlOptions method +// usage: +// type MyController struct { +// web.Controller +// } +// func (m MyController) Ping() { +// m.Ctx.Output.Body([]byte("hello world")) +// } +// +// CtrlOptions("/api/:id", MyController.Ping) +func (app *HttpServer) CtrlOptions(rootpath string, f interface{}) *HttpServer { + app.Handlers.CtrlOptions(rootpath, f) + return app +} + +// CtrlAny see HttpServer.CtrlAny +func CtrlAny(rootpath string, f interface{}) { + BeeApp.CtrlAny(rootpath, f) +} + +// CtrlAny used to register router for CtrlAny method +// usage: +// type MyController struct { +// web.Controller +// } +// func (m MyController) Ping() { +// m.Ctx.Output.Body([]byte("hello world")) +// } +// +// CtrlAny("/api/:id", MyController.Ping) +func (app *HttpServer) CtrlAny(rootpath string, f interface{}) *HttpServer { + app.Handlers.CtrlAny(rootpath, f) + return app +} + +// Get see HttpServer.Get +func Get(rootpath string, f HandleFunc) *HttpServer { + return BeeApp.Get(rootpath, f) +} + +// Get used to register router for Get method +// usage: +// beego.Get("/", func(ctx *context.Context){ +// ctx.Output.Body("hello world") +// }) +func (app *HttpServer) Get(rootpath string, f HandleFunc) *HttpServer { + app.Handlers.Get(rootpath, f) + return app +} + +// Post see HttpServer.Post +func Post(rootpath string, f HandleFunc) *HttpServer { + return BeeApp.Post(rootpath, f) +} + +// Post used to register router for Post method +// usage: +// beego.Post("/api", func(ctx *context.Context){ +// ctx.Output.Body("hello world") +// }) +func (app *HttpServer) Post(rootpath string, f HandleFunc) *HttpServer { + app.Handlers.Post(rootpath, f) + return app +} + +// Delete see HttpServer.Delete +func Delete(rootpath string, f HandleFunc) *HttpServer { + return BeeApp.Delete(rootpath, f) +} + +// Delete used to register router for Delete method +// usage: +// beego.Delete("/api", func(ctx *context.Context){ +// ctx.Output.Body("hello world") +// }) +func (app *HttpServer) Delete(rootpath string, f HandleFunc) *HttpServer { + app.Handlers.Delete(rootpath, f) + return app +} + +// Put see HttpServer.Put +func Put(rootpath string, f HandleFunc) *HttpServer { + return BeeApp.Put(rootpath, f) +} + +// Put used to register router for Put method +// usage: +// beego.Put("/api", func(ctx *context.Context){ +// ctx.Output.Body("hello world") +// }) +func (app *HttpServer) Put(rootpath string, f HandleFunc) *HttpServer { + app.Handlers.Put(rootpath, f) + return app +} + +// Head see HttpServer.Head +func Head(rootpath string, f HandleFunc) *HttpServer { + return BeeApp.Head(rootpath, f) +} + +// Head used to register router for Head method +// usage: +// beego.Head("/api", func(ctx *context.Context){ +// ctx.Output.Body("hello world") +// }) +func (app *HttpServer) Head(rootpath string, f HandleFunc) *HttpServer { + app.Handlers.Head(rootpath, f) + return app +} + +// Options see HttpServer.Options +func Options(rootpath string, f HandleFunc) *HttpServer { + BeeApp.Handlers.Options(rootpath, f) + return BeeApp +} + +// Options used to register router for Options method +// usage: +// beego.Options("/api", func(ctx *context.Context){ +// ctx.Output.Body("hello world") +// }) +func (app *HttpServer) Options(rootpath string, f HandleFunc) *HttpServer { + app.Handlers.Options(rootpath, f) + return app +} + +// Patch see HttpServer.Patch +func Patch(rootpath string, f HandleFunc) *HttpServer { + return BeeApp.Patch(rootpath, f) +} + +// Patch used to register router for Patch method +// usage: +// beego.Patch("/api", func(ctx *context.Context){ +// ctx.Output.Body("hello world") +// }) +func (app *HttpServer) Patch(rootpath string, f HandleFunc) *HttpServer { + app.Handlers.Patch(rootpath, f) + return app +} + +// Any see HttpServer.Any +func Any(rootpath string, f HandleFunc) *HttpServer { + return BeeApp.Any(rootpath, f) +} + +// Any used to register router for all methods +// usage: +// beego.Any("/api", func(ctx *context.Context){ +// ctx.Output.Body("hello world") +// }) +func (app *HttpServer) Any(rootpath string, f HandleFunc) *HttpServer { + app.Handlers.Any(rootpath, f) + return app +} + +// Handler see HttpServer.Handler +func Handler(rootpath string, h http.Handler, options ...interface{}) *HttpServer { + return BeeApp.Handler(rootpath, h, options...) +} + +// Handler used to register a Handler router +// usage: +// beego.Handler("/api", http.HandlerFunc(func (w http.ResponseWriter, r *http.Request) { +// fmt.Fprintf(w, "Hello, %q", html.EscapeString(r.URL.Path)) +// })) +func (app *HttpServer) Handler(rootpath string, h http.Handler, options ...interface{}) *HttpServer { + app.Handlers.Handler(rootpath, h, options...) + return app +} + +// InserFilter see HttpServer.InsertFilter +func InsertFilter(pattern string, pos int, filter FilterFunc, opts ...FilterOpt) *HttpServer { + return BeeApp.InsertFilter(pattern, pos, filter, opts...) +} + +// InsertFilter adds a FilterFunc with pattern condition and action constant. +// The pos means action constant including +// beego.BeforeStatic, beego.BeforeRouter, beego.BeforeExec, beego.AfterExec and beego.FinishRouter. +// The bool params is for setting the returnOnOutput value (false allows multiple filters to execute) +func (app *HttpServer) InsertFilter(pattern string, pos int, filter FilterFunc, opts ...FilterOpt) *HttpServer { + app.Handlers.InsertFilter(pattern, pos, filter, opts...) + return app +} + +// InsertFilterChain see HttpServer.InsertFilterChain +func InsertFilterChain(pattern string, filterChain FilterChain, opts ...FilterOpt) *HttpServer { + return BeeApp.InsertFilterChain(pattern, filterChain, opts...) +} + +// InsertFilterChain adds a FilterFunc built by filterChain. +// This filter will be executed before all filters. +// the filter's behavior like stack's behavior +// and the last filter is serving the http request +func (app *HttpServer) InsertFilterChain(pattern string, filterChain FilterChain, opts ...FilterOpt) *HttpServer { + app.Handlers.InsertFilterChain(pattern, filterChain, opts...) + return app +} + +func (app *HttpServer) initAddr(addr string) { + strs := strings.Split(addr, ":") + if len(strs) > 0 && strs[0] != "" { + app.Cfg.Listen.HTTPAddr = strs[0] + app.Cfg.Listen.Domains = []string{strs[0]} + } + if len(strs) > 1 && strs[1] != "" { + app.Cfg.Listen.HTTPPort, _ = strconv.Atoi(strs[1]) + } +} + +func (app *HttpServer) LogAccess(ctx *beecontext.Context, startTime *time.Time, statusCode int) { + // Skip logging if AccessLogs config is false + if !app.Cfg.Log.AccessLogs { + return + } + // Skip logging static requests unless EnableStaticLogs config is true + if !app.Cfg.Log.EnableStaticLogs && DefaultAccessLogFilter.Filter(ctx) { + return + } + var ( + requestTime time.Time + elapsedTime time.Duration + r = ctx.Request + ) + if startTime != nil { + requestTime = *startTime + elapsedTime = time.Since(*startTime) + } + record := &logs.AccessLogRecord{ + RemoteAddr: ctx.Input.IP(), + RequestTime: requestTime, + RequestMethod: r.Method, + Request: fmt.Sprintf("%s %s %s", r.Method, r.RequestURI, r.Proto), + ServerProtocol: r.Proto, + Host: r.Host, + Status: statusCode, + ElapsedTime: elapsedTime, + HTTPReferrer: r.Header.Get("Referer"), + HTTPUserAgent: r.Header.Get("User-Agent"), + RemoteUser: r.Header.Get("Remote-User"), + BodyBytesSent: r.ContentLength, + } + logs.AccessLog(record, app.Cfg.Log.AccessLogsFormat) +} + +// PrintTree prints all registered routers. +func (app *HttpServer) PrintTree() M { + var ( + content = M{} + methods = []string{} + methodsData = make(M) + ) + for method, t := range app.Handlers.routers { + + resultList := new([][]string) + + printTree(resultList, t) + + methods = append(methods, template.HTMLEscapeString(method)) + methodsData[template.HTMLEscapeString(method)] = resultList + } + + content["Data"] = methodsData + content["Methods"] = methods + return content +} + +func printTree(resultList *[][]string, t *Tree) { + for _, tr := range t.fixrouters { + printTree(resultList, tr) + } + if t.wildcard != nil { + printTree(resultList, t.wildcard) + } + for _, l := range t.leaves { + if v, ok := l.runObject.(*ControllerInfo); ok { + if v.routerType == routerTypeBeego { + result := []string{ + template.HTMLEscapeString(v.pattern), + template.HTMLEscapeString(fmt.Sprintf("%s", v.methods)), + template.HTMLEscapeString(v.controllerType.String()), + } + *resultList = append(*resultList, result) + } else if v.routerType == routerTypeRESTFul { + result := []string{ + template.HTMLEscapeString(v.pattern), + template.HTMLEscapeString(fmt.Sprintf("%s", v.methods)), + "", + } + *resultList = append(*resultList, result) + } else if v.routerType == routerTypeHandler { + result := []string{ + template.HTMLEscapeString(v.pattern), + "", + "", + } + *resultList = append(*resultList, result) + } + } + } +} + +func (app *HttpServer) reportFilter() M { + filterTypeData := make(M) + // filterTypes := []string{} + if app.Handlers.enableFilter { + // var filterType string + for k, fr := range map[int]string{ + BeforeStatic: "Before Static", + BeforeRouter: "Before Router", + BeforeExec: "Before Exec", + AfterExec: "After Exec", + FinishRouter: "Finish Router", + } { + if bf := app.Handlers.filters[k]; len(bf) > 0 { + resultList := new([][]string) + for _, f := range bf { + result := []string{ + // void xss + template.HTMLEscapeString(f.pattern), + template.HTMLEscapeString(utils.GetFuncName(f.filterFunc)), + } + *resultList = append(*resultList, result) + } + filterTypeData[fr] = resultList + } + } + } + + return filterTypeData +} diff --git a/src/vendor/github.com/beego/beego/session/README.md b/src/vendor/github.com/beego/beego/v2/server/web/session/README.md similarity index 61% rename from src/vendor/github.com/beego/beego/session/README.md rename to src/vendor/github.com/beego/beego/v2/server/web/session/README.md index a7cd5fa1e..8dd70f67d 100644 --- a/src/vendor/github.com/beego/beego/session/README.md +++ b/src/vendor/github.com/beego/beego/v2/server/web/session/README.md @@ -1,67 +1,66 @@ session ============== -session is a Go session manager. It can use many session providers. Just like the `database/sql` and `database/sql/driver`. +session is a Go session manager. It can use many session providers. Just like the `database/sql` +and `database/sql/driver`. ## How to install? - go get github.com/beego/beego/session - + go get github.com/beego/beego/v2/server/web/session ## What providers are supported? As of now this session manager support memory, file, Redis and MySQL. - ## How to use it? First you must import it import ( - "github.com/beego/beego/session" + "github.com/beego/beego/v2/server/web/session" ) Then in you web app init the global session manager - + var globalSessions *session.Manager * Use **memory** as provider: - func init() { - globalSessions, _ = session.NewManager("memory", `{"cookieName":"gosessionid","gclifetime":3600}`) - go globalSessions.GC() - } + func init() { + globalSessions, _ = session.NewManager("memory", `{"cookieName":"gosessionid","gclifetime":3600}`) + go globalSessions.GC() + } * Use **file** as provider, the last param is the path where you want file to be stored: - func init() { - globalSessions, _ = session.NewManager("file",`{"cookieName":"gosessionid","gclifetime":3600,"ProviderConfig":"./tmp"}`) - go globalSessions.GC() - } + func init() { + globalSessions, _ = session.NewManager("file",`{"cookieName":"gosessionid","gclifetime":3600,"ProviderConfig":"./tmp"}`) + go globalSessions.GC() + } * Use **Redis** as provider, the last param is the Redis conn address,poolsize,password: - func init() { - globalSessions, _ = session.NewManager("redis", `{"cookieName":"gosessionid","gclifetime":3600,"ProviderConfig":"127.0.0.1:6379,100,astaxie"}`) - go globalSessions.GC() - } - -* Use **MySQL** as provider, the last param is the DSN, learn more from [mysql](https://github.com/go-sql-driver/mysql#dsn-data-source-name): + func init() { + globalSessions, _ = session.NewManager("redis", `{"cookieName":"gosessionid","gclifetime":3600,"ProviderConfig":"127.0.0.1:6379,100,astaxie"}`) + go globalSessions.GC() + } - func init() { - globalSessions, _ = session.NewManager( - "mysql", `{"cookieName":"gosessionid","gclifetime":3600,"ProviderConfig":"username:password@protocol(address)/dbname?param=value"}`) - go globalSessions.GC() - } +* Use **MySQL** as provider, the last param is the DSN, learn more + from [mysql](https://github.com/go-sql-driver/mysql#dsn-data-source-name): + + func init() { + globalSessions, _ = session.NewManager( + "mysql", `{"cookieName":"gosessionid","gclifetime":3600,"ProviderConfig":"username:password@protocol(address)/dbname?param=value"}`) + go globalSessions.GC() + } * Use **Cookie** as provider: - func init() { - globalSessions, _ = session.NewManager( - "cookie", `{"cookieName":"gosessionid","enableSetCookie":false,"gclifetime":3600,"ProviderConfig":"{\"cookieName\":\"gosessionid\",\"securityKey\":\"beegocookiehashkey\"}"}`) - go globalSessions.GC() - } - + func init() { + globalSessions, _ = session.NewManager( + "cookie", `{"cookieName":"gosessionid","enableSetCookie":false,"gclifetime":3600,"ProviderConfig":"{\"cookieName\":\"gosessionid\",\"securityKey\":\"beegocookiehashkey\"}"}`) + go globalSessions.GC() + } Finally in the handlerfunc you can use it like this @@ -80,14 +79,13 @@ Finally in the handlerfunc you can use it like this } } - ## How to write own provider? When you develop a web app, maybe you want to write own provider because you must meet the requirements. -Writing a provider is easy. You only need to define two struct types -(Session and Provider), which satisfy the interface definition. -Maybe you will find the **memory** provider is a good example. +Writing a provider is easy. You only need to define two struct types +(Session and Provider), which satisfy the interface definition. Maybe you will find the **memory** provider is a good +example. type SessionStore interface { Set(key, value interface{}) error //set session value @@ -101,14 +99,13 @@ Maybe you will find the **memory** provider is a good example. type Provider interface { SessionInit(gclifetime int64, config string) error SessionRead(sid string) (SessionStore, error) - SessionExist(sid string) bool + SessionExist(sid string) (bool, error) SessionRegenerate(oldsid, sid string) (SessionStore, error) SessionDestroy(sid string) error SessionAll() int //get all active session SessionGC() } - ## LICENSE BSD License http://creativecommons.org/licenses/BSD/ diff --git a/src/vendor/github.com/beego/beego/session/sess_cookie.go b/src/vendor/github.com/beego/beego/v2/server/web/session/sess_cookie.go similarity index 75% rename from src/vendor/github.com/beego/beego/session/sess_cookie.go rename to src/vendor/github.com/beego/beego/v2/server/web/session/sess_cookie.go index 6ad5debc3..622fb2fe2 100644 --- a/src/vendor/github.com/beego/beego/session/sess_cookie.go +++ b/src/vendor/github.com/beego/beego/v2/server/web/session/sess_cookie.go @@ -15,6 +15,7 @@ package session import ( + "context" "crypto/aes" "crypto/cipher" "encoding/json" @@ -34,7 +35,7 @@ type CookieSessionStore struct { // Set value to cookie session. // the value are encoded as gob with hash block string. -func (st *CookieSessionStore) Set(key, value interface{}) error { +func (st *CookieSessionStore) Set(ctx context.Context, key, value interface{}) error { st.lock.Lock() defer st.lock.Unlock() st.values[key] = value @@ -42,7 +43,7 @@ func (st *CookieSessionStore) Set(key, value interface{}) error { } // Get value from cookie session -func (st *CookieSessionStore) Get(key interface{}) interface{} { +func (st *CookieSessionStore) Get(ctx context.Context, key interface{}) interface{} { st.lock.RLock() defer st.lock.RUnlock() if v, ok := st.values[key]; ok { @@ -52,7 +53,7 @@ func (st *CookieSessionStore) Get(key interface{}) interface{} { } // Delete value in cookie session -func (st *CookieSessionStore) Delete(key interface{}) error { +func (st *CookieSessionStore) Delete(ctx context.Context, key interface{}) error { st.lock.Lock() defer st.lock.Unlock() delete(st.values, key) @@ -60,7 +61,7 @@ func (st *CookieSessionStore) Delete(key interface{}) error { } // Flush Clean all values in cookie session -func (st *CookieSessionStore) Flush() error { +func (st *CookieSessionStore) Flush(context.Context) error { st.lock.Lock() defer st.lock.Unlock() st.values = make(map[interface{}]interface{}) @@ -68,22 +69,24 @@ func (st *CookieSessionStore) Flush() error { } // SessionID Return id of this cookie session -func (st *CookieSessionStore) SessionID() string { +func (st *CookieSessionStore) SessionID(context.Context) string { return st.sid } // SessionRelease Write cookie session to http response cookie -func (st *CookieSessionStore) SessionRelease(w http.ResponseWriter) { +func (st *CookieSessionStore) SessionRelease(ctx context.Context, w http.ResponseWriter) { st.lock.Lock() encodedCookie, err := encodeCookie(cookiepder.block, cookiepder.config.SecurityKey, cookiepder.config.SecurityName, st.values) st.lock.Unlock() if err == nil { - cookie := &http.Cookie{Name: cookiepder.config.CookieName, + cookie := &http.Cookie{ + Name: cookiepder.config.CookieName, Value: url.QueryEscape(encodedCookie), Path: "/", HttpOnly: true, Secure: cookiepder.config.Secure, - MaxAge: cookiepder.config.Maxage} + MaxAge: cookiepder.config.Maxage, + } http.SetCookie(w, cookie) } } @@ -112,7 +115,7 @@ type CookieProvider struct { // securityName - recognized name in encoded cookie string // cookieName - cookie name // maxage - cookie max life time. -func (pder *CookieProvider) SessionInit(maxlifetime int64, config string) error { +func (pder *CookieProvider) SessionInit(ctx context.Context, maxlifetime int64, config string) error { pder.config = &cookieConfig{} err := json.Unmarshal([]byte(config), pder.config) if err != nil { @@ -134,7 +137,7 @@ func (pder *CookieProvider) SessionInit(maxlifetime int64, config string) error // SessionRead Get SessionStore in cooke. // decode cooke string to map and put into SessionStore with sid. -func (pder *CookieProvider) SessionRead(sid string) (Store, error) { +func (pder *CookieProvider) SessionRead(ctx context.Context, sid string) (Store, error) { maps, _ := decodeCookie(pder.block, pder.config.SecurityKey, pder.config.SecurityName, @@ -147,31 +150,31 @@ func (pder *CookieProvider) SessionRead(sid string) (Store, error) { } // SessionExist Cookie session is always existed -func (pder *CookieProvider) SessionExist(sid string) bool { - return true +func (pder *CookieProvider) SessionExist(ctx context.Context, sid string) (bool, error) { + return true, nil } // SessionRegenerate Implement method, no used. -func (pder *CookieProvider) SessionRegenerate(oldsid, sid string) (Store, error) { +func (pder *CookieProvider) SessionRegenerate(ctx context.Context, oldsid, sid string) (Store, error) { return nil, nil } // SessionDestroy Implement method, no used. -func (pder *CookieProvider) SessionDestroy(sid string) error { +func (pder *CookieProvider) SessionDestroy(ctx context.Context, sid string) error { return nil } // SessionGC Implement method, no used. -func (pder *CookieProvider) SessionGC() { +func (pder *CookieProvider) SessionGC(context.Context) { } // SessionAll Implement method, return 0. -func (pder *CookieProvider) SessionAll() int { +func (pder *CookieProvider) SessionAll(context.Context) int { return 0 } // SessionUpdate Implement method, no used. -func (pder *CookieProvider) SessionUpdate(sid string) error { +func (pder *CookieProvider) SessionUpdate(ctx context.Context, sid string) error { return nil } diff --git a/src/vendor/github.com/beego/beego/session/sess_file.go b/src/vendor/github.com/beego/beego/v2/server/web/session/sess_file.go similarity index 77% rename from src/vendor/github.com/beego/beego/session/sess_file.go rename to src/vendor/github.com/beego/beego/v2/server/web/session/sess_file.go index 47ad54a7f..14cec1d9a 100644 --- a/src/vendor/github.com/beego/beego/session/sess_file.go +++ b/src/vendor/github.com/beego/beego/v2/server/web/session/sess_file.go @@ -15,6 +15,7 @@ package session import ( + "context" "errors" "fmt" "io/ioutil" @@ -40,7 +41,7 @@ type FileSessionStore struct { } // Set value to file session -func (fs *FileSessionStore) Set(key, value interface{}) error { +func (fs *FileSessionStore) Set(ctx context.Context, key, value interface{}) error { fs.lock.Lock() defer fs.lock.Unlock() fs.values[key] = value @@ -48,7 +49,7 @@ func (fs *FileSessionStore) Set(key, value interface{}) error { } // Get value from file session -func (fs *FileSessionStore) Get(key interface{}) interface{} { +func (fs *FileSessionStore) Get(ctx context.Context, key interface{}) interface{} { fs.lock.RLock() defer fs.lock.RUnlock() if v, ok := fs.values[key]; ok { @@ -58,7 +59,7 @@ func (fs *FileSessionStore) Get(key interface{}) interface{} { } // Delete value in file session by given key -func (fs *FileSessionStore) Delete(key interface{}) error { +func (fs *FileSessionStore) Delete(ctx context.Context, key interface{}) error { fs.lock.Lock() defer fs.lock.Unlock() delete(fs.values, key) @@ -66,7 +67,7 @@ func (fs *FileSessionStore) Delete(key interface{}) error { } // Flush Clean all values in file session -func (fs *FileSessionStore) Flush() error { +func (fs *FileSessionStore) Flush(context.Context) error { fs.lock.Lock() defer fs.lock.Unlock() fs.values = make(map[interface{}]interface{}) @@ -74,12 +75,12 @@ func (fs *FileSessionStore) Flush() error { } // SessionID Get file session store id -func (fs *FileSessionStore) SessionID() string { +func (fs *FileSessionStore) SessionID(context.Context) string { return fs.sid } // SessionRelease Write file session to local file with Gob string -func (fs *FileSessionStore) SessionRelease(w http.ResponseWriter) { +func (fs *FileSessionStore) SessionRelease(ctx context.Context, w http.ResponseWriter) { filepder.lock.Lock() defer filepder.lock.Unlock() b, err := EncodeGob(fs.values) @@ -90,7 +91,7 @@ func (fs *FileSessionStore) SessionRelease(w http.ResponseWriter) { _, err = os.Stat(path.Join(filepder.savePath, string(fs.sid[0]), string(fs.sid[1]), fs.sid)) var f *os.File if err == nil { - f, err = os.OpenFile(path.Join(filepder.savePath, string(fs.sid[0]), string(fs.sid[1]), fs.sid), os.O_RDWR, 0777) + f, err = os.OpenFile(path.Join(filepder.savePath, string(fs.sid[0]), string(fs.sid[1]), fs.sid), os.O_RDWR, 0o777) if err != nil { SLogger.Println(err) return @@ -119,7 +120,7 @@ type FileProvider struct { // SessionInit Init file session provider. // savePath sets the session files path. -func (fp *FileProvider) SessionInit(maxlifetime int64, savePath string) error { +func (fp *FileProvider) SessionInit(ctx context.Context, maxlifetime int64, savePath string) error { fp.maxlifetime = maxlifetime fp.savePath = savePath return nil @@ -128,7 +129,7 @@ func (fp *FileProvider) SessionInit(maxlifetime int64, savePath string) error { // SessionRead Read file session by sid. // if file is not exist, create it. // the file path is generated from sid string. -func (fp *FileProvider) SessionRead(sid string) (Store, error) { +func (fp *FileProvider) SessionRead(ctx context.Context, sid string) (Store, error) { invalidChars := "./" if strings.ContainsAny(sid, invalidChars) { return nil, errors.New("the sid shouldn't have following characters: " + invalidChars) @@ -139,23 +140,32 @@ func (fp *FileProvider) SessionRead(sid string) (Store, error) { filepder.lock.Lock() defer filepder.lock.Unlock() - err := os.MkdirAll(path.Join(fp.savePath, string(sid[0]), string(sid[1])), 0755) + sessionPath := filepath.Join(fp.savePath, string(sid[0]), string(sid[1])) + sidPath := filepath.Join(sessionPath, sid) + err := os.MkdirAll(sessionPath, 0o755) if err != nil { SLogger.Println(err.Error()) } - _, err = os.Stat(path.Join(fp.savePath, string(sid[0]), string(sid[1]), sid)) var f *os.File - if err == nil { - f, err = os.OpenFile(path.Join(fp.savePath, string(sid[0]), string(sid[1]), sid), os.O_RDWR, 0777) - } else if os.IsNotExist(err) { - f, err = os.Create(path.Join(fp.savePath, string(sid[0]), string(sid[1]), sid)) - } else { + _, err = os.Stat(sidPath) + switch { + case err == nil: + f, err = os.OpenFile(sidPath, os.O_RDWR, 0o777) + if err != nil { + return nil, err + } + case os.IsNotExist(err): + f, err = os.Create(sidPath) + if err != nil { + return nil, err + } + default: return nil, err } defer f.Close() - os.Chtimes(path.Join(fp.savePath, string(sid[0]), string(sid[1]), sid), time.Now(), time.Now()) + os.Chtimes(sidPath, time.Now(), time.Now()) var kv map[interface{}]interface{} b, err := ioutil.ReadAll(f) if err != nil { @@ -176,21 +186,21 @@ func (fp *FileProvider) SessionRead(sid string) (Store, error) { // SessionExist Check file session exist. // it checks the file named from sid exist or not. -func (fp *FileProvider) SessionExist(sid string) bool { +func (fp *FileProvider) SessionExist(ctx context.Context, sid string) (bool, error) { filepder.lock.Lock() defer filepder.lock.Unlock() if len(sid) < 2 { - SLogger.Println("min length of session id is 2", sid) - return false + SLogger.Println("min length of session id is 2 but got length: ", sid) + return false, errors.New("min length of session id is 2") } _, err := os.Stat(path.Join(fp.savePath, string(sid[0]), string(sid[1]), sid)) - return err == nil + return err == nil, nil } // SessionDestroy Remove all files in this save path -func (fp *FileProvider) SessionDestroy(sid string) error { +func (fp *FileProvider) SessionDestroy(ctx context.Context, sid string) error { filepder.lock.Lock() defer filepder.lock.Unlock() os.Remove(path.Join(fp.savePath, string(sid[0]), string(sid[1]), sid)) @@ -198,7 +208,7 @@ func (fp *FileProvider) SessionDestroy(sid string) error { } // SessionGC Recycle files in save path -func (fp *FileProvider) SessionGC() { +func (fp *FileProvider) SessionGC(context.Context) { filepder.lock.Lock() defer filepder.lock.Unlock() @@ -208,11 +218,9 @@ func (fp *FileProvider) SessionGC() { // SessionAll Get active file session number. // it walks save path to count files. -func (fp *FileProvider) SessionAll() int { +func (fp *FileProvider) SessionAll(context.Context) int { a := &activeSession{} - err := filepath.Walk(fp.savePath, func(path string, f os.FileInfo, err error) error { - return a.visit(path, f, err) - }) + err := filepath.Walk(fp.savePath, a.visit) if err != nil { SLogger.Printf("filepath.Walk() returned %v\n", err) return 0 @@ -222,7 +230,7 @@ func (fp *FileProvider) SessionAll() int { // SessionRegenerate Generate new sid for file session. // it delete old file and create new file named from new sid. -func (fp *FileProvider) SessionRegenerate(oldsid, sid string) (Store, error) { +func (fp *FileProvider) SessionRegenerate(ctx context.Context, oldsid, sid string) (Store, error) { filepder.lock.Lock() defer filepder.lock.Unlock() @@ -237,7 +245,7 @@ func (fp *FileProvider) SessionRegenerate(oldsid, sid string) (Store, error) { return nil, fmt.Errorf("newsid %s exist", newSidFile) } - err = os.MkdirAll(newPath, 0755) + err = os.MkdirAll(newPath, 0o755) if err != nil { SLogger.Println(err.Error()) } @@ -264,7 +272,7 @@ func (fp *FileProvider) SessionRegenerate(oldsid, sid string) (Store, error) { } } - ioutil.WriteFile(newSidFile, b, 0777) + ioutil.WriteFile(newSidFile, b, 0o777) os.Remove(oldSidFile) os.Chtimes(newSidFile, time.Now(), time.Now()) ss := &FileSessionStore{sid: sid, values: kv} diff --git a/src/vendor/github.com/beego/beego/session/sess_mem.go b/src/vendor/github.com/beego/beego/v2/server/web/session/sess_mem.go similarity index 75% rename from src/vendor/github.com/beego/beego/session/sess_mem.go rename to src/vendor/github.com/beego/beego/v2/server/web/session/sess_mem.go index 64d8b0561..b0a821ba6 100644 --- a/src/vendor/github.com/beego/beego/session/sess_mem.go +++ b/src/vendor/github.com/beego/beego/v2/server/web/session/sess_mem.go @@ -16,6 +16,7 @@ package session import ( "container/list" + "context" "net/http" "sync" "time" @@ -26,14 +27,14 @@ var mempder = &MemProvider{list: list.New(), sessions: make(map[string]*list.Ele // MemSessionStore memory session store. // it saved sessions in a map in memory. type MemSessionStore struct { - sid string //session id - timeAccessed time.Time //last access time - value map[interface{}]interface{} //session store + sid string // session id + timeAccessed time.Time // last access time + value map[interface{}]interface{} // session store lock sync.RWMutex } // Set value to memory session -func (st *MemSessionStore) Set(key, value interface{}) error { +func (st *MemSessionStore) Set(ctx context.Context, key, value interface{}) error { st.lock.Lock() defer st.lock.Unlock() st.value[key] = value @@ -41,7 +42,7 @@ func (st *MemSessionStore) Set(key, value interface{}) error { } // Get value from memory session by key -func (st *MemSessionStore) Get(key interface{}) interface{} { +func (st *MemSessionStore) Get(ctx context.Context, key interface{}) interface{} { st.lock.RLock() defer st.lock.RUnlock() if v, ok := st.value[key]; ok { @@ -51,7 +52,7 @@ func (st *MemSessionStore) Get(key interface{}) interface{} { } // Delete in memory session by key -func (st *MemSessionStore) Delete(key interface{}) error { +func (st *MemSessionStore) Delete(ctx context.Context, key interface{}) error { st.lock.Lock() defer st.lock.Unlock() delete(st.value, key) @@ -59,7 +60,7 @@ func (st *MemSessionStore) Delete(key interface{}) error { } // Flush clear all values in memory session -func (st *MemSessionStore) Flush() error { +func (st *MemSessionStore) Flush(context.Context) error { st.lock.Lock() defer st.lock.Unlock() st.value = make(map[interface{}]interface{}) @@ -67,12 +68,12 @@ func (st *MemSessionStore) Flush() error { } // SessionID get this id of memory session store -func (st *MemSessionStore) SessionID() string { +func (st *MemSessionStore) SessionID(context.Context) string { return st.sid } // SessionRelease Implement method, no used. -func (st *MemSessionStore) SessionRelease(w http.ResponseWriter) { +func (st *MemSessionStore) SessionRelease(ctx context.Context, w http.ResponseWriter) { } // MemProvider Implement the provider interface @@ -85,17 +86,17 @@ type MemProvider struct { } // SessionInit init memory session -func (pder *MemProvider) SessionInit(maxlifetime int64, savePath string) error { +func (pder *MemProvider) SessionInit(ctx context.Context, maxlifetime int64, savePath string) error { pder.maxlifetime = maxlifetime pder.savePath = savePath return nil } // SessionRead get memory session store by sid -func (pder *MemProvider) SessionRead(sid string) (Store, error) { +func (pder *MemProvider) SessionRead(ctx context.Context, sid string) (Store, error) { pder.lock.RLock() if element, ok := pder.sessions[sid]; ok { - go pder.SessionUpdate(sid) + go pder.SessionUpdate(nil, sid) pder.lock.RUnlock() return element.Value.(*MemSessionStore), nil } @@ -109,20 +110,20 @@ func (pder *MemProvider) SessionRead(sid string) (Store, error) { } // SessionExist check session store exist in memory session by sid -func (pder *MemProvider) SessionExist(sid string) bool { +func (pder *MemProvider) SessionExist(ctx context.Context, sid string) (bool, error) { pder.lock.RLock() defer pder.lock.RUnlock() if _, ok := pder.sessions[sid]; ok { - return true + return true, nil } - return false + return false, nil } // SessionRegenerate generate new sid for session store in memory session -func (pder *MemProvider) SessionRegenerate(oldsid, sid string) (Store, error) { +func (pder *MemProvider) SessionRegenerate(ctx context.Context, oldsid, sid string) (Store, error) { pder.lock.RLock() if element, ok := pder.sessions[oldsid]; ok { - go pder.SessionUpdate(oldsid) + go pder.SessionUpdate(nil, oldsid) pder.lock.RUnlock() pder.lock.Lock() element.Value.(*MemSessionStore).sid = sid @@ -141,7 +142,7 @@ func (pder *MemProvider) SessionRegenerate(oldsid, sid string) (Store, error) { } // SessionDestroy delete session store in memory session by id -func (pder *MemProvider) SessionDestroy(sid string) error { +func (pder *MemProvider) SessionDestroy(ctx context.Context, sid string) error { pder.lock.Lock() defer pder.lock.Unlock() if element, ok := pder.sessions[sid]; ok { @@ -153,7 +154,7 @@ func (pder *MemProvider) SessionDestroy(sid string) error { } // SessionGC clean expired session stores in memory session -func (pder *MemProvider) SessionGC() { +func (pder *MemProvider) SessionGC(context.Context) { pder.lock.RLock() for { element := pder.list.Back() @@ -175,12 +176,12 @@ func (pder *MemProvider) SessionGC() { } // SessionAll get count number of memory session -func (pder *MemProvider) SessionAll() int { +func (pder *MemProvider) SessionAll(context.Context) int { return pder.list.Len() } // SessionUpdate expand time of session store by id in memory session -func (pder *MemProvider) SessionUpdate(sid string) error { +func (pder *MemProvider) SessionUpdate(ctx context.Context, sid string) error { pder.lock.Lock() defer pder.lock.Unlock() if element, ok := pder.sessions[sid]; ok { diff --git a/src/vendor/github.com/beego/beego/session/sess_utils.go b/src/vendor/github.com/beego/beego/v2/server/web/session/sess_utils.go similarity index 98% rename from src/vendor/github.com/beego/beego/session/sess_utils.go rename to src/vendor/github.com/beego/beego/v2/server/web/session/sess_utils.go index a22ac6308..b26493367 100644 --- a/src/vendor/github.com/beego/beego/session/sess_utils.go +++ b/src/vendor/github.com/beego/beego/v2/server/web/session/sess_utils.go @@ -29,7 +29,7 @@ import ( "strconv" "time" - "github.com/beego/beego/utils" + "github.com/beego/beego/v2/core/utils" ) func init() { @@ -98,7 +98,7 @@ func encrypt(block cipher.Block, value []byte) ([]byte, error) { // decrypt decrypts a value using the given block in counter mode. // -// The value to be decrypted must be prepended by a initialization vector +// The value to be decrypted must be prepended by an initialization vector // (http://goo.gl/zF67k) with the length of the block size. func decrypt(block cipher.Block, value []byte) ([]byte, error) { size := block.BlockSize() diff --git a/src/vendor/github.com/beego/beego/session/session.go b/src/vendor/github.com/beego/beego/v2/server/web/session/session.go similarity index 76% rename from src/vendor/github.com/beego/beego/session/session.go rename to src/vendor/github.com/beego/beego/v2/server/web/session/session.go index 024c5eefe..b5300abc5 100644 --- a/src/vendor/github.com/beego/beego/session/session.go +++ b/src/vendor/github.com/beego/beego/v2/server/web/session/session.go @@ -16,7 +16,7 @@ // // Usage: // import( -// "github.com/beego/beego/session" +// "github.com/beego/beego/v2/server/web/session" // ) // // func init() { @@ -24,10 +24,11 @@ // go globalSessions.GC() // } // -// more docs: http://beego.me/docs/module/session.md +// more docs: http://beego.vip/docs/module/session.md package session import ( + "context" "crypto/rand" "encoding/hex" "errors" @@ -43,24 +44,24 @@ import ( // Store contains all data for one session process with specific id. type Store interface { - Set(key, value interface{}) error //set session value - Get(key interface{}) interface{} //get session value - Delete(key interface{}) error //delete session value - SessionID() string //back current sessionID - SessionRelease(w http.ResponseWriter) // release the resource & save data to provider & return the data - Flush() error //delete all data + Set(ctx context.Context, key, value interface{}) error // set session value + Get(ctx context.Context, key interface{}) interface{} // get session value + Delete(ctx context.Context, key interface{}) error // delete session value + SessionID(ctx context.Context) string // back current sessionID + SessionRelease(ctx context.Context, w http.ResponseWriter) // release the resource & save data to provider & return the data + Flush(ctx context.Context) error // delete all data } // Provider contains global session methods and saved SessionStores. // it can operate a SessionStore by its id. type Provider interface { - SessionInit(gclifetime int64, config string) error - SessionRead(sid string) (Store, error) - SessionExist(sid string) bool - SessionRegenerate(oldsid, sid string) (Store, error) - SessionDestroy(sid string) error - SessionAll() int //get all active session - SessionGC() + SessionInit(ctx context.Context, gclifetime int64, config string) error + SessionRead(ctx context.Context, sid string) (Store, error) + SessionExist(ctx context.Context, sid string) (bool, error) + SessionRegenerate(ctx context.Context, oldsid, sid string) (Store, error) + SessionDestroy(ctx context.Context, sid string) error + SessionAll(ctx context.Context) int // get all active session + SessionGC(ctx context.Context) } var provides = make(map[string]Provider) @@ -69,19 +70,15 @@ var provides = make(map[string]Provider) var SLogger = NewSessionLog(os.Stderr) // Register makes a session provide available by the provided name. -// If Register is called twice with the same name or if driver is nil, -// it panics. +// If provider is nil, it panic func Register(name string, provide Provider) { if provide == nil { panic("session: Register provide is nil") } - if _, dup := provides[name]; dup { - panic("session: Register called twice for provider " + name) - } provides[name] = provide } -//GetProvider +// GetProvider func GetProvider(name string) (Provider, error) { provider, ok := provides[name] if !ok { @@ -90,25 +87,6 @@ func GetProvider(name string) (Provider, error) { return provider, nil } -// ManagerConfig define the session config -type ManagerConfig struct { - CookieName string `json:"cookieName"` - EnableSetCookie bool `json:"enableSetCookie,omitempty"` - Gclifetime int64 `json:"gclifetime"` - Maxlifetime int64 `json:"maxLifetime"` - DisableHTTPOnly bool `json:"disableHTTPOnly"` - Secure bool `json:"secure"` - CookieLifeTime int `json:"cookieLifeTime"` - ProviderConfig string `json:"providerConfig"` - Domain string `json:"domain"` - SessionIDLength int64 `json:"sessionIDLength"` - EnableSidInHTTPHeader bool `json:"EnableSidInHTTPHeader"` - SessionNameInHTTPHeader string `json:"SessionNameInHTTPHeader"` - EnableSidInURLQuery bool `json:"EnableSidInURLQuery"` - SessionIDPrefix string `json:"sessionIDPrefix"` - CookieSameSite http.SameSite `json:"cookieSameSite"` -} - // Manager contains Provider and its configuration. type Manager struct { provider Provider @@ -149,7 +127,7 @@ func NewManager(provideName string, cf *ManagerConfig) (*Manager, error) { } } - err := provider.SessionInit(cf.Maxlifetime, cf.ProviderConfig) + err := provider.SessionInit(nil, cf.Maxlifetime, cf.ProviderConfig) if err != nil { return nil, err } @@ -212,8 +190,14 @@ func (manager *Manager) SessionStart(w http.ResponseWriter, r *http.Request) (se return nil, errs } - if sid != "" && manager.provider.SessionExist(sid) { - return manager.provider.SessionRead(sid) + if sid != "" { + exists, err := manager.provider.SessionExist(nil, sid) + if err != nil { + return nil, err + } + if exists { + return manager.provider.SessionRead(nil, sid) + } } // Generate a new session @@ -222,7 +206,7 @@ func (manager *Manager) SessionStart(w http.ResponseWriter, r *http.Request) (se return nil, errs } - session, err = manager.provider.SessionRead(sid) + session, err = manager.provider.SessionRead(nil, sid) if err != nil { return nil, err } @@ -265,10 +249,11 @@ func (manager *Manager) SessionDestroy(w http.ResponseWriter, r *http.Request) { } sid, _ := url.QueryUnescape(cookie.Value) - manager.provider.SessionDestroy(sid) + manager.provider.SessionDestroy(nil, sid) if manager.config.EnableSetCookie { expiration := time.Now() - cookie = &http.Cookie{Name: manager.config.CookieName, + cookie = &http.Cookie{ + Name: manager.config.CookieName, Path: "/", HttpOnly: !manager.config.DisableHTTPOnly, Expires: expiration, @@ -283,14 +268,14 @@ func (manager *Manager) SessionDestroy(w http.ResponseWriter, r *http.Request) { // GetSessionStore Get SessionStore by its id. func (manager *Manager) GetSessionStore(sid string) (sessions Store, err error) { - sessions, err = manager.provider.SessionRead(sid) + sessions, err = manager.provider.SessionRead(nil, sid) return } // GC Start session gc process. // it can do gc in times after gc lifetime. func (manager *Manager) GC() { - manager.provider.SessionGC() + manager.provider.SessionGC(nil) time.AfterFunc(time.Duration(manager.config.Gclifetime)*time.Second, func() { manager.GC() }) } @@ -300,15 +285,18 @@ func (manager *Manager) SessionRegenerateID(w http.ResponseWriter, r *http.Reque if err != nil { return nil, err } + var session Store + cookie, err := r.Cookie(manager.config.CookieName) if err != nil || cookie.Value == "" { // delete old cookie - session, err = manager.provider.SessionRead(sid) + session, err = manager.provider.SessionRead(nil, sid) if err != nil { return nil, err } - cookie = &http.Cookie{Name: manager.config.CookieName, + cookie = &http.Cookie{ + Name: manager.config.CookieName, Value: url.QueryEscape(sid), Path: "/", HttpOnly: !manager.config.DisableHTTPOnly, @@ -321,16 +309,15 @@ func (manager *Manager) SessionRegenerateID(w http.ResponseWriter, r *http.Reque if err != nil { return nil, err } - session, err = manager.provider.SessionRegenerate(oldsid, sid) + + session, err = manager.provider.SessionRegenerate(nil, oldsid, sid) if err != nil { return nil, err } + cookie.Value = url.QueryEscape(sid) cookie.HttpOnly = true cookie.Path = "/" - cookie.Secure = manager.isSecure(r) - cookie.Domain = manager.config.Domain - cookie.SameSite = manager.config.CookieSameSite } if manager.config.CookieLifeTime > 0 { cookie.MaxAge = manager.config.CookieLifeTime @@ -351,7 +338,7 @@ func (manager *Manager) SessionRegenerateID(w http.ResponseWriter, r *http.Reque // GetActiveSession Get all active sessions count number. func (manager *Manager) GetActiveSession() int { - return manager.provider.SessionAll() + return manager.provider.SessionAll(nil) } // SetSecure Set cookie with https. diff --git a/src/vendor/github.com/beego/beego/v2/server/web/session/session_config.go b/src/vendor/github.com/beego/beego/v2/server/web/session/session_config.go new file mode 100644 index 000000000..d9514003d --- /dev/null +++ b/src/vendor/github.com/beego/beego/v2/server/web/session/session_config.go @@ -0,0 +1,143 @@ +package session + +import "net/http" + +// ManagerConfig define the session config +type ManagerConfig struct { + EnableSetCookie bool `json:"enableSetCookie,omitempty"` + DisableHTTPOnly bool `json:"disableHTTPOnly"` + Secure bool `json:"secure"` + EnableSidInHTTPHeader bool `json:"EnableSidInHTTPHeader"` + EnableSidInURLQuery bool `json:"EnableSidInURLQuery"` + CookieName string `json:"cookieName"` + Gclifetime int64 `json:"gclifetime"` + Maxlifetime int64 `json:"maxLifetime"` + CookieLifeTime int `json:"cookieLifeTime"` + ProviderConfig string `json:"providerConfig"` + Domain string `json:"domain"` + SessionIDLength int64 `json:"sessionIDLength"` + SessionNameInHTTPHeader string `json:"SessionNameInHTTPHeader"` + SessionIDPrefix string `json:"sessionIDPrefix"` + CookieSameSite http.SameSite `json:"cookieSameSite"` +} + +func (c *ManagerConfig) Opts(opts ...ManagerConfigOpt) { + for _, opt := range opts { + opt(c) + } +} + +type ManagerConfigOpt func(config *ManagerConfig) + +func NewManagerConfig(opts ...ManagerConfigOpt) *ManagerConfig { + config := &ManagerConfig{} + for _, opt := range opts { + opt(config) + } + return config +} + +// CfgCookieName set key of session id +func CfgCookieName(cookieName string) ManagerConfigOpt { + return func(config *ManagerConfig) { + config.CookieName = cookieName + } +} + +// CfgCookieName set len of session id +func CfgSessionIdLength(length int64) ManagerConfigOpt { + return func(config *ManagerConfig) { + config.SessionIDLength = length + } +} + +// CfgSessionIdPrefix set prefix of session id +func CfgSessionIdPrefix(prefix string) ManagerConfigOpt { + return func(config *ManagerConfig) { + config.SessionIDPrefix = prefix + } +} + +// CfgSetCookie whether set `Set-Cookie` header in HTTP response +func CfgSetCookie(enable bool) ManagerConfigOpt { + return func(config *ManagerConfig) { + config.EnableSetCookie = enable + } +} + +// CfgGcLifeTime set session gc lift time +func CfgGcLifeTime(lifeTime int64) ManagerConfigOpt { + return func(config *ManagerConfig) { + config.Gclifetime = lifeTime + } +} + +// CfgMaxLifeTime set session lift time +func CfgMaxLifeTime(lifeTime int64) ManagerConfigOpt { + return func(config *ManagerConfig) { + config.Maxlifetime = lifeTime + } +} + +// CfgGcLifeTime set session lift time +func CfgCookieLifeTime(lifeTime int) ManagerConfigOpt { + return func(config *ManagerConfig) { + config.CookieLifeTime = lifeTime + } +} + +// CfgProviderConfig configure session provider +func CfgProviderConfig(providerConfig string) ManagerConfigOpt { + return func(config *ManagerConfig) { + config.ProviderConfig = providerConfig + } +} + +// CfgDomain set cookie domain +func CfgDomain(domain string) ManagerConfigOpt { + return func(config *ManagerConfig) { + config.Domain = domain + } +} + +// CfgSessionIdInHTTPHeader enable session id in http header +func CfgSessionIdInHTTPHeader(enable bool) ManagerConfigOpt { + return func(config *ManagerConfig) { + config.EnableSidInHTTPHeader = enable + } +} + +// CfgSetSessionNameInHTTPHeader set key of session id in http header +func CfgSetSessionNameInHTTPHeader(name string) ManagerConfigOpt { + return func(config *ManagerConfig) { + config.SessionNameInHTTPHeader = name + } +} + +// EnableSidInURLQuery enable session id in query string +func CfgEnableSidInURLQuery(enable bool) ManagerConfigOpt { + return func(config *ManagerConfig) { + config.EnableSidInURLQuery = enable + } +} + +// DisableHTTPOnly set HTTPOnly for http.Cookie +func CfgHTTPOnly(HTTPOnly bool) ManagerConfigOpt { + return func(config *ManagerConfig) { + config.DisableHTTPOnly = !HTTPOnly + } +} + +// CfgSecure set Secure for http.Cookie +func CfgSecure(Enable bool) ManagerConfigOpt { + return func(config *ManagerConfig) { + config.Secure = Enable + } +} + +// CfgSameSite set http.SameSite +func CfgSameSite(sameSite http.SameSite) ManagerConfigOpt { + return func(config *ManagerConfig) { + config.CookieSameSite = sameSite + } +} diff --git a/src/vendor/github.com/beego/beego/v2/server/web/session/session_provider_type.go b/src/vendor/github.com/beego/beego/v2/server/web/session/session_provider_type.go new file mode 100644 index 000000000..78dc116dc --- /dev/null +++ b/src/vendor/github.com/beego/beego/v2/server/web/session/session_provider_type.go @@ -0,0 +1,18 @@ +package session + +type ProviderType string + +const ( + ProviderCookie ProviderType = `cookie` + ProviderFile ProviderType = `file` + ProviderMemory ProviderType = `memory` + ProviderCouchbase ProviderType = `couchbase` + ProviderLedis ProviderType = `ledis` + ProviderMemcache ProviderType = `memcache` + ProviderMysql ProviderType = `mysql` + ProviderPostgresql ProviderType = `postgresql` + ProviderRedis ProviderType = `redis` + ProviderRedisCluster ProviderType = `redis_cluster` + ProviderRedisSentinel ProviderType = `redis_sentinel` + ProviderSsdb ProviderType = `ssdb` +) diff --git a/src/vendor/github.com/beego/beego/staticfile.go b/src/vendor/github.com/beego/beego/v2/server/web/staticfile.go similarity index 94% rename from src/vendor/github.com/beego/beego/staticfile.go rename to src/vendor/github.com/beego/beego/v2/server/web/staticfile.go index f1135e36c..7a120f68f 100644 --- a/src/vendor/github.com/beego/beego/staticfile.go +++ b/src/vendor/github.com/beego/beego/v2/server/web/staticfile.go @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -package beego +package web import ( "bytes" @@ -26,9 +26,10 @@ import ( "sync" "time" - "github.com/beego/beego/context" - "github.com/beego/beego/logs" - "github.com/hashicorp/golang-lru" + lru "github.com/hashicorp/golang-lru" + + "github.com/beego/beego/v2/core/logs" + "github.com/beego/beego/v2/server/web/context" ) var errNotStaticRequest = errors.New("request not a static file request") @@ -64,17 +65,17 @@ func serverStaticRouter(ctx *context.Context) { } ctx.Redirect(302, redirectURL) } else { - //serveFile will list dir + // serveFile will list dir http.ServeFile(ctx.ResponseWriter, ctx.Request, filePath) } return } else if fileInfo.Size() > int64(BConfig.WebConfig.StaticCacheFileSize) { - //over size file serve with http module + // over size file serve with http module http.ServeFile(ctx.ResponseWriter, ctx.Request, filePath) return } - var enableCompress = BConfig.EnableGzip && isStaticCompress(filePath) + enableCompress := BConfig.EnableGzip && isStaticCompress(filePath) var acceptEncoding string if enableCompress { acceptEncoding = context.ParseEncoding(ctx.Request) @@ -101,7 +102,7 @@ type serveContentHolder struct { data []byte modTime time.Time size int64 - originSize int64 //original file size:to judge file changed + originSize int64 // original file size:to judge file changed encoding string } @@ -116,7 +117,7 @@ var ( func openFile(filePath string, fi os.FileInfo, acceptEncoding string) (bool, string, *serveContentHolder, *serveContentReader, error) { if staticFileLruCache == nil { - //avoid lru cache error + // avoid lru cache error if BConfig.WebConfig.StaticCacheFileNum >= 1 { staticFileLruCache, _ = lru.New(BConfig.WebConfig.StaticCacheFileNum) } else { diff --git a/src/vendor/github.com/beego/beego/toolbox/statistics.go b/src/vendor/github.com/beego/beego/v2/server/web/statistics.go similarity index 80% rename from src/vendor/github.com/beego/beego/toolbox/statistics.go rename to src/vendor/github.com/beego/beego/v2/server/web/statistics.go index fd73dfb38..d0f3ebd9d 100644 --- a/src/vendor/github.com/beego/beego/toolbox/statistics.go +++ b/src/vendor/github.com/beego/beego/v2/server/web/statistics.go @@ -12,12 +12,15 @@ // See the License for the specific language governing permissions and // limitations under the License. -package toolbox +package web import ( "fmt" + "html/template" "sync" "time" + + "github.com/beego/beego/v2/core/utils" ) // Statistics struct @@ -33,7 +36,7 @@ type Statistics struct { // URLMap contains several statistics struct to log different data type URLMap struct { lock sync.RWMutex - LengthLimit int //limit the urlmap's length if it's equal to 0 there's no limit + LengthLimit int // limit the urlmap's length if it's equal to 0 there's no limit urlmap map[string]map[string]*Statistics } @@ -63,7 +66,6 @@ func (m *URLMap) AddStatistics(requestMethod, requestURL, requestController stri } m.urlmap[requestURL][requestMethod] = nb } - } else { if m.LengthLimit > 0 && m.LengthLimit <= len(m.urlmap) { return @@ -87,7 +89,7 @@ func (m *URLMap) GetMap() map[string]interface{} { m.lock.RLock() defer m.lock.RUnlock() - var fields = []string{"requestUrl", "method", "times", "used", "max used", "min used", "avg used"} + fields := []string{"requestUrl", "method", "times", "used", "max used", "min used", "avg used"} var resultLists [][]string content := make(map[string]interface{}) @@ -96,17 +98,17 @@ func (m *URLMap) GetMap() map[string]interface{} { for k, v := range m.urlmap { for kk, vv := range v { result := []string{ - fmt.Sprintf("% -50s", k), + fmt.Sprintf("% -50s", template.HTMLEscapeString(k)), fmt.Sprintf("% -10s", kk), fmt.Sprintf("% -16d", vv.RequestNum), fmt.Sprintf("%d", vv.TotalTime), - fmt.Sprintf("% -16s", toS(vv.TotalTime)), + fmt.Sprintf("% -16s", utils.ToShortTimeFormat(vv.TotalTime)), fmt.Sprintf("%d", vv.MaxTime), - fmt.Sprintf("% -16s", toS(vv.MaxTime)), + fmt.Sprintf("% -16s", utils.ToShortTimeFormat(vv.MaxTime)), fmt.Sprintf("%d", vv.MinTime), - fmt.Sprintf("% -16s", toS(vv.MinTime)), + fmt.Sprintf("% -16s", utils.ToShortTimeFormat(vv.MinTime)), fmt.Sprintf("%d", time.Duration(int64(vv.TotalTime)/vv.RequestNum)), - fmt.Sprintf("% -16s", toS(time.Duration(int64(vv.TotalTime)/vv.RequestNum))), + fmt.Sprintf("% -16s", utils.ToShortTimeFormat(time.Duration(int64(vv.TotalTime)/vv.RequestNum))), } resultLists = append(resultLists, result) } @@ -128,10 +130,10 @@ func (m *URLMap) GetMapData() []map[string]interface{} { "request_url": k, "method": kk, "times": vv.RequestNum, - "total_time": toS(vv.TotalTime), - "max_time": toS(vv.MaxTime), - "min_time": toS(vv.MinTime), - "avg_time": toS(time.Duration(int64(vv.TotalTime) / vv.RequestNum)), + "total_time": utils.ToShortTimeFormat(vv.TotalTime), + "max_time": utils.ToShortTimeFormat(vv.MaxTime), + "min_time": utils.ToShortTimeFormat(vv.MinTime), + "avg_time": utils.ToShortTimeFormat(time.Duration(int64(vv.TotalTime) / vv.RequestNum)), } resultLists = append(resultLists, result) } diff --git a/src/vendor/github.com/beego/beego/template.go b/src/vendor/github.com/beego/beego/v2/server/web/template.go similarity index 94% rename from src/vendor/github.com/beego/beego/template.go rename to src/vendor/github.com/beego/beego/v2/server/web/template.go index b871e74b0..715c992d5 100644 --- a/src/vendor/github.com/beego/beego/template.go +++ b/src/vendor/github.com/beego/beego/v2/server/web/template.go @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -package beego +package web import ( "errors" @@ -27,8 +27,8 @@ import ( "strings" "sync" - "github.com/beego/beego/logs" - "github.com/beego/beego/utils" + "github.com/beego/beego/v2/core/logs" + "github.com/beego/beego/v2/core/utils" ) var ( @@ -103,7 +103,7 @@ func init() { beegoTplFuncMap["lt"] = lt // < beegoTplFuncMap["ne"] = ne // != - beegoTplFuncMap["urlfor"] = URLFor // build a URL to match a Controller and it's method + beegoTplFuncMap["urlfor"] = URLFor // build an URL to match a Controller and it's method } // AddFuncMap let user to register a func in the template. @@ -163,12 +163,12 @@ func AddTemplateExt(ext string) { } // AddViewPath adds a new path to the supported view paths. -//Can later be used by setting a controller ViewPath to this folder -//will panic if called after beego.Run() +// Can later be used by setting a controller ViewPath to this folder +// will panic if called after beego.Run() func AddViewPath(viewPath string) error { if beeViewPathTemplateLocked { if _, exist := beeViewPathTemplates[viewPath]; exist { - return nil //Ignore if viewpath already exists + return nil // Ignore if viewpath already exists } panic("Can not add new view paths after beego.Run()") } @@ -202,9 +202,7 @@ func BuildTemplate(dir string, files ...string) error { root: dir, files: make(map[string][]string), } - err = Walk(fs, dir, func(path string, f os.FileInfo, err error) error { - return self.visit(path, f, err) - }) + err = Walk(fs, dir, self.visit) if err != nil { fmt.Printf("Walk() returned %v\n", err) return err @@ -303,7 +301,7 @@ func _getTemplate(t0 *template.Template, root string, fs http.FileSystem, subMod if tpl != nil { continue } - //first check filename + // first check filename for _, otherFile := range others { if otherFile == m[1] { var subMods1 [][]string @@ -316,7 +314,7 @@ func _getTemplate(t0 *template.Template, root string, fs http.FileSystem, subMod break } } - //second check define + // second check define for _, otherFile := range others { var data []byte fileAbsPath := filepath.Join(root, otherFile) @@ -351,7 +349,6 @@ func _getTemplate(t0 *template.Template, root string, fs http.FileSystem, subMod } } } - } return } @@ -368,14 +365,14 @@ func SetTemplateFSFunc(fnt templateFSFunc) { } // SetViewsPath sets view directory path in beego application. -func SetViewsPath(path string) *App { +func SetViewsPath(path string) *HttpServer { BConfig.WebConfig.ViewsPath = path return BeeApp } // SetStaticPath sets static directory path and proper url pattern in beego application. // if beego.SetStaticPath("static","public"), visit /static/* to load static file in folder "public". -func SetStaticPath(url string, path string) *App { +func SetStaticPath(url string, path string) *HttpServer { if !strings.HasPrefix(url, "/") { url = "/" + url } @@ -387,7 +384,7 @@ func SetStaticPath(url string, path string) *App { } // DelStaticPath removes the static folder setting in this url pattern in beego application. -func DelStaticPath(url string) *App { +func DelStaticPath(url string) *HttpServer { if !strings.HasPrefix(url, "/") { url = "/" + url } @@ -399,7 +396,7 @@ func DelStaticPath(url string) *App { } // AddTemplateEngine add a new templatePreProcessor which support extension -func AddTemplateEngine(extension string, fn templatePreProcessor) *App { +func AddTemplateEngine(extension string, fn templatePreProcessor) *HttpServer { AddTemplateExt(extension) beeTemplateEngines[extension] = fn return BeeApp diff --git a/src/vendor/github.com/beego/beego/templatefunc.go b/src/vendor/github.com/beego/beego/v2/server/web/templatefunc.go similarity index 75% rename from src/vendor/github.com/beego/beego/templatefunc.go rename to src/vendor/github.com/beego/beego/v2/server/web/templatefunc.go index 6f02b8d65..a44784d86 100644 --- a/src/vendor/github.com/beego/beego/templatefunc.go +++ b/src/vendor/github.com/beego/beego/v2/server/web/templatefunc.go @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -package beego +package web import ( "errors" @@ -25,13 +25,8 @@ import ( "strconv" "strings" "time" -) -const ( - formatTime = "15:04:05" - formatDate = "2006-01-02" - formatDateTime = "2006-01-02 15:04:05" - formatDateTimeT = "2006-01-02T15:04:05" + "github.com/beego/beego/v2/server/web/context" ) // Substr returns the substr from start to length. @@ -54,15 +49,14 @@ func Substr(s string, start, length int) string { // HTML2str returns escaping text convert from html. func HTML2str(html string) string { - re := regexp.MustCompile(`\<[\S\s]+?\>`) html = re.ReplaceAllStringFunc(html, strings.ToLower) - //remove STYLE + // remove STYLE re = regexp.MustCompile(`\`) html = re.ReplaceAllString(html, "") - //remove SCRIPT + // remove SCRIPT re = regexp.MustCompile(`\`) html = re.ReplaceAllString(html, "") @@ -85,7 +79,7 @@ func DateFormat(t time.Time, layout string) (datestring string) { var datePatterns = []string{ // year "Y", "2006", // A full numeric representation of a year, 4 digits Examples: 1999 or 2003 - "y", "06", //A two digit representation of a year Examples: 99 or 03 + "y", "06", // A two digit representation of a year Examples: 99 or 03 // month "m", "01", // Numeric representation of a month, with leading zeros 01 through 12 @@ -160,7 +154,7 @@ func NotNil(a interface{}) (isNil bool) { func GetConfig(returnType, key string, defaultVal interface{}) (value interface{}, err error) { switch returnType { case "String": - value = AppConfig.String(key) + value, err = AppConfig.String(key) case "Bool": value, err = AppConfig.Bool(key) case "Int": @@ -201,7 +195,7 @@ func Str2html(raw string) template.HTML { // Htmlquote returns quoted html string. func Htmlquote(text string) string { - //HTML编码为实体符号 + // HTML编码为实体符号 /* Encodes `text` for raw use in HTML. >>> htmlquote("<'&\\">") @@ -220,7 +214,7 @@ func Htmlquote(text string) string { // Htmlunquote returns unquoted html string. func Htmlunquote(text string) string { - //实体符号解释为HTML + // 实体符号解释为HTML /* Decodes `text` that's HTML quoted. >>> htmlunquote('<'&">') @@ -248,14 +242,13 @@ func Htmlunquote(text string) string { // /login?next=/ // /user/John%20Doe // -// more detail http://beego.me/docs/mvc/controller/urlbuilding.md +// more detail http://beego.vip/docs/mvc/controller/urlbuilding.md func URLFor(endpoint string, values ...interface{}) string { return BeeApp.Handlers.URLFor(endpoint, values...) } // AssetsJs returns script tag with src string. func AssetsJs(text string) template.HTML { - text = "" return template.HTML(text) @@ -263,169 +256,16 @@ func AssetsJs(text string) template.HTML { // AssetsCSS returns stylesheet link tag with src string. func AssetsCSS(text string) template.HTML { - text = "" return template.HTML(text) } -// ParseForm will parse form values to struct via tag. -// Support for anonymous struct. -func parseFormToStruct(form url.Values, objT reflect.Type, objV reflect.Value) error { - for i := 0; i < objT.NumField(); i++ { - fieldV := objV.Field(i) - if !fieldV.CanSet() { - continue - } - - fieldT := objT.Field(i) - if fieldT.Anonymous && fieldT.Type.Kind() == reflect.Struct { - err := parseFormToStruct(form, fieldT.Type, fieldV) - if err != nil { - return err - } - continue - } - - tags := strings.Split(fieldT.Tag.Get("form"), ",") - var tag string - if len(tags) == 0 || len(tags[0]) == 0 { - tag = fieldT.Name - } else if tags[0] == "-" { - continue - } else { - tag = tags[0] - } - - formValues := form[tag] - var value string - if len(formValues) == 0 { - defaultValue := fieldT.Tag.Get("default") - if defaultValue != "" { - value = defaultValue - } else { - continue - } - } - if len(formValues) == 1 { - value = formValues[0] - if value == "" { - continue - } - } - - switch fieldT.Type.Kind() { - case reflect.Bool: - if strings.ToLower(value) == "on" || strings.ToLower(value) == "1" || strings.ToLower(value) == "yes" { - fieldV.SetBool(true) - continue - } - if strings.ToLower(value) == "off" || strings.ToLower(value) == "0" || strings.ToLower(value) == "no" { - fieldV.SetBool(false) - continue - } - b, err := strconv.ParseBool(value) - if err != nil { - return err - } - fieldV.SetBool(b) - case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: - x, err := strconv.ParseInt(value, 10, 64) - if err != nil { - return err - } - fieldV.SetInt(x) - case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: - x, err := strconv.ParseUint(value, 10, 64) - if err != nil { - return err - } - fieldV.SetUint(x) - case reflect.Float32, reflect.Float64: - x, err := strconv.ParseFloat(value, 64) - if err != nil { - return err - } - fieldV.SetFloat(x) - case reflect.Interface: - fieldV.Set(reflect.ValueOf(value)) - case reflect.String: - fieldV.SetString(value) - case reflect.Struct: - switch fieldT.Type.String() { - case "time.Time": - var ( - t time.Time - err error - ) - if len(value) >= 25 { - value = value[:25] - t, err = time.ParseInLocation(time.RFC3339, value, time.Local) - } else if strings.HasSuffix(strings.ToUpper(value), "Z") { - t, err = time.ParseInLocation(time.RFC3339, value, time.Local) - } else if len(value) >= 19 { - if strings.Contains(value, "T") { - value = value[:19] - t, err = time.ParseInLocation(formatDateTimeT, value, time.Local) - } else { - value = value[:19] - t, err = time.ParseInLocation(formatDateTime, value, time.Local) - } - } else if len(value) >= 10 { - if len(value) > 10 { - value = value[:10] - } - t, err = time.ParseInLocation(formatDate, value, time.Local) - } else if len(value) >= 8 { - if len(value) > 8 { - value = value[:8] - } - t, err = time.ParseInLocation(formatTime, value, time.Local) - } - if err != nil { - return err - } - fieldV.Set(reflect.ValueOf(t)) - } - case reflect.Slice: - if fieldT.Type == sliceOfInts { - formVals := form[tag] - fieldV.Set(reflect.MakeSlice(reflect.SliceOf(reflect.TypeOf(int(1))), len(formVals), len(formVals))) - for i := 0; i < len(formVals); i++ { - val, err := strconv.Atoi(formVals[i]) - if err != nil { - return err - } - fieldV.Index(i).SetInt(int64(val)) - } - } else if fieldT.Type == sliceOfStrings { - formVals := form[tag] - fieldV.Set(reflect.MakeSlice(reflect.SliceOf(reflect.TypeOf("")), len(formVals), len(formVals))) - for i := 0; i < len(formVals); i++ { - fieldV.Index(i).SetString(formVals[i]) - } - } - } - } - return nil -} - // ParseForm will parse form values to struct via tag. func ParseForm(form url.Values, obj interface{}) error { - objT := reflect.TypeOf(obj) - objV := reflect.ValueOf(obj) - if !isStructPtr(objT) { - return fmt.Errorf("%v must be a struct pointer", obj) - } - objT = objT.Elem() - objV = objV.Elem() - - return parseFormToStruct(form, objT, objV) + return context.ParseForm(form, obj) } -var sliceOfInts = reflect.TypeOf([]int(nil)) -var sliceOfStrings = reflect.TypeOf([]string(nil)) - var unKind = map[reflect.Kind]bool{ reflect.Uintptr: true, reflect.Complex64: true, @@ -442,10 +282,11 @@ var unKind = map[reflect.Kind]bool{ // RenderForm will render object to form html. // obj must be a struct pointer. +// nolint func RenderForm(obj interface{}) template.HTML { objT := reflect.TypeOf(obj) objV := reflect.ValueOf(obj) - if !isStructPtr(objT) { + if objT.Kind() != reflect.Ptr || objT.Elem().Kind() != reflect.Struct { return template.HTML("") } objT = objT.Elem() @@ -504,7 +345,7 @@ func isValidForInput(fType string) bool { } // parseFormTag takes the stuct-tag of a StructField and parses the `form` value. -// returned are the form label, name-property, type and wether the field should be ignored. +// returned are the form label, name-property, type and whether the field should be ignored. func parseFormTag(fieldT reflect.StructField) (label, name, fType string, id string, class string, ignored bool, required bool) { tags := strings.Split(fieldT.Tag.Get("form"), ",") label = fieldT.Name + ": " @@ -550,10 +391,6 @@ func parseFormTag(fieldT reflect.StructField) (label, name, fType string, id str return } -func isStructPtr(t reflect.Type) bool { - return t.Kind() == reflect.Ptr && t.Elem().Kind() == reflect.Struct -} - // go1.2 added template funcs. begin var ( errBadComparisonType = errors.New("invalid type for comparison") @@ -607,10 +444,23 @@ func eq(arg1 interface{}, arg2 ...interface{}) (bool, error) { if err != nil { return false, err } - if k1 != k2 { - return false, errBadComparison - } truth := false + if k1 != k2 { + // Special case: Can compare integer values regardless of type's sign. + switch { + case k1 == intKind && k2 == uintKind: + truth = v1.Int() >= 0 && uint64(v1.Int()) == v2.Uint() + case k1 == uintKind && k2 == intKind: + truth = v2.Int() >= 0 && v1.Uint() == uint64(v2.Int()) + default: + return false, errBadComparison + } + if truth { + return true, nil + } else { + return false, nil + } + } switch k1 { case boolKind: truth = v1.Bool() == v2.Bool() @@ -653,23 +503,32 @@ func lt(arg1, arg2 interface{}) (bool, error) { if err != nil { return false, err } - if k1 != k2 { - return false, errBadComparison - } truth := false - switch k1 { - case boolKind, complexKind: - return false, errBadComparisonType - case floatKind: - truth = v1.Float() < v2.Float() - case intKind: - truth = v1.Int() < v2.Int() - case stringKind: - truth = v1.String() < v2.String() - case uintKind: - truth = v1.Uint() < v2.Uint() - default: - panic("invalid kind") + if k1 != k2 { + // Special case: Can compare integer values regardless of type's sign. + switch { + case k1 == intKind && k2 == uintKind: + truth = v1.Int() < 0 || uint64(v1.Int()) < v2.Uint() + case k1 == uintKind && k2 == intKind: + truth = v2.Int() >= 0 && v1.Uint() < uint64(v2.Int()) + default: + return false, errBadComparison + } + } else { + switch k1 { + case boolKind, complexKind: + return false, errBadComparisonType + case floatKind: + truth = v1.Float() < v2.Float() + case intKind: + truth = v1.Int() < v2.Int() + case stringKind: + truth = v1.String() < v2.String() + case uintKind: + truth = v1.Uint() < v2.Uint() + default: + return false, errBadComparisonType + } } return truth, nil } diff --git a/src/vendor/github.com/beego/beego/tree.go b/src/vendor/github.com/beego/beego/v2/server/web/tree.go similarity index 95% rename from src/vendor/github.com/beego/beego/tree.go rename to src/vendor/github.com/beego/beego/v2/server/web/tree.go index 28b515fff..adcb88537 100644 --- a/src/vendor/github.com/beego/beego/tree.go +++ b/src/vendor/github.com/beego/beego/v2/server/web/tree.go @@ -12,20 +12,18 @@ // See the License for the specific language governing permissions and // limitations under the License. -package beego +package web import ( "path" "regexp" "strings" - "github.com/beego/beego/context" - "github.com/beego/beego/utils" + "github.com/beego/beego/v2/core/utils" + "github.com/beego/beego/v2/server/web/context" ) -var ( - allowSuffixExt = []string{".json", ".xml", ".html"} -) +var allowSuffixExt = []string{".json", ".xml", ".html"} // Tree has three elements: FixRouter/wildcard/leaves // fixRouter stores Fixed Router @@ -209,9 +207,9 @@ func (t *Tree) AddRouter(pattern string, runObject interface{}) { func (t *Tree) addseg(segments []string, route interface{}, wildcards []string, reg string) { if len(segments) == 0 { if reg != "" { - t.leaves = append(t.leaves, &leafInfo{runObject: route, wildcards: wildcards, regexps: regexp.MustCompile("^" + reg + "$")}) + t.leaves = append([]*leafInfo{{runObject: route, wildcards: wildcards, regexps: regexp.MustCompile("^" + reg + "$")}}, t.leaves...) } else { - t.leaves = append(t.leaves, &leafInfo{runObject: route, wildcards: wildcards}) + t.leaves = append([]*leafInfo{{runObject: route, wildcards: wildcards}}, t.leaves...) } } else { seg := segments[0] @@ -284,9 +282,9 @@ func (t *Tree) addseg(segments []string, route interface{}, wildcards []string, // Match router to runObject & params func (t *Tree) Match(pattern string, ctx *context.Context) (runObject interface{}) { - // fix issue 4961, deal with "./ ../ //" - pattern = path.Clean(pattern) - if len(pattern) == 0 || pattern[0] != '/' { + // fix issue 4961, deal with "./ ../ //" + pattern = path.Clean(pattern) + if pattern == "" || pattern[0] != '/' { return nil } w := make([]string, 0, 20) @@ -295,13 +293,14 @@ func (t *Tree) Match(pattern string, ctx *context.Context) (runObject interface{ func (t *Tree) match(treePattern string, pattern string, wildcardValues []string, ctx *context.Context) (runObject interface{}) { if len(pattern) > 0 { - i := 0 - for ; i < len(pattern) && pattern[i] == '/'; i++ { + i, l := 0, len(pattern) + for i < l && pattern[i] == '/' { + i++ } pattern = pattern[i:] } // Handle leaf nodes: - if len(pattern) == 0 { + if pattern == "" { for _, l := range t.leaves { if ok := l.match(treePattern, wildcardValues, ctx); ok { return l.runObject @@ -318,7 +317,8 @@ func (t *Tree) match(treePattern string, pattern string, wildcardValues []string } var seg string i, l := 0, len(pattern) - for ; i < l && pattern[i] != '/'; i++ { + for i < l && pattern[i] != '/' { + i++ } if i == 0 { seg = pattern @@ -329,7 +329,7 @@ func (t *Tree) match(treePattern string, pattern string, wildcardValues []string } for _, subTree := range t.fixrouters { if subTree.prefix == seg { - if len(pattern) != 0 && pattern[0] == '/' { + if pattern != "" && pattern[0] == '/' { treePattern = pattern[1:] } else { treePattern = pattern @@ -343,9 +343,9 @@ func (t *Tree) match(treePattern string, pattern string, wildcardValues []string if runObject == nil && len(t.fixrouters) > 0 { // Filter the .json .xml .html extension for _, str := range allowSuffixExt { + // pattern == "" avoid cases: /aaa.html/aaa.html could access /aaa/:bbb if strings.HasSuffix(seg, str) && pattern == "" { for _, subTree := range t.fixrouters { - // strings.HasSuffix(treePattern, seg) avoid cases: /aaa.html/bbb could access /aaa/bbb if subTree.prefix == seg[:len(seg)-len(str)] { runObject = subTree.match(treePattern, pattern, wildcardValues, ctx) if runObject != nil { diff --git a/src/vendor/github.com/beego/beego/v2/sonar-project.properties b/src/vendor/github.com/beego/beego/v2/sonar-project.properties new file mode 100644 index 000000000..dbd5d6ae6 --- /dev/null +++ b/src/vendor/github.com/beego/beego/v2/sonar-project.properties @@ -0,0 +1,7 @@ +sonar.organization=beego +sonar.projectKey=beego_beego + +# relative paths to source directories. More details and properties are described +# in https://sonarcloud.io/documentation/project-administration/narrowing-the-focus/ +sonar.sources=. +sonar.exclusions=**/*_test.go diff --git a/src/vendor/github.com/casbin/casbin/.gitignore b/src/vendor/github.com/casbin/casbin/.gitignore index 72ed6f474..da27805f5 100644 --- a/src/vendor/github.com/casbin/casbin/.gitignore +++ b/src/vendor/github.com/casbin/casbin/.gitignore @@ -24,4 +24,7 @@ _testmain.go *.prof .idea/ -*.iml \ No newline at end of file +*.iml + +# vendor files +vendor diff --git a/src/vendor/github.com/casbin/casbin/.travis.yml b/src/vendor/github.com/casbin/casbin/.travis.yml index be7f63415..39f9bd348 100644 --- a/src/vendor/github.com/casbin/casbin/.travis.yml +++ b/src/vendor/github.com/casbin/casbin/.travis.yml @@ -1,12 +1,14 @@ -language: go - -sudo: false - -go: - - tip - -before_install: - - go get github.com/mattn/goveralls - -script: +language: go + +sudo: false + +go: + - "1.10" + - "1.11" + - "1.12" + +before_install: + - go get github.com/mattn/goveralls + +script: - $HOME/gopath/bin/goveralls -service=travis-ci \ No newline at end of file diff --git a/src/vendor/github.com/casbin/casbin/README.md b/src/vendor/github.com/casbin/casbin/README.md index 185afe65f..f1d66084c 100644 --- a/src/vendor/github.com/casbin/casbin/README.md +++ b/src/vendor/github.com/casbin/casbin/README.md @@ -3,12 +3,9 @@ Casbin [![Go Report Card](https://goreportcard.com/badge/github.com/casbin/casbin)](https://goreportcard.com/report/github.com/casbin/casbin) [![Build Status](https://travis-ci.org/casbin/casbin.svg?branch=master)](https://travis-ci.org/casbin/casbin) -[![Coverage Status](https://coveralls.io/repos/github/casbin/casbin/badge.svg?branch=master)](https://coveralls.io/github/casbin/casbin?branch=master) [![Godoc](https://godoc.org/github.com/casbin/casbin?status.svg)](https://godoc.org/github.com/casbin/casbin) [![Release](https://img.shields.io/github/release/casbin/casbin.svg)](https://github.com/casbin/casbin/releases/latest) [![Gitter](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/casbin/lobby) -[![Patreon](https://img.shields.io/badge/patreon-donate-yellow.svg)](http://www.patreon.com/yangluo) -[![Sourcegraph Badge](https://sourcegraph.com/github.com/casbin/casbin/-/badge.svg)](https://sourcegraph.com/github.com/casbin/casbin?badge) **News**: still worry about how to write the correct Casbin policy? ``Casbin online editor`` is coming to help! Try it at: http://casbin.org/editor/ @@ -18,11 +15,16 @@ Casbin is a powerful and efficient open-source access control library for Golang ## All the languages supported by Casbin: -[![golang](https://casbin.org/docs/assets/langs/golang.png)](https://github.com/casbin/casbin) | [![java](https://casbin.org/docs/assets/langs/java.png)](https://github.com/casbin/jcasbin) | [![nodejs](https://casbin.org/docs/assets/langs/nodejs.png)](https://github.com/casbin/node-casbin) | [![php](https://casbin.org/docs/assets/langs/php.png)](https://github.com/php-casbin/php-casbin) +[![golang](https://casbin.org/img/langs/golang.png)](https://github.com/casbin/casbin) | [![java](https://casbin.org/img/langs/java.png)](https://github.com/casbin/jcasbin) | [![nodejs](https://casbin.org/img/langs/nodejs.png)](https://github.com/casbin/node-casbin) | [![php](https://casbin.org/img/langs/php.png)](https://github.com/php-casbin/php-casbin) ----|----|----|---- [Casbin](https://github.com/casbin/casbin) | [jCasbin](https://github.com/casbin/jcasbin) | [node-Casbin](https://github.com/casbin/node-casbin) | [PHP-Casbin](https://github.com/php-casbin/php-casbin) production-ready | production-ready | production-ready | production-ready +[![python](https://casbin.org/img/langs/python.png)](https://github.com/casbin/pycasbin) | [![dotnet](https://casbin.org/img/langs/dotnet.png)](https://github.com/casbin-net/Casbin.NET) | [![delphi](https://casbin.org/img/langs/delphi.png)](https://github.com/casbin4d/Casbin4D) | [![rust](https://casbin.org/img/langs/rust.png)](https://github.com/Devolutions/casbin-rs) +----|----|----|---- +[PyCasbin](https://github.com/casbin/pycasbin) | [Casbin.NET](https://github.com/casbin-net/Casbin.NET) | [Casbin4D](https://github.com/casbin4d/Casbin4D) | [Casbin-RS](https://github.com/Devolutions/casbin-rs) +production-ready | production-ready | experimental | WIP + ## Table of contents - [Supported models](#supported-models) @@ -37,10 +39,9 @@ production-ready | production-ready | production-ready | production-ready - [Policy persistence](#policy-persistence) - [Policy consistence between multiple nodes](#policy-consistence-between-multiple-nodes) - [Role manager](#role-manager) -- [Multi-threading](#multi-threading) - [Benchmarks](#benchmarks) - [Examples](#examples) -- [How to use Casbin as a service?](#how-to-use-casbin-as-a-service) +- [Middlewares](#middlewares) - [Our adopters](#our-adopters) ## Supported models @@ -175,7 +176,7 @@ Note: you can also initialize an enforcer with policy in DB instead of file, see 3. Besides the static policy file, Casbin also provides API for permission management at run-time. For example, You can get all the roles assigned to a user as below: ```go - roles := e.GetRoles("alice") + roles := e.GetImplicitRolesForUser(sub) ``` See [Policy management APIs](#policy-management) for more usage. @@ -197,113 +198,19 @@ We also provide a web-based UI for model management and policy management: ## Policy persistence -In Casbin, the policy storage is implemented as an adapter (aka middleware for Casbin). To keep light-weight, we don't put adapter code in the main library (except the default file adapter). A complete list of Casbin adapters is provided as below. Any 3rd-party contribution on a new adapter is welcomed, please inform us and I will put it in this list:) - -Adapter | Type | Author | Description -----|------|----|---- -[File Adapter (built-in)](https://casbin.org/docs/en/policy-storage#file-adapter-built-in) | File | Casbin | Persistence for [.CSV (Comma-Separated Values)](https://en.wikipedia.org/wiki/Comma-separated_values) files -[Filtered File Adapter (built-in)](https://github.com/casbin/casbin#policy-enforcement-at-scale) | File | [@faceless-saint](https://github.com/faceless-saint) | Persistence for [.CSV (Comma-Separated Values)](https://en.wikipedia.org/wiki/Comma-separated_values) files with policy subset loading support -[Xorm Adapter](https://github.com/casbin/xorm-adapter) | ORM | Casbin | MySQL, PostgreSQL, TiDB, SQLite, SQL Server, Oracle are supported by [Xorm](https://github.com/go-xorm/xorm/) -[Gorm Adapter](https://github.com/casbin/gorm-adapter) | ORM | Casbin | MySQL, PostgreSQL, Sqlite3, SQL Server are supported by [Gorm](https://github.com/jinzhu/gorm/) -[Beego ORM Adapter](https://github.com/casbin/beego-orm-adapter) | ORM | Casbin | MySQL, PostgreSQL, Sqlite3 are supported by [Beego ORM](https://beego.me/docs/mvc/model/overview.md) -[MongoDB Adapter](https://github.com/casbin/mongodb-adapter) | NoSQL | Casbin | Persistence for [MongoDB](https://www.mongodb.com) -[Cassandra Adapter](https://github.com/casbin/cassandra-adapter) | NoSQL | Casbin | Persistence for [Apache Cassandra DB](http://cassandra.apache.org) -[Consul Adapter](https://github.com/ankitm123/consul-adapter) | KV store | [@ankitm123](https://github.com/ankitm123) | Persistence for [HashiCorp Consul](https://www.consul.io/) -[Redis Adapter](https://github.com/casbin/redis-adapter) | KV store | Casbin | Persistence for [Redis](https://redis.io/) -[Protobuf Adapter](https://github.com/casbin/protobuf-adapter) | Stream | Casbin | Persistence for [Google Protocol Buffers](https://developers.google.com/protocol-buffers/) -[JSON Adapter](https://github.com/casbin/json-adapter) | String | Casbin | Persistence for [JSON](https://www.json.org/) -[String Adapter](https://github.com/qiangmzsx/string-adapter) | String | [@qiangmzsx](https://github.com/qiangmzsx) | Persistence for String -[RQLite Adapter](https://github.com/edomosystems/rqlite-adapter) | SQL | [EDOMO Systems](https://github.com/edomosystems) | Persistence for [RQLite](https://github.com/rqlite/rqlite/) -[PostgreSQL Adapter](https://github.com/going/casbin-postgres-adapter) | SQL | [Going](https://github.com/going) | Persistence for [PostgreSQL](https://www.postgresql.org/) -[RethinkDB Adapter](https://github.com/adityapandey9/rethinkdb-adapter) | NoSQL | [@adityapandey9](https://github.com/adityapandey9) | Persistence for [RethinkDB](https://rethinkdb.com/) -[DynamoDB Adapter](https://github.com/HOOQTV/dynacasbin) | NoSQL | [HOOQ](https://github.com/HOOQTV) | Persistence for [Amazon DynamoDB](https://aws.amazon.com/dynamodb/) -[Minio/AWS S3 Adapter](https://github.com/Soluto/casbin-minio-adapter) | Object storage | [Soluto](https://github.com/Soluto) | Persistence for [Minio](https://github.com/minio/minio) and [Amazon S3](https://aws.amazon.com/s3/) -[Bolt Adapter](https://github.com/wirepair/bolt-adapter) | KV store | [@wirepair](https://github.com/wirepair) | Persistence for [Bolt](https://github.com/boltdb/bolt) - -For details of adapters, please refer to the documentation: https://casbin.org/docs/en/policy-storage - -## Policy enforcement at scale - -Some adapters support filtered policy management. This means that the policy loaded by Casbin is a subset of the policy in storage based on a given filter. This allows for efficient policy enforcement in large, multi-tenant environments when parsing the entire policy becomes a performance bottleneck. - -To use filtered policies with a supported adapter, simply call the `LoadFilteredPolicy` method. The valid format for the filter parameter depends on the adapter used. To prevent accidental data loss, the `SavePolicy` method is disabled when a filtered policy is loaded. - -For example, the following code snippet uses the built-in filtered file adapter and the RBAC model with domains. In this case, the filter limits the policy to a single domain. Any policy lines for domains other than `"domain1"` are omitted from the loaded policy: - -```go -import ( - "github.com/casbin/casbin" -) - -enforcer := casbin.NewEnforcer() - -adapter := fileadapter.NewFilteredAdapter("examples/rbac_with_domains_policy.csv") -enforcer.InitWithAdapter("examples/rbac_with_domains_model.conf", adapter) - -filter := &fileadapter.Filter{ - P: []string{"", "domain1"}, - G: []string{"", "", "domain1"}, -} -enforcer.LoadFilteredPolicy(filter) - -// The loaded policy now only contains the entries pertaining to "domain1". -``` +https://casbin.org/docs/en/adapters ## Policy consistence between multiple nodes -We support to use distributed messaging systems like [etcd](https://github.com/coreos/etcd) to keep consistence between multiple Casbin enforcer instances. So our users can concurrently use multiple Casbin enforcers to handle large number of permission checking requests. - -Similar to policy storage adapters, we don't put watcher code in the main library. Any support for a new messaging system should be implemented as a watcher. A complete list of Casbin watchers is provided as below. Any 3rd-party contribution on a new watcher is welcomed, please inform us and I will put it in this list:) - -Watcher | Type | Author | Description -----|------|----|---- -[Etcd Watcher](https://github.com/casbin/etcd-watcher) | KV store | Casbin | Watcher for [etcd](https://github.com/coreos/etcd) -[NATS Watcher](https://github.com/Soluto/casbin-nats-watcher) | Messaging system | [Soluto](https://github.com/Soluto) | Watcher for [NATS](https://nats.io/) -[ZooKeeper Watcher](https://github.com/grepsr/casbin-zk-watcher) | KV store | [Grepsr](https://github.com/grepsr) | Watcher for [Apache ZooKeeper](https://zookeeper.apache.org/) -[Redis Watcher](https://github.com/billcobbler/casbin-redis-watcher) | KV store | [@billcobbler](https://github.com/billcobbler) | Watcher for [Redis](http://redis.io/) +https://casbin.org/docs/en/watchers ## Role manager -The role manager is used to manage the RBAC role hierarchy (user-role mapping) in Casbin. A role manager can retrieve the role data from Casbin policy rules or external sources such as LDAP, Okta, Auth0, Azure AD, etc. We support different implementations of a role manager. To keep light-weight, we don't put role manager code in the main library (except the default role manager). A complete list of Casbin role managers is provided as below. Any 3rd-party contribution on a new role manager is welcomed, please inform us and I will put it in this list:) - -Role manager | Author | Description -----|----|---- -[Default Role Manager (built-in)](https://github.com/casbin/casbin/blob/master/rbac/default-role-manager/role_manager.go) | Casbin | Supports role hierarchy stored in Casbin policy -[Session Role Manager](https://github.com/casbin/session-role-manager) | [EDOMO Systems](https://github.com/edomosystems) | Supports role hierarchy stored in Casbin policy, with time-range-based sessions -[Okta Role Manager](https://github.com/casbin/okta-role-manager) | Casbin | Supports role hierarchy stored in [Okta](https://www.okta.com/) -[Auth0 Role Manager](https://github.com/casbin/auth0-role-manager) | Casbin | Supports role hierarchy stored in [Auth0](https://auth0.com/)'s [Authorization Extension](https://auth0.com/docs/extensions/authorization-extension/v2) - -For developers: all role managers must implement the [RoleManager](https://github.com/casbin/casbin/blob/master/rbac/role_manager.go) interface. [Session Role Manager](https://github.com/casbin/session-role-manager) can be used as a reference implementation. - -## Multi-threading - -If you use Casbin in a multi-threading manner, you can use the synchronized wrapper of the Casbin enforcer: https://github.com/casbin/casbin/blob/master/enforcer_synced.go. - -It also supports the ``AutoLoad`` feature, which means the Casbin enforcer will automatically load the latest policy rules from DB if it has changed. Call ``StartAutoLoadPolicy()`` to start automatically loading policy periodically and call ``StopAutoLoadPolicy()`` to stop it. +https://casbin.org/docs/en/role-managers ## Benchmarks -The overhead of policy enforcement is benchmarked in [model_b_test.go](https://github.com/casbin/casbin/blob/master/model_b_test.go). The testbed is: - -``` -Intel(R) Core(TM) i7-6700HQ CPU @ 2.60GHz, 2601 Mhz, 4 Core(s), 8 Logical Processor(s) -``` - -The benchmarking result of ``go test -bench=. -benchmem`` is as follows (op = an ``Enforce()`` call, ms = millisecond, KB = kilo bytes): - -Test case | Size | Time overhead | Memory overhead -----|------|------|---- -ACL | 2 rules (2 users) | 0.015493 ms/op | 5.649 KB -RBAC | 5 rules (2 users, 1 role) | 0.021738 ms/op | 7.522 KB -RBAC (small) | 1100 rules (1000 users, 100 roles) | 0.164309 ms/op | 80.620 KB -RBAC (medium) | 11000 rules (10000 users, 1000 roles) | 2.258262 ms/op | 765.152 KB -RBAC (large) | 110000 rules (100000 users, 10000 roles) | 23.916776 ms/op | 7.606 MB -RBAC with resource roles | 6 rules (2 users, 2 roles) | 0.021146 ms/op | 7.906 KB -RBAC with domains/tenants | 6 rules (2 users, 1 role, 2 domains) | 0.032696 ms/op | 10.755 KB -ABAC | 0 rule (0 user) | 0.007510 ms/op | 2.328 KB -RESTful | 5 rules (3 users) | 0.045398 ms/op | 91.774 KB -Deny-override | 6 rules (2 users, 1 role) | 0.023281 ms/op | 8.370 KB -Priority | 9 rules (2 users, 2 roles) | 0.016389 ms/op | 5.313 KB +https://casbin.org/docs/en/benchmark ## Examples @@ -321,44 +228,39 @@ RESTful | [keymatch_model.conf](https://github.com/casbin/casbin/blob/master/exa Deny-override | [rbac_model_with_deny.conf](https://github.com/casbin/casbin/blob/master/examples/rbac_with_deny_model.conf) | [rbac_policy_with_deny.csv](https://github.com/casbin/casbin/blob/master/examples/rbac_with_deny_policy.csv) Priority | [priority_model.conf](https://github.com/casbin/casbin/blob/master/examples/priority_model.conf) | [priority_policy.csv](https://github.com/casbin/casbin/blob/master/examples/priority_policy.csv) -## How to use Casbin as a service? +## Middlewares -- [Casbin Server](https://github.com/casbin/casbin-server): The official ``Casbin as a Service`` solution based on [gRPC](https://grpc.io/), both Management API and RBAC API are provided. -- [Go-Simple-API-Gateway](https://github.com/Soontao/go-simple-api-gateway): A simple API gateway written by golang, supports for authentication and authorization. -- [middleware-acl](https://github.com/luk4z7/middleware-acl): RESTful access control middleware based on Casbin. +Authz middlewares for web frameworks: https://casbin.org/docs/en/middlewares ## Our adopters -### Web frameworks +https://casbin.org/docs/en/adopters -- [Beego](https://github.com/astaxie/beego): An open-source, high-performance web framework for Go, via built-in plugin: [plugins/authz](https://github.com/astaxie/beego/blob/master/plugins/authz) -- [Caddy](https://github.com/mholt/caddy): Fast, cross-platform HTTP/2 web server with automatic HTTPS, via plugin: [caddy-authz](https://github.com/casbin/caddy-authz) -- [Gin](https://github.com/gin-gonic/gin): A HTTP web framework featuring a Martini-like API with much better performance, via plugin: [authz](https://github.com/gin-contrib/authz) -- [Revel](https://github.com/revel/revel): A high productivity, full-stack web framework for the Go language, via plugin: [auth/casbin](https://github.com/revel/modules/tree/master/auth/casbin) -- [Echo](https://github.com/labstack/echo): High performance, minimalist Go web framework, via plugin: [echo-authz](https://github.com/labstack/echo-contrib/tree/master/casbin) (thanks to [@xqbumu](https://github.com/xqbumu)) -- [Iris](https://github.com/kataras/iris): The fastest web framework for Go in (THIS) Earth. HTTP/2 Ready-To-GO, via plugin: [casbin](https://github.com/iris-contrib/middleware/tree/master/casbin) (thanks to [@hiveminded](https://github.com/hiveminded)) -- [Negroni](https://github.com/urfave/negroni): Idiomatic HTTP Middleware for Golang, via plugin: [negroni-authz](https://github.com/casbin/negroni-authz) -- [Tango](https://github.com/lunny/tango): Micro & pluggable web framework for Go, via plugin: [authz](https://github.com/tango-contrib/authz) -- [Chi](https://github.com/pressly/chi): A lightweight, idiomatic and composable router for building HTTP services, via plugin: [chi-authz](https://github.com/casbin/chi-authz) -- [Macaron](https://github.com/go-macaron/macaron): A high productive and modular web framework in Go, via plugin: [authz](https://github.com/go-macaron/authz) -- [DotWeb](https://github.com/devfeel/dotweb): Simple and easy go web micro framework, via plugin: [authz](https://github.com/devfeel/middleware/tree/master/authz) -- [Baa](https://github.com/go-baa/baa): An express Go web framework with routing, middleware, dependency injection and http context, via plugin: [authz](https://github.com/baa-middleware/authz) +## Contributors -### Others +This project exists thanks to all the people who contribute. + -- [Intel RMD](https://github.com/intel/rmd): Intel's resource management daemon, via direct integration, see: [model](https://github.com/intel/rmd/blob/master/etc/rmd/acl/url/model.conf), [policy rules](https://github.com/intel/rmd/blob/master/etc/rmd/acl/url/policy.csv) -- [VMware Dispatch](https://github.com/vmware/dispatch): A framework for deploying and managing serverless style applications, via direct integration, see: [model (in code)](https://github.com/vmware/dispatch/blob/master/pkg/identity-manager/handlers.go#L46-L55), [policy rules (in code)](https://github.com/vmware/dispatch/blob/master/pkg/identity-manager/handlers_test.go#L35-L45) -- [Banzai Pipeline](https://github.com/banzaicloud/pipeline): [Banzai Cloud](https://github.com/banzaicloud)'s RESTful API to provision or reuse managed Kubernetes clusters in the cloud, via direct integration, see: [model (in code)](https://github.com/banzaicloud/pipeline/blob/master/auth/authz.go#L15-L30), [policy rules (in code)](https://github.com/banzaicloud/pipeline/blob/master/auth/authz.go#L84-L93) -- [Docker](https://github.com/docker/docker): The world's leading software container platform, via plugin: [casbin-authz-plugin](https://github.com/casbin/casbin-authz-plugin) ([recommended by Docker](https://docs.docker.com/engine/extend/legacy_plugins/#authorization-plugins)) -- [Gobis](https://github.com/orange-cloudfoundry/gobis): [Orange](https://github.com/orange-cloudfoundry)'s lightweight API Gateway written in go, via plugin: [casbin](https://github.com/orange-cloudfoundry/gobis-middlewares/tree/master/casbin), see [model (in code)](https://github.com/orange-cloudfoundry/gobis-middlewares/blob/master/casbin/model.go#L52-L65), [policy rules (from request)](https://github.com/orange-cloudfoundry/gobis-middlewares/blob/master/casbin/adapter.go#L46-L64) -- [Skydive](https://github.com/skydive-project/skydive): An open source real-time network topology and protocols analyzer, via direct integration, see: [model (in code)](https://github.com/skydive-project/skydive/blob/master/config/config.go#L136-L140), [policy rules](https://github.com/skydive-project/skydive/blob/master/rbac/policy.csv) -- [Zenpress](https://github.com/insionng/zenpress): A CMS system written in Golang, via direct integration, see: [model](https://github.com/insionng/zenpress/blob/master/content/config/rbac_model.conf), [policy rules (in Gorm)](https://github.com/insionng/zenpress/blob/master/model/user.go#L53-L77) -- [Argo CD](https://github.com/argoproj/argo-cd): GitOps continuous delivery for Kubernetes, via direct integration, see: [model](https://github.com/argoproj/argo-cd/blob/master/util/rbac/model.conf), [policy rules](https://github.com/argoproj/argo-cd/blob/master/util/rbac/builtin-policy.csv) -- [Muxi Cloud](https://github.com/muxiyun/Mae): PaaS of Muxi Cloud, an easier way to manage Kubernetes cluster, via direct integration, see: [model](https://github.com/muxiyun/Mae/blob/master/conf/casbinmodel.conf), [policy rules (in code)](https://github.com/muxiyun/Mae/blob/master/pkg/casbin/initPolicy.go#L21-L95) -- [EngineerCMS](https://github.com/3xxx/EngineerCMS): A CMS to manage knowledge for engineers, via direct integration, see: [model](https://github.com/3xxx/EngineerCMS/blob/master/conf/rbac_model.conf), [policy rules (in SQLite)](https://github.com/3xxx/EngineerCMS/blob/master/database/engineer.db) -- [Cyber Auth API](https://github.com/CyberlifeCN/cyber-auth-api): A Golang authentication API project, via direct integration, see: [model](https://github.com/CyberlifeCN/cyber-auth-api/blob/master/conf/authz_model.conf), [policy rules](https://github.com/CyberlifeCN/cyber-auth-api/blob/master/conf/authz_policy.csv) -- [IRIS Community](https://github.com/irisnet/iris-community): Website for IRIS Community Activities, via direct integration, see: [model](https://github.com/irisnet/iris-community/blob/master/authz/authz_model.conf), [policy rules](https://github.com/irisnet/iris-community/blob/master/authz/authz_policy.csv) -- [Metadata DB](https://github.com/Bnei-Baruch/mdb): BB archive metadata database, via direct integration, see: [model](https://github.com/Bnei-Baruch/mdb/blob/master/data/permissions_model.conf), [policy rules](https://github.com/Bnei-Baruch/mdb/blob/master/data/permissions_policy.csv) +## Backers + +Thank you to all our backers! 🙏 [[Become a backer](https://opencollective.com/casbin#backer)] + + + +## Sponsors + +Support this project by becoming a sponsor. Your logo will show up here with a link to your website. [[Become a sponsor](https://opencollective.com/casbin#sponsor)] + + + + + + + + + + + ## License diff --git a/src/vendor/github.com/casbin/casbin/effect/default_effector.go b/src/vendor/github.com/casbin/casbin/effect/default_effector.go index 7368e19f2..bf8ae80d5 100644 --- a/src/vendor/github.com/casbin/casbin/effect/default_effector.go +++ b/src/vendor/github.com/casbin/casbin/effect/default_effector.go @@ -14,7 +14,7 @@ package effect -import "github.com/pkg/errors" +import "errors" // DefaultEffector is default effector for Casbin. type DefaultEffector struct { diff --git a/src/vendor/github.com/casbin/casbin/enforcer.go b/src/vendor/github.com/casbin/casbin/enforcer.go index e6c965dc0..33cdb89b5 100644 --- a/src/vendor/github.com/casbin/casbin/enforcer.go +++ b/src/vendor/github.com/casbin/casbin/enforcer.go @@ -23,9 +23,9 @@ import ( "github.com/casbin/casbin/log" "github.com/casbin/casbin/model" "github.com/casbin/casbin/persist" - "github.com/casbin/casbin/persist/file-adapter" + fileadapter "github.com/casbin/casbin/persist/file-adapter" "github.com/casbin/casbin/rbac" - "github.com/casbin/casbin/rbac/default-role-manager" + defaultrolemanager "github.com/casbin/casbin/rbac/default-role-manager" "github.com/casbin/casbin/util" ) @@ -121,7 +121,9 @@ func (e *Enforcer) InitWithModelAndAdapter(m model.Model, adapter persist.Adapte e.initialize() - if e.adapter != nil { + // Do not initialize the full policy when using a filtered adapter + fa, ok := e.adapter.(persist.FilteredAdapter) + if e.adapter != nil && (!ok || ok && !fa.IsFiltered()) { // error intentionally ignored e.LoadPolicy() } @@ -209,7 +211,7 @@ func (e *Enforcer) ClearPolicy() { // LoadPolicy reloads the policy from file/database. func (e *Enforcer) LoadPolicy() error { e.model.ClearPolicy() - if err := e.adapter.LoadPolicy(e.model); err != nil { + if err := e.adapter.LoadPolicy(e.model); err != nil && err.Error() != "invalid file path, file path cannot be empty" { return err } @@ -233,7 +235,7 @@ func (e *Enforcer) LoadFilteredPolicy(filter interface{}) error { default: return errors.New("filtered policies are not supported by this adapter") } - if err := filteredAdapter.LoadFilteredPolicy(e.model, filter); err != nil { + if err := filteredAdapter.LoadFilteredPolicy(e.model, filter); err != nil && err.Error() != "invalid file path, file path cannot be empty" { return err } @@ -317,24 +319,49 @@ func (e *Enforcer) Enforce(rvals ...interface{}) bool { panic(err) } + rTokens := make(map[string]int, len(e.model["r"]["r"].Tokens)) + for i, token := range e.model["r"]["r"].Tokens { + rTokens[token] = i + } + pTokens := make(map[string]int, len(e.model["p"]["p"].Tokens)) + for i, token := range e.model["p"]["p"].Tokens { + pTokens[token] = i + } + + parameters := enforceParameters{ + rTokens: rTokens, + rVals: rvals, + + pTokens: pTokens, + } + var policyEffects []effect.Effect var matcherResults []float64 if policyLen := len(e.model["p"]["p"].Policy); policyLen != 0 { policyEffects = make([]effect.Effect, policyLen) matcherResults = make([]float64, policyLen) - + if len(e.model["r"]["r"].Tokens) != len(rvals) { + panic( + fmt.Sprintf( + "Invalid Request Definition size: expected %d got %d rvals: %v", + len(e.model["r"]["r"].Tokens), + len(rvals), + rvals)) + } for i, pvals := range e.model["p"]["p"].Policy { // log.LogPrint("Policy Rule: ", pvals) - - parameters := make(map[string]interface{}, 8) - for j, token := range e.model["r"]["r"].Tokens { - parameters[token] = rvals[j] - } - for j, token := range e.model["p"]["p"].Tokens { - parameters[token] = pvals[j] + if len(e.model["p"]["p"].Tokens) != len(pvals) { + panic( + fmt.Sprintf( + "Invalid Policy Rule size: expected %d got %d pvals: %v", + len(e.model["p"]["p"].Tokens), + len(pvals), + pvals)) } - result, err := expression.Evaluate(parameters) + parameters.pVals = pvals + + result, err := expression.Eval(parameters) // log.LogPrint("Result: ", result) if err != nil { @@ -359,7 +386,8 @@ func (e *Enforcer) Enforce(rvals ...interface{}) bool { panic(errors.New("matcher result should be bool, int or float")) } - if eft, ok := parameters["p_eft"]; ok { + if j, ok := parameters.pTokens["p_eft"]; ok { + eft := parameters.pVals[j] if eft == "allow" { policyEffects[i] = effect.Allow } else if eft == "deny" { @@ -380,15 +408,9 @@ func (e *Enforcer) Enforce(rvals ...interface{}) bool { policyEffects = make([]effect.Effect, 1) matcherResults = make([]float64, 1) - parameters := make(map[string]interface{}, 8) - for j, token := range e.model["r"]["r"].Tokens { - parameters[token] = rvals[j] - } - for _, token := range e.model["p"]["p"].Tokens { - parameters[token] = "" - } + parameters.pVals = make([]string, len(parameters.pTokens)) - result, err := expression.Evaluate(parameters) + result, err := expression.Eval(parameters) // log.LogPrint("Result: ", result) if err != nil { @@ -426,3 +448,36 @@ func (e *Enforcer) Enforce(rvals ...interface{}) bool { return result } + +// assumes bounds have already been checked +type enforceParameters struct { + rTokens map[string]int + rVals []interface{} + + pTokens map[string]int + pVals []string +} + +// implements govaluate.Parameters +func (p enforceParameters) Get(name string) (interface{}, error) { + if name == "" { + return nil, nil + } + + switch name[0] { + case 'p': + i, ok := p.pTokens[name] + if !ok { + return nil, errors.New("No parameter '" + name + "' found.") + } + return p.pVals[i], nil + case 'r': + i, ok := p.rTokens[name] + if !ok { + return nil, errors.New("No parameter '" + name + "' found.") + } + return p.rVals[i], nil + default: + return nil, errors.New("No parameter '" + name + "' found.") + } +} diff --git a/src/vendor/github.com/casbin/casbin/enforcer_cached.go b/src/vendor/github.com/casbin/casbin/enforcer_cached.go index fffc83403..1d1fcfc35 100644 --- a/src/vendor/github.com/casbin/casbin/enforcer_cached.go +++ b/src/vendor/github.com/casbin/casbin/enforcer_cached.go @@ -23,7 +23,7 @@ type CachedEnforcer struct { *Enforcer m map[string]bool enableCache bool - locker *sync.Mutex + locker *sync.RWMutex } // NewCachedEnforcer creates a cached enforcer via file or DB. @@ -32,7 +32,7 @@ func NewCachedEnforcer(params ...interface{}) *CachedEnforcer { e.Enforcer = NewEnforcer(params...) e.enableCache = true e.m = make(map[string]bool) - e.locker = new(sync.Mutex) + e.locker = new(sync.RWMutex) return e } @@ -57,17 +57,28 @@ func (e *CachedEnforcer) Enforce(rvals ...interface{}) bool { } } - e.locker.Lock() - defer e.locker.Unlock() - if _, ok := e.m[key]; ok { - return e.m[key] + if res, ok := e.getCachedResult(key); ok { + return res } else { res := e.Enforcer.Enforce(rvals...) - e.m[key] = res + e.setCachedResult(key, res) return res } } +func (e *CachedEnforcer) getCachedResult(key string) (res bool, ok bool) { + e.locker.RLock() + defer e.locker.RUnlock() + res, ok = e.m[key] + return res, ok +} + +func (e *CachedEnforcer) setCachedResult(key string, res bool) { + e.locker.Lock() + defer e.locker.Unlock() + e.m[key] = res +} + // InvalidateCache deletes all the existing cached decisions. func (e *CachedEnforcer) InvalidateCache() { e.m = make(map[string]bool) diff --git a/src/vendor/github.com/casbin/casbin/enforcer_safe.go b/src/vendor/github.com/casbin/casbin/enforcer_safe.go index 1cb358fee..adeedf6e5 100644 --- a/src/vendor/github.com/casbin/casbin/enforcer_safe.go +++ b/src/vendor/github.com/casbin/casbin/enforcer_safe.go @@ -100,3 +100,101 @@ func (e *Enforcer) RemoveFilteredPolicySafe(fieldIndex int, fieldValues ...strin err = nil return } + +// AddGroupingPolicySafe calls AddGroupingPolicy in a safe way, returns error instead of causing panic. +func (e *Enforcer) AddGroupingPolicySafe(params ...interface{}) (result bool, err error) { + defer func() { + if r := recover(); r != nil { + err = fmt.Errorf("%v", r) + result = false + } + }() + + result = e.AddGroupingPolicy(params...) + err = nil + return +} + +// AddNamedGroupingPolicySafe calls AddNamedGroupingPolicy in a safe way, returns error instead of causing panic. +func (e *Enforcer) AddNamedGroupingPolicySafe(ptype string, params ...interface{}) (result bool, err error) { + defer func() { + if r := recover(); r != nil { + err = fmt.Errorf("%v", r) + result = false + } + }() + + result = e.AddNamedGroupingPolicy(ptype, params...) + err = nil + return +} + +// AddNamedPolicySafe calls AddNamedPolicy in a safe way, returns error instead of causing panic. +func (e *Enforcer) AddNamedPolicySafe(ptype string, params ...interface{}) (result bool, err error) { + defer func() { + if r := recover(); r != nil { + err = fmt.Errorf("%v", r) + result = false + } + }() + + result = e.AddNamedPolicy(ptype, params...) + err = nil + return +} + +// RemoveGroupingPolicySafe calls RemoveGroupingPolicy in a safe way, returns error instead of causing panic. +func (e *Enforcer) RemoveGroupingPolicySafe(params ...interface{}) (result bool, err error) { + defer func() { + if r := recover(); r != nil { + err = fmt.Errorf("%v", r) + result = false + } + }() + + result = e.RemoveGroupingPolicy(params...) + err = nil + return +} + +// RemoveFilteredGroupingPolicySafe calls RemoveFilteredGroupingPolicy in a safe way, returns error instead of causing panic. +func (e *Enforcer) RemoveFilteredGroupingPolicySafe(fieldIndex int, fieldValues ...string) (result bool, err error) { + defer func() { + if r := recover(); r != nil { + err = fmt.Errorf("%v", r) + result = false + } + }() + + result = e.RemoveFilteredGroupingPolicy(fieldIndex, fieldValues...) + err = nil + return +} + +// RemoveNamedGroupingPolicySafe calls RemoveNamedGroupingPolicy in a safe way, returns error instead of causing panic. +func (e *Enforcer) RemoveNamedGroupingPolicySafe(ptype string, params ...interface{}) (result bool, err error) { + defer func() { + if r := recover(); r != nil { + err = fmt.Errorf("%v", r) + result = false + } + }() + + result = e.RemoveNamedGroupingPolicy(ptype, params...) + err = nil + return +} + +// RemoveFilteredNamedGroupingPolicySafe calls RemoveFilteredNamedGroupingPolicy in a safe way, returns error instead of causing panic. +func (e *Enforcer) RemoveFilteredNamedGroupingPolicySafe(ptype string, fieldIndex int, fieldValues ...string) (result bool, err error) { + defer func() { + if r := recover(); r != nil { + err = fmt.Errorf("%v", r) + result = false + } + }() + + result = e.RemoveFilteredNamedGroupingPolicy(ptype, fieldIndex, fieldValues...) + err = nil + return +} diff --git a/src/vendor/github.com/casbin/casbin/enforcer_synced_safe.go b/src/vendor/github.com/casbin/casbin/enforcer_synced_safe.go new file mode 100644 index 000000000..d61f4b7c3 --- /dev/null +++ b/src/vendor/github.com/casbin/casbin/enforcer_synced_safe.go @@ -0,0 +1,63 @@ +// Copyright 2017 The casbin Authors. All Rights Reserved. +// +// 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 casbin + +// NewSyncedEnforcerSafe creates a synchronized enforcer via file or DB. +func NewSyncedEnforcerSafe(params ...interface{}) (enforcer *SyncedEnforcer, err error) { + e := &SyncedEnforcer{} + e.Enforcer, err = NewEnforcerSafe(params...) + + if err != nil { + return nil, err + } + + e.autoLoad = false + return e, nil +} + +// LoadModelSafe calls LoadModel in a safe way, returns error instead of causing panic. +func (e *SyncedEnforcer) LoadModelSafe() (err error) { + e.m.RLock() + defer e.m.RUnlock() + return e.Enforcer.LoadModelSafe() +} + +// EnforceSafe calls Enforce in a safe way, returns error instead of causing panic. +func (e *SyncedEnforcer) EnforceSafe(rvals ...interface{}) (result bool, err error) { + e.m.RLock() + defer e.m.RUnlock() + return e.Enforcer.EnforceSafe(rvals...) +} + +// AddPolicySafe calls AddPolicy in a safe way, returns error instead of causing panic. +func (e *SyncedEnforcer) AddPolicySafe(params ...interface{}) (result bool, err error) { + e.m.Lock() + defer e.m.Unlock() + return e.Enforcer.AddPolicySafe(params...) +} + +// RemovePolicySafe calls RemovePolicy in a safe way, returns error instead of causing panic. +func (e *SyncedEnforcer) RemovePolicySafe(params ...interface{}) (result bool, err error) { + e.m.Lock() + defer e.m.Unlock() + return e.Enforcer.RemovePolicySafe(params...) +} + +// RemoveFilteredPolicySafe calls RemoveFilteredPolicy in a safe way, returns error instead of causing panic. +func (e *SyncedEnforcer) RemoveFilteredPolicySafe(fieldIndex int, fieldValues ...string) (result bool, err error) { + e.m.Lock() + defer e.m.Unlock() + return e.Enforcer.RemoveFilteredPolicySafe(fieldIndex, fieldValues...) +} diff --git a/src/vendor/github.com/casbin/casbin/errors/rbac_errors.go b/src/vendor/github.com/casbin/casbin/errors/rbac_errors.go new file mode 100644 index 000000000..68b1450ce --- /dev/null +++ b/src/vendor/github.com/casbin/casbin/errors/rbac_errors.go @@ -0,0 +1,24 @@ +// Copyright 2018 The casbin Authors. All Rights Reserved. +// +// 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 errors + +import "errors" + +// Global errors for rbac defined here +var ( + ERR_NAME_NOT_FOUND = errors.New("error: name does not exist") + ERR_DOMAIN_PARAMETER = errors.New("error: domain should be 1 parameter") + ERR_NAMES12_NOT_FOUND = errors.New("error: name1 or name2 does not exist") +) diff --git a/src/vendor/github.com/casbin/casbin/persist/adapter.go b/src/vendor/github.com/casbin/casbin/persist/adapter.go index 0d994b98f..7a5271b61 100644 --- a/src/vendor/github.com/casbin/casbin/persist/adapter.go +++ b/src/vendor/github.com/casbin/casbin/persist/adapter.go @@ -22,16 +22,15 @@ import ( // LoadPolicyLine loads a text line as a policy rule to model. func LoadPolicyLine(line string, model model.Model) { - if line == "" { + if line == "" || strings.HasPrefix(line, "#") { return } - if strings.HasPrefix(line, "#") { - return + tokens := strings.Split(line, ",") + for i := 0; i < len(tokens); i++ { + tokens[i] = strings.TrimSpace(tokens[i]) } - tokens := strings.Split(line, ", ") - key := tokens[0] sec := key[:1] model[sec][key].Policy = append(model[sec][key].Policy, tokens[1:]) diff --git a/src/vendor/github.com/casbin/casbin/persist/file-adapter/adapter_filtered.go b/src/vendor/github.com/casbin/casbin/persist/file-adapter/adapter_filtered.go index f0b18c3b0..0aa636040 100644 --- a/src/vendor/github.com/casbin/casbin/persist/file-adapter/adapter_filtered.go +++ b/src/vendor/github.com/casbin/casbin/persist/file-adapter/adapter_filtered.go @@ -41,6 +41,7 @@ type Filter struct { // NewFilteredAdapter is the constructor for FilteredAdapter. func NewFilteredAdapter(filePath string) *FilteredAdapter { a := FilteredAdapter{} + a.filtered = true a.Adapter = NewAdapter(filePath) return &a } diff --git a/src/vendor/github.com/casbin/casbin/persist/watcher.go b/src/vendor/github.com/casbin/casbin/persist/watcher.go index 089fbc0f2..0d843606b 100644 --- a/src/vendor/github.com/casbin/casbin/persist/watcher.go +++ b/src/vendor/github.com/casbin/casbin/persist/watcher.go @@ -24,4 +24,6 @@ type Watcher interface { // It is usually called after changing the policy in DB, like Enforcer.SavePolicy(), // Enforcer.AddPolicy(), Enforcer.RemovePolicy(), etc. Update() error + // Close stops and releases the watcher, the callback function will not be called any more. + Close() } diff --git a/src/vendor/github.com/casbin/casbin/rbac/default-role-manager/role_manager.go b/src/vendor/github.com/casbin/casbin/rbac/default-role-manager/role_manager.go index e7eb901f5..d333cb631 100644 --- a/src/vendor/github.com/casbin/casbin/rbac/default-role-manager/role_manager.go +++ b/src/vendor/github.com/casbin/casbin/rbac/default-role-manager/role_manager.go @@ -15,17 +15,21 @@ package defaultrolemanager import ( - "errors" "sync" + "github.com/casbin/casbin/errors" "github.com/casbin/casbin/log" "github.com/casbin/casbin/rbac" ) +type MatchingFunc func(arg1, arg2 string) bool + // RoleManager provides a default implementation for the RoleManager interface type RoleManager struct { allRoles *sync.Map maxHierarchyLevel int + hasPattern bool + matchingFunc MatchingFunc } // NewRoleManager is the constructor for creating an instance of the @@ -34,15 +38,41 @@ func NewRoleManager(maxHierarchyLevel int) rbac.RoleManager { rm := RoleManager{} rm.allRoles = &sync.Map{} rm.maxHierarchyLevel = maxHierarchyLevel + rm.hasPattern = false + return &rm } +func (rm *RoleManager) AddMatchingFunc(name string, fn MatchingFunc) { + rm.hasPattern = true + rm.matchingFunc = fn +} + func (rm *RoleManager) hasRole(name string) bool { - _, ok := rm.allRoles.Load(name) + var ok bool + if rm.hasPattern { + rm.allRoles.Range(func(key, value interface{}) bool { + if rm.matchingFunc(name, key.(string)) { + ok = true + } + return true + }) + } else { + _, ok = rm.allRoles.Load(name) + } + return ok } func (rm *RoleManager) createRole(name string) *Role { + if rm.hasPattern { + rm.allRoles.Range(func(key, value interface{}) bool { + if rm.matchingFunc(name, key.(string)) { + name = key.(string) + } + return true + }) + } role, _ := rm.allRoles.LoadOrStore(name, newRole(name)) return role.(*Role) } @@ -61,7 +91,7 @@ func (rm *RoleManager) AddLink(name1 string, name2 string, domain ...string) err name1 = domain[0] + "::" + name1 name2 = domain[0] + "::" + name2 } else if len(domain) > 1 { - return errors.New("error: domain should be 1 parameter") + return errors.ERR_DOMAIN_PARAMETER } role1 := rm.createRole(name1) @@ -78,11 +108,11 @@ func (rm *RoleManager) DeleteLink(name1 string, name2 string, domain ...string) name1 = domain[0] + "::" + name1 name2 = domain[0] + "::" + name2 } else if len(domain) > 1 { - return errors.New("error: domain should be 1 parameter") + return errors.ERR_DOMAIN_PARAMETER } if !rm.hasRole(name1) || !rm.hasRole(name2) { - return errors.New("error: name1 or name2 does not exist") + return errors.ERR_NAMES12_NOT_FOUND } role1 := rm.createRole(name1) @@ -98,7 +128,7 @@ func (rm *RoleManager) HasLink(name1 string, name2 string, domain ...string) (bo name1 = domain[0] + "::" + name1 name2 = domain[0] + "::" + name2 } else if len(domain) > 1 { - return false, errors.New("error: domain should be 1 parameter") + return false, errors.ERR_DOMAIN_PARAMETER } if name1 == name2 { @@ -119,7 +149,7 @@ func (rm *RoleManager) GetRoles(name string, domain ...string) ([]string, error) if len(domain) == 1 { name = domain[0] + "::" + name } else if len(domain) > 1 { - return nil, errors.New("error: domain should be 1 parameter") + return nil, errors.ERR_DOMAIN_PARAMETER } if !rm.hasRole(name) { @@ -138,8 +168,14 @@ func (rm *RoleManager) GetRoles(name string, domain ...string) ([]string, error) // GetUsers gets the users that inherits a subject. // domain is an unreferenced parameter here, may be used in other implementations. func (rm *RoleManager) GetUsers(name string, domain ...string) ([]string, error) { + if len(domain) == 1 { + name = domain[0] + "::" + name + } else if len(domain) > 1 { + return nil, errors.ERR_DOMAIN_PARAMETER + } + if !rm.hasRole(name) { - return nil, errors.New("error: name does not exist") + return nil, errors.ERR_NAME_NOT_FOUND } names := []string{} @@ -150,6 +186,11 @@ func (rm *RoleManager) GetUsers(name string, domain ...string) ([]string, error) } return true }) + if len(domain) == 1 { + for i := range names { + names[i] = names[i][len(domain[0])+2:] + } + } return names, nil } diff --git a/src/vendor/github.com/casbin/casbin/rbac_api.go b/src/vendor/github.com/casbin/casbin/rbac_api.go index 33bf952b7..0364893b1 100644 --- a/src/vendor/github.com/casbin/casbin/rbac_api.go +++ b/src/vendor/github.com/casbin/casbin/rbac_api.go @@ -14,22 +14,26 @@ package casbin +import "github.com/casbin/casbin/util" + // GetRolesForUser gets the roles that a user has. -func (e *Enforcer) GetRolesForUser(name string) []string { - res, _ := e.model["g"]["g"].RM.GetRoles(name) - return res +func (e *Enforcer) GetRolesForUser(name string) ([]string, error) { + res, err := e.model["g"]["g"].RM.GetRoles(name) + return res, err } // GetUsersForRole gets the users that has a role. -func (e *Enforcer) GetUsersForRole(name string) []string { - res, _ := e.model["g"]["g"].RM.GetUsers(name) - return res +func (e *Enforcer) GetUsersForRole(name string) ([]string, error) { + res, err := e.model["g"]["g"].RM.GetUsers(name) + return res, err } // HasRoleForUser determines whether a user has a role. -func (e *Enforcer) HasRoleForUser(name string, role string) bool { - roles := e.GetRolesForUser(name) - +func (e *Enforcer) HasRoleForUser(name string, role string) (bool, error) { + roles, err := e.GetRolesForUser(name) + if err != nil { + return false, err + } hasRole := false for _, r := range roles { if r == role { @@ -38,7 +42,7 @@ func (e *Enforcer) HasRoleForUser(name string, role string) bool { } } - return hasRole + return hasRole, nil } // AddRoleForUser adds a role for a user. @@ -80,27 +84,13 @@ func (e *Enforcer) DeletePermission(permission ...string) bool { // AddPermissionForUser adds a permission for a user or role. // Returns false if the user or role already has the permission (aka not affected). func (e *Enforcer) AddPermissionForUser(user string, permission ...string) bool { - params := make([]interface{}, 0, len(permission)+1) - - params = append(params, user) - for _, perm := range permission { - params = append(params, perm) - } - - return e.AddPolicy(params...) + return e.AddPolicy(util.JoinSlice(user, permission...)) } // DeletePermissionForUser deletes a permission for a user or role. // Returns false if the user or role does not have the permission (aka not affected). func (e *Enforcer) DeletePermissionForUser(user string, permission ...string) bool { - params := make([]interface{}, 0, len(permission)+1) - - params = append(params, user) - for _, perm := range permission { - params = append(params, perm) - } - - return e.RemovePolicy(params...) + return e.RemovePolicy(util.JoinSlice(user, permission...)) } // DeletePermissionsForUser deletes permissions for a user or role. @@ -116,14 +106,7 @@ func (e *Enforcer) GetPermissionsForUser(user string) [][]string { // HasPermissionForUser determines whether a user has a permission. func (e *Enforcer) HasPermissionForUser(user string, permission ...string) bool { - params := make([]interface{}, 0, len(permission)+1) - - params = append(params, user) - for _, perm := range permission { - params = append(params, perm) - } - - return e.HasPolicy(params...) + return e.HasPolicy(util.JoinSlice(user, permission...)) } // GetImplicitRolesForUser gets implicit roles that a user has. @@ -134,7 +117,7 @@ func (e *Enforcer) HasPermissionForUser(user string, permission ...string) bool // // GetRolesForUser("alice") can only get: ["role:admin"]. // But GetImplicitRolesForUser("alice") will get: ["role:admin", "role:user"]. -func (e *Enforcer) GetImplicitRolesForUser(name string) []string { +func (e *Enforcer) GetImplicitRolesForUser(name string, domain ...string) []string { res := []string{} roleSet := make(map[string]bool) roleSet[name] = true @@ -146,7 +129,7 @@ func (e *Enforcer) GetImplicitRolesForUser(name string) []string { name := q[0] q = q[1:] - roles, err := e.rm.GetRoles(name) + roles, err := e.rm.GetRoles(name, domain...) if err != nil { panic(err) } @@ -171,14 +154,52 @@ func (e *Enforcer) GetImplicitRolesForUser(name string) []string { // // GetPermissionsForUser("alice") can only get: [["alice", "data2", "read"]]. // But GetImplicitPermissionsForUser("alice") will get: [["admin", "data1", "read"], ["alice", "data2", "read"]]. -func (e *Enforcer) GetImplicitPermissionsForUser(user string) [][]string { - roles := e.GetImplicitRolesForUser(user) +func (e *Enforcer) GetImplicitPermissionsForUser(user string, domain ...string) [][]string { + roles := e.GetImplicitRolesForUser(user, domain...) roles = append([]string{user}, roles...) + withDomain := false + if len(domain) == 1 { + withDomain = true + } else if len(domain) > 1 { + panic("error: domain should be 1 parameter") + } + res := [][]string{} + permissions := [][]string{} for _, role := range roles { - permissions := e.GetPermissionsForUser(role) + if withDomain { + permissions = e.GetPermissionsForUserInDomain(role, domain[0]) + } else { + permissions = e.GetPermissionsForUser(role) + } res = append(res, permissions...) } + + return res +} + +// GetImplicitUsersForPermission gets implicit users for a permission. +// For example: +// p, admin, data1, read +// p, bob, data1, read +// g, alice, admin +// +// GetImplicitUsersForPermission("data1", "read") will get: ["alice", "bob"]. +// Note: only users will be returned, roles (2nd arg in "g") will be excluded. +func (e *Enforcer) GetImplicitUsersForPermission(permission ...string) []string { + subjects := e.GetAllSubjects() + roles := e.GetAllRoles() + + users := util.SetSubtract(subjects, roles) + + res := []string{} + for _, user := range users { + req := util.JoinSliceAny(user, permission...) + if e.Enforce(req...) { + res = append(res, user) + } + } + return res } diff --git a/src/vendor/github.com/casbin/casbin/rbac_api_synced.go b/src/vendor/github.com/casbin/casbin/rbac_api_synced.go index 119ce9f8b..c32a0731a 100644 --- a/src/vendor/github.com/casbin/casbin/rbac_api_synced.go +++ b/src/vendor/github.com/casbin/casbin/rbac_api_synced.go @@ -15,23 +15,23 @@ package casbin // GetRolesForUser gets the roles that a user has. -func (e *SyncedEnforcer) GetRolesForUser(name string) []string { - e.m.Lock() - defer e.m.Unlock() +func (e *SyncedEnforcer) GetRolesForUser(name string) ([]string, error) { + e.m.RLock() + defer e.m.RUnlock() return e.Enforcer.GetRolesForUser(name) } // GetUsersForRole gets the users that has a role. -func (e *SyncedEnforcer) GetUsersForRole(name string) []string { - e.m.Lock() - defer e.m.Unlock() +func (e *SyncedEnforcer) GetUsersForRole(name string) ([]string, error) { + e.m.RLock() + defer e.m.RUnlock() return e.Enforcer.GetUsersForRole(name) } // HasRoleForUser determines whether a user has a role. -func (e *SyncedEnforcer) HasRoleForUser(name string, role string) bool { - e.m.Lock() - defer e.m.Unlock() +func (e *SyncedEnforcer) HasRoleForUser(name string, role string) (bool, error) { + e.m.RLock() + defer e.m.RUnlock() return e.Enforcer.HasRoleForUser(name, role) } @@ -108,14 +108,14 @@ func (e *SyncedEnforcer) DeletePermissionsForUser(user string) bool { // GetPermissionsForUser gets permissions for a user or role. func (e *SyncedEnforcer) GetPermissionsForUser(user string) [][]string { - e.m.Lock() - defer e.m.Unlock() + e.m.RLock() + defer e.m.RUnlock() return e.Enforcer.GetPermissionsForUser(user) } // HasPermissionForUser determines whether a user has a permission. func (e *SyncedEnforcer) HasPermissionForUser(user string, permission ...string) bool { - e.m.Lock() - defer e.m.Unlock() + e.m.RLock() + defer e.m.RUnlock() return e.Enforcer.HasPermissionForUser(user, permission...) } diff --git a/src/vendor/github.com/casbin/casbin/rbac_api_with_domains.go b/src/vendor/github.com/casbin/casbin/rbac_api_with_domains.go index d6af0d6d6..9d81816ef 100644 --- a/src/vendor/github.com/casbin/casbin/rbac_api_with_domains.go +++ b/src/vendor/github.com/casbin/casbin/rbac_api_with_domains.go @@ -14,6 +14,12 @@ package casbin +// GetUsersForRoleInDomain gets the users that has a role inside a domain. Add by Gordon +func (e *Enforcer) GetUsersForRoleInDomain(name string, domain string) []string { + res, _ := e.model["g"]["g"].RM.GetUsers(name, domain) + return res +} + // GetRolesForUserInDomain gets the roles that a user has inside a domain. func (e *Enforcer) GetRolesForUserInDomain(name string, domain string) []string { res, _ := e.model["g"]["g"].RM.GetRoles(name, domain) diff --git a/src/vendor/github.com/casbin/casbin/rbac_api_with_domains_synced.go b/src/vendor/github.com/casbin/casbin/rbac_api_with_domains_synced.go new file mode 100644 index 000000000..c2d3c0636 --- /dev/null +++ b/src/vendor/github.com/casbin/casbin/rbac_api_with_domains_synced.go @@ -0,0 +1,52 @@ +// Copyright 2017 The casbin Authors. All Rights Reserved. +// +// 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 casbin + +// GetUsersForRoleInDomain gets the users that has a role inside a domain. Add by Gordon +func (e *SyncedEnforcer) GetUsersForRoleInDomain(name string, domain string) []string { + e.m.Lock() + defer e.m.Unlock() + return e.Enforcer.GetUsersForRoleInDomain(name, domain) +} + +// GetRolesForUserInDomain gets the roles that a user has inside a domain. +func (e *SyncedEnforcer) GetRolesForUserInDomain(name string, domain string) []string { + e.m.Lock() + defer e.m.Unlock() + return e.Enforcer.GetRolesForUserInDomain(name, domain) +} + +// GetPermissionsForUserInDomain gets permissions for a user or role inside a domain. +func (e *SyncedEnforcer) GetPermissionsForUserInDomain(user string, domain string) [][]string { + e.m.Lock() + defer e.m.Unlock() + return e.Enforcer.GetPermissionsForUserInDomain(user, domain) +} + +// AddRoleForUserInDomain adds a role for a user inside a domain. +// Returns false if the user already has the role (aka not affected). +func (e *SyncedEnforcer) AddRoleForUserInDomain(user string, role string, domain string) bool { + e.m.Lock() + defer e.m.Unlock() + return e.Enforcer.AddRoleForUserInDomain(user, role, domain) +} + +// DeleteRoleForUserInDomain deletes a role for a user inside a domain. +// Returns false if the user does not have the role (aka not affected). +func (e *SyncedEnforcer) DeleteRoleForUserInDomain(user string, role string, domain string) bool { + e.m.Lock() + defer e.m.Unlock() + return e.Enforcer.DeleteRoleForUserInDomain(user, role, domain) +} diff --git a/src/vendor/github.com/casbin/casbin/util/builtin_operators.go b/src/vendor/github.com/casbin/casbin/util/builtin_operators.go index 79a8825e5..53011e7e1 100644 --- a/src/vendor/github.com/casbin/casbin/util/builtin_operators.go +++ b/src/vendor/github.com/casbin/casbin/util/builtin_operators.go @@ -55,10 +55,10 @@ func KeyMatch2(key1 string, key2 string) bool { break } - key2 = "^" + re.ReplaceAllString(key2, "$1[^/]+$2") + "$" + key2 = re.ReplaceAllString(key2, "$1[^/]+$2") } - return RegexMatch(key1, key2) + return RegexMatch(key1, "^" + key2 + "$") } // KeyMatch2Func is the wrapper for KeyMatch2. @@ -83,7 +83,7 @@ func KeyMatch3(key1 string, key2 string) bool { key2 = re.ReplaceAllString(key2, "$1[^/]+$2") } - return RegexMatch(key1, key2) + return RegexMatch(key1, "^" + key2 + "$") } // KeyMatch3Func is the wrapper for KeyMatch3. diff --git a/src/vendor/github.com/casbin/casbin/util/util.go b/src/vendor/github.com/casbin/casbin/util/util.go index 4ce033d5a..e25dc2231 100644 --- a/src/vendor/github.com/casbin/casbin/util/util.go +++ b/src/vendor/github.com/casbin/casbin/util/util.go @@ -15,14 +15,21 @@ package util import ( + "regexp" "sort" "strings" ) // EscapeAssertion escapes the dots in the assertion, because the expression evaluation doesn't support such variable names. func EscapeAssertion(s string) string { - s = strings.Replace(s, "r.", "r_", -1) - s = strings.Replace(s, "p.", "p_", -1) + //Replace the first dot, because it can't be recognized by the regexp. + if (strings.HasPrefix(s, "r") || strings.HasPrefix(s, "p")) { + s = strings.Replace(s, ".", "_",1) + } + var regex = regexp.MustCompile(`(\|| |=|\)|\(|&|<|>|,|\+|-|!|\*|\/)(r|p)\.`) + s = regex.ReplaceAllStringFunc(s, func(m string) string { + return strings.Replace(m, ".", "_", 1) + }) return s } @@ -103,3 +110,42 @@ func SetEquals(a []string, b []string) bool { } return true } + +// JoinSlice joins a string and a slice into a new slice. +func JoinSlice(a string, b ...string) []string { + res := make([]string, 0, len(b)+1) + + res = append(res, a) + for _, s := range b { + res = append(res, s) + } + + return res +} + +// JoinSliceAny joins a string and a slice into a new interface{} slice. +func JoinSliceAny(a string, b ...string) []interface{} { + res := make([]interface{}, 0, len(b)+1) + + res = append(res, a) + for _, s := range b { + res = append(res, s) + } + + return res +} + +// SetSubtract returns the elements in `a` that aren't in `b`. +func SetSubtract(a []string, b []string) []string { + mb := make(map[string]struct{}, len(b)) + for _, x := range b { + mb[x] = struct{}{} + } + var diff []string + for _, x := range a { + if _, found := mb[x]; !found { + diff = append(diff, x) + } + } + return diff +} diff --git a/src/vendor/github.com/go-sql-driver/mysql/.travis.yml b/src/vendor/github.com/go-sql-driver/mysql/.travis.yml deleted file mode 100644 index 56fcf25f2..000000000 --- a/src/vendor/github.com/go-sql-driver/mysql/.travis.yml +++ /dev/null @@ -1,129 +0,0 @@ -sudo: false -language: go -go: - - 1.10.x - - 1.11.x - - 1.12.x - - 1.13.x - - master - -before_install: - - go get golang.org/x/tools/cmd/cover - - go get github.com/mattn/goveralls - -before_script: - - echo -e "[server]\ninnodb_log_file_size=256MB\ninnodb_buffer_pool_size=512MB\nmax_allowed_packet=16MB" | sudo tee -a /etc/mysql/my.cnf - - sudo service mysql restart - - .travis/wait_mysql.sh - - mysql -e 'create database gotest;' - -matrix: - include: - - env: DB=MYSQL8 - sudo: required - dist: trusty - go: 1.10.x - services: - - docker - before_install: - - go get golang.org/x/tools/cmd/cover - - go get github.com/mattn/goveralls - - docker pull mysql:8.0 - - docker run -d -p 127.0.0.1:3307:3306 --name mysqld -e MYSQL_DATABASE=gotest -e MYSQL_USER=gotest -e MYSQL_PASSWORD=secret -e MYSQL_ROOT_PASSWORD=verysecret - mysql:8.0 --innodb_log_file_size=256MB --innodb_buffer_pool_size=512MB --max_allowed_packet=16MB --local-infile=1 - - cp .travis/docker.cnf ~/.my.cnf - - .travis/wait_mysql.sh - before_script: - - export MYSQL_TEST_USER=gotest - - export MYSQL_TEST_PASS=secret - - export MYSQL_TEST_ADDR=127.0.0.1:3307 - - export MYSQL_TEST_CONCURRENT=1 - - - env: DB=MYSQL57 - sudo: required - dist: trusty - go: 1.10.x - services: - - docker - before_install: - - go get golang.org/x/tools/cmd/cover - - go get github.com/mattn/goveralls - - docker pull mysql:5.7 - - docker run -d -p 127.0.0.1:3307:3306 --name mysqld -e MYSQL_DATABASE=gotest -e MYSQL_USER=gotest -e MYSQL_PASSWORD=secret -e MYSQL_ROOT_PASSWORD=verysecret - mysql:5.7 --innodb_log_file_size=256MB --innodb_buffer_pool_size=512MB --max_allowed_packet=16MB --local-infile=1 - - cp .travis/docker.cnf ~/.my.cnf - - .travis/wait_mysql.sh - before_script: - - export MYSQL_TEST_USER=gotest - - export MYSQL_TEST_PASS=secret - - export MYSQL_TEST_ADDR=127.0.0.1:3307 - - export MYSQL_TEST_CONCURRENT=1 - - - env: DB=MARIA55 - sudo: required - dist: trusty - go: 1.10.x - services: - - docker - before_install: - - go get golang.org/x/tools/cmd/cover - - go get github.com/mattn/goveralls - - docker pull mariadb:5.5 - - docker run -d -p 127.0.0.1:3307:3306 --name mysqld -e MYSQL_DATABASE=gotest -e MYSQL_USER=gotest -e MYSQL_PASSWORD=secret -e MYSQL_ROOT_PASSWORD=verysecret - mariadb:5.5 --innodb_log_file_size=256MB --innodb_buffer_pool_size=512MB --max_allowed_packet=16MB --local-infile=1 - - cp .travis/docker.cnf ~/.my.cnf - - .travis/wait_mysql.sh - before_script: - - export MYSQL_TEST_USER=gotest - - export MYSQL_TEST_PASS=secret - - export MYSQL_TEST_ADDR=127.0.0.1:3307 - - export MYSQL_TEST_CONCURRENT=1 - - - env: DB=MARIA10_1 - sudo: required - dist: trusty - go: 1.10.x - services: - - docker - before_install: - - go get golang.org/x/tools/cmd/cover - - go get github.com/mattn/goveralls - - docker pull mariadb:10.1 - - docker run -d -p 127.0.0.1:3307:3306 --name mysqld -e MYSQL_DATABASE=gotest -e MYSQL_USER=gotest -e MYSQL_PASSWORD=secret -e MYSQL_ROOT_PASSWORD=verysecret - mariadb:10.1 --innodb_log_file_size=256MB --innodb_buffer_pool_size=512MB --max_allowed_packet=16MB --local-infile=1 - - cp .travis/docker.cnf ~/.my.cnf - - .travis/wait_mysql.sh - before_script: - - export MYSQL_TEST_USER=gotest - - export MYSQL_TEST_PASS=secret - - export MYSQL_TEST_ADDR=127.0.0.1:3307 - - export MYSQL_TEST_CONCURRENT=1 - - - os: osx - osx_image: xcode10.1 - addons: - homebrew: - packages: - - mysql - update: true - go: 1.12.x - before_install: - - go get golang.org/x/tools/cmd/cover - - go get github.com/mattn/goveralls - before_script: - - echo -e "[server]\ninnodb_log_file_size=256MB\ninnodb_buffer_pool_size=512MB\nmax_allowed_packet=16MB\nlocal_infile=1" >> /usr/local/etc/my.cnf - - mysql.server start - - mysql -uroot -e 'CREATE USER gotest IDENTIFIED BY "secret"' - - mysql -uroot -e 'GRANT ALL ON *.* TO gotest' - - mysql -uroot -e 'create database gotest;' - - export MYSQL_TEST_USER=gotest - - export MYSQL_TEST_PASS=secret - - export MYSQL_TEST_ADDR=127.0.0.1:3306 - - export MYSQL_TEST_CONCURRENT=1 - -script: - - go test -v -covermode=count -coverprofile=coverage.out - - go vet ./... - - .travis/gofmt.sh -after_script: - - $HOME/gopath/bin/goveralls -coverprofile=coverage.out -service=travis-ci diff --git a/src/vendor/github.com/go-sql-driver/mysql/AUTHORS b/src/vendor/github.com/go-sql-driver/mysql/AUTHORS index ad5989800..50afa2c85 100644 --- a/src/vendor/github.com/go-sql-driver/mysql/AUTHORS +++ b/src/vendor/github.com/go-sql-driver/mysql/AUTHORS @@ -13,11 +13,15 @@ Aaron Hopkins Achille Roussel +Alex Snast Alexey Palazhchenko Andrew Reid +Animesh Ray Arne Hormann +Ariel Mashraki Asta Xie Bulat Gaifullin +Caine Jette Carlos Nieto Chris Moos Craig Wilson @@ -52,6 +56,7 @@ Julien Schmidt Justin Li Justin Nuß Kamil Dziedzic +Kei Kamikawa Kevin Malachowski Kieron Woodhouse Lennart Rudolph @@ -74,20 +79,26 @@ Reed Allman Richard Wilkes Robert Russell Runrioter Wung +Sho Iizuka +Sho Ikeda Shuode Li Simon J Mudd Soroush Pour Stan Putrya Stanley Gunawan Steven Hartland +Tan Jinhua <312841925 at qq.com> Thomas Wodarek Tim Ruffles Tom Jenkinson Vladimir Kovpak +Vladyslav Zhelezniak Xiangyu Hu Xiaobing Jiang Xiuming Chen +Xuehong Chan Zhenye Xie +Zhixin Wen # Organizations @@ -103,3 +114,4 @@ Multiplay Ltd. Percona LLC Pivotal Inc. Stripe Inc. +Zendesk Inc. diff --git a/src/vendor/github.com/go-sql-driver/mysql/CHANGELOG.md b/src/vendor/github.com/go-sql-driver/mysql/CHANGELOG.md index 9cb97b38d..72a738ed5 100644 --- a/src/vendor/github.com/go-sql-driver/mysql/CHANGELOG.md +++ b/src/vendor/github.com/go-sql-driver/mysql/CHANGELOG.md @@ -1,3 +1,29 @@ +## Version 1.6 (2021-04-01) + +Changes: + + - Migrate the CI service from travis-ci to GitHub Actions (#1176, #1183, #1190) + - `NullTime` is deprecated (#960, #1144) + - Reduce allocations when building SET command (#1111) + - Performance improvement for time formatting (#1118) + - Performance improvement for time parsing (#1098, #1113) + +New Features: + + - Implement `driver.Validator` interface (#1106, #1174) + - Support returning `uint64` from `Valuer` in `ConvertValue` (#1143) + - Add `json.RawMessage` for converter and prepared statement (#1059) + - Interpolate `json.RawMessage` as `string` (#1058) + - Implements `CheckNamedValue` (#1090) + +Bugfixes: + + - Stop rounding times (#1121, #1172) + - Put zero filler into the SSL handshake packet (#1066) + - Fix checking cancelled connections back into the connection pool (#1095) + - Fix remove last 0 byte for mysql_old_password when password is empty (#1133) + + ## Version 1.5 (2020-01-07) Changes: diff --git a/src/vendor/github.com/go-sql-driver/mysql/README.md b/src/vendor/github.com/go-sql-driver/mysql/README.md index d2627a41a..0b13154fc 100644 --- a/src/vendor/github.com/go-sql-driver/mysql/README.md +++ b/src/vendor/github.com/go-sql-driver/mysql/README.md @@ -35,7 +35,7 @@ A MySQL-Driver for Go's [database/sql](https://golang.org/pkg/database/sql/) pac * Supports queries larger than 16MB * Full [`sql.RawBytes`](https://golang.org/pkg/database/sql/#RawBytes) support. * Intelligent `LONG DATA` handling in prepared statements - * Secure `LOAD DATA LOCAL INFILE` support with file Whitelisting and `io.Reader` support + * Secure `LOAD DATA LOCAL INFILE` support with file allowlisting and `io.Reader` support * Optional `time.Time` parsing * Optional placeholder interpolation @@ -56,15 +56,37 @@ Make sure [Git is installed](https://git-scm.com/downloads) on your machine and _Go MySQL Driver_ is an implementation of Go's `database/sql/driver` interface. You only need to import the driver and can use the full [`database/sql`](https://golang.org/pkg/database/sql/) API then. Use `mysql` as `driverName` and a valid [DSN](#dsn-data-source-name) as `dataSourceName`: + ```go -import "database/sql" -import _ "github.com/go-sql-driver/mysql" +import ( + "database/sql" + "time" + + _ "github.com/go-sql-driver/mysql" +) + +// ... db, err := sql.Open("mysql", "user:password@/dbname") +if err != nil { + panic(err) +} +// See "Important settings" section. +db.SetConnMaxLifetime(time.Minute * 3) +db.SetMaxOpenConns(10) +db.SetMaxIdleConns(10) ``` [Examples are available in our Wiki](https://github.com/go-sql-driver/mysql/wiki/Examples "Go-MySQL-Driver Examples"). +### Important settings + +`db.SetConnMaxLifetime()` is required to ensure connections are closed by the driver safely before connection is closed by MySQL server, OS, or other middlewares. Since some middlewares close idle connections by 5 minutes, we recommend timeout shorter than 5 minutes. This setting helps load balancing and changing system variables too. + +`db.SetMaxOpenConns()` is highly recommended to limit the number of connection used by the application. There is no recommended limit number because it depends on application and MySQL server. + +`db.SetMaxIdleConns()` is recommended to be set same to (or greater than) `db.SetMaxOpenConns()`. When it is smaller than `SetMaxOpenConns()`, connections can be opened and closed very frequently than you expect. Idle connections can be closed by the `db.SetConnMaxLifetime()`. If you want to close idle connections more rapidly, you can use `db.SetConnMaxIdleTime()` since Go 1.15. + ### DSN (Data Source Name) @@ -122,7 +144,7 @@ Valid Values: true, false Default: false ``` -`allowAllFiles=true` disables the file Whitelist for `LOAD DATA LOCAL INFILE` and allows *all* files. +`allowAllFiles=true` disables the file allowlist for `LOAD DATA LOCAL INFILE` and allows *all* files. [*Might be insecure!*](http://dev.mysql.com/doc/refman/5.7/en/load-data-local.html) ##### `allowCleartextPasswords` @@ -133,7 +155,7 @@ Valid Values: true, false Default: false ``` -`allowCleartextPasswords=true` allows using the [cleartext client side plugin](http://dev.mysql.com/doc/en/cleartext-authentication-plugin.html) if required by an account, such as one defined with the [PAM authentication plugin](http://dev.mysql.com/doc/en/pam-authentication-plugin.html). Sending passwords in clear text may be a security problem in some configurations. To avoid problems if there is any possibility that the password would be intercepted, clients should connect to MySQL Server using a method that protects the password. Possibilities include [TLS / SSL](#tls), IPsec, or a private network. +`allowCleartextPasswords=true` allows using the [cleartext client side plugin](https://dev.mysql.com/doc/en/cleartext-pluggable-authentication.html) if required by an account, such as one defined with the [PAM authentication plugin](http://dev.mysql.com/doc/en/pam-authentication-plugin.html). Sending passwords in clear text may be a security problem in some configurations. To avoid problems if there is any possibility that the password would be intercepted, clients should connect to MySQL Server using a method that protects the password. Possibilities include [TLS / SSL](#tls), IPsec, or a private network. ##### `allowNativePasswords` @@ -230,7 +252,7 @@ Default: false If `interpolateParams` is true, placeholders (`?`) in calls to `db.Query()` and `db.Exec()` are interpolated into a single query string with given parameters. This reduces the number of roundtrips, since the driver has to prepare a statement, execute it with given parameters and close the statement again with `interpolateParams=false`. -*This can not be used together with the multibyte encodings BIG5, CP932, GB2312, GBK or SJIS. These are blacklisted as they may [introduce a SQL injection vulnerability](http://stackoverflow.com/a/12118602/3430118)!* +*This can not be used together with the multibyte encodings BIG5, CP932, GB2312, GBK or SJIS. These are rejected as they may [introduce a SQL injection vulnerability](http://stackoverflow.com/a/12118602/3430118)!* ##### `loc` @@ -376,7 +398,7 @@ Rules: Examples: * `autocommit=1`: `SET autocommit=1` * [`time_zone=%27Europe%2FParis%27`](https://dev.mysql.com/doc/refman/5.5/en/time-zone-support.html): `SET time_zone='Europe/Paris'` - * [`tx_isolation=%27REPEATABLE-READ%27`](https://dev.mysql.com/doc/refman/5.5/en/server-system-variables.html#sysvar_tx_isolation): `SET tx_isolation='REPEATABLE-READ'` + * [`transaction_isolation=%27REPEATABLE-READ%27`](https://dev.mysql.com/doc/refman/5.7/en/server-system-variables.html#sysvar_transaction_isolation): `SET transaction_isolation='REPEATABLE-READ'` #### Examples @@ -445,7 +467,7 @@ For this feature you need direct access to the package. Therefore you must chang import "github.com/go-sql-driver/mysql" ``` -Files must be whitelisted by registering them with `mysql.RegisterLocalFile(filepath)` (recommended) or the Whitelist check must be deactivated by using the DSN parameter `allowAllFiles=true` ([*Might be insecure!*](http://dev.mysql.com/doc/refman/5.7/en/load-data-local.html)). +Files must be explicitly allowed by registering them with `mysql.RegisterLocalFile(filepath)` (recommended) or the allowlist check must be deactivated by using the DSN parameter `allowAllFiles=true` ([*Might be insecure!*](http://dev.mysql.com/doc/refman/5.7/en/load-data-local.html)). To use a `io.Reader` a handler function must be registered with `mysql.RegisterReaderHandler(name, handler)` which returns a `io.Reader` or `io.ReadCloser`. The Reader is available with the filepath `Reader::` then. Choose different names for different handlers and `DeregisterReaderHandler` when you don't need it anymore. @@ -459,8 +481,6 @@ However, many want to scan MySQL `DATE` and `DATETIME` values into `time.Time` v **Caution:** As of Go 1.1, this makes `time.Time` the only variable type you can scan `DATE` and `DATETIME` values into. This breaks for example [`sql.RawBytes` support](https://github.com/go-sql-driver/mysql/wiki/Examples#rawbytes). -Alternatively you can use the [`NullTime`](https://godoc.org/github.com/go-sql-driver/mysql#NullTime) type as the scan destination, which works with both `time.Time` and `string` / `[]byte`. - ### Unicode support Since version 1.5 Go-MySQL-Driver automatically uses the collation ` utf8mb4_general_ci` by default. @@ -477,7 +497,7 @@ To run the driver tests you may need to adjust the configuration. See the [Testi Go-MySQL-Driver is not feature-complete yet. Your help is very appreciated. If you want to contribute, you can work on an [open issue](https://github.com/go-sql-driver/mysql/issues?state=open) or review a [pull request](https://github.com/go-sql-driver/mysql/pulls). -See the [Contribution Guidelines](https://github.com/go-sql-driver/mysql/blob/master/CONTRIBUTING.md) for details. +See the [Contribution Guidelines](https://github.com/go-sql-driver/mysql/blob/master/.github/CONTRIBUTING.md) for details. --------------------------------------- @@ -498,4 +518,3 @@ Please read the [MPL 2.0 FAQ](https://www.mozilla.org/en-US/MPL/2.0/FAQ/) if you You can read the full terms here: [LICENSE](https://raw.github.com/go-sql-driver/mysql/master/LICENSE). ![Go Gopher and MySQL Dolphin](https://raw.github.com/wiki/go-sql-driver/mysql/go-mysql-driver_m.jpg "Golang Gopher transporting the MySQL Dolphin in a wheelbarrow") - diff --git a/src/vendor/github.com/go-sql-driver/mysql/auth.go b/src/vendor/github.com/go-sql-driver/mysql/auth.go index fec7040d4..b2f19e8f0 100644 --- a/src/vendor/github.com/go-sql-driver/mysql/auth.go +++ b/src/vendor/github.com/go-sql-driver/mysql/auth.go @@ -15,6 +15,7 @@ import ( "crypto/sha256" "crypto/x509" "encoding/pem" + "fmt" "sync" ) @@ -136,10 +137,6 @@ func pwHash(password []byte) (result [2]uint32) { // Hash password using insecure pre 4.1 method func scrambleOldPassword(scramble []byte, password string) []byte { - if len(password) == 0 { - return nil - } - scramble = scramble[:8] hashPw := pwHash([]byte(password)) @@ -247,6 +244,9 @@ func (mc *mysqlConn) auth(authData []byte, plugin string) ([]byte, error) { if !mc.cfg.AllowOldPasswords { return nil, ErrOldPassword } + if len(mc.cfg.Passwd) == 0 { + return nil, nil + } // Note: there are edge cases where this should work but doesn't; // this is currently "wontfix": // https://github.com/go-sql-driver/mysql/issues/184 @@ -372,7 +372,10 @@ func (mc *mysqlConn) handleAuthResult(oldAuthData []byte, plugin string) error { return err } - block, _ := pem.Decode(data[1:]) + block, rest := pem.Decode(data[1:]) + if block == nil { + return fmt.Errorf("No Pem data found, data: %s", rest) + } pkix, err := x509.ParsePKIXPublicKey(block.Bytes) if err != nil { return err diff --git a/src/vendor/github.com/go-sql-driver/mysql/collations.go b/src/vendor/github.com/go-sql-driver/mysql/collations.go index 8d2b55676..326a9f7fa 100644 --- a/src/vendor/github.com/go-sql-driver/mysql/collations.go +++ b/src/vendor/github.com/go-sql-driver/mysql/collations.go @@ -247,7 +247,7 @@ var collations = map[string]byte{ "utf8mb4_0900_ai_ci": 255, } -// A blacklist of collations which is unsafe to interpolate parameters. +// A denylist of collations which is unsafe to interpolate parameters. // These multibyte encodings may contains 0x5c (`\`) in their trailing bytes. var unsafeCollations = map[string]bool{ "big5_chinese_ci": true, diff --git a/src/vendor/github.com/go-sql-driver/mysql/connection.go b/src/vendor/github.com/go-sql-driver/mysql/connection.go index e4bb59e67..835f89729 100644 --- a/src/vendor/github.com/go-sql-driver/mysql/connection.go +++ b/src/vendor/github.com/go-sql-driver/mysql/connection.go @@ -12,6 +12,7 @@ import ( "context" "database/sql" "database/sql/driver" + "encoding/json" "io" "net" "strconv" @@ -46,9 +47,10 @@ type mysqlConn struct { // Handles parameters set in DSN after the connection is established func (mc *mysqlConn) handleParams() (err error) { + var cmdSet strings.Builder for param, val := range mc.cfg.Params { switch param { - // Charset + // Charset: character_set_connection, character_set_client, character_set_results case "charset": charsets := strings.Split(val, ",") for i := range charsets { @@ -62,12 +64,25 @@ func (mc *mysqlConn) handleParams() (err error) { return } - // System Vars + // Other system vars accumulated in a single SET command default: - err = mc.exec("SET " + param + "=" + val + "") - if err != nil { - return + if cmdSet.Len() == 0 { + // Heuristic: 29 chars for each other key=value to reduce reallocations + cmdSet.Grow(4 + len(param) + 1 + len(val) + 30*(len(mc.cfg.Params)-1)) + cmdSet.WriteString("SET ") + } else { + cmdSet.WriteByte(',') } + cmdSet.WriteString(param) + cmdSet.WriteByte('=') + cmdSet.WriteString(val) + } + } + + if cmdSet.Len() > 0 { + err = mc.exec(cmdSet.String()) + if err != nil { + return } } @@ -230,47 +245,21 @@ func (mc *mysqlConn) interpolateParams(query string, args []driver.Value) (strin if v.IsZero() { buf = append(buf, "'0000-00-00'"...) } else { - v := v.In(mc.cfg.Loc) - v = v.Add(time.Nanosecond * 500) // To round under microsecond - year := v.Year() - year100 := year / 100 - year1 := year % 100 - month := v.Month() - day := v.Day() - hour := v.Hour() - minute := v.Minute() - second := v.Second() - micro := v.Nanosecond() / 1000 - - buf = append(buf, []byte{ - '\'', - digits10[year100], digits01[year100], - digits10[year1], digits01[year1], - '-', - digits10[month], digits01[month], - '-', - digits10[day], digits01[day], - ' ', - digits10[hour], digits01[hour], - ':', - digits10[minute], digits01[minute], - ':', - digits10[second], digits01[second], - }...) - - if micro != 0 { - micro10000 := micro / 10000 - micro100 := micro / 100 % 100 - micro1 := micro % 100 - buf = append(buf, []byte{ - '.', - digits10[micro10000], digits01[micro10000], - digits10[micro100], digits01[micro100], - digits10[micro1], digits01[micro1], - }...) + buf = append(buf, '\'') + buf, err = appendDateTime(buf, v.In(mc.cfg.Loc)) + if err != nil { + return "", err } buf = append(buf, '\'') } + case json.RawMessage: + buf = append(buf, '\'') + if mc.status&statusNoBackslashEscapes == 0 { + buf = escapeBytesBackslash(buf, v) + } else { + buf = escapeBytesQuotes(buf, v) + } + buf = append(buf, '\'') case []byte: if v == nil { buf = append(buf, "NULL"...) @@ -480,6 +469,10 @@ func (mc *mysqlConn) Ping(ctx context.Context) (err error) { // BeginTx implements driver.ConnBeginTx interface func (mc *mysqlConn) BeginTx(ctx context.Context, opts driver.TxOptions) (driver.Tx, error) { + if mc.closed.IsSet() { + return nil, driver.ErrBadConn + } + if err := mc.watchCancel(ctx); err != nil { return nil, err } @@ -649,3 +642,9 @@ func (mc *mysqlConn) ResetSession(ctx context.Context) error { mc.reset = true return nil } + +// IsValid implements driver.Validator interface +// (From Go 1.15) +func (mc *mysqlConn) IsValid() bool { + return !mc.closed.IsSet() +} diff --git a/src/vendor/github.com/go-sql-driver/mysql/dsn.go b/src/vendor/github.com/go-sql-driver/mysql/dsn.go index 75c8c2489..93f3548cb 100644 --- a/src/vendor/github.com/go-sql-driver/mysql/dsn.go +++ b/src/vendor/github.com/go-sql-driver/mysql/dsn.go @@ -375,7 +375,7 @@ func parseDSNParams(cfg *Config, params string) (err error) { // cfg params switch value := param[1]; param[0] { - // Disable INFILE whitelist / enable all files + // Disable INFILE allowlist / enable all files case "allowAllFiles": var isBool bool cfg.AllowAllFiles, isBool = readBool(value) diff --git a/src/vendor/github.com/go-sql-driver/mysql/fields.go b/src/vendor/github.com/go-sql-driver/mysql/fields.go index e1e2ece4b..ed6c7a37d 100644 --- a/src/vendor/github.com/go-sql-driver/mysql/fields.go +++ b/src/vendor/github.com/go-sql-driver/mysql/fields.go @@ -106,7 +106,7 @@ var ( scanTypeInt64 = reflect.TypeOf(int64(0)) scanTypeNullFloat = reflect.TypeOf(sql.NullFloat64{}) scanTypeNullInt = reflect.TypeOf(sql.NullInt64{}) - scanTypeNullTime = reflect.TypeOf(NullTime{}) + scanTypeNullTime = reflect.TypeOf(nullTime{}) scanTypeUint8 = reflect.TypeOf(uint8(0)) scanTypeUint16 = reflect.TypeOf(uint16(0)) scanTypeUint32 = reflect.TypeOf(uint32(0)) diff --git a/src/vendor/github.com/go-sql-driver/mysql/fuzz.go b/src/vendor/github.com/go-sql-driver/mysql/fuzz.go new file mode 100644 index 000000000..fa75adf6a --- /dev/null +++ b/src/vendor/github.com/go-sql-driver/mysql/fuzz.go @@ -0,0 +1,24 @@ +// Go MySQL Driver - A MySQL-Driver for Go's database/sql package. +// +// Copyright 2020 The Go-MySQL-Driver Authors. All rights reserved. +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this file, +// You can obtain one at http://mozilla.org/MPL/2.0/. + +// +build gofuzz + +package mysql + +import ( + "database/sql" +) + +func Fuzz(data []byte) int { + db, err := sql.Open("mysql", string(data)) + if err != nil { + return 0 + } + db.Close() + return 1 +} diff --git a/src/vendor/github.com/go-sql-driver/mysql/infile.go b/src/vendor/github.com/go-sql-driver/mysql/infile.go index 273cb0ba5..60effdfc2 100644 --- a/src/vendor/github.com/go-sql-driver/mysql/infile.go +++ b/src/vendor/github.com/go-sql-driver/mysql/infile.go @@ -23,7 +23,7 @@ var ( readerRegisterLock sync.RWMutex ) -// RegisterLocalFile adds the given file to the file whitelist, +// RegisterLocalFile adds the given file to the file allowlist, // so that it can be used by "LOAD DATA LOCAL INFILE ". // Alternatively you can allow the use of all local files with // the DSN parameter 'allowAllFiles=true' @@ -45,7 +45,7 @@ func RegisterLocalFile(filePath string) { fileRegisterLock.Unlock() } -// DeregisterLocalFile removes the given filepath from the whitelist. +// DeregisterLocalFile removes the given filepath from the allowlist. func DeregisterLocalFile(filePath string) { fileRegisterLock.Lock() delete(fileRegister, strings.Trim(filePath, `"`)) diff --git a/src/vendor/github.com/go-sql-driver/mysql/nulltime.go b/src/vendor/github.com/go-sql-driver/mysql/nulltime.go index afa8a89e9..651723a96 100644 --- a/src/vendor/github.com/go-sql-driver/mysql/nulltime.go +++ b/src/vendor/github.com/go-sql-driver/mysql/nulltime.go @@ -28,11 +28,11 @@ func (nt *NullTime) Scan(value interface{}) (err error) { nt.Time, nt.Valid = v, true return case []byte: - nt.Time, err = parseDateTime(string(v), time.UTC) + nt.Time, err = parseDateTime(v, time.UTC) nt.Valid = (err == nil) return case string: - nt.Time, err = parseDateTime(v, time.UTC) + nt.Time, err = parseDateTime([]byte(v), time.UTC) nt.Valid = (err == nil) return } diff --git a/src/vendor/github.com/go-sql-driver/mysql/nulltime_go113.go b/src/vendor/github.com/go-sql-driver/mysql/nulltime_go113.go index c392594dd..453b4b394 100644 --- a/src/vendor/github.com/go-sql-driver/mysql/nulltime_go113.go +++ b/src/vendor/github.com/go-sql-driver/mysql/nulltime_go113.go @@ -28,4 +28,13 @@ import ( // } // // This NullTime implementation is not driver-specific +// +// Deprecated: NullTime doesn't honor the loc DSN parameter. +// NullTime.Scan interprets a time as UTC, not the loc DSN parameter. +// Use sql.NullTime instead. type NullTime sql.NullTime + +// for internal use. +// the mysql package uses sql.NullTime if it is available. +// if not, the package uses mysql.NullTime. +type nullTime = sql.NullTime // sql.NullTime is available diff --git a/src/vendor/github.com/go-sql-driver/mysql/nulltime_legacy.go b/src/vendor/github.com/go-sql-driver/mysql/nulltime_legacy.go index 86d159d44..9f7ae27a8 100644 --- a/src/vendor/github.com/go-sql-driver/mysql/nulltime_legacy.go +++ b/src/vendor/github.com/go-sql-driver/mysql/nulltime_legacy.go @@ -32,3 +32,8 @@ type NullTime struct { Time time.Time Valid bool // Valid is true if Time is not NULL } + +// for internal use. +// the mysql package uses sql.NullTime if it is available. +// if not, the package uses mysql.NullTime. +type nullTime = NullTime // sql.NullTime is not available diff --git a/src/vendor/github.com/go-sql-driver/mysql/packets.go b/src/vendor/github.com/go-sql-driver/mysql/packets.go index 82ad7a200..6664e5ae5 100644 --- a/src/vendor/github.com/go-sql-driver/mysql/packets.go +++ b/src/vendor/github.com/go-sql-driver/mysql/packets.go @@ -13,6 +13,7 @@ import ( "crypto/tls" "database/sql/driver" "encoding/binary" + "encoding/json" "errors" "fmt" "io" @@ -348,6 +349,12 @@ func (mc *mysqlConn) writeHandshakeResponsePacket(authResp []byte, plugin string return errors.New("unknown collation") } + // Filler [23 bytes] (all 0x00) + pos := 13 + for ; pos < 13+23; pos++ { + data[pos] = 0 + } + // SSL Connection Request Packet // http://dev.mysql.com/doc/internals/en/connection-phase-packets.html#packet-Protocol::SSLRequest if mc.cfg.tls != nil { @@ -366,12 +373,6 @@ func (mc *mysqlConn) writeHandshakeResponsePacket(authResp []byte, plugin string mc.buf.nc = tlsConn } - // Filler [23 bytes] (all 0x00) - pos := 13 - for ; pos < 13+23; pos++ { - data[pos] = 0 - } - // User [null terminated string] if len(mc.cfg.User) > 0 { pos += copy(data[pos:], mc.cfg.User) @@ -777,7 +778,7 @@ func (rows *textRows) readRow(dest []driver.Value) error { case fieldTypeTimestamp, fieldTypeDateTime, fieldTypeDate, fieldTypeNewDate: dest[i], err = parseDateTime( - string(dest[i].([]byte)), + dest[i].([]byte), mc.cfg.Loc, ) if err == nil { @@ -1003,6 +1004,9 @@ func (stmt *mysqlStmt) writeExecutePacket(args []driver.Value) error { continue } + if v, ok := arg.(json.RawMessage); ok { + arg = []byte(v) + } // cache types and values switch v := arg.(type) { case int64: @@ -1112,7 +1116,10 @@ func (stmt *mysqlStmt) writeExecutePacket(args []driver.Value) error { if v.IsZero() { b = append(b, "0000-00-00"...) } else { - b = v.In(mc.cfg.Loc).AppendFormat(b, timeFormat) + b, err = appendDateTime(b, v.In(mc.cfg.Loc)) + if err != nil { + return err + } } paramValues = appendLengthEncodedInteger(paramValues, diff --git a/src/vendor/github.com/go-sql-driver/mysql/statement.go b/src/vendor/github.com/go-sql-driver/mysql/statement.go index f7e370939..18a3ae498 100644 --- a/src/vendor/github.com/go-sql-driver/mysql/statement.go +++ b/src/vendor/github.com/go-sql-driver/mysql/statement.go @@ -10,6 +10,7 @@ package mysql import ( "database/sql/driver" + "encoding/json" "fmt" "io" "reflect" @@ -43,6 +44,11 @@ func (stmt *mysqlStmt) ColumnConverter(idx int) driver.ValueConverter { return converter{} } +func (stmt *mysqlStmt) CheckNamedValue(nv *driver.NamedValue) (err error) { + nv.Value, err = converter{}.ConvertValue(nv.Value) + return +} + func (stmt *mysqlStmt) Exec(args []driver.Value) (driver.Result, error) { if stmt.mc.closed.IsSet() { errLog.Print(ErrInvalidConn) @@ -129,6 +135,8 @@ func (stmt *mysqlStmt) query(args []driver.Value) (*binaryRows, error) { return rows, err } +var jsonType = reflect.TypeOf(json.RawMessage{}) + type converter struct{} // ConvertValue mirrors the reference/default converter in database/sql/driver @@ -146,12 +154,17 @@ func (c converter) ConvertValue(v interface{}) (driver.Value, error) { if err != nil { return nil, err } - if !driver.IsValue(sv) { - return nil, fmt.Errorf("non-Value type %T returned from Value", sv) + if driver.IsValue(sv) { + return sv, nil } - return sv, nil + // A value returend from the Valuer interface can be "a type handled by + // a database driver's NamedValueChecker interface" so we should accept + // uint64 here as well. + if u, ok := sv.(uint64); ok { + return u, nil + } + return nil, fmt.Errorf("non-Value type %T returned from Value", sv) } - rv := reflect.ValueOf(v) switch rv.Kind() { case reflect.Ptr: @@ -170,11 +183,14 @@ func (c converter) ConvertValue(v interface{}) (driver.Value, error) { case reflect.Bool: return rv.Bool(), nil case reflect.Slice: - ek := rv.Type().Elem().Kind() - if ek == reflect.Uint8 { + switch t := rv.Type(); { + case t == jsonType: + return v, nil + case t.Elem().Kind() == reflect.Uint8: return rv.Bytes(), nil + default: + return nil, fmt.Errorf("unsupported type %T, a slice of %s", v, t.Elem().Kind()) } - return nil, fmt.Errorf("unsupported type %T, a slice of %s", v, ek) case reflect.String: return rv.String(), nil } diff --git a/src/vendor/github.com/go-sql-driver/mysql/utils.go b/src/vendor/github.com/go-sql-driver/mysql/utils.go index 9552e80b5..d6545f5be 100644 --- a/src/vendor/github.com/go-sql-driver/mysql/utils.go +++ b/src/vendor/github.com/go-sql-driver/mysql/utils.go @@ -106,27 +106,136 @@ func readBool(input string) (value bool, valid bool) { * Time related utils * ******************************************************************************/ -func parseDateTime(str string, loc *time.Location) (t time.Time, err error) { - base := "0000-00-00 00:00:00.0000000" - switch len(str) { +func parseDateTime(b []byte, loc *time.Location) (time.Time, error) { + const base = "0000-00-00 00:00:00.000000" + switch len(b) { case 10, 19, 21, 22, 23, 24, 25, 26: // up to "YYYY-MM-DD HH:MM:SS.MMMMMM" - if str == base[:len(str)] { - return + if string(b) == base[:len(b)] { + return time.Time{}, nil } - t, err = time.Parse(timeFormat[:len(str)], str) + + year, err := parseByteYear(b) + if err != nil { + return time.Time{}, err + } + if year <= 0 { + year = 1 + } + + if b[4] != '-' { + return time.Time{}, fmt.Errorf("bad value for field: `%c`", b[4]) + } + + m, err := parseByte2Digits(b[5], b[6]) + if err != nil { + return time.Time{}, err + } + if m <= 0 { + m = 1 + } + month := time.Month(m) + + if b[7] != '-' { + return time.Time{}, fmt.Errorf("bad value for field: `%c`", b[7]) + } + + day, err := parseByte2Digits(b[8], b[9]) + if err != nil { + return time.Time{}, err + } + if day <= 0 { + day = 1 + } + if len(b) == 10 { + return time.Date(year, month, day, 0, 0, 0, 0, loc), nil + } + + if b[10] != ' ' { + return time.Time{}, fmt.Errorf("bad value for field: `%c`", b[10]) + } + + hour, err := parseByte2Digits(b[11], b[12]) + if err != nil { + return time.Time{}, err + } + if b[13] != ':' { + return time.Time{}, fmt.Errorf("bad value for field: `%c`", b[13]) + } + + min, err := parseByte2Digits(b[14], b[15]) + if err != nil { + return time.Time{}, err + } + if b[16] != ':' { + return time.Time{}, fmt.Errorf("bad value for field: `%c`", b[16]) + } + + sec, err := parseByte2Digits(b[17], b[18]) + if err != nil { + return time.Time{}, err + } + if len(b) == 19 { + return time.Date(year, month, day, hour, min, sec, 0, loc), nil + } + + if b[19] != '.' { + return time.Time{}, fmt.Errorf("bad value for field: `%c`", b[19]) + } + nsec, err := parseByteNanoSec(b[20:]) + if err != nil { + return time.Time{}, err + } + return time.Date(year, month, day, hour, min, sec, nsec, loc), nil default: - err = fmt.Errorf("invalid time string: %s", str) - return + return time.Time{}, fmt.Errorf("invalid time bytes: %s", b) } +} - // Adjust location - if err == nil && loc != time.UTC { - y, mo, d := t.Date() - h, mi, s := t.Clock() - t, err = time.Date(y, mo, d, h, mi, s, t.Nanosecond(), loc), nil +func parseByteYear(b []byte) (int, error) { + year, n := 0, 1000 + for i := 0; i < 4; i++ { + v, err := bToi(b[i]) + if err != nil { + return 0, err + } + year += v * n + n = n / 10 } + return year, nil +} - return +func parseByte2Digits(b1, b2 byte) (int, error) { + d1, err := bToi(b1) + if err != nil { + return 0, err + } + d2, err := bToi(b2) + if err != nil { + return 0, err + } + return d1*10 + d2, nil +} + +func parseByteNanoSec(b []byte) (int, error) { + ns, digit := 0, 100000 // max is 6-digits + for i := 0; i < len(b); i++ { + v, err := bToi(b[i]) + if err != nil { + return 0, err + } + ns += v * digit + digit /= 10 + } + // nanoseconds has 10-digits. (needs to scale digits) + // 10 - 6 = 4, so we have to multiple 1000. + return ns * 1000, nil +} + +func bToi(b byte) (int, error) { + if b < '0' || b > '9' { + return 0, errors.New("not [0-9]") + } + return int(b - '0'), nil } func parseBinaryDateTime(num uint64, data []byte, loc *time.Location) (driver.Value, error) { @@ -167,6 +276,64 @@ func parseBinaryDateTime(num uint64, data []byte, loc *time.Location) (driver.Va return nil, fmt.Errorf("invalid DATETIME packet length %d", num) } +func appendDateTime(buf []byte, t time.Time) ([]byte, error) { + year, month, day := t.Date() + hour, min, sec := t.Clock() + nsec := t.Nanosecond() + + if year < 1 || year > 9999 { + return buf, errors.New("year is not in the range [1, 9999]: " + strconv.Itoa(year)) // use errors.New instead of fmt.Errorf to avoid year escape to heap + } + year100 := year / 100 + year1 := year % 100 + + var localBuf [len("2006-01-02T15:04:05.999999999")]byte // does not escape + localBuf[0], localBuf[1], localBuf[2], localBuf[3] = digits10[year100], digits01[year100], digits10[year1], digits01[year1] + localBuf[4] = '-' + localBuf[5], localBuf[6] = digits10[month], digits01[month] + localBuf[7] = '-' + localBuf[8], localBuf[9] = digits10[day], digits01[day] + + if hour == 0 && min == 0 && sec == 0 && nsec == 0 { + return append(buf, localBuf[:10]...), nil + } + + localBuf[10] = ' ' + localBuf[11], localBuf[12] = digits10[hour], digits01[hour] + localBuf[13] = ':' + localBuf[14], localBuf[15] = digits10[min], digits01[min] + localBuf[16] = ':' + localBuf[17], localBuf[18] = digits10[sec], digits01[sec] + + if nsec == 0 { + return append(buf, localBuf[:19]...), nil + } + nsec100000000 := nsec / 100000000 + nsec1000000 := (nsec / 1000000) % 100 + nsec10000 := (nsec / 10000) % 100 + nsec100 := (nsec / 100) % 100 + nsec1 := nsec % 100 + localBuf[19] = '.' + + // milli second + localBuf[20], localBuf[21], localBuf[22] = + digits01[nsec100000000], digits10[nsec1000000], digits01[nsec1000000] + // micro second + localBuf[23], localBuf[24], localBuf[25] = + digits10[nsec10000], digits01[nsec10000], digits10[nsec100] + // nano second + localBuf[26], localBuf[27], localBuf[28] = + digits01[nsec100], digits10[nsec1], digits01[nsec1] + + // trim trailing zeros + n := len(localBuf) + for n > 0 && localBuf[n-1] == '0' { + n-- + } + + return append(buf, localBuf[:n]...), nil +} + // zeroDateTime is used in formatBinaryDateTime to avoid an allocation // if the DATE or DATETIME has the zero value. // It must never be changed. diff --git a/src/vendor/github.com/mitchellh/mapstructure/CHANGELOG.md b/src/vendor/github.com/mitchellh/mapstructure/CHANGELOG.md index 1955f2878..38a099162 100644 --- a/src/vendor/github.com/mitchellh/mapstructure/CHANGELOG.md +++ b/src/vendor/github.com/mitchellh/mapstructure/CHANGELOG.md @@ -1,6 +1,16 @@ -## unreleased +## 1.4.3 -* Fix regression where `*time.Time` value would be set to empty and not be sent +* Fix cases where `json.Number` didn't decode properly [GH-261] + +## 1.4.2 + +* Custom name matchers to support any sort of casing, formatting, etc. for + field names. [GH-250] +* Fix possible panic in ComposeDecodeHookFunc [GH-251] + +## 1.4.1 + +* Fix regression where `*time.Time` value would be set to empty and not be sent to decode hooks properly [GH-232] ## 1.4.0 diff --git a/src/vendor/github.com/mitchellh/mapstructure/decode_hooks.go b/src/vendor/github.com/mitchellh/mapstructure/decode_hooks.go index 92e6f76ff..4d4bbc733 100644 --- a/src/vendor/github.com/mitchellh/mapstructure/decode_hooks.go +++ b/src/vendor/github.com/mitchellh/mapstructure/decode_hooks.go @@ -62,7 +62,8 @@ func DecodeHookExec( func ComposeDecodeHookFunc(fs ...DecodeHookFunc) DecodeHookFunc { return func(f reflect.Value, t reflect.Value) (interface{}, error) { var err error - var data interface{} + data := f.Interface() + newFrom := f for _, f1 := range fs { data, err = DecodeHookExec(f1, newFrom, t) diff --git a/src/vendor/github.com/mitchellh/mapstructure/mapstructure.go b/src/vendor/github.com/mitchellh/mapstructure/mapstructure.go index 3643901f5..6b81b0067 100644 --- a/src/vendor/github.com/mitchellh/mapstructure/mapstructure.go +++ b/src/vendor/github.com/mitchellh/mapstructure/mapstructure.go @@ -192,7 +192,7 @@ type DecodeHookFuncType func(reflect.Type, reflect.Type, interface{}) (interface // source and target types. type DecodeHookFuncKind func(reflect.Kind, reflect.Kind, interface{}) (interface{}, error) -// DecodeHookFuncRaw is a DecodeHookFunc which has complete access to both the source and target +// DecodeHookFuncValue is a DecodeHookFunc which has complete access to both the source and target // values. type DecodeHookFuncValue func(from reflect.Value, to reflect.Value) (interface{}, error) @@ -258,6 +258,11 @@ type DecoderConfig struct { // The tag name that mapstructure reads for field names. This // defaults to "mapstructure" TagName string + + // MatchName is the function used to match the map key to the struct + // field name or tag. Defaults to `strings.EqualFold`. This can be used + // to implement case-sensitive tag values, support snake casing, etc. + MatchName func(mapKey, fieldName string) bool } // A Decoder takes a raw interface value and turns it into structured @@ -376,6 +381,10 @@ func NewDecoder(config *DecoderConfig) (*Decoder, error) { config.TagName = "mapstructure" } + if config.MatchName == nil { + config.MatchName = strings.EqualFold + } + result := &Decoder{ config: config, } @@ -675,16 +684,12 @@ func (d *Decoder) decodeUint(name string, data interface{}, val reflect.Value) e } case dataType.PkgPath() == "encoding/json" && dataType.Name() == "Number": jn := data.(json.Number) - i, err := jn.Int64() + i, err := strconv.ParseUint(string(jn), 0, 64) if err != nil { return fmt.Errorf( "error decoding json.Number into %s: %s", name, err) } - if i < 0 && !d.config.WeaklyTypedInput { - return fmt.Errorf("cannot parse '%s', %d overflows uint", - name, i) - } - val.SetUint(uint64(i)) + val.SetUint(i) default: return fmt.Errorf( "'%s' expected type '%s', got unconvertible type '%s', value: '%v'", @@ -1340,7 +1345,7 @@ func (d *Decoder) decodeStructFromMap(name string, dataVal, val reflect.Value) e continue } - if strings.EqualFold(mK, fieldName) { + if d.config.MatchName(mK, fieldName) { rawMapKey = dataValKey rawMapVal = dataVal.MapIndex(dataValKey) break diff --git a/src/vendor/github.com/shiena/ansicolor/README.md b/src/vendor/github.com/shiena/ansicolor/README.md index cf2ae4055..546f05aa3 100644 --- a/src/vendor/github.com/shiena/ansicolor/README.md +++ b/src/vendor/github.com/shiena/ansicolor/README.md @@ -1,4 +1,5 @@ -[![GoDoc](https://godoc.org/github.com/shiena/ansicolor?status.svg)](https://godoc.org/github.com/shiena/ansicolor) +[![PkgGoDev](https://pkg.go.dev/badge/github.com/shiena/ansicolor)](https://pkg.go.dev/github.com/shiena/ansicolor) +[![Go Report Card](https://goreportcard.com/badge/github.com/shiena/ansicolor)](https://goreportcard.com/report/github.com/shiena/ansicolor) # ansicolor diff --git a/src/vendor/github.com/shiena/ansicolor/ansicolor_windows.go b/src/vendor/github.com/shiena/ansicolor/ansicolor_windows.go index ff2ae0ef3..f422026a5 100644 --- a/src/vendor/github.com/shiena/ansicolor/ansicolor_windows.go +++ b/src/vendor/github.com/shiena/ansicolor/ansicolor_windows.go @@ -409,7 +409,7 @@ func (cw *ansiColorWriter) Write(p []byte) (int, error) { } if cw.mode != DiscardNonColorEscSeq || cw.state == outsideCsiCode { - nw, err = cw.w.Write(p[first:len(p)]) + nw, err = cw.w.Write(p[first:]) r += nw } diff --git a/src/vendor/go.opentelemetry.io/otel/.gitignore b/src/vendor/go.opentelemetry.io/otel/.gitignore index 759cf53e0..0b605b3d6 100644 --- a/src/vendor/go.opentelemetry.io/otel/.gitignore +++ b/src/vendor/go.opentelemetry.io/otel/.gitignore @@ -11,11 +11,11 @@ coverage.* gen/ /example/fib/fib +/example/fib/traces.txt /example/jaeger/jaeger /example/namedtracer/namedtracer /example/opencensus/opencensus /example/passthrough/passthrough /example/prometheus/prometheus -/example/prom-collector/prom-collector /example/zipkin/zipkin /example/otel-collector/otel-collector diff --git a/src/vendor/go.opentelemetry.io/otel/.golangci.yml b/src/vendor/go.opentelemetry.io/otel/.golangci.yml index d40bdedc3..253e3b35b 100644 --- a/src/vendor/go.opentelemetry.io/otel/.golangci.yml +++ b/src/vendor/go.opentelemetry.io/otel/.golangci.yml @@ -4,29 +4,243 @@ run: tests: true #Default linters: + # Disable everything by default so upgrades to not include new "default + # enabled" linters. + disable-all: true + # Specifically enable linters we want to use. enable: - - misspell - - goimports - - revive + - deadcode + - depguard + - errcheck + - godot - gofmt + - goimports + - gosimple + - govet + - ineffassign + - misspell + - revive + - staticcheck + - structcheck + - typecheck + - unused + - varcheck issues: + # Maximum issues count per one linter. + # Set to 0 to disable. + # Default: 50 + # Setting to unlimited so the linter only is run once to debug all issues. + max-issues-per-linter: 0 + # Maximum count of issues with the same text. + # Set to 0 to disable. + # Default: 3 + # Setting to unlimited so the linter only is run once to debug all issues. + max-same-issues: 0 + # Excluding configuration per-path, per-linter, per-text and per-source. exclude-rules: - # helpers in tests often (rightfully) pass a *testing.T as their first argument - - path: _test\.go - text: "context.Context should be the first parameter of a function" + # TODO: Having appropriate comments for exported objects helps development, + # even for objects in internal packages. Appropriate comments for all + # exported objects should be added and this exclusion removed. + - path: '.*internal/.*' + text: "exported (method|function|type|const) (.+) should have comment or be unexported" linters: - revive - # Yes, they are, but it's okay in a test + # Yes, they are, but it's okay in a test. - path: _test\.go text: "exported func.*returns unexported type.*which can be annoying to use" linters: - revive + # Example test functions should be treated like main. + - path: example.*_test\.go + text: "calls to (.+) only in main[(][)] or init[(][)] functions" + linters: + - revive + include: + # revive exported should have comment or be unexported. + - EXC0012 + # revive package comment should be of the form ... + - EXC0013 linters-settings: + depguard: + # Check the list against standard lib. + # Default: false + include-go-root: true + # A list of packages for the list type specified. + # Default: [] + packages: + - "crypto/md5" + - "crypto/sha1" + - "crypto/**/pkix" + ignore-file-rules: + - "**/*_test.go" + additional-guards: + # Do not allow testing packages in non-test files. + - list-type: denylist + include-go-root: true + packages: + - testing + - github.com/stretchr/testify + ignore-file-rules: + - "**/*_test.go" + - "**/*test/*.go" + - "**/internal/matchers/*.go" + godot: + exclude: + # Exclude sentence fragments for lists. + - '^[ ]*[-•]' + # Exclude sentences prefixing a list. + - ':$' + goimports: + local-prefixes: go.opentelemetry.io misspell: locale: US ignore-words: - cancelled - goimports: - local-prefixes: go.opentelemetry.io + revive: + # Sets the default failure confidence. + # This means that linting errors with less than 0.8 confidence will be ignored. + # Default: 0.8 + confidence: 0.01 + rules: + # https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#blank-imports + - name: blank-imports + disabled: false + # https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#bool-literal-in-expr + - name: bool-literal-in-expr + disabled: false + # https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#constant-logical-expr + - name: constant-logical-expr + disabled: false + # https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#context-as-argument + - name: context-as-argument + disabled: false + arguments: + allowTypesBefore: "*testing.T" + # https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#context-keys-type + - name: context-keys-type + disabled: false + # https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#deep-exit + - name: deep-exit + disabled: false + # https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#defer + - name: defer + disabled: false + arguments: + - ["call-chain", "loop"] + # https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#dot-imports + - name: dot-imports + disabled: false + # https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#duplicated-imports + - name: duplicated-imports + disabled: false + # https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#early-return + - name: early-return + disabled: false + # https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#empty-block + - name: empty-block + disabled: false + # https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#empty-lines + - name: empty-lines + disabled: false + # https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#error-naming + - name: error-naming + disabled: false + # https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#error-return + - name: error-return + disabled: false + # https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#error-strings + - name: error-strings + disabled: false + # https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#errorf + - name: errorf + disabled: false + # https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#exported + - name: exported + disabled: false + arguments: + - "sayRepetitiveInsteadOfStutters" + # https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#flag-parameter + - name: flag-parameter + disabled: false + # https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#identical-branches + - name: identical-branches + disabled: false + # https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#if-return + - name: if-return + disabled: false + # https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#increment-decrement + - name: increment-decrement + disabled: false + # https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#indent-error-flow + - name: indent-error-flow + disabled: false + # https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#import-shadowing + - name: import-shadowing + disabled: false + # https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#package-comments + - name: package-comments + disabled: false + # https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#range + - name: range + disabled: false + # https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#range-val-in-closure + - name: range-val-in-closure + disabled: false + # https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#range-val-address + - name: range-val-address + disabled: false + # https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#redefines-builtin-id + - name: redefines-builtin-id + disabled: false + # https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#string-format + - name: string-format + disabled: false + arguments: + - - panic + - '/^[^\n]*$/' + - must not contain line breaks + # https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#struct-tag + - name: struct-tag + disabled: false + # https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#superfluous-else + - name: superfluous-else + disabled: false + # https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#time-equal + - name: time-equal + disabled: false + # https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#var-naming + - name: var-naming + disabled: false + arguments: + - ["ID"] # AllowList + - ["Otel", "Aws", "Gcp"] # DenyList + # https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#var-declaration + - name: var-declaration + disabled: false + # https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#unconditional-recursion + - name: unconditional-recursion + disabled: false + # https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#unexported-return + - name: unexported-return + disabled: false + # https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#unhandled-error + - name: unhandled-error + disabled: false + arguments: + - "fmt.Fprint" + - "fmt.Fprintf" + - "fmt.Fprintln" + - "fmt.Print" + - "fmt.Printf" + - "fmt.Println" + # https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#unnecessary-stmt + - name: unnecessary-stmt + disabled: false + # https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#useless-break + - name: useless-break + disabled: false + # https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#waitgroup-by-value + - name: waitgroup-by-value + disabled: false diff --git a/src/vendor/go.opentelemetry.io/otel/.lycheeignore b/src/vendor/go.opentelemetry.io/otel/.lycheeignore new file mode 100644 index 000000000..545d63452 --- /dev/null +++ b/src/vendor/go.opentelemetry.io/otel/.lycheeignore @@ -0,0 +1,3 @@ +http://localhost +http://jaeger-collector +https://github.com/open-telemetry/opentelemetry-go/milestone/ diff --git a/src/vendor/go.opentelemetry.io/otel/.markdown-link.json b/src/vendor/go.opentelemetry.io/otel/.markdown-link.json deleted file mode 100644 index f222ad89c..000000000 --- a/src/vendor/go.opentelemetry.io/otel/.markdown-link.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - "ignorePatterns": [ - { - "pattern": "^http(s)?://localhost" - } - ], - "replacementPatterns": [ - { - "pattern": "^/registry", - "replacement": "https://opentelemetry.io/registry" - } - ], - "retryOn429": true, - "retryCount": 5, - "fallbackRetryDelay": "30s" -} diff --git a/src/vendor/go.opentelemetry.io/otel/CHANGELOG.md b/src/vendor/go.opentelemetry.io/otel/CHANGELOG.md index 6665b12de..e92ae7c5b 100644 --- a/src/vendor/go.opentelemetry.io/otel/CHANGELOG.md +++ b/src/vendor/go.opentelemetry.io/otel/CHANGELOG.md @@ -8,6 +8,243 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm ## [Unreleased] +## [1.8.0/0.31.0] - 2022-07-08 + +### Added + +- Add support for `opentracing.TextMap` format in the `Inject` and `Extract` methods +of the `"go.opentelemetry.io/otel/bridge/opentracing".BridgeTracer` type. (#2911) + +### Changed + +- The `crosslink` make target has been updated to use the `go.opentelemetry.io/build-tools/crosslink` package. (#2886) +- In the `go.opentelemetry.io/otel/sdk/instrumentation` package rename `Library` to `Scope` and alias `Library` as `Scope` (#2976) +- Move metric no-op implementation form `nonrecording` to `metric` package. (#2866) + +### Removed + +- Support for go1.16. Support is now only for go1.17 and go1.18 (#2917) + +### Deprecated + +- The `Library` struct in the `go.opentelemetry.io/otel/sdk/instrumentation` package is deprecated. + Use the equivalent `Scope` struct instead. (#2977) +- The `ReadOnlySpan.InstrumentationLibrary` method from the `go.opentelemetry.io/otel/sdk/trace` package is deprecated. + Use the equivalent `ReadOnlySpan.InstrumentationScope` method instead. (#2977) + +## [1.7.0/0.30.0] - 2022-04-28 + +### Added + +- Add the `go.opentelemetry.io/otel/semconv/v1.8.0` package. + The package contains semantic conventions from the `v1.8.0` version of the OpenTelemetry specification. (#2763) +- Add the `go.opentelemetry.io/otel/semconv/v1.9.0` package. + The package contains semantic conventions from the `v1.9.0` version of the OpenTelemetry specification. (#2792) +- Add the `go.opentelemetry.io/otel/semconv/v1.10.0` package. + The package contains semantic conventions from the `v1.10.0` version of the OpenTelemetry specification. (#2842) +- Added an in-memory exporter to metrictest to aid testing with a full SDK. (#2776) + +### Fixed + +- Globally delegated instruments are unwrapped before delegating asynchronous callbacks. (#2784) +- Remove import of `testing` package in non-tests builds of the `go.opentelemetry.io/otel` package. (#2786) + +### Changed + +- The `WithLabelEncoder` option from the `go.opentelemetry.io/otel/exporters/stdout/stdoutmetric` package is renamed to `WithAttributeEncoder`. (#2790) +- The `LabelFilterSelector` interface from `go.opentelemetry.io/otel/sdk/metric/processor/reducer` is renamed to `AttributeFilterSelector`. + The method included in the renamed interface also changed from `LabelFilterFor` to `AttributeFilterFor`. (#2790) +- The `Metadata.Labels` method from the `go.opentelemetry.io/otel/sdk/metric/export` package is renamed to `Metadata.Attributes`. + Consequentially, the `Record` type from the same package also has had the embedded method renamed. (#2790) + +### Deprecated + +- The `Iterator.Label` method in the `go.opentelemetry.io/otel/attribute` package is deprecated. + Use the equivalent `Iterator.Attribute` method instead. (#2790) +- The `Iterator.IndexedLabel` method in the `go.opentelemetry.io/otel/attribute` package is deprecated. + Use the equivalent `Iterator.IndexedAttribute` method instead. (#2790) +- The `MergeIterator.Label` method in the `go.opentelemetry.io/otel/attribute` package is deprecated. + Use the equivalent `MergeIterator.Attribute` method instead. (#2790) + +### Removed + +- Removed the `Batch` type from the `go.opentelemetry.io/otel/sdk/metric/metrictest` package. (#2864) +- Removed the `Measurement` type from the `go.opentelemetry.io/otel/sdk/metric/metrictest` package. (#2864) + +## [0.29.0] - 2022-04-11 + +### Added + +- The metrics global package was added back into several test files. (#2764) +- The `Meter` function is added back to the `go.opentelemetry.io/otel/metric/global` package. + This function is a convenience function equivalent to calling `global.MeterProvider().Meter(...)`. (#2750) + +### Removed + +- Removed module the `go.opentelemetry.io/otel/sdk/export/metric`. + Use the `go.opentelemetry.io/otel/sdk/metric` module instead. (#2720) + +### Changed + +- Don't panic anymore when setting a global MeterProvider to itself. (#2749) +- Upgrade `go.opentelemetry.io/proto/otlp` in `go.opentelemetry.io/otel/exporters/otlp/otlpmetric` from `v0.12.1` to `v0.15.0`. + This replaces the use of the now deprecated `InstrumentationLibrary` and `InstrumentationLibraryMetrics` types and fields in the proto library with the equivalent `InstrumentationScope` and `ScopeMetrics`. (#2748) + +## [1.6.3] - 2022-04-07 + +### Fixed + +- Allow non-comparable global `MeterProvider`, `TracerProvider`, and `TextMapPropagator` types to be set. (#2772, #2773) + +## [1.6.2] - 2022-04-06 + +### Changed + +- Don't panic anymore when setting a global TracerProvider or TextMapPropagator to itself. (#2749) +- Upgrade `go.opentelemetry.io/proto/otlp` in `go.opentelemetry.io/otel/exporters/otlp/otlptrace` from `v0.12.1` to `v0.15.0`. + This replaces the use of the now deprecated `InstrumentationLibrary` and `InstrumentationLibrarySpans` types and fields in the proto library with the equivalent `InstrumentationScope` and `ScopeSpans`. (#2748) + +## [1.6.1] - 2022-03-28 + +### Fixed + +- The `go.opentelemetry.io/otel/schema/*` packages now use the correct schema URL for their `SchemaURL` constant. + Instead of using `"https://opentelemetry.io/schemas/v"` they now use the correct URL without a `v` prefix, `"https://opentelemetry.io/schemas/"`. (#2743, #2744) + +### Security + +- Upgrade `go.opentelemetry.io/proto/otlp` from `v0.12.0` to `v0.12.1`. + This includes an indirect upgrade of `github.com/grpc-ecosystem/grpc-gateway` which resolves [a vulnerability](https://nvd.nist.gov/vuln/detail/CVE-2019-11254) from `gopkg.in/yaml.v2` in version `v2.2.3`. (#2724, #2728) + +## [1.6.0/0.28.0] - 2022-03-23 + +### ⚠️ Notice ⚠️ + +This update is a breaking change of the unstable Metrics API. +Code instrumented with the `go.opentelemetry.io/otel/metric` will need to be modified. + +### Added + +- Add metrics exponential histogram support. + New mapping functions have been made available in `sdk/metric/aggregator/exponential/mapping` for other OpenTelemetry projects to take dependencies on. (#2502) +- Add Go 1.18 to our compatibility tests. (#2679) +- Allow configuring the Sampler with the `OTEL_TRACES_SAMPLER` and `OTEL_TRACES_SAMPLER_ARG` environment variables. (#2305, #2517) +- Add the `metric/global` for obtaining and setting the global `MeterProvider`. (#2660) + +### Changed + +- The metrics API has been significantly changed to match the revised OpenTelemetry specification. + High-level changes include: + + - Synchronous and asynchronous instruments are now handled by independent `InstrumentProvider`s. + These `InstrumentProvider`s are managed with a `Meter`. + - Synchronous and asynchronous instruments are grouped into their own packages based on value types. + - Asynchronous callbacks can now be registered with a `Meter`. + + Be sure to check out the metric module documentation for more information on how to use the revised API. (#2587, #2660) + +### Fixed + +- Fallback to general attribute limits when span specific ones are not set in the environment. (#2675, #2677) + +## [1.5.0] - 2022-03-16 + +### Added + +- Log the Exporters configuration in the TracerProviders message. (#2578) +- Added support to configure the span limits with environment variables. + The following environment variables are supported. (#2606, #2637) + - `OTEL_SPAN_ATTRIBUTE_VALUE_LENGTH_LIMIT` + - `OTEL_SPAN_ATTRIBUTE_COUNT_LIMIT` + - `OTEL_SPAN_EVENT_COUNT_LIMIT` + - `OTEL_EVENT_ATTRIBUTE_COUNT_LIMIT` + - `OTEL_SPAN_LINK_COUNT_LIMIT` + - `OTEL_LINK_ATTRIBUTE_COUNT_LIMIT` + + If the provided environment variables are invalid (negative), the default values would be used. +- Rename the `gc` runtime name to `go` (#2560) +- Add resource container ID detection. (#2418) +- Add span attribute value length limit. + The new `AttributeValueLengthLimit` field is added to the `"go.opentelemetry.io/otel/sdk/trace".SpanLimits` type to configure this limit for a `TracerProvider`. + The default limit for this resource is "unlimited". (#2637) +- Add the `WithRawSpanLimits` option to `go.opentelemetry.io/otel/sdk/trace`. + This option replaces the `WithSpanLimits` option. + Zero or negative values will not be changed to the default value like `WithSpanLimits` does. + Setting a limit to zero will effectively disable the related resource it limits and setting to a negative value will mean that resource is unlimited. + Consequentially, limits should be constructed using `NewSpanLimits` and updated accordingly. (#2637) + +### Changed + +- Drop oldest tracestate `Member` when capacity is reached. (#2592) +- Add event and link drop counts to the exported data from the `oltptrace` exporter. (#2601) +- Unify path cleaning functionally in the `otlpmetric` and `otlptrace` configuration. (#2639) +- Change the debug message from the `sdk/trace.BatchSpanProcessor` to reflect the count is cumulative. (#2640) +- Introduce new internal `envconfig` package for OTLP exporters. (#2608) +- If `http.Request.Host` is empty, fall back to use `URL.Host` when populating `http.host` in the `semconv` packages. (#2661) + +### Fixed + +- Remove the OTLP trace exporter limit of SpanEvents when exporting. (#2616) +- Default to port `4318` instead of `4317` for the `otlpmetrichttp` and `otlptracehttp` client. (#2614, #2625) +- Unlimited span limits are now supported (negative values). (#2636, #2637) + +### Deprecated + +- Deprecated `"go.opentelemetry.io/otel/sdk/trace".WithSpanLimits`. + Use `WithRawSpanLimits` instead. + That option allows setting unlimited and zero limits, this option does not. + This option will be kept until the next major version incremented release. (#2637) + +## [1.4.1] - 2022-02-16 + +### Fixed + +- Fix race condition in reading the dropped spans number for the `BatchSpanProcessor`. (#2615) + +## [1.4.0] - 2022-02-11 + +### Added + +- Use `OTEL_EXPORTER_ZIPKIN_ENDPOINT` environment variable to specify zipkin collector endpoint. (#2490) +- Log the configuration of `TracerProvider`s, and `Tracer`s for debugging. + To enable use a logger with Verbosity (V level) `>=1`. (#2500) +- Added support to configure the batch span-processor with environment variables. + The following environment variables are used. (#2515) + - `OTEL_BSP_SCHEDULE_DELAY` + - `OTEL_BSP_EXPORT_TIMEOUT` + - `OTEL_BSP_MAX_QUEUE_SIZE`. + - `OTEL_BSP_MAX_EXPORT_BATCH_SIZE` + +### Changed + +- Zipkin exporter exports `Resource` attributes in the `Tags` field. (#2589) + +### Deprecated + +- Deprecate module the `go.opentelemetry.io/otel/sdk/export/metric`. + Use the `go.opentelemetry.io/otel/sdk/metric` module instead. (#2382) +- Deprecate `"go.opentelemetry.io/otel/sdk/metric".AtomicFieldOffsets`. (#2445) + +### Fixed + +- Fixed the instrument kind for noop async instruments to correctly report an implementation. (#2461) +- Fix UDP packets overflowing with Jaeger payloads. (#2489, #2512) +- Change the `otlpmetric.Client` interface's `UploadMetrics` method to accept a single `ResourceMetrics` instead of a slice of them. (#2491) +- Specify explicit buckets in Prometheus example, fixing issue where example only has `+inf` bucket. (#2419, #2493) +- W3C baggage will now decode urlescaped values. (#2529) +- Baggage members are now only validated once, when calling `NewMember` and not also when adding it to the baggage itself. (#2522) +- The order attributes are dropped from spans in the `go.opentelemetry.io/otel/sdk/trace` package when capacity is reached is fixed to be in compliance with the OpenTelemetry specification. + Instead of dropping the least-recently-used attribute, the last added attribute is dropped. + This drop order still only applies to attributes with unique keys not already contained in the span. + If an attribute is added with a key already contained in the span, that attribute is updated to the new value being added. (#2576) + +### Removed + +- Updated `go.opentelemetry.io/proto/otlp` from `v0.11.0` to `v0.12.0`. This version removes a number of deprecated methods. (#2546) + - [`Metric.GetIntGauge()`](https://pkg.go.dev/go.opentelemetry.io/proto/otlp@v0.11.0/metrics/v1#Metric.GetIntGauge) + - [`Metric.GetIntHistogram()`](https://pkg.go.dev/go.opentelemetry.io/proto/otlp@v0.11.0/metrics/v1#Metric.GetIntHistogram) + - [`Metric.GetIntSum()`](https://pkg.go.dev/go.opentelemetry.io/proto/otlp@v0.11.0/metrics/v1#Metric.GetIntSum) + ## [1.3.0] - 2021-12-10 ### ⚠️ Notice ⚠️ @@ -1639,7 +1876,17 @@ It contains api and sdk for trace and meter. - CircleCI build CI manifest files. - CODEOWNERS file to track owners of this project. -[Unreleased]: https://github.com/open-telemetry/opentelemetry-go/compare/v1.3.0...HEAD +[Unreleased]: https://github.com/open-telemetry/opentelemetry-go/compare/v1.8.0...HEAD +[1.8.0/0.31.0]: https://github.com/open-telemetry/opentelemetry-go/releases/tag/v1.8.0 +[1.7.0/0.30.0]: https://github.com/open-telemetry/opentelemetry-go/releases/tag/v1.7.0 +[0.29.0]: https://github.com/open-telemetry/opentelemetry-go/releases/tag/metric/v0.29.0 +[1.6.3]: https://github.com/open-telemetry/opentelemetry-go/releases/tag/v1.6.3 +[1.6.2]: https://github.com/open-telemetry/opentelemetry-go/releases/tag/v1.6.2 +[1.6.1]: https://github.com/open-telemetry/opentelemetry-go/releases/tag/v1.6.1 +[1.6.0/0.28.0]: https://github.com/open-telemetry/opentelemetry-go/releases/tag/v1.6.0 +[1.5.0]: https://github.com/open-telemetry/opentelemetry-go/releases/tag/v1.5.0 +[1.4.1]: https://github.com/open-telemetry/opentelemetry-go/releases/tag/v1.4.1 +[1.4.0]: https://github.com/open-telemetry/opentelemetry-go/releases/tag/v1.4.0 [1.3.0]: https://github.com/open-telemetry/opentelemetry-go/releases/tag/v1.3.0 [1.2.0]: https://github.com/open-telemetry/opentelemetry-go/releases/tag/v1.2.0 [1.1.0]: https://github.com/open-telemetry/opentelemetry-go/releases/tag/v1.1.0 diff --git a/src/vendor/go.opentelemetry.io/otel/CODEOWNERS b/src/vendor/go.opentelemetry.io/otel/CODEOWNERS index 808755fe2..c4012ed6c 100644 --- a/src/vendor/go.opentelemetry.io/otel/CODEOWNERS +++ b/src/vendor/go.opentelemetry.io/otel/CODEOWNERS @@ -12,6 +12,6 @@ # https://help.github.com/en/articles/about-code-owners # -* @jmacd @MrAlias @Aneurysm9 @evantorrie @XSAM @dashpole @paivagustavo @MadVikingGod @pellared +* @jmacd @MrAlias @Aneurysm9 @evantorrie @XSAM @dashpole @MadVikingGod @pellared @hanyuancheung @dmathieu CODEOWNERS @MrAlias @Aneurysm9 @MadVikingGod diff --git a/src/vendor/go.opentelemetry.io/otel/CONTRIBUTING.md b/src/vendor/go.opentelemetry.io/otel/CONTRIBUTING.md index fd6c1e3ad..9371a481a 100644 --- a/src/vendor/go.opentelemetry.io/otel/CONTRIBUTING.md +++ b/src/vendor/go.opentelemetry.io/otel/CONTRIBUTING.md @@ -228,11 +228,11 @@ all options to create a configured `config`. ```go // newConfig returns an appropriately configured config. -func newConfig([]Option) config { +func newConfig(options ...Option) config { // Set default values for config. config := config{/* […] */} for _, option := range options { - option.apply(&config) + config = option.apply(config) } // Preform any validation here. return config @@ -253,7 +253,7 @@ To set the value of the options a `config` contains, a corresponding ```go type Option interface { - apply(*config) + apply(config) config } ``` @@ -261,6 +261,9 @@ Having `apply` unexported makes sure that it will not be used externally. Moreover, the interface becomes sealed so the user cannot easily implement the interface on its own. +The `apply` method should return a modified version of the passed config. +This approach, instead of passing a pointer, is used to prevent the config from being allocated to the heap. + The name of the interface should be prefixed in the same way the corresponding `config` is (if at all). @@ -283,8 +286,9 @@ func With*(…) Option { … } ```go type defaultFalseOption bool -func (o defaultFalseOption) apply(c *config) { +func (o defaultFalseOption) apply(c config) config { c.Bool = bool(o) + return c } // WithOption sets a T to have an option included. @@ -296,8 +300,9 @@ func WithOption() Option { ```go type defaultTrueOption bool -func (o defaultTrueOption) apply(c *config) { +func (o defaultTrueOption) apply(c config) config { c.Bool = bool(o) + return c } // WithoutOption sets a T to have Bool option excluded. @@ -313,8 +318,9 @@ type myTypeOption struct { MyType MyType } -func (o myTypeOption) apply(c *config) { +func (o myTypeOption) apply(c config) config { c.MyType = o.MyType + return c } // WithMyType sets T to have include MyType. @@ -326,16 +332,17 @@ func WithMyType(t MyType) Option { ##### Functional Options ```go -type optionFunc func(*config) +type optionFunc func(config) config -func (fn optionFunc) apply(c *config) { - fn(c) +func (fn optionFunc) apply(c config) config { + return fn(c) } // WithMyType sets t as MyType. func WithMyType(t MyType) Option { - return optionFunc(func(c *config) { + return optionFunc(func(c config) config { c.MyType = t + return c }) } ``` @@ -370,12 +377,12 @@ type config struct { // DogOption apply Dog specific options. type DogOption interface { - applyDog(*config) + applyDog(config) config } // BirdOption apply Bird specific options. type BirdOption interface { - applyBird(*config) + applyBird(config) config } // Option apply options for all animals. @@ -385,17 +392,36 @@ type Option interface { } type weightOption float64 -func (o weightOption) applyDog(c *config) { c.Weight = float64(o) } -func (o weightOption) applyBird(c *config) { c.Weight = float64(o) } -func WithWeight(w float64) Option { return weightOption(w) } + +func (o weightOption) applyDog(c config) config { + c.Weight = float64(o) + return c +} + +func (o weightOption) applyBird(c config) config { + c.Weight = float64(o) + return c +} + +func WithWeight(w float64) Option { return weightOption(w) } type furColorOption string -func (o furColorOption) applyDog(c *config) { c.Color = string(o) } -func WithFurColor(c string) DogOption { return furColorOption(c) } + +func (o furColorOption) applyDog(c config) config { + c.Color = string(o) + return c +} + +func WithFurColor(c string) DogOption { return furColorOption(c) } type maxAltitudeOption float64 -func (o maxAltitudeOption) applyBird(c *config) { c.MaxAltitude = float64(o) } -func WithMaxAltitude(a float64) BirdOption { return maxAltitudeOption(a) } + +func (o maxAltitudeOption) applyBird(c config) config { + c.MaxAltitude = float64(o) + return c +} + +func WithMaxAltitude(a float64) BirdOption { return maxAltitudeOption(a) } func NewDog(name string, o ...DogOption) Dog {…} func NewBird(name string, o ...BirdOption) Bird {…} @@ -478,10 +504,11 @@ Approvers: - [Evan Torrie](https://github.com/evantorrie), Verizon Media - [Josh MacDonald](https://github.com/jmacd), LightStep -- [Sam Xie](https://github.com/XSAM) +- [Sam Xie](https://github.com/XSAM), Cisco/AppDynamics - [David Ashpole](https://github.com/dashpole), Google -- [Gustavo Silva Paiva](https://github.com/paivagustavo), LightStep - [Robert Pająk](https://github.com/pellared), Splunk +- [Chester Cheung](https://github.com/hanyuancheung), Tencent +- [Damien Mathieu](https://github.com/dmathieu), Auth0/Okta Maintainers: @@ -489,6 +516,10 @@ Maintainers: - [Anthony Mirabella](https://github.com/Aneurysm9), AWS - [Tyler Yahn](https://github.com/MrAlias), Splunk +Emeritus: + +- [Gustavo Silva Paiva](https://github.com/paivagustavo), LightStep + ### Become an Approver or a Maintainer See the [community membership document in OpenTelemetry community diff --git a/src/vendor/go.opentelemetry.io/otel/Makefile b/src/vendor/go.opentelemetry.io/otel/Makefile index f360b56da..18ffaa33a 100644 --- a/src/vendor/go.opentelemetry.io/otel/Makefile +++ b/src/vendor/go.opentelemetry.io/otel/Makefile @@ -12,13 +12,11 @@ # See the License for the specific language governing permissions and # limitations under the License. -EXAMPLES := $(shell ./get_main_pkgs.sh ./example) TOOLS_MOD_DIR := ./internal/tools -# All source code and documents. Used in spell check. ALL_DOCS := $(shell find . -name '*.md' -type f | sort) -# All directories with go.mod files related to opentelemetry library. Used for building, testing and linting. -ALL_GO_MOD_DIRS := $(filter-out $(TOOLS_MOD_DIR), $(shell find . -type f -name 'go.mod' -exec dirname {} \; | egrep -v '^./example' | sort)) $(shell find ./example -type f -name 'go.mod' -exec dirname {} \; | sort) +ALL_GO_MOD_DIRS := $(shell find . -type f -name 'go.mod' -exec dirname {} \; | sort) +OTEL_GO_MOD_DIRS := $(filter-out $(TOOLS_MOD_DIR), $(ALL_GO_MOD_DIRS)) ALL_COVERAGE_MOD_DIRS := $(shell find . -type f -name 'go.mod' -exec dirname {} \; | egrep -v '^./example|^$(TOOLS_MOD_DIR)' | sort) GO = go @@ -27,8 +25,8 @@ TIMEOUT = 60 .DEFAULT_GOAL := precommit .PHONY: precommit ci -precommit: dependabot-check license-check lint build examples test-default -ci: precommit check-clean-work-tree test-coverage +precommit: dependabot-generate license-check vanity-import-fix misspell go-mod-tidy golangci-lint-fix test-default +ci: dependabot-check license-check lint vanity-import-check build test-default check-clean-work-tree test-coverage # Tools @@ -47,7 +45,13 @@ SEMCONVGEN = $(TOOLS)/semconvgen $(TOOLS)/semconvgen: PACKAGE=go.opentelemetry.io/build-tools/semconvgen CROSSLINK = $(TOOLS)/crosslink -$(TOOLS)/crosslink: PACKAGE=go.opentelemetry.io/otel/$(TOOLS_MOD_DIR)/crosslink +$(TOOLS)/crosslink: PACKAGE=go.opentelemetry.io/build-tools/crosslink + +SEMCONVKIT = $(TOOLS)/semconvkit +$(TOOLS)/semconvkit: PACKAGE=go.opentelemetry.io/otel/$(TOOLS_MOD_DIR)/semconvkit + +DBOTCONF = $(TOOLS)/dbotconf +$(TOOLS)/dbotconf: PACKAGE=go.opentelemetry.io/build-tools/dbotconf GOLANGCI_LINT = $(TOOLS)/golangci-lint $(TOOLS)/golangci-lint: PACKAGE=github.com/golangci/golangci-lint/cmd/golangci-lint @@ -68,55 +72,51 @@ GOJQ = $(TOOLS)/gojq $(TOOLS)/gojq: PACKAGE=github.com/itchyny/gojq/cmd/gojq .PHONY: tools -tools: $(CROSSLINK) $(GOLANGCI_LINT) $(MISSPELL) $(GOCOVMERGE) $(STRINGER) $(PORTO) $(GOJQ) $(SEMCONVGEN) $(MULTIMOD) +tools: $(CROSSLINK) $(DBOTCONF) $(GOLANGCI_LINT) $(MISSPELL) $(GOCOVMERGE) $(STRINGER) $(PORTO) $(GOJQ) $(SEMCONVGEN) $(MULTIMOD) $(SEMCONVKIT) # Build -.PHONY: examples generate build -examples: - @set -e; for dir in $(EXAMPLES); do \ - echo "$(GO) build $${dir}/..."; \ - (cd "$${dir}" && \ - $(GO) build .); \ - done +.PHONY: generate build -generate: $(STRINGER) $(PORTO) - set -e; for dir in $(ALL_GO_MOD_DIRS); do \ - echo "$(GO) generate $${dir}/..."; \ - (cd "$${dir}" && \ - PATH="$(TOOLS):$${PATH}" $(GO) generate ./... && \ - $(PORTO) -w .); \ - done +generate: $(OTEL_GO_MOD_DIRS:%=generate/%) +generate/%: DIR=$* +generate/%: | $(STRINGER) $(PORTO) + @echo "$(GO) generate $(DIR)/..." \ + && cd $(DIR) \ + && PATH="$(TOOLS):$${PATH}" $(GO) generate ./... && $(PORTO) -w . -build: generate - # Build all package code including testing code. - set -e; for dir in $(ALL_GO_MOD_DIRS); do \ - echo "$(GO) build $${dir}/..."; \ - (cd "$${dir}" && \ - $(GO) build ./... && \ - $(GO) list ./... \ - | grep -v third_party \ - | xargs $(GO) test -vet=off -run xxxxxMatchNothingxxxxx >/dev/null); \ - done +build: generate $(OTEL_GO_MOD_DIRS:%=build/%) $(OTEL_GO_MOD_DIRS:%=build-tests/%) +build/%: DIR=$* +build/%: + @echo "$(GO) build $(DIR)/..." \ + && cd $(DIR) \ + && $(GO) build ./... + +build-tests/%: DIR=$* +build-tests/%: + @echo "$(GO) build tests $(DIR)/..." \ + && cd $(DIR) \ + && $(GO) list ./... \ + | grep -v third_party \ + | xargs $(GO) test -vet=off -run xxxxxMatchNothingxxxxx >/dev/null # Tests TEST_TARGETS := test-default test-bench test-short test-verbose test-race .PHONY: $(TEST_TARGETS) test -test-default: ARGS=-v -race +test-default test-race: ARGS=-race test-bench: ARGS=-run=xxxxxMatchNothingxxxxx -test.benchtime=1ms -bench=. test-short: ARGS=-short -test-verbose: ARGS=-v -test-race: ARGS=-race +test-verbose: ARGS=-v -race $(TEST_TARGETS): test -test: - @set -e; for dir in $(ALL_GO_MOD_DIRS); do \ - echo "$(GO) test -timeout $(TIMEOUT)s $(ARGS) $${dir}/..."; \ - (cd "$${dir}" && \ - $(GO) list ./... \ - | grep -v third_party \ - | xargs $(GO) test -timeout $(TIMEOUT)s $(ARGS)); \ - done +test: $(OTEL_GO_MOD_DIRS:%=test/%) +test/%: DIR=$* +test/%: + @echo "$(GO) test -timeout $(TIMEOUT)s $(ARGS) $(DIR)/..." \ + && cd $(DIR) \ + && $(GO) list ./... \ + | grep -v third_party \ + | xargs $(GO) test -timeout $(TIMEOUT)s $(ARGS) COVERAGE_MODE = atomic COVERAGE_PROFILE = coverage.out @@ -129,41 +129,56 @@ test-coverage: | $(GOCOVMERGE) (cd "$${dir}" && \ $(GO) list ./... \ | grep -v third_party \ + | grep -v 'semconv/v.*' \ | xargs $(GO) test -coverpkg=./... -covermode=$(COVERAGE_MODE) -coverprofile="$(COVERAGE_PROFILE)" && \ $(GO) tool cover -html=coverage.out -o coverage.html); \ done; \ $(GOCOVMERGE) $$(find . -name coverage.out) > coverage.txt +.PHONY: golangci-lint golangci-lint-fix +golangci-lint-fix: ARGS=--fix +golangci-lint-fix: golangci-lint +golangci-lint: $(OTEL_GO_MOD_DIRS:%=golangci-lint/%) +golangci-lint/%: DIR=$* +golangci-lint/%: | $(GOLANGCI_LINT) + @echo 'golangci-lint $(if $(ARGS),$(ARGS) ,)$(DIR)' \ + && cd $(DIR) \ + && $(GOLANGCI_LINT) run --allow-serial-runners $(ARGS) + +.PHONY: crosslink +crosslink: | $(CROSSLINK) + @echo "Updating intra-repository dependencies in all go modules" \ + && $(CROSSLINK) --root=$(shell pwd) --prune + +.PHONY: go-mod-tidy +go-mod-tidy: $(ALL_GO_MOD_DIRS:%=go-mod-tidy/%) +go-mod-tidy/%: DIR=$* +go-mod-tidy/%: | crosslink + @echo "$(GO) mod tidy in $(DIR)" \ + && cd $(DIR) \ + && $(GO) mod tidy -compat=1.17 + +.PHONY: lint-modules +lint-modules: go-mod-tidy + .PHONY: lint -lint: misspell lint-modules | $(GOLANGCI_LINT) - set -e; for dir in $(ALL_GO_MOD_DIRS); do \ - echo "golangci-lint in $${dir}"; \ - (cd "$${dir}" && \ - $(GOLANGCI_LINT) run --fix && \ - $(GOLANGCI_LINT) run); \ - done +lint: misspell lint-modules golangci-lint .PHONY: vanity-import-check vanity-import-check: | $(PORTO) - $(PORTO) --include-internal -l . + @$(PORTO) --include-internal -l . || echo "(run: make vanity-import-fix)" + +.PHONY: vanity-import-fix +vanity-import-fix: | $(PORTO) + @$(PORTO) --include-internal -w . .PHONY: misspell misspell: | $(MISSPELL) - $(MISSPELL) -w $(ALL_DOCS) - -.PHONY: lint-modules -lint-modules: | $(CROSSLINK) - set -e; for dir in $(ALL_GO_MOD_DIRS) $(TOOLS_MOD_DIR); do \ - echo "$(GO) mod tidy in $${dir}"; \ - (cd "$${dir}" && \ - $(GO) mod tidy); \ - done - echo "cross-linking all go modules" - $(CROSSLINK) + @$(MISSPELL) -w $(ALL_DOCS) .PHONY: license-check license-check: - @licRes=$$(for f in $$(find . -type f \( -iname '*.go' -o -iname '*.sh' \) ! -path '**/third_party/*') ; do \ + @licRes=$$(for f in $$(find . -type f \( -iname '*.go' -o -iname '*.sh' \) ! -path '**/third_party/*' ! -path './.git/*' ) ; do \ awk '/Copyright The OpenTelemetry Authors|generated|GENERATED/ && NR<=3 { found=1; next } END { if (!found) print FILENAME }' $$f; \ done); \ if [ -n "$${licRes}" ]; then \ @@ -171,19 +186,14 @@ license-check: exit 1; \ fi +DEPENDABOT_CONFIG = .github/dependabot.yml .PHONY: dependabot-check -dependabot-check: - @result=$$( \ - for f in $$( find . -type f -name go.mod -exec dirname {} \; | sed 's/^.//' ); \ - do grep -q "directory: \+$$f" .github/dependabot.yml \ - || echo "$$f"; \ - done; \ - ); \ - if [ -n "$$result" ]; then \ - echo "missing go.mod dependabot check:"; echo "$$result"; \ - echo "new modules need to be added to the .github/dependabot.yml file"; \ - exit 1; \ - fi +dependabot-check: | $(DBOTCONF) + @$(DBOTCONF) verify $(DEPENDABOT_CONFIG) || echo "(run: make dependabot-generate)" + +.PHONY: dependabot-generate +dependabot-generate: | $(DBOTCONF) + @$(DBOTCONF) generate > $(DEPENDABOT_CONFIG) .PHONY: check-clean-work-tree check-clean-work-tree: @@ -195,6 +205,15 @@ check-clean-work-tree: exit 1; \ fi +SEMCONVPKG ?= "semconv/" +.PHONY: semconv-generate +semconv-generate: | $(SEMCONVGEN) $(SEMCONVKIT) + @[ "$(TAG)" ] || ( echo "TAG unset: missing opentelemetry specification tag"; exit 1 ) + @[ "$(OTEL_SPEC_REPO)" ] || ( echo "OTEL_SPEC_REPO unset: missing path to opentelemetry specification repo"; exit 1 ) + @$(SEMCONVGEN) -i "$(OTEL_SPEC_REPO)/semantic_conventions/trace" -t "$(SEMCONVPKG)/template.j2" -s "$(TAG)" + @$(SEMCONVGEN) -i "$(OTEL_SPEC_REPO)/semantic_conventions/resource" -t "$(SEMCONVPKG)/template.j2" -s "$(TAG)" + @$(SEMCONVKIT) -output "$(SEMCONVPKG)/$(TAG)" -tag "$(TAG)" + .PHONY: prerelease prerelease: | $(MULTIMOD) @[ "${MODSET}" ] || ( echo ">> env var MODSET is not set"; exit 1 ) diff --git a/src/vendor/go.opentelemetry.io/otel/README.md b/src/vendor/go.opentelemetry.io/otel/README.md index 21c2a7161..c87f800b9 100644 --- a/src/vendor/go.opentelemetry.io/otel/README.md +++ b/src/vendor/go.opentelemetry.io/otel/README.md @@ -41,16 +41,16 @@ This project is tested on the following systems. | OS | Go Version | Architecture | | ------- | ---------- | ------------ | +| Ubuntu | 1.18 | amd64 | | Ubuntu | 1.17 | amd64 | -| Ubuntu | 1.16 | amd64 | +| Ubuntu | 1.18 | 386 | | Ubuntu | 1.17 | 386 | -| Ubuntu | 1.16 | 386 | +| MacOS | 1.18 | amd64 | | MacOS | 1.17 | amd64 | -| MacOS | 1.16 | amd64 | +| Windows | 1.18 | amd64 | | Windows | 1.17 | amd64 | -| Windows | 1.16 | amd64 | +| Windows | 1.18 | 386 | | Windows | 1.17 | 386 | -| Windows | 1.16 | 386 | While this project should work for other systems, no compatibility guarantees are made for those systems currently. @@ -76,7 +76,7 @@ libraries](https://github.com/open-telemetry/opentelemetry-go-contrib/tree/main/ If you need to extend the telemetry an instrumentation library provides or want to build your own instrumentation for your application directly you will need to use the -[go.opentelemetry.io/otel/api](https://pkg.go.dev/go.opentelemetry.io/otel/api) +[Go otel](https://pkg.go.dev/go.opentelemetry.io/otel) package. The included [examples](./example/) are a good way to see some practical uses of this process. @@ -95,8 +95,6 @@ All officially supported exporters for the OpenTelemetry project are contained i | [stdout](./exporters/stdout/) | ✓ | ✓ | | [Zipkin](./exporters/zipkin/) | | ✓ | -Additionally, OpenTelemetry community supported exporters can be found in the [contrib repository](https://github.com/open-telemetry/opentelemetry-go-contrib/tree/main/exporters). - ## Contributing See the [contributing documentation](CONTRIBUTING.md). diff --git a/src/vendor/go.opentelemetry.io/otel/RELEASING.md b/src/vendor/go.opentelemetry.io/otel/RELEASING.md index aef6510a2..71e576254 100644 --- a/src/vendor/go.opentelemetry.io/otel/RELEASING.md +++ b/src/vendor/go.opentelemetry.io/otel/RELEASING.md @@ -2,35 +2,23 @@ ## Semantic Convention Generation -If a new version of the OpenTelemetry Specification has been released it will be necessary to generate a new -semantic convention package from the YAML definitions in the specification repository. There is a `semconvgen` utility -installed by `make tools` that can be used to generate the a package with the name matching the specification -version number under the `semconv` package. This will ideally be done soon after the specification release is -tagged. Make sure that the specification repo contains a checkout of the the latest tagged release so that the -generated files match the released semantic conventions. +New versions of the [OpenTelemetry specification] mean new versions of the `semconv` package need to be generated. +The `semconv-generate` make target is used for this. -There are currently two categories of semantic conventions that must be generated, `resource` and `trace`. +1. Checkout a local copy of the [OpenTelemetry specification] to the desired release tag. +2. Run the `make semconv-generate ...` target from this repository. -``` -.tools/semconvgen -i /path/to/specification/repo/semantic_conventions/resource -t semconv/template.j2 -.tools/semconvgen -i /path/to/specification/repo/semantic_conventions/trace -t semconv/template.j2 +For example, + +```sh +export TAG="v1.7.0" # Change to the release version you are generating. +export OTEL_SPEC_REPO="/absolute/path/to/opentelemetry-specification" +git -C "$OTEL_SPEC_REPO" checkout "tags/$TAG" +make semconv-generate # Uses the exported TAG and OTEL_SPEC_REPO. ``` -Using default values for all options other than `input` will result in using the `template.j2` template to -generate `resource.go` and `trace.go` in `/path/to/otelgo/repo/semconv/`. - -There are several ancillary files that are not generated and should be copied into the new package from the -prior package, with updates made as appropriate to canonical import path statements and constant values. -These files include: - -* doc.go -* exception.go -* http(_test)?.go -* schema.go - -Uses of the previous schema version in this repository should be updated to use the newly generated version. -No tooling for this exists at present, so use find/replace in your editor of choice or craft a `grep | sed` -pipeline if you like living on the edge. +This should create a new sub-package of [`semconv`](./semconv). +Ensure things look correct before submitting a pull request to include the addition. ## Pre-Release @@ -108,7 +96,6 @@ It is critical you make sure the version you push upstream is correct. Finally create a Release for the new `` on GitHub. The release body should include all the release notes from the Changelog for this release. -Additionally, the `tag.sh` script generates commit logs since last release which can be used to supplement the release notes. ## Verify Examples @@ -131,3 +118,5 @@ Once verified be sure to [make a release for the `contrib` repository](https://g Update [the documentation](./website_docs) for [the OpenTelemetry website](https://opentelemetry.io/docs/go/). Importantly, bump any package versions referenced to be the latest one you just released and ensure all code examples still compile and are accurate. + +[OpenTelemetry specification]: https://github.com/open-telemetry/opentelemetry-specification diff --git a/src/vendor/go.opentelemetry.io/otel/attribute/encoder.go b/src/vendor/go.opentelemetry.io/otel/attribute/encoder.go index 8b940f78d..fe2bc5766 100644 --- a/src/vendor/go.opentelemetry.io/otel/attribute/encoder.go +++ b/src/vendor/go.opentelemetry.io/otel/attribute/encoder.go @@ -21,19 +21,17 @@ import ( ) type ( - // Encoder is a mechanism for serializing a label set into a - // specific string representation that supports caching, to - // avoid repeated serialization. An example could be an - // exporter encoding the label set into a wire representation. + // Encoder is a mechanism for serializing an attribute set into a specific + // string representation that supports caching, to avoid repeated + // serialization. An example could be an exporter encoding the attribute + // set into a wire representation. Encoder interface { - // Encode returns the serialized encoding of the label - // set using its Iterator. This result may be cached - // by a attribute.Set. + // Encode returns the serialized encoding of the attribute set using + // its Iterator. This result may be cached by a attribute.Set. Encode(iterator Iterator) string - // ID returns a value that is unique for each class of - // label encoder. Label encoders allocate these using - // `NewEncoderID`. + // ID returns a value that is unique for each class of attribute + // encoder. Attribute encoders allocate these using `NewEncoderID`. ID() EncoderID } @@ -43,54 +41,53 @@ type ( value uint64 } - // defaultLabelEncoder uses a sync.Pool of buffers to reduce - // the number of allocations used in encoding labels. This - // implementation encodes a comma-separated list of key=value, - // with '/'-escaping of '=', ',', and '\'. - defaultLabelEncoder struct { - // pool is a pool of labelset builders. The buffers in this - // pool grow to a size that most label encodings will not - // allocate new memory. + // defaultAttrEncoder uses a sync.Pool of buffers to reduce the number of + // allocations used in encoding attributes. This implementation encodes a + // comma-separated list of key=value, with '/'-escaping of '=', ',', and + // '\'. + defaultAttrEncoder struct { + // pool is a pool of attribute set builders. The buffers in this pool + // grow to a size that most attribute encodings will not allocate new + // memory. pool sync.Pool // *bytes.Buffer } ) -// escapeChar is used to ensure uniqueness of the label encoding where -// keys or values contain either '=' or ','. Since there is no parser -// needed for this encoding and its only requirement is to be unique, -// this choice is arbitrary. Users will see these in some exporters -// (e.g., stdout), so the backslash ('\') is used as a conventional choice. +// escapeChar is used to ensure uniqueness of the attribute encoding where +// keys or values contain either '=' or ','. Since there is no parser needed +// for this encoding and its only requirement is to be unique, this choice is +// arbitrary. Users will see these in some exporters (e.g., stdout), so the +// backslash ('\') is used as a conventional choice. const escapeChar = '\\' var ( - _ Encoder = &defaultLabelEncoder{} + _ Encoder = &defaultAttrEncoder{} - // encoderIDCounter is for generating IDs for other label - // encoders. + // encoderIDCounter is for generating IDs for other attribute encoders. encoderIDCounter uint64 defaultEncoderOnce sync.Once defaultEncoderID = NewEncoderID() - defaultEncoderInstance *defaultLabelEncoder + defaultEncoderInstance *defaultAttrEncoder ) -// NewEncoderID returns a unique label encoder ID. It should be -// called once per each type of label encoder. Preferably in init() or -// in var definition. +// NewEncoderID returns a unique attribute encoder ID. It should be called +// once per each type of attribute encoder. Preferably in init() or in var +// definition. func NewEncoderID() EncoderID { return EncoderID{value: atomic.AddUint64(&encoderIDCounter, 1)} } -// DefaultEncoder returns a label encoder that encodes labels -// in such a way that each escaped label's key is followed by an equal -// sign and then by an escaped label's value. All key-value pairs are -// separated by a comma. +// DefaultEncoder returns an attribute encoder that encodes attributes in such +// a way that each escaped attribute's key is followed by an equal sign and +// then by an escaped attribute's value. All key-value pairs are separated by +// a comma. // -// Escaping is done by prepending a backslash before either a -// backslash, equal sign or a comma. +// Escaping is done by prepending a backslash before either a backslash, equal +// sign or a comma. func DefaultEncoder() Encoder { defaultEncoderOnce.Do(func() { - defaultEncoderInstance = &defaultLabelEncoder{ + defaultEncoderInstance = &defaultAttrEncoder{ pool: sync.Pool{ New: func() interface{} { return &bytes.Buffer{} @@ -101,15 +98,14 @@ func DefaultEncoder() Encoder { return defaultEncoderInstance } -// Encode is a part of an implementation of the LabelEncoder -// interface. -func (d *defaultLabelEncoder) Encode(iter Iterator) string { +// Encode is a part of an implementation of the AttributeEncoder interface. +func (d *defaultAttrEncoder) Encode(iter Iterator) string { buf := d.pool.Get().(*bytes.Buffer) defer d.pool.Put(buf) buf.Reset() for iter.Next() { - i, keyValue := iter.IndexedLabel() + i, keyValue := iter.IndexedAttribute() if i > 0 { _, _ = buf.WriteRune(',') } @@ -126,8 +122,8 @@ func (d *defaultLabelEncoder) Encode(iter Iterator) string { return buf.String() } -// ID is a part of an implementation of the LabelEncoder interface. -func (*defaultLabelEncoder) ID() EncoderID { +// ID is a part of an implementation of the AttributeEncoder interface. +func (*defaultAttrEncoder) ID() EncoderID { return defaultEncoderID } @@ -137,9 +133,9 @@ func copyAndEscape(buf *bytes.Buffer, val string) { for _, ch := range val { switch ch { case '=', ',', escapeChar: - buf.WriteRune(escapeChar) + _, _ = buf.WriteRune(escapeChar) } - buf.WriteRune(ch) + _, _ = buf.WriteRune(ch) } } diff --git a/src/vendor/go.opentelemetry.io/otel/attribute/iterator.go b/src/vendor/go.opentelemetry.io/otel/attribute/iterator.go index e03aabb62..841b271fb 100644 --- a/src/vendor/go.opentelemetry.io/otel/attribute/iterator.go +++ b/src/vendor/go.opentelemetry.io/otel/attribute/iterator.go @@ -14,16 +14,16 @@ package attribute // import "go.opentelemetry.io/otel/attribute" -// Iterator allows iterating over the set of labels in order, -// sorted by key. +// Iterator allows iterating over the set of attributes in order, sorted by +// key. type Iterator struct { storage *Set idx int } -// MergeIterator supports iterating over two sets of labels while -// eliminating duplicate values from the combined set. The first -// iterator value takes precedence. +// MergeIterator supports iterating over two sets of attributes while +// eliminating duplicate values from the combined set. The first iterator +// value takes precedence. type MergeIterator struct { one oneIterator two oneIterator @@ -31,13 +31,13 @@ type MergeIterator struct { } type oneIterator struct { - iter Iterator - done bool - label KeyValue + iter Iterator + done bool + attr KeyValue } -// Next moves the iterator to the next position. Returns false if there -// are no more labels. +// Next moves the iterator to the next position. Returns false if there are no +// more attributes. func (i *Iterator) Next() bool { i.idx++ return i.idx < i.Len() @@ -45,30 +45,41 @@ func (i *Iterator) Next() bool { // Label returns current KeyValue. Must be called only after Next returns // true. +// +// Deprecated: Use Attribute instead. func (i *Iterator) Label() KeyValue { + return i.Attribute() +} + +// Attribute returns the current KeyValue of the Iterator. It must be called +// only after Next returns true. +func (i *Iterator) Attribute() KeyValue { kv, _ := i.storage.Get(i.idx) return kv } -// Attribute is a synonym for Label(). -func (i *Iterator) Attribute() KeyValue { - return i.Label() -} - // IndexedLabel returns current index and attribute. Must be called only // after Next returns true. +// +// Deprecated: Use IndexedAttribute instead. func (i *Iterator) IndexedLabel() (int, KeyValue) { - return i.idx, i.Label() + return i.idx, i.Attribute() } -// Len returns a number of labels in the iterator's `*Set`. +// IndexedAttribute returns current index and attribute. Must be called only +// after Next returns true. +func (i *Iterator) IndexedAttribute() (int, KeyValue) { + return i.idx, i.Attribute() +} + +// Len returns a number of attributes in the iterated set. func (i *Iterator) Len() int { return i.storage.Len() } -// ToSlice is a convenience function that creates a slice of labels -// from the passed iterator. The iterator is set up to start from the -// beginning before creating the slice. +// ToSlice is a convenience function that creates a slice of attributes from +// the passed iterator. The iterator is set up to start from the beginning +// before creating the slice. func (i *Iterator) ToSlice() []KeyValue { l := i.Len() if l == 0 { @@ -77,12 +88,12 @@ func (i *Iterator) ToSlice() []KeyValue { i.idx = -1 slice := make([]KeyValue, 0, l) for i.Next() { - slice = append(slice, i.Label()) + slice = append(slice, i.Attribute()) } return slice } -// NewMergeIterator returns a MergeIterator for merging two label sets +// NewMergeIterator returns a MergeIterator for merging two attribute sets. // Duplicates are resolved by taking the value from the first set. func NewMergeIterator(s1, s2 *Set) MergeIterator { mi := MergeIterator{ @@ -102,42 +113,49 @@ func makeOne(iter Iterator) oneIterator { func (oi *oneIterator) advance() { if oi.done = !oi.iter.Next(); !oi.done { - oi.label = oi.iter.Label() + oi.attr = oi.iter.Attribute() } } -// Next returns true if there is another label available. +// Next returns true if there is another attribute available. func (m *MergeIterator) Next() bool { if m.one.done && m.two.done { return false } if m.one.done { - m.current = m.two.label + m.current = m.two.attr m.two.advance() return true } if m.two.done { - m.current = m.one.label + m.current = m.one.attr m.one.advance() return true } - if m.one.label.Key == m.two.label.Key { - m.current = m.one.label // first iterator label value wins + if m.one.attr.Key == m.two.attr.Key { + m.current = m.one.attr // first iterator attribute value wins m.one.advance() m.two.advance() return true } - if m.one.label.Key < m.two.label.Key { - m.current = m.one.label + if m.one.attr.Key < m.two.attr.Key { + m.current = m.one.attr m.one.advance() return true } - m.current = m.two.label + m.current = m.two.attr m.two.advance() return true } // Label returns the current value after Next() returns true. +// +// Deprecated: Use Attribute instead. func (m *MergeIterator) Label() KeyValue { return m.current } + +// Attribute returns the current value after Next() returns true. +func (m *MergeIterator) Attribute() KeyValue { + return m.current +} diff --git a/src/vendor/go.opentelemetry.io/otel/attribute/kv.go b/src/vendor/go.opentelemetry.io/otel/attribute/kv.go index 8f5793385..1ddf3ce05 100644 --- a/src/vendor/go.opentelemetry.io/otel/attribute/kv.go +++ b/src/vendor/go.opentelemetry.io/otel/attribute/kv.go @@ -26,7 +26,7 @@ type KeyValue struct { // Valid returns if kv is a valid OpenTelemetry attribute. func (kv KeyValue) Valid() bool { - return kv.Key != "" && kv.Value.Type() != INVALID + return kv.Key.Defined() && kv.Value.Type() != INVALID } // Bool creates a KeyValue with a BOOL Value type. diff --git a/src/vendor/go.opentelemetry.io/otel/attribute/set.go b/src/vendor/go.opentelemetry.io/otel/attribute/set.go index bd591894d..26be59832 100644 --- a/src/vendor/go.opentelemetry.io/otel/attribute/set.go +++ b/src/vendor/go.opentelemetry.io/otel/attribute/set.go @@ -21,49 +21,42 @@ import ( ) type ( - // Set is the representation for a distinct label set. It - // manages an immutable set of labels, with an internal cache - // for storing label encodings. + // Set is the representation for a distinct attribute set. It manages an + // immutable set of attributes, with an internal cache for storing + // attribute encodings. // - // This type supports the `Equivalent` method of comparison - // using values of type `Distinct`. - // - // This type is used to implement: - // 1. Metric labels - // 2. Resource sets - // 3. Correlation map (TODO) + // This type supports the Equivalent method of comparison using values of + // type Distinct. Set struct { equivalent Distinct } - // Distinct wraps a variable-size array of `KeyValue`, - // constructed with keys in sorted order. This can be used as - // a map key or for equality checking between Sets. + // Distinct wraps a variable-size array of KeyValue, constructed with keys + // in sorted order. This can be used as a map key or for equality checking + // between Sets. Distinct struct { iface interface{} } - // Filter supports removing certain labels from label sets. - // When the filter returns true, the label will be kept in - // the filtered label set. When the filter returns false, the - // label is excluded from the filtered label set, and the - // label instead appears in the `removed` list of excluded labels. + // Filter supports removing certain attributes from attribute sets. When + // the filter returns true, the attribute will be kept in the filtered + // attribute set. When the filter returns false, the attribute is excluded + // from the filtered attribute set, and the attribute instead appears in + // the removed list of excluded attributes. Filter func(KeyValue) bool - // Sortable implements `sort.Interface`, used for sorting - // `KeyValue`. This is an exported type to support a - // memory optimization. A pointer to one of these is needed - // for the call to `sort.Stable()`, which the caller may - // provide in order to avoid an allocation. See - // `NewSetWithSortable()`. + // Sortable implements sort.Interface, used for sorting KeyValue. This is + // an exported type to support a memory optimization. A pointer to one of + // these is needed for the call to sort.Stable(), which the caller may + // provide in order to avoid an allocation. See NewSetWithSortable(). Sortable []KeyValue ) var ( - // keyValueType is used in `computeDistinctReflect`. + // keyValueType is used in computeDistinctReflect. keyValueType = reflect.TypeOf(KeyValue{}) - // emptySet is returned for empty label sets. + // emptySet is returned for empty attribute sets. emptySet = &Set{ equivalent: Distinct{ iface: [0]KeyValue{}, @@ -78,30 +71,30 @@ func EmptySet() *Set { return emptySet } -// reflect abbreviates `reflect.ValueOf`. -func (d Distinct) reflect() reflect.Value { +// reflectValue abbreviates reflect.ValueOf(d). +func (d Distinct) reflectValue() reflect.Value { return reflect.ValueOf(d.iface) } -// Valid returns true if this value refers to a valid `*Set`. +// Valid returns true if this value refers to a valid Set. func (d Distinct) Valid() bool { return d.iface != nil } -// Len returns the number of labels in this set. +// Len returns the number of attributes in this set. func (l *Set) Len() int { if l == nil || !l.equivalent.Valid() { return 0 } - return l.equivalent.reflect().Len() + return l.equivalent.reflectValue().Len() } -// Get returns the KeyValue at ordered position `idx` in this set. +// Get returns the KeyValue at ordered position idx in this set. func (l *Set) Get(idx int) (KeyValue, bool) { if l == nil { return KeyValue{}, false } - value := l.equivalent.reflect() + value := l.equivalent.reflectValue() if idx >= 0 && idx < value.Len() { // Note: The Go compiler successfully avoids an allocation for @@ -117,7 +110,7 @@ func (l *Set) Value(k Key) (Value, bool) { if l == nil { return Value{}, false } - rValue := l.equivalent.reflect() + rValue := l.equivalent.reflectValue() vlen := rValue.Len() idx := sort.Search(vlen, func(idx int) bool { @@ -142,7 +135,7 @@ func (l *Set) HasValue(k Key) bool { return ok } -// Iter returns an iterator for visiting the labels in this set. +// Iter returns an iterator for visiting the attributes in this set. func (l *Set) Iter() Iterator { return Iterator{ storage: l, @@ -150,18 +143,17 @@ func (l *Set) Iter() Iterator { } } -// ToSlice returns the set of labels belonging to this set, sorted, -// where keys appear no more than once. +// ToSlice returns the set of attributes belonging to this set, sorted, where +// keys appear no more than once. func (l *Set) ToSlice() []KeyValue { iter := l.Iter() return iter.ToSlice() } -// Equivalent returns a value that may be used as a map key. The -// Distinct type guarantees that the result will equal the equivalent -// Distinct value of any label set with the same elements as this, -// where sets are made unique by choosing the last value in the input -// for any given key. +// Equivalent returns a value that may be used as a map key. The Distinct type +// guarantees that the result will equal the equivalent. Distinct value of any +// attribute set with the same elements as this, where sets are made unique by +// choosing the last value in the input for any given key. func (l *Set) Equivalent() Distinct { if l == nil || !l.equivalent.Valid() { return emptySet.equivalent @@ -174,8 +166,7 @@ func (l *Set) Equals(o *Set) bool { return l.Equivalent() == o.Equivalent() } -// Encoded returns the encoded form of this set, according to -// `encoder`. +// Encoded returns the encoded form of this set, according to encoder. func (l *Set) Encoded(encoder Encoder) string { if l == nil || encoder == nil { return "" @@ -190,11 +181,11 @@ func empty() Set { } } -// NewSet returns a new `Set`. See the documentation for -// `NewSetWithSortableFiltered` for more details. +// NewSet returns a new Set. See the documentation for +// NewSetWithSortableFiltered for more details. // -// Except for empty sets, this method adds an additional allocation -// compared with calls that include a `*Sortable`. +// Except for empty sets, this method adds an additional allocation compared +// with calls that include a Sortable. func NewSet(kvs ...KeyValue) Set { // Check for empty set. if len(kvs) == 0 { @@ -204,10 +195,10 @@ func NewSet(kvs ...KeyValue) Set { return s } -// NewSetWithSortable returns a new `Set`. See the documentation for -// `NewSetWithSortableFiltered` for more details. +// NewSetWithSortable returns a new Set. See the documentation for +// NewSetWithSortableFiltered for more details. // -// This call includes a `*Sortable` option as a memory optimization. +// This call includes a Sortable option as a memory optimization. func NewSetWithSortable(kvs []KeyValue, tmp *Sortable) Set { // Check for empty set. if len(kvs) == 0 { @@ -217,12 +208,11 @@ func NewSetWithSortable(kvs []KeyValue, tmp *Sortable) Set { return s } -// NewSetWithFiltered returns a new `Set`. See the documentation for -// `NewSetWithSortableFiltered` for more details. +// NewSetWithFiltered returns a new Set. See the documentation for +// NewSetWithSortableFiltered for more details. // -// This call includes a `Filter` to include/exclude label keys from -// the return value. Excluded keys are returned as a slice of label -// values. +// This call includes a Filter to include/exclude attribute keys from the +// return value. Excluded keys are returned as a slice of attribute values. func NewSetWithFiltered(kvs []KeyValue, filter Filter) (Set, []KeyValue) { // Check for empty set. if len(kvs) == 0 { @@ -231,7 +221,7 @@ func NewSetWithFiltered(kvs []KeyValue, filter Filter) (Set, []KeyValue) { return NewSetWithSortableFiltered(kvs, new(Sortable), filter) } -// NewSetWithSortableFiltered returns a new `Set`. +// NewSetWithSortableFiltered returns a new Set. // // Duplicate keys are eliminated by taking the last value. This // re-orders the input slice so that unique last-values are contiguous @@ -243,17 +233,16 @@ func NewSetWithFiltered(kvs []KeyValue, filter Filter) (Set, []KeyValue) { // - Caller sees the reordering, but doesn't lose values // - Repeated call preserve last-value wins. // -// Note that methods are defined on `*Set`, although this returns `Set`. -// Callers can avoid memory allocations by: +// Note that methods are defined on Set, although this returns Set. Callers +// can avoid memory allocations by: // -// - allocating a `Sortable` for use as a temporary in this method -// - allocating a `Set` for storing the return value of this -// constructor. +// - allocating a Sortable for use as a temporary in this method +// - allocating a Set for storing the return value of this constructor. // -// The result maintains a cache of encoded labels, by attribute.EncoderID. +// The result maintains a cache of encoded attributes, by attribute.EncoderID. // This value should not be copied after its first use. // -// The second `[]KeyValue` return value is a list of labels that were +// The second []KeyValue return value is a list of attributes that were // excluded by the Filter (if non-nil). func NewSetWithSortableFiltered(kvs []KeyValue, tmp *Sortable, filter Filter) (Set, []KeyValue) { // Check for empty set. @@ -293,13 +282,13 @@ func NewSetWithSortableFiltered(kvs []KeyValue, tmp *Sortable, filter Filter) (S }, nil } -// filterSet reorders `kvs` so that included keys are contiguous at -// the end of the slice, while excluded keys precede the included keys. +// filterSet reorders kvs so that included keys are contiguous at the end of +// the slice, while excluded keys precede the included keys. func filterSet(kvs []KeyValue, filter Filter) (Set, []KeyValue) { var excluded []KeyValue - // Move labels that do not match the filter so - // they're adjacent before calling computeDistinct(). + // Move attributes that do not match the filter so they're adjacent before + // calling computeDistinct(). distinctPosition := len(kvs) // Swap indistinct keys forward and distinct keys toward the @@ -319,8 +308,8 @@ func filterSet(kvs []KeyValue, filter Filter) (Set, []KeyValue) { }, excluded } -// Filter returns a filtered copy of this `Set`. See the -// documentation for `NewSetWithSortableFiltered` for more details. +// Filter returns a filtered copy of this Set. See the documentation for +// NewSetWithSortableFiltered for more details. func (l *Set) Filter(re Filter) (Set, []KeyValue) { if re == nil { return Set{ @@ -333,9 +322,9 @@ func (l *Set) Filter(re Filter) (Set, []KeyValue) { return filterSet(l.ToSlice(), re) } -// computeDistinct returns a `Distinct` using either the fixed- or -// reflect-oriented code path, depending on the size of the input. -// The input slice is assumed to already be sorted and de-duplicated. +// computeDistinct returns a Distinct using either the fixed- or +// reflect-oriented code path, depending on the size of the input. The input +// slice is assumed to already be sorted and de-duplicated. func computeDistinct(kvs []KeyValue) Distinct { iface := computeDistinctFixed(kvs) if iface == nil { @@ -346,8 +335,8 @@ func computeDistinct(kvs []KeyValue) Distinct { } } -// computeDistinctFixed computes a `Distinct` for small slices. It -// returns nil if the input is too large for this code path. +// computeDistinctFixed computes a Distinct for small slices. It returns nil +// if the input is too large for this code path. func computeDistinctFixed(kvs []KeyValue) interface{} { switch len(kvs) { case 1: @@ -395,8 +384,8 @@ func computeDistinctFixed(kvs []KeyValue) interface{} { } } -// computeDistinctReflect computes a `Distinct` using reflection, -// works for any size input. +// computeDistinctReflect computes a Distinct using reflection, works for any +// size input. func computeDistinctReflect(kvs []KeyValue) interface{} { at := reflect.New(reflect.ArrayOf(len(kvs), keyValueType)).Elem() for i, keyValue := range kvs { @@ -405,22 +394,31 @@ func computeDistinctReflect(kvs []KeyValue) interface{} { return at.Interface() } -// MarshalJSON returns the JSON encoding of the `*Set`. +// MarshalJSON returns the JSON encoding of the Set. func (l *Set) MarshalJSON() ([]byte, error) { return json.Marshal(l.equivalent.iface) } -// Len implements `sort.Interface`. +// MarshalLog is the marshaling function used by the logging system to represent this exporter. +func (l Set) MarshalLog() interface{} { + kvs := make(map[string]string) + for _, kv := range l.ToSlice() { + kvs[string(kv.Key)] = kv.Value.Emit() + } + return kvs +} + +// Len implements sort.Interface. func (l *Sortable) Len() int { return len(*l) } -// Swap implements `sort.Interface`. +// Swap implements sort.Interface. func (l *Sortable) Swap(i, j int) { (*l)[i], (*l)[j] = (*l)[j], (*l)[i] } -// Less implements `sort.Interface`. +// Less implements sort.Interface. func (l *Sortable) Less(i, j int) bool { return (*l)[i].Key < (*l)[j].Key } diff --git a/src/vendor/go.opentelemetry.io/otel/attribute/value.go b/src/vendor/go.opentelemetry.io/otel/attribute/value.go index 545bea50c..57899f682 100644 --- a/src/vendor/go.opentelemetry.io/otel/attribute/value.go +++ b/src/vendor/go.opentelemetry.io/otel/attribute/value.go @@ -25,7 +25,7 @@ import ( //go:generate stringer -type=Type // Type describes the type of the data Value holds. -type Type int +type Type int // nolint: revive // redefines builtin Type. // Value represents the value part in key-value pairs. type Value struct { @@ -187,7 +187,7 @@ func (v Value) AsFloat64() float64 { } // AsFloat64Slice returns the []float64 value. Make sure that the Value's type is -// INT64SLICE. +// FLOAT64SLICE. func (v Value) AsFloat64Slice() []float64 { if s, ok := v.slice.(*[]float64); ok { return *s @@ -202,7 +202,7 @@ func (v Value) AsString() string { } // AsStringSlice returns the []string value. Make sure that the Value's type is -// INT64SLICE. +// STRINGSLICE. func (v Value) AsStringSlice() []string { if s, ok := v.slice.(*[]string); ok { return *s diff --git a/src/vendor/go.opentelemetry.io/otel/baggage/baggage.go b/src/vendor/go.opentelemetry.io/otel/baggage/baggage.go index 3427c51b4..eba180e04 100644 --- a/src/vendor/go.opentelemetry.io/otel/baggage/baggage.go +++ b/src/vendor/go.opentelemetry.io/otel/baggage/baggage.go @@ -61,45 +61,63 @@ type Property struct { // hasValue indicates if a zero-value value means the property does not // have a value or if it was the zero-value. hasValue bool + + // hasData indicates whether the created property contains data or not. + // Properties that do not contain data are invalid with no other check + // required. + hasData bool } +// NewKeyProperty returns a new Property for key. +// +// If key is invalid, an error will be returned. func NewKeyProperty(key string) (Property, error) { - p := Property{} if !keyRe.MatchString(key) { - return p, fmt.Errorf("%w: %q", errInvalidKey, key) + return newInvalidProperty(), fmt.Errorf("%w: %q", errInvalidKey, key) } - p.key = key + + p := Property{key: key, hasData: true} return p, nil } +// NewKeyValueProperty returns a new Property for key with value. +// +// If key or value are invalid, an error will be returned. func NewKeyValueProperty(key, value string) (Property, error) { - p := Property{} if !keyRe.MatchString(key) { - return p, fmt.Errorf("%w: %q", errInvalidKey, key) + return newInvalidProperty(), fmt.Errorf("%w: %q", errInvalidKey, key) } if !valueRe.MatchString(value) { - return p, fmt.Errorf("%w: %q", errInvalidValue, value) + return newInvalidProperty(), fmt.Errorf("%w: %q", errInvalidValue, value) + } + + p := Property{ + key: key, + value: value, + hasValue: true, + hasData: true, } - p.key = key - p.value = value - p.hasValue = true return p, nil } +func newInvalidProperty() Property { + return Property{} +} + // parseProperty attempts to decode a Property from the passed string. It // returns an error if the input is invalid according to the W3C Baggage // specification. func parseProperty(property string) (Property, error) { - p := Property{} if property == "" { - return p, nil + return newInvalidProperty(), nil } match := propertyRe.FindStringSubmatch(property) if len(match) != 4 { - return p, fmt.Errorf("%w: %q", errInvalidProperty, property) + return newInvalidProperty(), fmt.Errorf("%w: %q", errInvalidProperty, property) } + p := Property{hasData: true} if match[1] != "" { p.key = match[1] } else { @@ -107,6 +125,7 @@ func parseProperty(property string) (Property, error) { p.value = match[3] p.hasValue = true } + return p, nil } @@ -117,6 +136,10 @@ func (p Property) validate() error { return fmt.Errorf("invalid property: %w", err) } + if !p.hasData { + return errFunc(fmt.Errorf("%w: %q", errInvalidProperty, p)) + } + if !keyRe.MatchString(p.key) { return errFunc(fmt.Errorf("%w: %q", errInvalidKey, p.key)) } @@ -134,7 +157,7 @@ func (p Property) Key() string { return p.key } -// Value returns the Property value. Additionally a boolean value is returned +// Value returns the Property value. Additionally, a boolean value is returned // indicating if the returned value is the empty if the Property has a value // that is empty or if the value is not set. func (p Property) Value() (string, bool) { @@ -220,26 +243,40 @@ func (p properties) String() string { type Member struct { key, value string properties properties + + // hasData indicates whether the created property contains data or not. + // Properties that do not contain data are invalid with no other check + // required. + hasData bool } // NewMember returns a new Member from the passed arguments. An error is // returned if the created Member would be invalid according to the W3C // Baggage specification. func NewMember(key, value string, props ...Property) (Member, error) { - m := Member{key: key, value: value, properties: properties(props).Copy()} + m := Member{ + key: key, + value: value, + properties: properties(props).Copy(), + hasData: true, + } if err := m.validate(); err != nil { - return Member{}, err + return newInvalidMember(), err } return m, nil } +func newInvalidMember() Member { + return Member{} +} + // parseMember attempts to decode a Member from the passed string. It returns // an error if the input is invalid according to the W3C Baggage // specification. func parseMember(member string) (Member, error) { if n := len(member); n > maxBytesPerMembers { - return Member{}, fmt.Errorf("%w: %d", errMemberBytes, n) + return newInvalidMember(), fmt.Errorf("%w: %d", errMemberBytes, n) } var ( @@ -254,7 +291,7 @@ func parseMember(member string) (Member, error) { for _, pStr := range strings.Split(parts[1], propertyDelimiter) { p, err := parseProperty(pStr) if err != nil { - return Member{}, err + return newInvalidMember(), err } props = append(props, p) } @@ -265,16 +302,21 @@ func parseMember(member string) (Member, error) { // Take into account a value can contain equal signs (=). kv := strings.SplitN(parts[0], keyValueDelimiter, 2) if len(kv) != 2 { - return Member{}, fmt.Errorf("%w: %q", errInvalidMember, member) + return newInvalidMember(), fmt.Errorf("%w: %q", errInvalidMember, member) } // "Leading and trailing whitespaces are allowed but MUST be trimmed // when converting the header into a data structure." - key, value = strings.TrimSpace(kv[0]), strings.TrimSpace(kv[1]) + key = strings.TrimSpace(kv[0]) + var err error + value, err = url.QueryUnescape(strings.TrimSpace(kv[1])) + if err != nil { + return newInvalidMember(), fmt.Errorf("%w: %q", err, value) + } if !keyRe.MatchString(key) { - return Member{}, fmt.Errorf("%w: %q", errInvalidKey, key) + return newInvalidMember(), fmt.Errorf("%w: %q", errInvalidKey, key) } if !valueRe.MatchString(value) { - return Member{}, fmt.Errorf("%w: %q", errInvalidValue, value) + return newInvalidMember(), fmt.Errorf("%w: %q", errInvalidValue, value) } default: // This should never happen unless a developer has changed the string @@ -283,12 +325,16 @@ func parseMember(member string) (Member, error) { panic("failed to parse baggage member") } - return Member{key: key, value: value, properties: props}, nil + return Member{key: key, value: value, properties: props, hasData: true}, nil } // validate ensures m conforms to the W3C Baggage specification, returning an // error otherwise. func (m Member) validate() error { + if !m.hasData { + return fmt.Errorf("%w: %q", errInvalidMember, m) + } + if !keyRe.MatchString(m.key) { return fmt.Errorf("%w: %q", errInvalidKey, m.key) } @@ -324,9 +370,10 @@ type Baggage struct { //nolint:golint list baggage.List } -// New returns a new valid Baggage. It returns an error if the passed members -// are invalid according to the W3C Baggage specification or if it results in -// a Baggage exceeding limits set in that specification. +// New returns a new valid Baggage. It returns an error if it results in a +// Baggage exceeding limits set in that specification. +// +// It expects all the provided members to have already been validated. func New(members ...Member) (Baggage, error) { if len(members) == 0 { return Baggage{}, nil @@ -334,9 +381,10 @@ func New(members ...Member) (Baggage, error) { b := make(baggage.List) for _, m := range members { - if err := m.validate(); err != nil { - return Baggage{}, err + if !m.hasData { + return Baggage{}, errInvalidMember } + // OpenTelemetry resolves duplicates by last-one-wins. b[m.key] = baggage.Item{ Value: m.value, @@ -344,7 +392,7 @@ func New(members ...Member) (Baggage, error) { } } - // Check member numbers after deduplicating. + // Check member numbers after deduplication. if len(b) > maxMembers { return Baggage{}, errMemberNumber } @@ -401,14 +449,16 @@ func Parse(bStr string) (Baggage, error) { // // If there is no list-member matching the passed key the returned Member will // be a zero-value Member. +// The returned member is not validated, as we assume the validation happened +// when it was added to the Baggage. func (b Baggage) Member(key string) Member { v, ok := b.list[key] if !ok { - // We do not need to worry about distiguising between the situation + // We do not need to worry about distinguishing between the situation // where a zero-valued Member is included in the Baggage because a // zero-valued Member is invalid according to the W3C Baggage // specification (it has an empty key). - return Member{} + return newInvalidMember() } return Member{ @@ -420,6 +470,9 @@ func (b Baggage) Member(key string) Member { // Members returns all the baggage list-members. // The order of the returned list-members does not have significance. +// +// The returned members are not validated, as we assume the validation happened +// when they were added to the Baggage. func (b Baggage) Members() []Member { if len(b.list) == 0 { return nil @@ -443,8 +496,8 @@ func (b Baggage) Members() []Member { // If member is invalid according to the W3C Baggage specification, an error // is returned with the original Baggage. func (b Baggage) SetMember(member Member) (Baggage, error) { - if err := member.validate(); err != nil { - return b, fmt.Errorf("%w: %s", errInvalidMember, err) + if !member.hasData { + return b, errInvalidMember } n := len(b.list) diff --git a/src/vendor/go.opentelemetry.io/otel/handler.go b/src/vendor/go.opentelemetry.io/otel/handler.go index 35263e01a..36cf09f72 100644 --- a/src/vendor/go.opentelemetry.io/otel/handler.go +++ b/src/vendor/go.opentelemetry.io/otel/handler.go @@ -56,7 +56,6 @@ func defaultErrorHandler() *delegator { lock: &sync.RWMutex{}, eh: &errLogger{l: log.New(os.Stderr, "", log.LstdFlags)}, } - } // errLogger logs errors if no delegate is set, otherwise they are delegated. @@ -92,7 +91,7 @@ func SetErrorHandler(h ErrorHandler) { globalErrorHandler.setDelegate(h) } -// Handle is a convenience function for ErrorHandler().Handle(err) +// Handle is a convenience function for ErrorHandler().Handle(err). func Handle(err error) { GetErrorHandler().Handle(err) } diff --git a/src/vendor/go.opentelemetry.io/otel/internal/baggage/context.go b/src/vendor/go.opentelemetry.io/otel/internal/baggage/context.go index 3c2784eea..4469700d9 100644 --- a/src/vendor/go.opentelemetry.io/otel/internal/baggage/context.go +++ b/src/vendor/go.opentelemetry.io/otel/internal/baggage/context.go @@ -39,8 +39,7 @@ type baggageState struct { // Passing nil SetHookFunc creates a context with no set hook to call. func ContextWithSetHook(parent context.Context, hook SetHookFunc) context.Context { var s baggageState - switch v := parent.Value(baggageKey).(type) { - case baggageState: + if v, ok := parent.Value(baggageKey).(baggageState); ok { s = v } @@ -54,8 +53,7 @@ func ContextWithSetHook(parent context.Context, hook SetHookFunc) context.Contex // Passing nil GetHookFunc creates a context with no get hook to call. func ContextWithGetHook(parent context.Context, hook GetHookFunc) context.Context { var s baggageState - switch v := parent.Value(baggageKey).(type) { - case baggageState: + if v, ok := parent.Value(baggageKey).(baggageState); ok { s = v } @@ -67,8 +65,7 @@ func ContextWithGetHook(parent context.Context, hook GetHookFunc) context.Contex // returns a context without any baggage. func ContextWithList(parent context.Context, list List) context.Context { var s baggageState - switch v := parent.Value(baggageKey).(type) { - case baggageState: + if v, ok := parent.Value(baggageKey).(baggageState); ok { s = v } diff --git a/src/vendor/go.opentelemetry.io/otel/internal/global/internal_logging.go b/src/vendor/go.opentelemetry.io/otel/internal/global/internal_logging.go index 0a378476b..ccb325871 100644 --- a/src/vendor/go.opentelemetry.io/otel/internal/global/internal_logging.go +++ b/src/vendor/go.opentelemetry.io/otel/internal/global/internal_logging.go @@ -33,7 +33,7 @@ var globalLoggerLock = &sync.RWMutex{} // SetLogger overrides the globalLogger with l. // // To see Info messages use a logger with `l.V(1).Enabled() == true` -// To see Debug messages use a logger with `l.V(5).Enabled() == true` +// To see Debug messages use a logger with `l.V(5).Enabled() == true`. func SetLogger(l logr.Logger) { globalLoggerLock.Lock() defer globalLoggerLock.Unlock() @@ -41,7 +41,7 @@ func SetLogger(l logr.Logger) { } // Info prints messages about the general state of the API or SDK. -// This should usually be less then 5 messages a minute +// This should usually be less then 5 messages a minute. func Info(msg string, keysAndValues ...interface{}) { globalLoggerLock.RLock() defer globalLoggerLock.RUnlock() diff --git a/src/vendor/go.opentelemetry.io/otel/internal/global/state.go b/src/vendor/go.opentelemetry.io/otel/internal/global/state.go index d6b3e900c..1ad38f828 100644 --- a/src/vendor/go.opentelemetry.io/otel/internal/global/state.go +++ b/src/vendor/go.opentelemetry.io/otel/internal/global/state.go @@ -15,6 +15,7 @@ package global // import "go.opentelemetry.io/otel/internal/global" import ( + "errors" "sync" "sync/atomic" @@ -47,17 +48,24 @@ func TracerProvider() trace.TracerProvider { // SetTracerProvider is the internal implementation for global.SetTracerProvider. func SetTracerProvider(tp trace.TracerProvider) { + current := TracerProvider() + + if _, cOk := current.(*tracerProvider); cOk { + if _, tpOk := tp.(*tracerProvider); tpOk && current == tp { + // Do not assign the default delegating TracerProvider to delegate + // to itself. + Error( + errors.New("no delegate configured in tracer provider"), + "Setting tracer provider to it's current value. No delegate will be configured", + ) + return + } + } + delegateTraceOnce.Do(func() { - current := TracerProvider() - if current == tp { - // Setting the provider to the prior default is nonsense, panic. - // Panic is acceptable because we are likely still early in the - // process lifetime. - panic("invalid TracerProvider, the global instance cannot be reinstalled") - } else if def, ok := current.(*tracerProvider); ok { + if def, ok := current.(*tracerProvider); ok { def.setDelegate(tp) } - }) globalTracer.Store(tracerProviderHolder{tp: tp}) } @@ -69,15 +77,24 @@ func TextMapPropagator() propagation.TextMapPropagator { // SetTextMapPropagator is the internal implementation for global.SetTextMapPropagator. func SetTextMapPropagator(p propagation.TextMapPropagator) { + current := TextMapPropagator() + + if _, cOk := current.(*textMapPropagator); cOk { + if _, pOk := p.(*textMapPropagator); pOk && current == p { + // Do not assign the default delegating TextMapPropagator to + // delegate to itself. + Error( + errors.New("no delegate configured in text map propagator"), + "Setting text map propagator to it's current value. No delegate will be configured", + ) + return + } + } + // For the textMapPropagator already returned by TextMapPropagator // delegate to p. delegateTextMapPropagatorOnce.Do(func() { - if current := TextMapPropagator(); current == p { - // Setting the provider to the prior default is nonsense, panic. - // Panic is acceptable because we are likely still early in the - // process lifetime. - panic("invalid TextMapPropagator, the global instance cannot be reinstalled") - } else if def, ok := current.(*textMapPropagator); ok { + if def, ok := current.(*textMapPropagator); ok { def.SetDelegate(p) } }) @@ -96,11 +113,3 @@ func defaultPropagatorsValue() *atomic.Value { v.Store(propagatorsHolder{tm: newTextMapPropagator()}) return v } - -// ResetForTest restores the initial global state, for testing purposes. -func ResetForTest() { - globalTracer = defaultTracerValue() - globalPropagators = defaultPropagatorsValue() - delegateTraceOnce = sync.Once{} - delegateTextMapPropagatorOnce = sync.Once{} -} diff --git a/src/vendor/go.opentelemetry.io/otel/internal/rawhelpers.go b/src/vendor/go.opentelemetry.io/otel/internal/rawhelpers.go index ce7afaa18..e07e79400 100644 --- a/src/vendor/go.opentelemetry.io/otel/internal/rawhelpers.go +++ b/src/vendor/go.opentelemetry.io/otel/internal/rawhelpers.go @@ -19,7 +19,7 @@ import ( "unsafe" ) -func BoolToRaw(b bool) uint64 { +func BoolToRaw(b bool) uint64 { // nolint:revive // b is not a control flag. if b { return 1 } diff --git a/src/vendor/go.opentelemetry.io/otel/pre_release.sh b/src/vendor/go.opentelemetry.io/otel/pre_release.sh deleted file mode 100644 index 0de22169c..000000000 --- a/src/vendor/go.opentelemetry.io/otel/pre_release.sh +++ /dev/null @@ -1,95 +0,0 @@ -#!/bin/bash - -# Copyright The OpenTelemetry 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. - -set -e - -help() -{ - printf "\n" - printf "Usage: $0 -t tag\n" - printf "\t-t Unreleased tag. Update all go.mod with this tag.\n" - exit 1 # Exit script after printing help -} - -while getopts "t:" opt -do - case "$opt" in - t ) TAG="$OPTARG" ;; - ? ) help ;; # Print help - esac -done - -# Print help in case parameters are empty -if [ -z "$TAG" ] -then - printf "Tag is missing\n"; - help -fi - -# Validate semver -SEMVER_REGEX="^v(0|[1-9][0-9]*)\\.(0|[1-9][0-9]*)\\.(0|[1-9][0-9]*)(\\-[0-9A-Za-z-]+(\\.[0-9A-Za-z-]+)*)?(\\+[0-9A-Za-z-]+(\\.[0-9A-Za-z-]+)*)?$" -if [[ "${TAG}" =~ ${SEMVER_REGEX} ]]; then - printf "${TAG} is valid semver tag.\n" -else - printf "${TAG} is not a valid semver tag.\n" - exit -1 -fi - -TAG_FOUND=`git tag --list ${TAG}` -if [[ ${TAG_FOUND} = ${TAG} ]] ; then - printf "Tag ${TAG} already exists\n" - exit -1 -fi - -# Get version for version.go -OTEL_VERSION=$(echo "${TAG}" | grep -o '^v[0-9]\+\.[0-9]\+\.[0-9]\+') -# Strip leading v -OTEL_VERSION="${OTEL_VERSION#v}" - -cd $(dirname $0) - -if ! git diff --quiet; then \ - printf "Working tree is not clean, can't proceed with the release process\n" - git status - git diff - exit 1 -fi - -# Update version.go -cp ./version.go ./version.go.bak -sed "s/\(return \"\)[0-9]*\.[0-9]*\.[0-9]*\"/\1${OTEL_VERSION}\"/" ./version.go.bak >./version.go -rm -f ./version.go.bak - -# Update go.mod -git checkout -b pre_release_${TAG} main -PACKAGE_DIRS=$(find . -mindepth 2 -type f -name 'go.mod' -exec dirname {} \; | egrep -v 'tools' | sed 's/^\.\///' | sort) - -for dir in $PACKAGE_DIRS; do - cp "${dir}/go.mod" "${dir}/go.mod.bak" - sed "s/opentelemetry.io\/otel\([^ ]*\) v[0-9]*\.[0-9]*\.[0-9]/opentelemetry.io\/otel\1 ${TAG}/" "${dir}/go.mod.bak" >"${dir}/go.mod" - rm -f "${dir}/go.mod.bak" -done - -# Run lint to update go.sum -make lint - -# Add changes and commit. -git add . -make ci -git commit -m "Prepare for releasing $TAG" - -printf "Now run following to verify the changes.\ngit diff main\n" -printf "\nThen push the changes to upstream\n" diff --git a/src/vendor/go.opentelemetry.io/otel/sdk/instrumentation/library.go b/src/vendor/go.opentelemetry.io/otel/sdk/instrumentation/library.go index 6f0016169..246873345 100644 --- a/src/vendor/go.opentelemetry.io/otel/sdk/instrumentation/library.go +++ b/src/vendor/go.opentelemetry.io/otel/sdk/instrumentation/library.go @@ -22,12 +22,5 @@ For more information see package instrumentation // import "go.opentelemetry.io/otel/sdk/instrumentation" // Library represents the instrumentation library. -type Library struct { - // Name is the name of the instrumentation library. This should be the - // Go package name of that library. - Name string - // Version is the version of the instrumentation library. - Version string - // SchemaURL of the telemetry emitted by the library. - SchemaURL string -} +// Deprecated: please use Scope instead. +type Library = Scope diff --git a/src/vendor/go.opentelemetry.io/otel/sdk/instrumentation/scope.go b/src/vendor/go.opentelemetry.io/otel/sdk/instrumentation/scope.go new file mode 100644 index 000000000..775de40e3 --- /dev/null +++ b/src/vendor/go.opentelemetry.io/otel/sdk/instrumentation/scope.go @@ -0,0 +1,33 @@ +// Copyright The OpenTelemetry 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 instrumentation provides an instrumentation scope structure to be +passed to both the OpenTelemetry Tracer and Meter components. + +For more information see +[this](https://github.com/open-telemetry/oteps/blob/main/text/0083-component.md). +*/ +package instrumentation // import "go.opentelemetry.io/otel/sdk/instrumentation" + +// Scope represents the instrumentation scope. +type Scope struct { + // Name is the name of the instrumentation scope. This should be the + // Go package name of that scope. + Name string + // Version is the version of the instrumentation scope. + Version string + // SchemaURL of the telemetry emitted by the scope. + SchemaURL string +} diff --git a/src/vendor/go.opentelemetry.io/otel/sdk/internal/env/env.go b/src/vendor/go.opentelemetry.io/otel/sdk/internal/env/env.go new file mode 100644 index 000000000..5e94b8ae5 --- /dev/null +++ b/src/vendor/go.opentelemetry.io/otel/sdk/internal/env/env.go @@ -0,0 +1,177 @@ +// Copyright The OpenTelemetry 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 env // import "go.opentelemetry.io/otel/sdk/internal/env" + +import ( + "os" + "strconv" + + "go.opentelemetry.io/otel/internal/global" +) + +// Environment variable names. +const ( + // BatchSpanProcessorScheduleDelayKey is the delay interval between two + // consecutive exports (i.e. 5000). + BatchSpanProcessorScheduleDelayKey = "OTEL_BSP_SCHEDULE_DELAY" + // BatchSpanProcessorExportTimeoutKey is the maximum allowed time to + // export data (i.e. 3000). + BatchSpanProcessorExportTimeoutKey = "OTEL_BSP_EXPORT_TIMEOUT" + // BatchSpanProcessorMaxQueueSizeKey is the maximum queue size (i.e. 2048). + BatchSpanProcessorMaxQueueSizeKey = "OTEL_BSP_MAX_QUEUE_SIZE" + // BatchSpanProcessorMaxExportBatchSizeKey is the maximum batch size (i.e. + // 512). Note: it must be less than or equal to + // EnvBatchSpanProcessorMaxQueueSize. + BatchSpanProcessorMaxExportBatchSizeKey = "OTEL_BSP_MAX_EXPORT_BATCH_SIZE" + + // AttributeValueLengthKey is the maximum allowed attribute value size. + AttributeValueLengthKey = "OTEL_ATTRIBUTE_VALUE_LENGTH_LIMIT" + + // AttributeCountKey is the maximum allowed span attribute count. + AttributeCountKey = "OTEL_ATTRIBUTE_COUNT_LIMIT" + + // SpanAttributeValueLengthKey is the maximum allowed attribute value size + // for a span. + SpanAttributeValueLengthKey = "OTEL_SPAN_ATTRIBUTE_VALUE_LENGTH_LIMIT" + + // SpanAttributeCountKey is the maximum allowed span attribute count for a + // span. + SpanAttributeCountKey = "OTEL_SPAN_ATTRIBUTE_COUNT_LIMIT" + + // SpanEventCountKey is the maximum allowed span event count. + SpanEventCountKey = "OTEL_SPAN_EVENT_COUNT_LIMIT" + + // SpanEventAttributeCountKey is the maximum allowed attribute per span + // event count. + SpanEventAttributeCountKey = "OTEL_EVENT_ATTRIBUTE_COUNT_LIMIT" + + // SpanLinkCountKey is the maximum allowed span link count. + SpanLinkCountKey = "OTEL_SPAN_LINK_COUNT_LIMIT" + + // SpanLinkAttributeCountKey is the maximum allowed attribute per span + // link count. + SpanLinkAttributeCountKey = "OTEL_LINK_ATTRIBUTE_COUNT_LIMIT" +) + +// firstInt returns the value of the first matching environment variable from +// keys. If the value is not an integer or no match is found, defaultValue is +// returned. +func firstInt(defaultValue int, keys ...string) int { + for _, key := range keys { + value, ok := os.LookupEnv(key) + if !ok { + continue + } + + intValue, err := strconv.Atoi(value) + if err != nil { + global.Info("Got invalid value, number value expected.", key, value) + return defaultValue + } + + return intValue + } + + return defaultValue +} + +// IntEnvOr returns the int value of the environment variable with name key if +// it exists and the value is an int. Otherwise, defaultValue is returned. +func IntEnvOr(key string, defaultValue int) int { + value, ok := os.LookupEnv(key) + if !ok { + return defaultValue + } + + intValue, err := strconv.Atoi(value) + if err != nil { + global.Info("Got invalid value, number value expected.", key, value) + return defaultValue + } + + return intValue +} + +// BatchSpanProcessorScheduleDelay returns the environment variable value for +// the OTEL_BSP_SCHEDULE_DELAY key if it exists, otherwise defaultValue is +// returned. +func BatchSpanProcessorScheduleDelay(defaultValue int) int { + return IntEnvOr(BatchSpanProcessorScheduleDelayKey, defaultValue) +} + +// BatchSpanProcessorExportTimeout returns the environment variable value for +// the OTEL_BSP_EXPORT_TIMEOUT key if it exists, otherwise defaultValue is +// returned. +func BatchSpanProcessorExportTimeout(defaultValue int) int { + return IntEnvOr(BatchSpanProcessorExportTimeoutKey, defaultValue) +} + +// BatchSpanProcessorMaxQueueSize returns the environment variable value for +// the OTEL_BSP_MAX_QUEUE_SIZE key if it exists, otherwise defaultValue is +// returned. +func BatchSpanProcessorMaxQueueSize(defaultValue int) int { + return IntEnvOr(BatchSpanProcessorMaxQueueSizeKey, defaultValue) +} + +// BatchSpanProcessorMaxExportBatchSize returns the environment variable value for +// the OTEL_BSP_MAX_EXPORT_BATCH_SIZE key if it exists, otherwise defaultValue +// is returned. +func BatchSpanProcessorMaxExportBatchSize(defaultValue int) int { + return IntEnvOr(BatchSpanProcessorMaxExportBatchSizeKey, defaultValue) +} + +// SpanAttributeValueLength returns the environment variable value for the +// OTEL_SPAN_ATTRIBUTE_VALUE_LENGTH_LIMIT key if it exists. Otherwise, the +// environment variable value for OTEL_ATTRIBUTE_VALUE_LENGTH_LIMIT is +// returned or defaultValue if that is not set. +func SpanAttributeValueLength(defaultValue int) int { + return firstInt(defaultValue, SpanAttributeValueLengthKey, AttributeValueLengthKey) +} + +// SpanAttributeCount returns the environment variable value for the +// OTEL_SPAN_ATTRIBUTE_COUNT_LIMIT key if it exists. Otherwise, the +// environment variable value for OTEL_ATTRIBUTE_COUNT_LIMIT is returned or +// defaultValue if that is not set. +func SpanAttributeCount(defaultValue int) int { + return firstInt(defaultValue, SpanAttributeCountKey, AttributeCountKey) +} + +// SpanEventCount returns the environment variable value for the +// OTEL_SPAN_EVENT_COUNT_LIMIT key if it exists, otherwise defaultValue is +// returned. +func SpanEventCount(defaultValue int) int { + return IntEnvOr(SpanEventCountKey, defaultValue) +} + +// SpanEventAttributeCount returns the environment variable value for the +// OTEL_EVENT_ATTRIBUTE_COUNT_LIMIT key if it exists, otherwise defaultValue +// is returned. +func SpanEventAttributeCount(defaultValue int) int { + return IntEnvOr(SpanEventAttributeCountKey, defaultValue) +} + +// SpanLinkCount returns the environment variable value for the +// OTEL_SPAN_LINK_COUNT_LIMIT key if it exists, otherwise defaultValue is +// returned. +func SpanLinkCount(defaultValue int) int { + return IntEnvOr(SpanLinkCountKey, defaultValue) +} + +// SpanLinkAttributeCount returns the environment variable value for the +// OTEL_LINK_ATTRIBUTE_COUNT_LIMIT key if it exists, otherwise defaultValue is +// returned. +func SpanLinkAttributeCount(defaultValue int) int { + return IntEnvOr(SpanLinkAttributeCountKey, defaultValue) +} diff --git a/src/vendor/go.opentelemetry.io/otel/sdk/internal/sanitize.go b/src/vendor/go.opentelemetry.io/otel/sdk/internal/sanitize.go deleted file mode 100644 index ff3518556..000000000 --- a/src/vendor/go.opentelemetry.io/otel/sdk/internal/sanitize.go +++ /dev/null @@ -1,50 +0,0 @@ -// Copyright The OpenTelemetry 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 internal // import "go.opentelemetry.io/otel/sdk/internal" - -import ( - "strings" - "unicode" -) - -const labelKeySizeLimit = 100 - -// Sanitize returns a string that is trunacated to 100 characters if it's too -// long, and replaces non-alphanumeric characters to underscores. -func Sanitize(s string) string { - if len(s) == 0 { - return s - } - if len(s) > labelKeySizeLimit { - s = s[:labelKeySizeLimit] - } - s = strings.Map(sanitizeRune, s) - if unicode.IsDigit(rune(s[0])) { - s = "key_" + s - } - if s[0] == '_' { - s = "key" + s - } - return s -} - -// converts anything that is not a letter or digit to an underscore -func sanitizeRune(r rune) rune { - if unicode.IsLetter(r) || unicode.IsDigit(r) { - return r - } - // Everything else turns into an underscore - return '_' -} diff --git a/src/vendor/go.opentelemetry.io/otel/sdk/resource/auto.go b/src/vendor/go.opentelemetry.io/otel/sdk/resource/auto.go index a5eaa7e5d..c1d220408 100644 --- a/src/vendor/go.opentelemetry.io/otel/sdk/resource/auto.go +++ b/src/vendor/go.opentelemetry.io/otel/sdk/resource/auto.go @@ -27,7 +27,7 @@ var ( ErrPartialResource = errors.New("partial resource") ) -// Detector detects OpenTelemetry resource information +// Detector detects OpenTelemetry resource information. type Detector interface { // DO NOT CHANGE: any modification will not be backwards compatible and // must never be done outside of a new major release. diff --git a/src/vendor/go.opentelemetry.io/otel/sdk/resource/builtin.go b/src/vendor/go.opentelemetry.io/otel/sdk/resource/builtin.go index 701eae40a..c5069d111 100644 --- a/src/vendor/go.opentelemetry.io/otel/sdk/resource/builtin.go +++ b/src/vendor/go.opentelemetry.io/otel/sdk/resource/builtin.go @@ -22,7 +22,7 @@ import ( "go.opentelemetry.io/otel" "go.opentelemetry.io/otel/attribute" - semconv "go.opentelemetry.io/otel/semconv/v1.7.0" + semconv "go.opentelemetry.io/otel/semconv/v1.10.0" ) type ( @@ -92,7 +92,7 @@ func (sd stringDetector) Detect(ctx context.Context) (*Resource, error) { return NewWithAttributes(sd.schemaURL, sd.K.String(value)), nil } -// Detect implements Detector +// Detect implements Detector. func (defaultServiceNameDetector) Detect(ctx context.Context) (*Resource, error) { return StringDetector( semconv.SchemaURL, diff --git a/src/vendor/go.opentelemetry.io/otel/sdk/resource/config.go b/src/vendor/go.opentelemetry.io/otel/sdk/resource/config.go index 5fa45859b..8e212b121 100644 --- a/src/vendor/go.opentelemetry.io/otel/sdk/resource/config.go +++ b/src/vendor/go.opentelemetry.io/otel/sdk/resource/config.go @@ -31,7 +31,7 @@ type config struct { // Option is the interface that applies a configuration option. type Option interface { // apply sets the Option value of a config. - apply(*config) + apply(config) config } // WithAttributes adds attributes to the configured Resource. @@ -56,8 +56,9 @@ type detectorsOption struct { detectors []Detector } -func (o detectorsOption) apply(cfg *config) { +func (o detectorsOption) apply(cfg config) config { cfg.detectors = append(cfg.detectors, o.detectors...) + return cfg } // WithFromEnv adds attributes from environment variables to the configured resource. @@ -82,8 +83,9 @@ func WithSchemaURL(schemaURL string) Option { type schemaURLOption string -func (o schemaURLOption) apply(cfg *config) { +func (o schemaURLOption) apply(cfg config) config { cfg.schemaURL = string(o) + return cfg } // WithOS adds all the OS attributes to the configured Resource. @@ -108,7 +110,16 @@ func WithOSDescription() Option { } // WithProcess adds all the Process attributes to the configured Resource. -// See individual WithProcess* functions to configure specific attributes. +// +// Warning! This option will include process command line arguments. If these +// contain sensitive information it will be included in the exported resource. +// +// This option is equivalent to calling WithProcessPID, +// WithProcessExecutableName, WithProcessExecutablePath, +// WithProcessCommandArgs, WithProcessOwner, WithProcessRuntimeName, +// WithProcessRuntimeVersion, and WithProcessRuntimeDescription. See each +// option function for information about what resource attributes each +// includes. func WithProcess() Option { return WithDetectors( processPIDDetector{}, @@ -141,7 +152,11 @@ func WithProcessExecutablePath() Option { } // WithProcessCommandArgs adds an attribute with all the command arguments (including -// the command/executable itself) as received by the process the configured Resource. +// the command/executable itself) as received by the process to the configured +// Resource. +// +// Warning! This option will include process command line arguments. If these +// contain sensitive information it will be included in the exported resource. func WithProcessCommandArgs() Option { return WithDetectors(processCommandArgsDetector{}) } @@ -169,3 +184,16 @@ func WithProcessRuntimeVersion() Option { func WithProcessRuntimeDescription() Option { return WithDetectors(processRuntimeDescriptionDetector{}) } + +// WithContainer adds all the Container attributes to the configured Resource. +// See individual WithContainer* functions to configure specific attributes. +func WithContainer() Option { + return WithDetectors( + cgroupContainerIDDetector{}, + ) +} + +// WithContainerID adds an attribute with the id of the container to the configured Resource. +func WithContainerID() Option { + return WithDetectors(cgroupContainerIDDetector{}) +} diff --git a/src/vendor/go.opentelemetry.io/otel/sdk/resource/container.go b/src/vendor/go.opentelemetry.io/otel/sdk/resource/container.go new file mode 100644 index 000000000..f19470fe3 --- /dev/null +++ b/src/vendor/go.opentelemetry.io/otel/sdk/resource/container.go @@ -0,0 +1,100 @@ +// Copyright The OpenTelemetry 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 resource // import "go.opentelemetry.io/otel/sdk/resource" + +import ( + "bufio" + "context" + "errors" + "io" + "os" + "regexp" + + semconv "go.opentelemetry.io/otel/semconv/v1.10.0" +) + +type containerIDProvider func() (string, error) + +var ( + containerID containerIDProvider = getContainerIDFromCGroup + cgroupContainerIDRe = regexp.MustCompile(`^.*/(?:.*-)?([0-9a-f]+)(?:\.|\s*$)`) +) + +type cgroupContainerIDDetector struct{} + +const cgroupPath = "/proc/self/cgroup" + +// Detect returns a *Resource that describes the id of the container. +// If no container id found, an empty resource will be returned. +func (cgroupContainerIDDetector) Detect(ctx context.Context) (*Resource, error) { + containerID, err := containerID() + if err != nil { + return nil, err + } + + if containerID == "" { + return Empty(), nil + } + return NewWithAttributes(semconv.SchemaURL, semconv.ContainerIDKey.String(containerID)), nil +} + +var ( + defaultOSStat = os.Stat + osStat = defaultOSStat + + defaultOSOpen = func(name string) (io.ReadCloser, error) { + return os.Open(name) + } + osOpen = defaultOSOpen +) + +// getContainerIDFromCGroup returns the id of the container from the cgroup file. +// If no container id found, an empty string will be returned. +func getContainerIDFromCGroup() (string, error) { + if _, err := osStat(cgroupPath); errors.Is(err, os.ErrNotExist) { + // File does not exist, skip + return "", nil + } + + file, err := osOpen(cgroupPath) + if err != nil { + return "", err + } + defer file.Close() + + return getContainerIDFromReader(file), nil +} + +// getContainerIDFromReader returns the id of the container from reader. +func getContainerIDFromReader(reader io.Reader) string { + scanner := bufio.NewScanner(reader) + for scanner.Scan() { + line := scanner.Text() + + if id := getContainerIDFromLine(line); id != "" { + return id + } + } + return "" +} + +// getContainerIDFromLine returns the id of the container from one string line. +func getContainerIDFromLine(line string) string { + matches := cgroupContainerIDRe.FindStringSubmatch(line) + if len(matches) <= 1 { + return "" + } + return matches[1] +} diff --git a/src/vendor/go.opentelemetry.io/otel/sdk/resource/env.go b/src/vendor/go.opentelemetry.io/otel/sdk/resource/env.go index 9392296cb..531a68424 100644 --- a/src/vendor/go.opentelemetry.io/otel/sdk/resource/env.go +++ b/src/vendor/go.opentelemetry.io/otel/sdk/resource/env.go @@ -21,7 +21,7 @@ import ( "strings" "go.opentelemetry.io/otel/attribute" - semconv "go.opentelemetry.io/otel/semconv/v1.7.0" + semconv "go.opentelemetry.io/otel/semconv/v1.10.0" ) const ( @@ -42,10 +42,10 @@ var ( // builtin. type fromEnv struct{} -// compile time assertion that FromEnv implements Detector interface +// compile time assertion that FromEnv implements Detector interface. var _ Detector = fromEnv{} -// Detect collects resources from environment +// Detect collects resources from environment. func (fromEnv) Detect(context.Context) (*Resource, error) { attrs := strings.TrimSpace(os.Getenv(resourceAttrKey)) svcName := strings.TrimSpace(os.Getenv(svcNameKey)) diff --git a/src/vendor/go.opentelemetry.io/otel/sdk/resource/os.go b/src/vendor/go.opentelemetry.io/otel/sdk/resource/os.go index 59329770c..a4d88fa9c 100644 --- a/src/vendor/go.opentelemetry.io/otel/sdk/resource/os.go +++ b/src/vendor/go.opentelemetry.io/otel/sdk/resource/os.go @@ -19,7 +19,7 @@ import ( "strings" "go.opentelemetry.io/otel/attribute" - semconv "go.opentelemetry.io/otel/semconv/v1.7.0" + semconv "go.opentelemetry.io/otel/semconv/v1.10.0" ) type osDescriptionProvider func() (string, error) diff --git a/src/vendor/go.opentelemetry.io/otel/sdk/resource/os_unix.go b/src/vendor/go.opentelemetry.io/otel/sdk/resource/os_unix.go index 42894a15b..1c84afc18 100644 --- a/src/vendor/go.opentelemetry.io/otel/sdk/resource/os_unix.go +++ b/src/vendor/go.opentelemetry.io/otel/sdk/resource/os_unix.go @@ -18,7 +18,6 @@ package resource // import "go.opentelemetry.io/otel/sdk/resource" import ( - "bytes" "fmt" "os" @@ -69,23 +68,14 @@ func uname() (string, error) { } return fmt.Sprintf("%s %s %s %s %s", - charsToString(utsName.Sysname[:]), - charsToString(utsName.Nodename[:]), - charsToString(utsName.Release[:]), - charsToString(utsName.Version[:]), - charsToString(utsName.Machine[:]), + unix.ByteSliceToString(utsName.Sysname[:]), + unix.ByteSliceToString(utsName.Nodename[:]), + unix.ByteSliceToString(utsName.Release[:]), + unix.ByteSliceToString(utsName.Version[:]), + unix.ByteSliceToString(utsName.Machine[:]), ), nil } -// charsToString converts a C-like null-terminated char array to a Go string. -func charsToString(charArray []byte) string { - if i := bytes.IndexByte(charArray, 0); i >= 0 { - charArray = charArray[:i] - } - - return string(charArray) -} - // getFirstAvailableFile returns an *os.File of the first available // file from a list of candidate file paths. func getFirstAvailableFile(candidates []string) (*os.File, error) { diff --git a/src/vendor/go.opentelemetry.io/otel/sdk/resource/process.go b/src/vendor/go.opentelemetry.io/otel/sdk/resource/process.go index 80d5e6993..ad262e0ba 100644 --- a/src/vendor/go.opentelemetry.io/otel/sdk/resource/process.go +++ b/src/vendor/go.opentelemetry.io/otel/sdk/resource/process.go @@ -22,7 +22,7 @@ import ( "path/filepath" "runtime" - semconv "go.opentelemetry.io/otel/semconv/v1.7.0" + semconv "go.opentelemetry.io/otel/semconv/v1.10.0" ) type pidProvider func() int @@ -39,7 +39,12 @@ var ( defaultExecutablePathProvider executablePathProvider = os.Executable defaultCommandArgsProvider commandArgsProvider = func() []string { return os.Args } defaultOwnerProvider ownerProvider = user.Current - defaultRuntimeNameProvider runtimeNameProvider = func() string { return runtime.Compiler } + defaultRuntimeNameProvider runtimeNameProvider = func() string { + if runtime.Compiler == "gc" { + return "go" + } + return runtime.Compiler + } defaultRuntimeVersionProvider runtimeVersionProvider = runtime.Version defaultRuntimeOSProvider runtimeOSProvider = func() string { return runtime.GOOS } defaultRuntimeArchProvider runtimeArchProvider = func() string { return runtime.GOARCH } diff --git a/src/vendor/go.opentelemetry.io/otel/sdk/resource/resource.go b/src/vendor/go.opentelemetry.io/otel/sdk/resource/resource.go index eb0ecd2cf..c425ff05d 100644 --- a/src/vendor/go.opentelemetry.io/otel/sdk/resource/resource.go +++ b/src/vendor/go.opentelemetry.io/otel/sdk/resource/resource.go @@ -48,7 +48,7 @@ var errMergeConflictSchemaURL = errors.New("cannot merge resource due to conflic func New(ctx context.Context, opts ...Option) (*Resource, error) { cfg := config{} for _, opt := range opts { - opt.apply(&cfg) + cfg = opt.apply(cfg) } resource, err := Detect(ctx, cfg.detectors...) @@ -109,6 +109,17 @@ func (r *Resource) String() string { return r.attrs.Encoded(attribute.DefaultEncoder()) } +// MarshalLog is the marshaling function used by the logging system to represent this exporter. +func (r *Resource) MarshalLog() interface{} { + return struct { + Attributes attribute.Set + SchemaURL string + }{ + Attributes: r.attrs, + SchemaURL: r.schemaURL, + } +} + // Attributes returns a copy of attributes from the resource in a sorted order. // To avoid allocating a new slice, use an iterator. func (r *Resource) Attributes() []attribute.KeyValue { @@ -118,6 +129,7 @@ func (r *Resource) Attributes() []attribute.KeyValue { return r.attrs.ToSlice() } +// SchemaURL returns the schema URL associated with Resource r. func (r *Resource) SchemaURL() string { if r == nil { return "" @@ -168,13 +180,14 @@ func Merge(a, b *Resource) (*Resource, error) { // Merge the schema URL. var schemaURL string - if a.schemaURL == "" { + switch true { + case a.schemaURL == "": schemaURL = b.schemaURL - } else if b.schemaURL == "" { + case b.schemaURL == "": schemaURL = a.schemaURL - } else if a.schemaURL == b.schemaURL { + case a.schemaURL == b.schemaURL: schemaURL = a.schemaURL - } else { + default: return Empty(), errMergeConflictSchemaURL } @@ -183,7 +196,7 @@ func Merge(a, b *Resource) (*Resource, error) { mi := attribute.NewMergeIterator(b.Set(), a.Set()) combine := make([]attribute.KeyValue, 0, a.Len()+b.Len()) for mi.Next() { - combine = append(combine, mi.Label()) + combine = append(combine, mi.Attribute()) } merged := NewWithAttributes(schemaURL, combine...) return merged, nil diff --git a/src/vendor/go.opentelemetry.io/otel/sdk/trace/attributesmap.go b/src/vendor/go.opentelemetry.io/otel/sdk/trace/attributesmap.go deleted file mode 100644 index b891c8178..000000000 --- a/src/vendor/go.opentelemetry.io/otel/sdk/trace/attributesmap.go +++ /dev/null @@ -1,91 +0,0 @@ -// Copyright The OpenTelemetry 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 trace // import "go.opentelemetry.io/otel/sdk/trace" - -import ( - "container/list" - - "go.opentelemetry.io/otel/attribute" -) - -// attributesMap is a capped map of attributes, holding the most recent attributes. -// Eviction is done via a LRU method, the oldest entry is removed to create room for a new entry. -// Updates are allowed and they refresh the usage of the key. -// -// This is based from https://github.com/hashicorp/golang-lru/blob/master/simplelru/lru.go -// With a subset of the its operations and specific for holding attribute.KeyValue -type attributesMap struct { - attributes map[attribute.Key]*list.Element - evictList *list.List - droppedCount int - capacity int -} - -func newAttributesMap(capacity int) *attributesMap { - lm := &attributesMap{ - attributes: make(map[attribute.Key]*list.Element), - evictList: list.New(), - capacity: capacity, - } - return lm -} - -func (am *attributesMap) add(kv attribute.KeyValue) { - // Check for existing item - if ent, ok := am.attributes[kv.Key]; ok { - am.evictList.MoveToFront(ent) - ent.Value = &kv - return - } - - // Add new item - entry := am.evictList.PushFront(&kv) - am.attributes[kv.Key] = entry - - // Verify size not exceeded - if am.evictList.Len() > am.capacity { - am.removeOldest() - am.droppedCount++ - } -} - -// toKeyValue copies the attributesMap into a slice of attribute.KeyValue and -// returns it. If the map is empty, a nil is returned. -// TODO: Is it more efficient to return a pointer to the slice? -func (am *attributesMap) toKeyValue() []attribute.KeyValue { - len := am.evictList.Len() - if len == 0 { - return nil - } - - attributes := make([]attribute.KeyValue, 0, len) - for ent := am.evictList.Back(); ent != nil; ent = ent.Prev() { - if value, ok := ent.Value.(*attribute.KeyValue); ok { - attributes = append(attributes, *value) - } - } - - return attributes -} - -// removeOldest removes the oldest item from the cache. -func (am *attributesMap) removeOldest() { - ent := am.evictList.Back() - if ent != nil { - am.evictList.Remove(ent) - kv := ent.Value.(*attribute.KeyValue) - delete(am.attributes, kv.Key) - } -} diff --git a/src/vendor/go.opentelemetry.io/otel/sdk/trace/batch_span_processor.go b/src/vendor/go.opentelemetry.io/otel/sdk/trace/batch_span_processor.go index 75f519a67..a2d7db490 100644 --- a/src/vendor/go.opentelemetry.io/otel/sdk/trace/batch_span_processor.go +++ b/src/vendor/go.opentelemetry.io/otel/sdk/trace/batch_span_processor.go @@ -23,19 +23,23 @@ import ( "go.opentelemetry.io/otel" "go.opentelemetry.io/otel/internal/global" + "go.opentelemetry.io/otel/sdk/internal/env" "go.opentelemetry.io/otel/trace" ) // Defaults for BatchSpanProcessorOptions. const ( DefaultMaxQueueSize = 2048 - DefaultBatchTimeout = 5000 * time.Millisecond - DefaultExportTimeout = 30000 * time.Millisecond + DefaultScheduleDelay = 5000 + DefaultExportTimeout = 30000 DefaultMaxExportBatchSize = 512 ) +// BatchSpanProcessorOption configures a BatchSpanProcessor. type BatchSpanProcessorOption func(o *BatchSpanProcessorOptions) +// BatchSpanProcessorOptions is configuration settings for a +// BatchSpanProcessor. type BatchSpanProcessorOptions struct { // MaxQueueSize is the maximum queue size to buffer spans for delayed processing. If the // queue gets full it drops the spans. Use BlockOnQueueFull to change this behavior. @@ -89,11 +93,22 @@ var _ SpanProcessor = (*batchSpanProcessor)(nil) // // If the exporter is nil, the span processor will preform no action. func NewBatchSpanProcessor(exporter SpanExporter, options ...BatchSpanProcessorOption) SpanProcessor { + maxQueueSize := env.BatchSpanProcessorMaxQueueSize(DefaultMaxQueueSize) + maxExportBatchSize := env.BatchSpanProcessorMaxExportBatchSize(DefaultMaxExportBatchSize) + + if maxExportBatchSize > maxQueueSize { + if DefaultMaxExportBatchSize > maxQueueSize { + maxExportBatchSize = maxQueueSize + } else { + maxExportBatchSize = DefaultMaxExportBatchSize + } + } + o := BatchSpanProcessorOptions{ - BatchTimeout: DefaultBatchTimeout, - ExportTimeout: DefaultExportTimeout, - MaxQueueSize: DefaultMaxQueueSize, - MaxExportBatchSize: DefaultMaxExportBatchSize, + BatchTimeout: time.Duration(env.BatchSpanProcessorScheduleDelay(DefaultScheduleDelay)) * time.Millisecond, + ExportTimeout: time.Duration(env.BatchSpanProcessorExportTimeout(DefaultExportTimeout)) * time.Millisecond, + MaxQueueSize: maxQueueSize, + MaxExportBatchSize: maxExportBatchSize, } for _, opt := range options { opt(&o) @@ -169,7 +184,7 @@ func (bsp *batchSpanProcessor) ForceFlush(ctx context.Context) error { var err error if bsp.e != nil { flushCh := make(chan struct{}) - if bsp.enqueueBlockOnQueueFull(ctx, forceFlushSpan{flushed: flushCh}, true) { + if bsp.enqueueBlockOnQueueFull(ctx, forceFlushSpan{flushed: flushCh}) { select { case <-flushCh: // Processed any items in queue prior to ForceFlush being called @@ -193,30 +208,43 @@ func (bsp *batchSpanProcessor) ForceFlush(ctx context.Context) error { return err } +// WithMaxQueueSize returns a BatchSpanProcessorOption that configures the +// maximum queue size allowed for a BatchSpanProcessor. func WithMaxQueueSize(size int) BatchSpanProcessorOption { return func(o *BatchSpanProcessorOptions) { o.MaxQueueSize = size } } +// WithMaxExportBatchSize returns a BatchSpanProcessorOption that configures +// the maximum export batch size allowed for a BatchSpanProcessor. func WithMaxExportBatchSize(size int) BatchSpanProcessorOption { return func(o *BatchSpanProcessorOptions) { o.MaxExportBatchSize = size } } +// WithBatchTimeout returns a BatchSpanProcessorOption that configures the +// maximum delay allowed for a BatchSpanProcessor before it will export any +// held span (whether the queue is full or not). func WithBatchTimeout(delay time.Duration) BatchSpanProcessorOption { return func(o *BatchSpanProcessorOptions) { o.BatchTimeout = delay } } +// WithExportTimeout returns a BatchSpanProcessorOption that configures the +// amount of time a BatchSpanProcessor waits for an exporter to export before +// abandoning the export. func WithExportTimeout(timeout time.Duration) BatchSpanProcessorOption { return func(o *BatchSpanProcessorOptions) { o.ExportTimeout = timeout } } +// WithBlocking returns a BatchSpanProcessorOption that configures a +// BatchSpanProcessor to wait for enqueue operations to succeed instead of +// dropping data when the queue is full. func WithBlocking() BatchSpanProcessorOption { return func(o *BatchSpanProcessorOptions) { o.BlockOnQueueFull = true @@ -225,7 +253,6 @@ func WithBlocking() BatchSpanProcessorOption { // exportSpans is a subroutine of processing and draining the queue. func (bsp *batchSpanProcessor) exportSpans(ctx context.Context) error { - bsp.timer.Reset(bsp.o.BatchTimeout) bsp.batchMutex.Lock() @@ -238,7 +265,7 @@ func (bsp *batchSpanProcessor) exportSpans(ctx context.Context) error { } if l := len(bsp.batch); l > 0 { - global.Debug("exporting spans", "count", len(bsp.batch)) + global.Debug("exporting spans", "count", len(bsp.batch), "total_dropped", atomic.LoadUint32(&bsp.dropped)) err := bsp.e.ExportSpans(ctx, bsp.batch) // A new batch is always created after exporting, even if the batch failed to be exported. @@ -323,28 +350,35 @@ func (bsp *batchSpanProcessor) drainQueue() { } func (bsp *batchSpanProcessor) enqueue(sd ReadOnlySpan) { - bsp.enqueueBlockOnQueueFull(context.TODO(), sd, bsp.o.BlockOnQueueFull) + ctx := context.TODO() + if bsp.o.BlockOnQueueFull { + bsp.enqueueBlockOnQueueFull(ctx, sd) + } else { + bsp.enqueueDrop(ctx, sd) + } } -func (bsp *batchSpanProcessor) enqueueBlockOnQueueFull(ctx context.Context, sd ReadOnlySpan, block bool) bool { +func recoverSendOnClosedChan() { + x := recover() + switch err := x.(type) { + case nil: + return + case runtime.Error: + if err.Error() == "send on closed channel" { + return + } + } + panic(x) +} + +func (bsp *batchSpanProcessor) enqueueBlockOnQueueFull(ctx context.Context, sd ReadOnlySpan) bool { if !sd.SpanContext().IsSampled() { return false } // This ensures the bsp.queue<- below does not panic as the // processor shuts down. - defer func() { - x := recover() - switch err := x.(type) { - case nil: - return - case runtime.Error: - if err.Error() == "send on closed channel" { - return - } - } - panic(x) - }() + defer recoverSendOnClosedChan() select { case <-bsp.stopCh: @@ -352,13 +386,27 @@ func (bsp *batchSpanProcessor) enqueueBlockOnQueueFull(ctx context.Context, sd R default: } - if block { - select { - case bsp.queue <- sd: - return true - case <-ctx.Done(): - return false - } + select { + case bsp.queue <- sd: + return true + case <-ctx.Done(): + return false + } +} + +func (bsp *batchSpanProcessor) enqueueDrop(ctx context.Context, sd ReadOnlySpan) bool { + if !sd.SpanContext().IsSampled() { + return false + } + + // This ensures the bsp.queue<- below does not panic as the + // processor shuts down. + defer recoverSendOnClosedChan() + + select { + case <-bsp.stopCh: + return false + default: } select { @@ -369,3 +417,16 @@ func (bsp *batchSpanProcessor) enqueueBlockOnQueueFull(ctx context.Context, sd R } return false } + +// MarshalLog is the marshaling function used by the logging system to represent this exporter. +func (bsp *batchSpanProcessor) MarshalLog() interface{} { + return struct { + Type string + SpanExporter SpanExporter + Config BatchSpanProcessorOptions + }{ + Type: "BatchSpanProcessor", + SpanExporter: bsp.e, + Config: bsp.o, + } +} diff --git a/src/vendor/go.opentelemetry.io/otel/sdk/trace/config.go b/src/vendor/go.opentelemetry.io/otel/sdk/trace/config.go deleted file mode 100644 index 61a304392..000000000 --- a/src/vendor/go.opentelemetry.io/otel/sdk/trace/config.go +++ /dev/null @@ -1,68 +0,0 @@ -// Copyright The OpenTelemetry 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 trace // import "go.opentelemetry.io/otel/sdk/trace" - -// SpanLimits represents the limits of a span. -type SpanLimits struct { - // AttributeCountLimit is the maximum allowed span attribute count. - AttributeCountLimit int - - // EventCountLimit is the maximum allowed span event count. - EventCountLimit int - - // LinkCountLimit is the maximum allowed span link count. - LinkCountLimit int - - // AttributePerEventCountLimit is the maximum allowed attribute per span event count. - AttributePerEventCountLimit int - - // AttributePerLinkCountLimit is the maximum allowed attribute per span link count. - AttributePerLinkCountLimit int -} - -func (sl *SpanLimits) ensureDefault() { - if sl.EventCountLimit <= 0 { - sl.EventCountLimit = DefaultEventCountLimit - } - if sl.AttributeCountLimit <= 0 { - sl.AttributeCountLimit = DefaultAttributeCountLimit - } - if sl.LinkCountLimit <= 0 { - sl.LinkCountLimit = DefaultLinkCountLimit - } - if sl.AttributePerEventCountLimit <= 0 { - sl.AttributePerEventCountLimit = DefaultAttributePerEventCountLimit - } - if sl.AttributePerLinkCountLimit <= 0 { - sl.AttributePerLinkCountLimit = DefaultAttributePerLinkCountLimit - } -} - -const ( - // DefaultAttributeCountLimit is the default maximum allowed span attribute count. - DefaultAttributeCountLimit = 128 - - // DefaultEventCountLimit is the default maximum allowed span event count. - DefaultEventCountLimit = 128 - - // DefaultLinkCountLimit is the default maximum allowed span link count. - DefaultLinkCountLimit = 128 - - // DefaultAttributePerEventCountLimit is the default maximum allowed attribute per span event count. - DefaultAttributePerEventCountLimit = 128 - - // DefaultAttributePerLinkCountLimit is the default maximum allowed attribute per span link count. - DefaultAttributePerLinkCountLimit = 128 -) diff --git a/src/vendor/go.opentelemetry.io/otel/sdk/trace/evictedqueue.go b/src/vendor/go.opentelemetry.io/otel/sdk/trace/evictedqueue.go index 3c5817a6a..d1c86e59b 100644 --- a/src/vendor/go.opentelemetry.io/otel/sdk/trace/evictedqueue.go +++ b/src/vendor/go.opentelemetry.io/otel/sdk/trace/evictedqueue.go @@ -14,24 +14,30 @@ package trace // import "go.opentelemetry.io/otel/sdk/trace" +// evictedQueue is a FIFO queue with a configurable capacity. type evictedQueue struct { queue []interface{} capacity int droppedCount int } -func newEvictedQueue(capacity int) *evictedQueue { - eq := &evictedQueue{ - capacity: capacity, - queue: make([]interface{}, 0), - } - - return eq +func newEvictedQueue(capacity int) evictedQueue { + // Do not pre-allocate queue, do this lazily. + return evictedQueue{capacity: capacity} } +// add adds value to the evictedQueue eq. If eq is at capacity, the oldest +// queued value will be discarded and the drop count incremented. func (eq *evictedQueue) add(value interface{}) { - if len(eq.queue) == eq.capacity { - eq.queue = eq.queue[1:] + if eq.capacity == 0 { + eq.droppedCount++ + return + } + + if eq.capacity > 0 && len(eq.queue) == eq.capacity { + // Drop first-in while avoiding allocating more capacity to eq.queue. + copy(eq.queue[:eq.capacity-1], eq.queue[1:]) + eq.queue = eq.queue[:eq.capacity-1] eq.droppedCount++ } eq.queue = append(eq.queue, value) diff --git a/src/vendor/go.opentelemetry.io/otel/sdk/trace/id_generator.go b/src/vendor/go.opentelemetry.io/otel/sdk/trace/id_generator.go index c9e2802ac..bba246041 100644 --- a/src/vendor/go.opentelemetry.io/otel/sdk/trace/id_generator.go +++ b/src/vendor/go.opentelemetry.io/otel/sdk/trace/id_generator.go @@ -52,7 +52,7 @@ func (gen *randomIDGenerator) NewSpanID(ctx context.Context, traceID trace.Trace gen.Lock() defer gen.Unlock() sid := trace.SpanID{} - gen.randSource.Read(sid[:]) + _, _ = gen.randSource.Read(sid[:]) return sid } @@ -62,9 +62,9 @@ func (gen *randomIDGenerator) NewIDs(ctx context.Context) (trace.TraceID, trace. gen.Lock() defer gen.Unlock() tid := trace.TraceID{} - gen.randSource.Read(tid[:]) + _, _ = gen.randSource.Read(tid[:]) sid := trace.SpanID{} - gen.randSource.Read(sid[:]) + _, _ = gen.randSource.Read(sid[:]) return tid, sid } diff --git a/src/vendor/go.opentelemetry.io/otel/sdk/trace/provider.go b/src/vendor/go.opentelemetry.io/otel/sdk/trace/provider.go index 47ff5fad1..eac69f34d 100644 --- a/src/vendor/go.opentelemetry.io/otel/sdk/trace/provider.go +++ b/src/vendor/go.opentelemetry.io/otel/sdk/trace/provider.go @@ -21,6 +21,7 @@ import ( "sync/atomic" "go.opentelemetry.io/otel" + "go.opentelemetry.io/otel/internal/global" "go.opentelemetry.io/otel/sdk/instrumentation" "go.opentelemetry.io/otel/sdk/resource" "go.opentelemetry.io/otel/trace" @@ -30,7 +31,7 @@ const ( defaultTracerName = "go.opentelemetry.io/otel/sdk/tracer" ) -// tracerProviderConfig +// tracerProviderConfig. type tracerProviderConfig struct { // processors contains collection of SpanProcessors that are processing pipeline // for spans in the trace signal. @@ -52,14 +53,36 @@ type tracerProviderConfig struct { resource *resource.Resource } +// MarshalLog is the marshaling function used by the logging system to represent this exporter. +func (cfg tracerProviderConfig) MarshalLog() interface{} { + return struct { + SpanProcessors []SpanProcessor + SamplerType string + IDGeneratorType string + SpanLimits SpanLimits + Resource *resource.Resource + }{ + SpanProcessors: cfg.processors, + SamplerType: fmt.Sprintf("%T", cfg.sampler), + IDGeneratorType: fmt.Sprintf("%T", cfg.idGenerator), + SpanLimits: cfg.spanLimits, + Resource: cfg.resource, + } +} + +// TracerProvider is an OpenTelemetry TracerProvider. It provides Tracers to +// instrumentation so it can trace operational flow through a system. type TracerProvider struct { mu sync.Mutex - namedTracer map[instrumentation.Library]*tracer + namedTracer map[instrumentation.Scope]*tracer spanProcessors atomic.Value - sampler Sampler - idGenerator IDGenerator - spanLimits SpanLimits - resource *resource.Resource + + // These fields are not protected by the lock mu. They are assumed to be + // immutable after creation of the TracerProvider. + sampler Sampler + idGenerator IDGenerator + spanLimits SpanLimits + resource *resource.Resource } var _ trace.TracerProvider = &TracerProvider{} @@ -75,22 +98,27 @@ var _ trace.TracerProvider = &TracerProvider{} // The passed opts are used to override these default values and configure the // returned TracerProvider appropriately. func NewTracerProvider(opts ...TracerProviderOption) *TracerProvider { - o := &tracerProviderConfig{} + o := tracerProviderConfig{ + spanLimits: NewSpanLimits(), + } + o = applyTracerProviderEnvConfigs(o) for _, opt := range opts { - opt.apply(o) + o = opt.apply(o) } - ensureValidTracerProviderConfig(o) + o = ensureValidTracerProviderConfig(o) tp := &TracerProvider{ - namedTracer: make(map[instrumentation.Library]*tracer), + namedTracer: make(map[instrumentation.Scope]*tracer), sampler: o.sampler, idGenerator: o.idGenerator, spanLimits: o.spanLimits, resource: o.resource, } + global.Info("TracerProvider created", "config", o) + for _, sp := range o.processors { tp.RegisterSpanProcessor(sp) } @@ -113,23 +141,24 @@ func (p *TracerProvider) Tracer(name string, opts ...trace.TracerOption) trace.T if name == "" { name = defaultTracerName } - il := instrumentation.Library{ + is := instrumentation.Scope{ Name: name, Version: c.InstrumentationVersion(), SchemaURL: c.SchemaURL(), } - t, ok := p.namedTracer[il] + t, ok := p.namedTracer[is] if !ok { t = &tracer{ - provider: p, - instrumentationLibrary: il, + provider: p, + instrumentationScope: is, } - p.namedTracer[il] = t + p.namedTracer[is] = t + global.Info("Tracer created", "name", name, "version", c.InstrumentationVersion(), "schemaURL", c.SchemaURL()) } return t } -// RegisterSpanProcessor adds the given SpanProcessor to the list of SpanProcessors +// RegisterSpanProcessor adds the given SpanProcessor to the list of SpanProcessors. func (p *TracerProvider) RegisterSpanProcessor(s SpanProcessor) { p.mu.Lock() defer p.mu.Unlock() @@ -145,7 +174,7 @@ func (p *TracerProvider) RegisterSpanProcessor(s SpanProcessor) { p.spanProcessors.Store(new) } -// UnregisterSpanProcessor removes the given SpanProcessor from the list of SpanProcessors +// UnregisterSpanProcessor removes the given SpanProcessor from the list of SpanProcessors. func (p *TracerProvider) UnregisterSpanProcessor(s SpanProcessor) { p.mu.Lock() defer p.mu.Unlock() @@ -234,14 +263,15 @@ func (p *TracerProvider) Shutdown(ctx context.Context) error { return nil } +// TracerProviderOption configures a TracerProvider. type TracerProviderOption interface { - apply(*tracerProviderConfig) + apply(tracerProviderConfig) tracerProviderConfig } -type traceProviderOptionFunc func(*tracerProviderConfig) +type traceProviderOptionFunc func(tracerProviderConfig) tracerProviderConfig -func (fn traceProviderOptionFunc) apply(cfg *tracerProviderConfig) { - fn(cfg) +func (fn traceProviderOptionFunc) apply(cfg tracerProviderConfig) tracerProviderConfig { + return fn(cfg) } // WithSyncer registers the exporter with the TracerProvider using a @@ -264,8 +294,9 @@ func WithBatcher(e SpanExporter, opts ...BatchSpanProcessorOption) TracerProvide // WithSpanProcessor registers the SpanProcessor with a TracerProvider. func WithSpanProcessor(sp SpanProcessor) TracerProviderOption { - return traceProviderOptionFunc(func(cfg *tracerProviderConfig) { + return traceProviderOptionFunc(func(cfg tracerProviderConfig) tracerProviderConfig { cfg.processors = append(cfg.processors, sp) + return cfg }) } @@ -277,12 +308,13 @@ func WithSpanProcessor(sp SpanProcessor) TracerProviderOption { // If this option is not used, the TracerProvider will use the // resource.Default() Resource by default. func WithResource(r *resource.Resource) TracerProviderOption { - return traceProviderOptionFunc(func(cfg *tracerProviderConfig) { + return traceProviderOptionFunc(func(cfg tracerProviderConfig) tracerProviderConfig { var err error cfg.resource, err = resource.Merge(resource.Environment(), r) if err != nil { otel.Handle(err) } + return cfg }) } @@ -294,10 +326,11 @@ func WithResource(r *resource.Resource) TracerProviderOption { // If this option is not used, the TracerProvider will use a random number // IDGenerator by default. func WithIDGenerator(g IDGenerator) TracerProviderOption { - return traceProviderOptionFunc(func(cfg *tracerProviderConfig) { + return traceProviderOptionFunc(func(cfg tracerProviderConfig) tracerProviderConfig { if g != nil { cfg.idGenerator = g } + return cfg }) } @@ -306,39 +339,115 @@ func WithIDGenerator(g IDGenerator) TracerProviderOption { // Tracers the TracerProvider creates to make their sampling decisions for the // Spans they create. // -// If this option is not used, the TracerProvider will use a +// This option overrides the Sampler configured through the OTEL_TRACES_SAMPLER +// and OTEL_TRACES_SAMPLER_ARG environment variables. If this option is not used +// and the sampler is not configured through environment variables or the environment +// contains invalid/unsupported configuration, the TracerProvider will use a // ParentBased(AlwaysSample) Sampler by default. func WithSampler(s Sampler) TracerProviderOption { - return traceProviderOptionFunc(func(cfg *tracerProviderConfig) { + return traceProviderOptionFunc(func(cfg tracerProviderConfig) tracerProviderConfig { if s != nil { cfg.sampler = s } + return cfg }) } -// WithSpanLimits returns a TracerProviderOption that will configure the -// SpanLimits sl as a TracerProvider's SpanLimits. The configured SpanLimits -// are used used by the Tracers the TracerProvider and the Spans they create -// to limit tracing resources used. +// WithSpanLimits returns a TracerProviderOption that configures a +// TracerProvider to use the SpanLimits sl. These SpanLimits bound any Span +// created by a Tracer from the TracerProvider. // -// If this option is not used, the TracerProvider will use the default -// SpanLimits. +// If any field of sl is zero or negative it will be replaced with the default +// value for that field. +// +// If this or WithRawSpanLimits are not provided, the TracerProvider will use +// the limits defined by environment variables, or the defaults if unset. +// Refer to the NewSpanLimits documentation for information about this +// relationship. +// +// Deprecated: Use WithRawSpanLimits instead which allows setting unlimited +// and zero limits. This option will be kept until the next major version +// incremented release. func WithSpanLimits(sl SpanLimits) TracerProviderOption { - return traceProviderOptionFunc(func(cfg *tracerProviderConfig) { + if sl.AttributeValueLengthLimit <= 0 { + sl.AttributeValueLengthLimit = DefaultAttributeValueLengthLimit + } + if sl.AttributeCountLimit <= 0 { + sl.AttributeCountLimit = DefaultAttributeCountLimit + } + if sl.EventCountLimit <= 0 { + sl.EventCountLimit = DefaultEventCountLimit + } + if sl.AttributePerEventCountLimit <= 0 { + sl.AttributePerEventCountLimit = DefaultAttributePerEventCountLimit + } + if sl.LinkCountLimit <= 0 { + sl.LinkCountLimit = DefaultLinkCountLimit + } + if sl.AttributePerLinkCountLimit <= 0 { + sl.AttributePerLinkCountLimit = DefaultAttributePerLinkCountLimit + } + return traceProviderOptionFunc(func(cfg tracerProviderConfig) tracerProviderConfig { cfg.spanLimits = sl + return cfg }) } +// WithRawSpanLimits returns a TracerProviderOption that configures a +// TracerProvider to use these limits. These limits bound any Span created by +// a Tracer from the TracerProvider. +// +// The limits will be used as-is. Zero or negative values will not be changed +// to the default value like WithSpanLimits does. Setting a limit to zero will +// effectively disable the related resource it limits and setting to a +// negative value will mean that resource is unlimited. Consequentially, this +// means that the zero-value SpanLimits will disable all span resources. +// Because of this, limits should be constructed using NewSpanLimits and +// updated accordingly. +// +// If this or WithSpanLimits are not provided, the TracerProvider will use the +// limits defined by environment variables, or the defaults if unset. Refer to +// the NewSpanLimits documentation for information about this relationship. +func WithRawSpanLimits(limits SpanLimits) TracerProviderOption { + return traceProviderOptionFunc(func(cfg tracerProviderConfig) tracerProviderConfig { + cfg.spanLimits = limits + return cfg + }) +} + +func applyTracerProviderEnvConfigs(cfg tracerProviderConfig) tracerProviderConfig { + for _, opt := range tracerProviderOptionsFromEnv() { + cfg = opt.apply(cfg) + } + + return cfg +} + +func tracerProviderOptionsFromEnv() []TracerProviderOption { + var opts []TracerProviderOption + + sampler, err := samplerFromEnv() + if err != nil { + otel.Handle(err) + } + + if sampler != nil { + opts = append(opts, WithSampler(sampler)) + } + + return opts +} + // ensureValidTracerProviderConfig ensures that given TracerProviderConfig is valid. -func ensureValidTracerProviderConfig(cfg *tracerProviderConfig) { +func ensureValidTracerProviderConfig(cfg tracerProviderConfig) tracerProviderConfig { if cfg.sampler == nil { cfg.sampler = ParentBased(AlwaysSample()) } if cfg.idGenerator == nil { cfg.idGenerator = defaultIDGenerator() } - cfg.spanLimits.ensureDefault() if cfg.resource == nil { cfg.resource = resource.Default() } + return cfg } diff --git a/src/vendor/go.opentelemetry.io/otel/sdk/trace/sampler_env.go b/src/vendor/go.opentelemetry.io/otel/sdk/trace/sampler_env.go new file mode 100644 index 000000000..02053b318 --- /dev/null +++ b/src/vendor/go.opentelemetry.io/otel/sdk/trace/sampler_env.go @@ -0,0 +1,108 @@ +// Copyright The OpenTelemetry 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 trace // import "go.opentelemetry.io/otel/sdk/trace" + +import ( + "errors" + "fmt" + "os" + "strconv" + "strings" +) + +const ( + tracesSamplerKey = "OTEL_TRACES_SAMPLER" + tracesSamplerArgKey = "OTEL_TRACES_SAMPLER_ARG" + + samplerAlwaysOn = "always_on" + samplerAlwaysOff = "always_off" + samplerTraceIDRatio = "traceidratio" + samplerParentBasedAlwaysOn = "parentbased_always_on" + samplerParsedBasedAlwaysOff = "parentbased_always_off" + samplerParentBasedTraceIDRatio = "parentbased_traceidratio" +) + +type errUnsupportedSampler string + +func (e errUnsupportedSampler) Error() string { + return fmt.Sprintf("unsupported sampler: %s", string(e)) +} + +var ( + errNegativeTraceIDRatio = errors.New("invalid trace ID ratio: less than 0.0") + errGreaterThanOneTraceIDRatio = errors.New("invalid trace ID ratio: greater than 1.0") +) + +type samplerArgParseError struct { + parseErr error +} + +func (e samplerArgParseError) Error() string { + return fmt.Sprintf("parsing sampler argument: %s", e.parseErr.Error()) +} + +func (e samplerArgParseError) Unwrap() error { + return e.parseErr +} + +func samplerFromEnv() (Sampler, error) { + sampler, ok := os.LookupEnv(tracesSamplerKey) + if !ok { + return nil, nil + } + + sampler = strings.ToLower(strings.TrimSpace(sampler)) + samplerArg, hasSamplerArg := os.LookupEnv(tracesSamplerArgKey) + samplerArg = strings.TrimSpace(samplerArg) + + switch sampler { + case samplerAlwaysOn: + return AlwaysSample(), nil + case samplerAlwaysOff: + return NeverSample(), nil + case samplerTraceIDRatio: + if !hasSamplerArg { + return TraceIDRatioBased(1.0), nil + } + return parseTraceIDRatio(samplerArg) + case samplerParentBasedAlwaysOn: + return ParentBased(AlwaysSample()), nil + case samplerParsedBasedAlwaysOff: + return ParentBased(NeverSample()), nil + case samplerParentBasedTraceIDRatio: + if !hasSamplerArg { + return ParentBased(TraceIDRatioBased(1.0)), nil + } + ratio, err := parseTraceIDRatio(samplerArg) + return ParentBased(ratio), err + default: + return nil, errUnsupportedSampler(sampler) + } +} + +func parseTraceIDRatio(arg string) (Sampler, error) { + v, err := strconv.ParseFloat(arg, 64) + if err != nil { + return TraceIDRatioBased(1.0), samplerArgParseError{err} + } + if v < 0.0 { + return TraceIDRatioBased(1.0), errNegativeTraceIDRatio + } + if v > 1.0 { + return TraceIDRatioBased(1.0), errGreaterThanOneTraceIDRatio + } + + return TraceIDRatioBased(v), nil +} diff --git a/src/vendor/go.opentelemetry.io/otel/sdk/trace/sampling.go b/src/vendor/go.opentelemetry.io/otel/sdk/trace/sampling.go index 849248638..a39d0341e 100644 --- a/src/vendor/go.opentelemetry.io/otel/sdk/trace/sampling.go +++ b/src/vendor/go.opentelemetry.io/otel/sdk/trace/sampling.go @@ -53,17 +53,17 @@ type SamplingParameters struct { // SamplingDecision indicates whether a span is dropped, recorded and/or sampled. type SamplingDecision uint8 -// Valid sampling decisions +// Valid sampling decisions. const ( - // Drop will not record the span and all attributes/events will be dropped + // Drop will not record the span and all attributes/events will be dropped. Drop SamplingDecision = iota // Record indicates the span's `IsRecording() == true`, but `Sampled` flag - // *must not* be set + // *must not* be set. RecordOnly // RecordAndSample has span's `IsRecording() == true` and `Sampled` flag - // *must* be set + // *must* be set. RecordAndSample ) @@ -187,7 +187,7 @@ func configureSamplersForParentBased(samplers []ParentBasedSamplerOption) sample } for _, so := range samplers { - so.apply(&c) + c = so.apply(c) } return c @@ -201,7 +201,7 @@ type samplerConfig struct { // ParentBasedSamplerOption configures the sampler for a particular sampling case. type ParentBasedSamplerOption interface { - apply(*samplerConfig) + apply(samplerConfig) samplerConfig } // WithRemoteParentSampled sets the sampler for the case of sampled remote parent. @@ -213,8 +213,9 @@ type remoteParentSampledOption struct { s Sampler } -func (o remoteParentSampledOption) apply(config *samplerConfig) { +func (o remoteParentSampledOption) apply(config samplerConfig) samplerConfig { config.remoteParentSampled = o.s + return config } // WithRemoteParentNotSampled sets the sampler for the case of remote parent @@ -227,8 +228,9 @@ type remoteParentNotSampledOption struct { s Sampler } -func (o remoteParentNotSampledOption) apply(config *samplerConfig) { +func (o remoteParentNotSampledOption) apply(config samplerConfig) samplerConfig { config.remoteParentNotSampled = o.s + return config } // WithLocalParentSampled sets the sampler for the case of sampled local parent. @@ -240,8 +242,9 @@ type localParentSampledOption struct { s Sampler } -func (o localParentSampledOption) apply(config *samplerConfig) { +func (o localParentSampledOption) apply(config samplerConfig) samplerConfig { config.localParentSampled = o.s + return config } // WithLocalParentNotSampled sets the sampler for the case of local parent @@ -254,8 +257,9 @@ type localParentNotSampledOption struct { s Sampler } -func (o localParentNotSampledOption) apply(config *samplerConfig) { +func (o localParentNotSampledOption) apply(config samplerConfig) samplerConfig { config.localParentNotSampled = o.s + return config } func (pb parentBased) ShouldSample(p SamplingParameters) SamplingResult { diff --git a/src/vendor/go.opentelemetry.io/otel/sdk/trace/simple_span_processor.go b/src/vendor/go.opentelemetry.io/otel/sdk/trace/simple_span_processor.go index ecc45bc6c..e8530a959 100644 --- a/src/vendor/go.opentelemetry.io/otel/sdk/trace/simple_span_processor.go +++ b/src/vendor/go.opentelemetry.io/otel/sdk/trace/simple_span_processor.go @@ -115,3 +115,14 @@ func (ssp *simpleSpanProcessor) Shutdown(ctx context.Context) error { func (ssp *simpleSpanProcessor) ForceFlush(context.Context) error { return nil } + +// MarshalLog is the marshaling function used by the logging system to represent this Span Processor. +func (ssp *simpleSpanProcessor) MarshalLog() interface{} { + return struct { + Type string + Exporter SpanExporter + }{ + Type: "SimpleSpanProcessor", + Exporter: ssp.exporter, + } +} diff --git a/src/vendor/go.opentelemetry.io/otel/sdk/trace/snapshot.go b/src/vendor/go.opentelemetry.io/otel/sdk/trace/snapshot.go index 53aac61f5..0349b2f19 100644 --- a/src/vendor/go.opentelemetry.io/otel/sdk/trace/snapshot.go +++ b/src/vendor/go.opentelemetry.io/otel/sdk/trace/snapshot.go @@ -26,22 +26,22 @@ import ( // snapshot is an record of a spans state at a particular checkpointed time. // It is used as a read-only representation of that state. type snapshot struct { - name string - spanContext trace.SpanContext - parent trace.SpanContext - spanKind trace.SpanKind - startTime time.Time - endTime time.Time - attributes []attribute.KeyValue - events []Event - links []Link - status Status - childSpanCount int - droppedAttributeCount int - droppedEventCount int - droppedLinkCount int - resource *resource.Resource - instrumentationLibrary instrumentation.Library + name string + spanContext trace.SpanContext + parent trace.SpanContext + spanKind trace.SpanKind + startTime time.Time + endTime time.Time + attributes []attribute.KeyValue + events []Event + links []Link + status Status + childSpanCount int + droppedAttributeCount int + droppedEventCount int + droppedLinkCount int + resource *resource.Resource + instrumentationScope instrumentation.Scope } var _ ReadOnlySpan = snapshot{} @@ -102,10 +102,16 @@ func (s snapshot) Status() Status { return s.status } +// InstrumentationScope returns information about the instrumentation +// scope that created the span. +func (s snapshot) InstrumentationScope() instrumentation.Scope { + return s.instrumentationScope +} + // InstrumentationLibrary returns information about the instrumentation // library that created the span. func (s snapshot) InstrumentationLibrary() instrumentation.Library { - return s.instrumentationLibrary + return s.instrumentationScope } // Resource returns information about the entity that produced the span. diff --git a/src/vendor/go.opentelemetry.io/otel/sdk/trace/span.go b/src/vendor/go.opentelemetry.io/otel/sdk/trace/span.go index 41a68b585..d380c2719 100644 --- a/src/vendor/go.opentelemetry.io/otel/sdk/trace/span.go +++ b/src/vendor/go.opentelemetry.io/otel/sdk/trace/span.go @@ -28,7 +28,7 @@ import ( "go.opentelemetry.io/otel/sdk/instrumentation" "go.opentelemetry.io/otel/sdk/internal" "go.opentelemetry.io/otel/sdk/resource" - semconv "go.opentelemetry.io/otel/semconv/v1.7.0" + semconv "go.opentelemetry.io/otel/semconv/v1.10.0" "go.opentelemetry.io/otel/trace" ) @@ -54,6 +54,7 @@ type ReadOnlySpan interface { // the span has not ended. EndTime() time.Time // Attributes returns the defining attributes of the span. + // The order of the returned attributes is not guaranteed to be stable across invocations. Attributes() []attribute.KeyValue // Links returns all the links the span has to other spans. Links() []Link @@ -62,8 +63,12 @@ type ReadOnlySpan interface { Events() []Event // Status returns the spans status. Status() Status + // InstrumentationScope returns information about the instrumentation + // scope that created the span. + InstrumentationScope() instrumentation.Scope // InstrumentationLibrary returns information about the instrumentation // library that created the span. + // Deprecated: please use InstrumentationScope instead. InstrumentationLibrary() instrumentation.Library // Resource returns information about the entity that produced the span. Resource() *resource.Resource @@ -126,35 +131,29 @@ type recordingSpan struct { // childSpanCount holds the number of child spans created for this span. childSpanCount int - // resource contains attributes representing an entity that produced this - // span. - resource *resource.Resource - - // instrumentationLibrary defines the instrumentation library used to - // provide instrumentation. - instrumentationLibrary instrumentation.Library - // spanContext holds the SpanContext of this span. spanContext trace.SpanContext - // attributes are capped at configured limit. When the capacity is reached - // an oldest entry is removed to create room for a new entry. - attributes *attributesMap + // attributes is a collection of user provided key/values. The collection + // is constrained by a configurable maximum held by the parent + // TracerProvider. When additional attributes are added after this maximum + // is reached these attributes the user is attempting to add are dropped. + // This dropped number of attributes is tracked and reported in the + // ReadOnlySpan exported when the span ends. + attributes []attribute.KeyValue + droppedAttributes int // events are stored in FIFO queue capped by configured limit. - events *evictedQueue + events evictedQueue // links are stored in FIFO queue capped by configured limit. - links *evictedQueue + links evictedQueue // executionTracerTaskEnd ends the execution tracer span. executionTracerTaskEnd func() // tracer is the SDK tracer that created this span. tracer *tracer - - // spanLimits holds the limits to this span. - spanLimits SpanLimits } var _ ReadWriteSpan = (*recordingSpan)(nil) @@ -205,11 +204,132 @@ func (s *recordingSpan) SetStatus(code codes.Code, description string) { // will be overwritten with the value contained in attributes. // // If this span is not being recorded than this method does nothing. +// +// If adding attributes to the span would exceed the maximum amount of +// attributes the span is configured to have, the last added attributes will +// be dropped. func (s *recordingSpan) SetAttributes(attributes ...attribute.KeyValue) { if !s.IsRecording() { return } - s.copyToCappedAttributes(attributes...) + + s.mu.Lock() + defer s.mu.Unlock() + + limit := s.tracer.provider.spanLimits.AttributeCountLimit + if limit == 0 { + // No attributes allowed. + s.droppedAttributes += len(attributes) + return + } + + // If adding these attributes could exceed the capacity of s perform a + // de-duplication and truncation while adding to avoid over allocation. + if limit > 0 && len(s.attributes)+len(attributes) > limit { + s.addOverCapAttrs(limit, attributes) + return + } + + // Otherwise, add without deduplication. When attributes are read they + // will be deduplicated, optimizing the operation. + for _, a := range attributes { + if !a.Valid() { + // Drop all invalid attributes. + s.droppedAttributes++ + continue + } + a = truncateAttr(s.tracer.provider.spanLimits.AttributeValueLengthLimit, a) + s.attributes = append(s.attributes, a) + } +} + +// addOverCapAttrs adds the attributes attrs to the span s while +// de-duplicating the attributes of s and attrs and dropping attributes that +// exceed the limit. +// +// This method assumes s.mu.Lock is held by the caller. +// +// This method should only be called when there is a possibility that adding +// attrs to s will exceed the limit. Otherwise, attrs should be added to s +// without checking for duplicates and all retrieval methods of the attributes +// for s will de-duplicate as needed. +// +// This method assumes limit is a value > 0. The argument should be validated +// by the caller. +func (s *recordingSpan) addOverCapAttrs(limit int, attrs []attribute.KeyValue) { + // In order to not allocate more capacity to s.attributes than needed, + // prune and truncate this addition of attributes while adding. + + // Do not set a capacity when creating this map. Benchmark testing has + // showed this to only add unused memory allocations in general use. + exists := make(map[attribute.Key]int) + s.dedupeAttrsFromRecord(&exists) + + // Now that s.attributes is deduplicated, adding unique attributes up to + // the capacity of s will not over allocate s.attributes. + for _, a := range attrs { + if !a.Valid() { + // Drop all invalid attributes. + s.droppedAttributes++ + continue + } + + if idx, ok := exists[a.Key]; ok { + // Perform all updates before dropping, even when at capacity. + s.attributes[idx] = a + continue + } + + if len(s.attributes) >= limit { + // Do not just drop all of the remaining attributes, make sure + // updates are checked and performed. + s.droppedAttributes++ + } else { + a = truncateAttr(s.tracer.provider.spanLimits.AttributeValueLengthLimit, a) + s.attributes = append(s.attributes, a) + exists[a.Key] = len(s.attributes) - 1 + } + } +} + +// truncateAttr returns a truncated version of attr. Only string and string +// slice attribute values are truncated. String values are truncated to at +// most a length of limit. Each string slice value is truncated in this fasion +// (the slice length itself is unaffected). +// +// No truncation is perfromed for a negative limit. +func truncateAttr(limit int, attr attribute.KeyValue) attribute.KeyValue { + if limit < 0 { + return attr + } + switch attr.Value.Type() { + case attribute.STRING: + if v := attr.Value.AsString(); len(v) > limit { + return attr.Key.String(v[:limit]) + } + case attribute.STRINGSLICE: + // Do no mutate the original, make a copy. + trucated := attr.Key.StringSlice(attr.Value.AsStringSlice()) + // Do not do this. + // + // v := trucated.Value.AsStringSlice() + // cp := make([]string, len(v)) + // /* Copy and truncate values to cp ... */ + // trucated.Value = attribute.StringSliceValue(cp) + // + // Copying the []string and then assigning it back as a new value with + // attribute.StringSliceValue will copy the data twice. Instead, we + // already made a copy above that only this function owns, update the + // underlying slice data of our copy. + v := trucated.Value.AsStringSlice() + for i := range v { + if len(v[i]) > limit { + v[i] = v[i][:limit] + } + } + return trucated + } + return attr } // End ends the span. This method does nothing if the span is already ended or @@ -332,22 +452,23 @@ func (s *recordingSpan) AddEvent(name string, o ...trace.EventOption) { func (s *recordingSpan) addEvent(name string, o ...trace.EventOption) { c := trace.NewEventConfig(o...) + e := Event{Name: name, Attributes: c.Attributes(), Time: c.Timestamp()} - // Discard over limited attributes - attributes := c.Attributes() - var discarded int - if len(attributes) > s.spanLimits.AttributePerEventCountLimit { - discarded = len(attributes) - s.spanLimits.AttributePerEventCountLimit - attributes = attributes[:s.spanLimits.AttributePerEventCountLimit] + // Discard attributes over limit. + limit := s.tracer.provider.spanLimits.AttributePerEventCountLimit + if limit == 0 { + // Drop all attributes. + e.DroppedAttributeCount = len(e.Attributes) + e.Attributes = nil + } else if limit > 0 && len(e.Attributes) > limit { + // Drop over capacity. + e.DroppedAttributeCount = len(e.Attributes) - limit + e.Attributes = e.Attributes[:limit] } + s.mu.Lock() - defer s.mu.Unlock() - s.events.add(Event{ - Name: name, - Attributes: attributes, - DroppedAttributeCount: discarded, - Time: c.Timestamp(), - }) + s.events.add(e) + s.mu.Unlock() } // SetName sets the name of this span. If this span is not being recorded than @@ -399,13 +520,45 @@ func (s *recordingSpan) EndTime() time.Time { } // Attributes returns the attributes of this span. +// +// The order of the returned attributes is not guaranteed to be stable. func (s *recordingSpan) Attributes() []attribute.KeyValue { s.mu.Lock() defer s.mu.Unlock() - if s.attributes.evictList.Len() == 0 { - return []attribute.KeyValue{} + s.dedupeAttrs() + return s.attributes +} + +// dedupeAttrs deduplicates the attributes of s to fit capacity. +// +// This method assumes s.mu.Lock is held by the caller. +func (s *recordingSpan) dedupeAttrs() { + // Do not set a capacity when creating this map. Benchmark testing has + // showed this to only add unused memory allocations in general use. + exists := make(map[attribute.Key]int) + s.dedupeAttrsFromRecord(&exists) +} + +// dedupeAttrsFromRecord deduplicates the attributes of s to fit capacity +// using record as the record of unique attribute keys to their index. +// +// This method assumes s.mu.Lock is held by the caller. +func (s *recordingSpan) dedupeAttrsFromRecord(record *map[attribute.Key]int) { + // Use the fact that slices share the same backing array. + unique := s.attributes[:0] + for _, a := range s.attributes { + if idx, ok := (*record)[a.Key]; ok { + unique[idx] = a + } else { + unique = append(unique, a) + (*record)[a.Key] = len(unique) - 1 + } } - return s.attributes.toKeyValue() + // s.attributes have element types of attribute.KeyValue. These types are + // not pointers and they themselves do not contain pointer fields, + // therefore the duplicate values do not need to be zeroed for them to be + // garbage collected. + s.attributes = unique } // Links returns the links of this span. @@ -435,12 +588,20 @@ func (s *recordingSpan) Status() Status { return s.status } +// InstrumentationScope returns the instrumentation.Scope associated with +// the Tracer that created this span. +func (s *recordingSpan) InstrumentationScope() instrumentation.Scope { + s.mu.Lock() + defer s.mu.Unlock() + return s.tracer.instrumentationScope +} + // InstrumentationLibrary returns the instrumentation.Library associated with // the Tracer that created this span. func (s *recordingSpan) InstrumentationLibrary() instrumentation.Library { s.mu.Lock() defer s.mu.Unlock() - return s.instrumentationLibrary + return s.tracer.instrumentationScope } // Resource returns the Resource associated with the Tracer that created this @@ -448,25 +609,30 @@ func (s *recordingSpan) InstrumentationLibrary() instrumentation.Library { func (s *recordingSpan) Resource() *resource.Resource { s.mu.Lock() defer s.mu.Unlock() - return s.resource + return s.tracer.provider.resource } func (s *recordingSpan) addLink(link trace.Link) { if !s.IsRecording() || !link.SpanContext.IsValid() { return } - s.mu.Lock() - defer s.mu.Unlock() - var droppedAttributeCount int + l := Link{SpanContext: link.SpanContext, Attributes: link.Attributes} - // Discard over limited attributes - if len(link.Attributes) > s.spanLimits.AttributePerLinkCountLimit { - droppedAttributeCount = len(link.Attributes) - s.spanLimits.AttributePerLinkCountLimit - link.Attributes = link.Attributes[:s.spanLimits.AttributePerLinkCountLimit] + // Discard attributes over limit. + limit := s.tracer.provider.spanLimits.AttributePerLinkCountLimit + if limit == 0 { + // Drop all attributes. + l.DroppedAttributeCount = len(l.Attributes) + l.Attributes = nil + } else if limit > 0 && len(l.Attributes) > limit { + l.DroppedAttributeCount = len(l.Attributes) - limit + l.Attributes = l.Attributes[:limit] } - s.links.add(Link{link.SpanContext, link.Attributes, droppedAttributeCount}) + s.mu.Lock() + s.links.add(l) + s.mu.Unlock() } // DroppedAttributes returns the number of attributes dropped by the span @@ -474,7 +640,7 @@ func (s *recordingSpan) addLink(link trace.Link) { func (s *recordingSpan) DroppedAttributes() int { s.mu.Lock() defer s.mu.Unlock() - return s.attributes.droppedCount + return s.droppedAttributes } // DroppedLinks returns the number of links dropped by the span due to limits @@ -514,20 +680,21 @@ func (s *recordingSpan) snapshot() ReadOnlySpan { defer s.mu.Unlock() sd.endTime = s.endTime - sd.instrumentationLibrary = s.instrumentationLibrary + sd.instrumentationScope = s.tracer.instrumentationScope sd.name = s.name sd.parent = s.parent - sd.resource = s.resource + sd.resource = s.tracer.provider.resource sd.spanContext = s.spanContext sd.spanKind = s.spanKind sd.startTime = s.startTime sd.status = s.status sd.childSpanCount = s.childSpanCount - if s.attributes.evictList.Len() > 0 { - sd.attributes = s.attributes.toKeyValue() - sd.droppedAttributeCount = s.attributes.droppedCount + if len(s.attributes) > 0 { + s.dedupeAttrs() + sd.attributes = s.attributes } + sd.droppedAttributeCount = s.droppedAttributes if len(s.events.queue) > 0 { sd.events = s.interfaceArrayToEventArray() sd.droppedEventCount = s.events.droppedCount @@ -555,18 +722,6 @@ func (s *recordingSpan) interfaceArrayToEventArray() []Event { return eventArr } -func (s *recordingSpan) copyToCappedAttributes(attributes ...attribute.KeyValue) { - s.mu.Lock() - defer s.mu.Unlock() - for _, a := range attributes { - // Ensure attributes conform to the specification: - // https://github.com/open-telemetry/opentelemetry-specification/blob/v1.0.1/specification/common/common.md#attributes - if a.Valid() { - s.attributes.add(a) - } - } -} - func (s *recordingSpan) addChild() { if !s.IsRecording() { return diff --git a/src/vendor/go.opentelemetry.io/otel/sdk/trace/span_limits.go b/src/vendor/go.opentelemetry.io/otel/sdk/trace/span_limits.go new file mode 100644 index 000000000..aa4d4221d --- /dev/null +++ b/src/vendor/go.opentelemetry.io/otel/sdk/trace/span_limits.go @@ -0,0 +1,125 @@ +// Copyright The OpenTelemetry 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 trace // import "go.opentelemetry.io/otel/sdk/trace" + +import "go.opentelemetry.io/otel/sdk/internal/env" + +const ( + // DefaultAttributeValueLengthLimit is the default maximum allowed + // attribute value length, unlimited. + DefaultAttributeValueLengthLimit = -1 + + // DefaultAttributeCountLimit is the default maximum number of attributes + // a span can have. + DefaultAttributeCountLimit = 128 + + // DefaultEventCountLimit is the default maximum number of events a span + // can have. + DefaultEventCountLimit = 128 + + // DefaultLinkCountLimit is the default maximum number of links a span can + // have. + DefaultLinkCountLimit = 128 + + // DefaultAttributePerEventCountLimit is the default maximum number of + // attributes a span event can have. + DefaultAttributePerEventCountLimit = 128 + + // DefaultAttributePerLinkCountLimit is the default maximum number of + // attributes a span link can have. + DefaultAttributePerLinkCountLimit = 128 +) + +// SpanLimits represents the limits of a span. +type SpanLimits struct { + // AttributeValueLengthLimit is the maximum allowed attribute value length. + // + // This limit only applies to string and string slice attribute values. + // Any string longer than this value will be truncated to this length. + // + // Setting this to a negative value means no limit is applied. + AttributeValueLengthLimit int + + // AttributeCountLimit is the maximum allowed span attribute count. Any + // attribute added to a span once this limit is reached will be dropped. + // + // Setting this to zero means no attributes will be recorded. + // + // Setting this to a negative value means no limit is applied. + AttributeCountLimit int + + // EventCountLimit is the maximum allowed span event count. Any event + // added to a span once this limit is reached means it will be added but + // the oldest event will be dropped. + // + // Setting this to zero means no events we be recorded. + // + // Setting this to a negative value means no limit is applied. + EventCountLimit int + + // LinkCountLimit is the maximum allowed span link count. Any link added + // to a span once this limit is reached means it will be added but the + // oldest link will be dropped. + // + // Setting this to zero means no links we be recorded. + // + // Setting this to a negative value means no limit is applied. + LinkCountLimit int + + // AttributePerEventCountLimit is the maximum number of attributes allowed + // per span event. Any attribute added after this limit reached will be + // dropped. + // + // Setting this to zero means no attributes will be recorded for events. + // + // Setting this to a negative value means no limit is applied. + AttributePerEventCountLimit int + + // AttributePerLinkCountLimit is the maximum number of attributes allowed + // per span link. Any attribute added after this limit reached will be + // dropped. + // + // Setting this to zero means no attributes will be recorded for links. + // + // Setting this to a negative value means no limit is applied. + AttributePerLinkCountLimit int +} + +// NewSpanLimits returns a SpanLimits with all limits set to the value their +// corresponding environment variable holds, or the default if unset. +// +// • AttributeValueLengthLimit: OTEL_SPAN_ATTRIBUTE_VALUE_LENGTH_LIMIT +// (default: unlimited) +// +// • AttributeCountLimit: OTEL_SPAN_ATTRIBUTE_COUNT_LIMIT (default: 128) +// +// • EventCountLimit: OTEL_SPAN_EVENT_COUNT_LIMIT (default: 128) +// +// • AttributePerEventCountLimit: OTEL_EVENT_ATTRIBUTE_COUNT_LIMIT (default: +// 128) +// +// • LinkCountLimit: OTEL_SPAN_LINK_COUNT_LIMIT (default: 128) +// +// • AttributePerLinkCountLimit: OTEL_LINK_ATTRIBUTE_COUNT_LIMIT (default: 128) +func NewSpanLimits() SpanLimits { + return SpanLimits{ + AttributeValueLengthLimit: env.SpanAttributeValueLength(DefaultAttributeValueLengthLimit), + AttributeCountLimit: env.SpanAttributeCount(DefaultAttributeCountLimit), + EventCountLimit: env.SpanEventCount(DefaultEventCountLimit), + LinkCountLimit: env.SpanLinkCount(DefaultLinkCountLimit), + AttributePerEventCountLimit: env.SpanEventAttributeCount(DefaultAttributePerEventCountLimit), + AttributePerLinkCountLimit: env.SpanLinkAttributeCount(DefaultAttributePerLinkCountLimit), + } +} diff --git a/src/vendor/go.opentelemetry.io/otel/sdk/trace/tracer.go b/src/vendor/go.opentelemetry.io/otel/sdk/trace/tracer.go index 1177c729a..f4a1f96f3 100644 --- a/src/vendor/go.opentelemetry.io/otel/sdk/trace/tracer.go +++ b/src/vendor/go.opentelemetry.io/otel/sdk/trace/tracer.go @@ -23,8 +23,8 @@ import ( ) type tracer struct { - provider *TracerProvider - instrumentationLibrary instrumentation.Library + provider *TracerProvider + instrumentationScope instrumentation.Scope } var _ trace.Tracer = &tracer{} @@ -122,18 +122,22 @@ func (tr *tracer) newRecordingSpan(psc, sc trace.SpanContext, name string, sr Sa } s := &recordingSpan{ - parent: psc, - spanContext: sc, - spanKind: trace.ValidateSpanKind(config.SpanKind()), - name: name, - startTime: startTime, - attributes: newAttributesMap(tr.provider.spanLimits.AttributeCountLimit), - events: newEvictedQueue(tr.provider.spanLimits.EventCountLimit), - links: newEvictedQueue(tr.provider.spanLimits.LinkCountLimit), - tracer: tr, - spanLimits: tr.provider.spanLimits, - resource: tr.provider.resource, - instrumentationLibrary: tr.instrumentationLibrary, + // Do not pre-allocate the attributes slice here! Doing so will + // allocate memory that is likely never going to be used, or if used, + // will be over-sized. The default Go compiler has been tested to + // dynamically allocate needed space very well. Benchmarking has shown + // it to be more performant than what we can predetermine here, + // especially for the common use case of few to no added + // attributes. + + parent: psc, + spanContext: sc, + spanKind: trace.ValidateSpanKind(config.SpanKind()), + name: name, + startTime: startTime, + events: newEvictedQueue(tr.provider.spanLimits.EventCountLimit), + links: newEvictedQueue(tr.provider.spanLimits.LinkCountLimit), + tracer: tr, } for _, l := range config.Links() { diff --git a/src/vendor/go.opentelemetry.io/otel/semconv/v1.7.0/http.go b/src/vendor/go.opentelemetry.io/otel/semconv/internal/http.go similarity index 60% rename from src/vendor/go.opentelemetry.io/otel/semconv/v1.7.0/http.go rename to src/vendor/go.opentelemetry.io/otel/semconv/internal/http.go index 9b430fac0..864ba3f39 100644 --- a/src/vendor/go.opentelemetry.io/otel/semconv/v1.7.0/http.go +++ b/src/vendor/go.opentelemetry.io/otel/semconv/internal/http.go @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -package semconv // import "go.opentelemetry.io/otel/semconv/v1.7.0" +package internal // import "go.opentelemetry.io/otel/semconv/internal" import ( "fmt" @@ -21,47 +21,70 @@ import ( "strconv" "strings" - "go.opentelemetry.io/otel/trace" - "go.opentelemetry.io/otel/attribute" "go.opentelemetry.io/otel/codes" + "go.opentelemetry.io/otel/trace" ) -// HTTP scheme attributes. -var ( - HTTPSchemeHTTP = HTTPSchemeKey.String("http") - HTTPSchemeHTTPS = HTTPSchemeKey.String("https") -) +// SemanticConventions are the semantic convention values defined for a +// version of the OpenTelemetry specification. +type SemanticConventions struct { + EnduserIDKey attribute.Key + HTTPClientIPKey attribute.Key + HTTPFlavorKey attribute.Key + HTTPHostKey attribute.Key + HTTPMethodKey attribute.Key + HTTPRequestContentLengthKey attribute.Key + HTTPRouteKey attribute.Key + HTTPSchemeHTTP attribute.KeyValue + HTTPSchemeHTTPS attribute.KeyValue + HTTPServerNameKey attribute.Key + HTTPStatusCodeKey attribute.Key + HTTPTargetKey attribute.Key + HTTPURLKey attribute.Key + HTTPUserAgentKey attribute.Key + NetHostIPKey attribute.Key + NetHostNameKey attribute.Key + NetHostPortKey attribute.Key + NetPeerIPKey attribute.Key + NetPeerNameKey attribute.Key + NetPeerPortKey attribute.Key + NetTransportIP attribute.KeyValue + NetTransportOther attribute.KeyValue + NetTransportTCP attribute.KeyValue + NetTransportUDP attribute.KeyValue + NetTransportUnix attribute.KeyValue +} // NetAttributesFromHTTPRequest generates attributes of the net // namespace as specified by the OpenTelemetry specification for a // span. The network parameter is a string that net.Dial function // from standard library can understand. -func NetAttributesFromHTTPRequest(network string, request *http.Request) []attribute.KeyValue { +func (sc *SemanticConventions) NetAttributesFromHTTPRequest(network string, request *http.Request) []attribute.KeyValue { attrs := []attribute.KeyValue{} switch network { case "tcp", "tcp4", "tcp6": - attrs = append(attrs, NetTransportTCP) + attrs = append(attrs, sc.NetTransportTCP) case "udp", "udp4", "udp6": - attrs = append(attrs, NetTransportUDP) + attrs = append(attrs, sc.NetTransportUDP) case "ip", "ip4", "ip6": - attrs = append(attrs, NetTransportIP) + attrs = append(attrs, sc.NetTransportIP) case "unix", "unixgram", "unixpacket": - attrs = append(attrs, NetTransportUnix) + attrs = append(attrs, sc.NetTransportUnix) default: - attrs = append(attrs, NetTransportOther) + attrs = append(attrs, sc.NetTransportOther) } peerIP, peerName, peerPort := hostIPNamePort(request.RemoteAddr) if peerIP != "" { - attrs = append(attrs, NetPeerIPKey.String(peerIP)) + attrs = append(attrs, sc.NetPeerIPKey.String(peerIP)) } if peerName != "" { - attrs = append(attrs, NetPeerNameKey.String(peerName)) + attrs = append(attrs, sc.NetPeerNameKey.String(peerName)) } if peerPort != 0 { - attrs = append(attrs, NetPeerPortKey.Int(peerPort)) + attrs = append(attrs, sc.NetPeerPortKey.Int(peerPort)) } hostIP, hostName, hostPort := "", "", 0 @@ -72,13 +95,13 @@ func NetAttributesFromHTTPRequest(network string, request *http.Request) []attri } } if hostIP != "" { - attrs = append(attrs, NetHostIPKey.String(hostIP)) + attrs = append(attrs, sc.NetHostIPKey.String(hostIP)) } if hostName != "" { - attrs = append(attrs, NetHostNameKey.String(hostName)) + attrs = append(attrs, sc.NetHostNameKey.String(hostName)) } if hostPort != 0 { - attrs = append(attrs, NetHostPortKey.Int(hostPort)) + attrs = append(attrs, sc.NetHostPortKey.Int(hostPort)) } return attrs @@ -111,9 +134,9 @@ func hostIPNamePort(hostWithPort string) (ip string, name string, port int) { // EndUserAttributesFromHTTPRequest generates attributes of the // enduser namespace as specified by the OpenTelemetry specification // for a span. -func EndUserAttributesFromHTTPRequest(request *http.Request) []attribute.KeyValue { +func (sc *SemanticConventions) EndUserAttributesFromHTTPRequest(request *http.Request) []attribute.KeyValue { if username, _, ok := request.BasicAuth(); ok { - return []attribute.KeyValue{EnduserIDKey.String(username)} + return []attribute.KeyValue{sc.EnduserIDKey.String(username)} } return nil } @@ -121,13 +144,13 @@ func EndUserAttributesFromHTTPRequest(request *http.Request) []attribute.KeyValu // HTTPClientAttributesFromHTTPRequest generates attributes of the // http namespace as specified by the OpenTelemetry specification for // a span on the client side. -func HTTPClientAttributesFromHTTPRequest(request *http.Request) []attribute.KeyValue { +func (sc *SemanticConventions) HTTPClientAttributesFromHTTPRequest(request *http.Request) []attribute.KeyValue { attrs := []attribute.KeyValue{} if request.Method != "" { - attrs = append(attrs, HTTPMethodKey.String(request.Method)) + attrs = append(attrs, sc.HTTPMethodKey.String(request.Method)) } else { - attrs = append(attrs, HTTPMethodKey.String(http.MethodGet)) + attrs = append(attrs, sc.HTTPMethodKey.String(http.MethodGet)) } // remove any username/password info that may be in the URL @@ -135,38 +158,40 @@ func HTTPClientAttributesFromHTTPRequest(request *http.Request) []attribute.KeyV userinfo := request.URL.User request.URL.User = nil - attrs = append(attrs, HTTPURLKey.String(request.URL.String())) + attrs = append(attrs, sc.HTTPURLKey.String(request.URL.String())) // restore any username/password info that was removed request.URL.User = userinfo - return append(attrs, httpCommonAttributesFromHTTPRequest(request)...) + return append(attrs, sc.httpCommonAttributesFromHTTPRequest(request)...) } -func httpCommonAttributesFromHTTPRequest(request *http.Request) []attribute.KeyValue { +func (sc *SemanticConventions) httpCommonAttributesFromHTTPRequest(request *http.Request) []attribute.KeyValue { attrs := []attribute.KeyValue{} if ua := request.UserAgent(); ua != "" { - attrs = append(attrs, HTTPUserAgentKey.String(ua)) + attrs = append(attrs, sc.HTTPUserAgentKey.String(ua)) } if request.ContentLength > 0 { - attrs = append(attrs, HTTPRequestContentLengthKey.Int64(request.ContentLength)) + attrs = append(attrs, sc.HTTPRequestContentLengthKey.Int64(request.ContentLength)) } - return append(attrs, httpBasicAttributesFromHTTPRequest(request)...) + return append(attrs, sc.httpBasicAttributesFromHTTPRequest(request)...) } -func httpBasicAttributesFromHTTPRequest(request *http.Request) []attribute.KeyValue { +func (sc *SemanticConventions) httpBasicAttributesFromHTTPRequest(request *http.Request) []attribute.KeyValue { // as these attributes are used by HTTPServerMetricAttributesFromHTTPRequest, they should be low-cardinality attrs := []attribute.KeyValue{} if request.TLS != nil { - attrs = append(attrs, HTTPSchemeHTTPS) + attrs = append(attrs, sc.HTTPSchemeHTTPS) } else { - attrs = append(attrs, HTTPSchemeHTTP) + attrs = append(attrs, sc.HTTPSchemeHTTP) } if request.Host != "" { - attrs = append(attrs, HTTPHostKey.String(request.Host)) + attrs = append(attrs, sc.HTTPHostKey.String(request.Host)) + } else if request.URL != nil && request.URL.Host != "" { + attrs = append(attrs, sc.HTTPHostKey.String(request.URL.Host)) } flavor := "" @@ -176,7 +201,7 @@ func httpBasicAttributesFromHTTPRequest(request *http.Request) []attribute.KeyVa flavor = "2" } if flavor != "" { - attrs = append(attrs, HTTPFlavorKey.String(flavor)) + attrs = append(attrs, sc.HTTPFlavorKey.String(flavor)) } return attrs @@ -184,45 +209,45 @@ func httpBasicAttributesFromHTTPRequest(request *http.Request) []attribute.KeyVa // HTTPServerMetricAttributesFromHTTPRequest generates low-cardinality attributes // to be used with server-side HTTP metrics. -func HTTPServerMetricAttributesFromHTTPRequest(serverName string, request *http.Request) []attribute.KeyValue { +func (sc *SemanticConventions) HTTPServerMetricAttributesFromHTTPRequest(serverName string, request *http.Request) []attribute.KeyValue { attrs := []attribute.KeyValue{} if serverName != "" { - attrs = append(attrs, HTTPServerNameKey.String(serverName)) + attrs = append(attrs, sc.HTTPServerNameKey.String(serverName)) } - return append(attrs, httpBasicAttributesFromHTTPRequest(request)...) + return append(attrs, sc.httpBasicAttributesFromHTTPRequest(request)...) } // HTTPServerAttributesFromHTTPRequest generates attributes of the // http namespace as specified by the OpenTelemetry specification for // a span on the server side. Currently, only basic authentication is // supported. -func HTTPServerAttributesFromHTTPRequest(serverName, route string, request *http.Request) []attribute.KeyValue { +func (sc *SemanticConventions) HTTPServerAttributesFromHTTPRequest(serverName, route string, request *http.Request) []attribute.KeyValue { attrs := []attribute.KeyValue{ - HTTPMethodKey.String(request.Method), - HTTPTargetKey.String(request.RequestURI), + sc.HTTPMethodKey.String(request.Method), + sc.HTTPTargetKey.String(request.RequestURI), } if serverName != "" { - attrs = append(attrs, HTTPServerNameKey.String(serverName)) + attrs = append(attrs, sc.HTTPServerNameKey.String(serverName)) } if route != "" { - attrs = append(attrs, HTTPRouteKey.String(route)) + attrs = append(attrs, sc.HTTPRouteKey.String(route)) } if values, ok := request.Header["X-Forwarded-For"]; ok && len(values) > 0 { if addresses := strings.SplitN(values[0], ",", 2); len(addresses) > 0 { - attrs = append(attrs, HTTPClientIPKey.String(addresses[0])) + attrs = append(attrs, sc.HTTPClientIPKey.String(addresses[0])) } } - return append(attrs, httpCommonAttributesFromHTTPRequest(request)...) + return append(attrs, sc.httpCommonAttributesFromHTTPRequest(request)...) } // HTTPAttributesFromHTTPStatusCode generates attributes of the http // namespace as specified by the OpenTelemetry specification for a // span. -func HTTPAttributesFromHTTPStatusCode(code int) []attribute.KeyValue { +func (sc *SemanticConventions) HTTPAttributesFromHTTPStatusCode(code int) []attribute.KeyValue { attrs := []attribute.KeyValue{ - HTTPStatusCodeKey.Int(code), + sc.HTTPStatusCodeKey.Int(code), } return attrs } @@ -286,9 +311,9 @@ func SpanStatusFromHTTPStatusCodeAndSpanKind(code int, spanKind trace.SpanKind) return spanCode, "" } -// Validates the HTTP status code and returns corresponding span status code. -// If the `code` is not a valid HTTP status code, returns span status Error -// and false. +// validateHTTPStatusCode validates the HTTP status code and returns +// corresponding span status code. If the `code` is not a valid HTTP status +// code, returns span status Error and false. func validateHTTPStatusCode(code int) (codes.Code, bool) { category := code / 100 ranges, ok := validRangesPerCategory[category] diff --git a/src/vendor/go.opentelemetry.io/otel/semconv/v1.7.0/doc.go b/src/vendor/go.opentelemetry.io/otel/semconv/v1.10.0/doc.go similarity index 85% rename from src/vendor/go.opentelemetry.io/otel/semconv/v1.7.0/doc.go rename to src/vendor/go.opentelemetry.io/otel/semconv/v1.10.0/doc.go index ba878d1cf..0c5fbdcee 100644 --- a/src/vendor/go.opentelemetry.io/otel/semconv/v1.7.0/doc.go +++ b/src/vendor/go.opentelemetry.io/otel/semconv/v1.10.0/doc.go @@ -16,5 +16,5 @@ // // OpenTelemetry semantic conventions are agreed standardized naming // patterns for OpenTelemetry things. This package represents the conventions -// as of the v1.7.0 version of the OpenTelemetry specification. -package semconv // import "go.opentelemetry.io/otel/semconv/v1.7.0" +// as of the v1.10.0 version of the OpenTelemetry specification. +package semconv // import "go.opentelemetry.io/otel/semconv/v1.10.0" diff --git a/src/vendor/go.opentelemetry.io/otel/semconv/v1.7.0/exception.go b/src/vendor/go.opentelemetry.io/otel/semconv/v1.10.0/exception.go similarity index 91% rename from src/vendor/go.opentelemetry.io/otel/semconv/v1.7.0/exception.go rename to src/vendor/go.opentelemetry.io/otel/semconv/v1.10.0/exception.go index ea3706862..f4fc8c7aa 100644 --- a/src/vendor/go.opentelemetry.io/otel/semconv/v1.7.0/exception.go +++ b/src/vendor/go.opentelemetry.io/otel/semconv/v1.10.0/exception.go @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -package semconv // import "go.opentelemetry.io/otel/semconv/v1.7.0" +package semconv // import "go.opentelemetry.io/otel/semconv/v1.10.0" const ( // ExceptionEventName is the name of the Span event representing an exception. diff --git a/src/vendor/go.opentelemetry.io/otel/semconv/v1.10.0/http.go b/src/vendor/go.opentelemetry.io/otel/semconv/v1.10.0/http.go new file mode 100644 index 000000000..6271b1926 --- /dev/null +++ b/src/vendor/go.opentelemetry.io/otel/semconv/v1.10.0/http.go @@ -0,0 +1,114 @@ +// Copyright The OpenTelemetry 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 semconv // import "go.opentelemetry.io/otel/semconv/v1.10.0" + +import ( + "net/http" + + "go.opentelemetry.io/otel/attribute" + "go.opentelemetry.io/otel/codes" + "go.opentelemetry.io/otel/semconv/internal" + "go.opentelemetry.io/otel/trace" +) + +// HTTP scheme attributes. +var ( + HTTPSchemeHTTP = HTTPSchemeKey.String("http") + HTTPSchemeHTTPS = HTTPSchemeKey.String("https") +) + +var sc = &internal.SemanticConventions{ + EnduserIDKey: EnduserIDKey, + HTTPClientIPKey: HTTPClientIPKey, + HTTPFlavorKey: HTTPFlavorKey, + HTTPHostKey: HTTPHostKey, + HTTPMethodKey: HTTPMethodKey, + HTTPRequestContentLengthKey: HTTPRequestContentLengthKey, + HTTPRouteKey: HTTPRouteKey, + HTTPSchemeHTTP: HTTPSchemeHTTP, + HTTPSchemeHTTPS: HTTPSchemeHTTPS, + HTTPServerNameKey: HTTPServerNameKey, + HTTPStatusCodeKey: HTTPStatusCodeKey, + HTTPTargetKey: HTTPTargetKey, + HTTPURLKey: HTTPURLKey, + HTTPUserAgentKey: HTTPUserAgentKey, + NetHostIPKey: NetHostIPKey, + NetHostNameKey: NetHostNameKey, + NetHostPortKey: NetHostPortKey, + NetPeerIPKey: NetPeerIPKey, + NetPeerNameKey: NetPeerNameKey, + NetPeerPortKey: NetPeerPortKey, + NetTransportIP: NetTransportIP, + NetTransportOther: NetTransportOther, + NetTransportTCP: NetTransportTCP, + NetTransportUDP: NetTransportUDP, + NetTransportUnix: NetTransportUnix, +} + +// NetAttributesFromHTTPRequest generates attributes of the net +// namespace as specified by the OpenTelemetry specification for a +// span. The network parameter is a string that net.Dial function +// from standard library can understand. +func NetAttributesFromHTTPRequest(network string, request *http.Request) []attribute.KeyValue { + return sc.NetAttributesFromHTTPRequest(network, request) +} + +// EndUserAttributesFromHTTPRequest generates attributes of the +// enduser namespace as specified by the OpenTelemetry specification +// for a span. +func EndUserAttributesFromHTTPRequest(request *http.Request) []attribute.KeyValue { + return sc.EndUserAttributesFromHTTPRequest(request) +} + +// HTTPClientAttributesFromHTTPRequest generates attributes of the +// http namespace as specified by the OpenTelemetry specification for +// a span on the client side. +func HTTPClientAttributesFromHTTPRequest(request *http.Request) []attribute.KeyValue { + return sc.HTTPClientAttributesFromHTTPRequest(request) +} + +// HTTPServerMetricAttributesFromHTTPRequest generates low-cardinality attributes +// to be used with server-side HTTP metrics. +func HTTPServerMetricAttributesFromHTTPRequest(serverName string, request *http.Request) []attribute.KeyValue { + return sc.HTTPServerMetricAttributesFromHTTPRequest(serverName, request) +} + +// HTTPServerAttributesFromHTTPRequest generates attributes of the +// http namespace as specified by the OpenTelemetry specification for +// a span on the server side. Currently, only basic authentication is +// supported. +func HTTPServerAttributesFromHTTPRequest(serverName, route string, request *http.Request) []attribute.KeyValue { + return sc.HTTPServerAttributesFromHTTPRequest(serverName, route, request) +} + +// HTTPAttributesFromHTTPStatusCode generates attributes of the http +// namespace as specified by the OpenTelemetry specification for a +// span. +func HTTPAttributesFromHTTPStatusCode(code int) []attribute.KeyValue { + return sc.HTTPAttributesFromHTTPStatusCode(code) +} + +// SpanStatusFromHTTPStatusCode generates a status code and a message +// as specified by the OpenTelemetry specification for a span. +func SpanStatusFromHTTPStatusCode(code int) (codes.Code, string) { + return internal.SpanStatusFromHTTPStatusCode(code) +} + +// SpanStatusFromHTTPStatusCodeAndSpanKind generates a status code and a message +// as specified by the OpenTelemetry specification for a span. +// Exclude 4xx for SERVER to set the appropriate status. +func SpanStatusFromHTTPStatusCodeAndSpanKind(code int, spanKind trace.SpanKind) (codes.Code, string) { + return internal.SpanStatusFromHTTPStatusCodeAndSpanKind(code, spanKind) +} diff --git a/src/vendor/go.opentelemetry.io/otel/semconv/v1.7.0/resource.go b/src/vendor/go.opentelemetry.io/otel/semconv/v1.10.0/resource.go similarity index 93% rename from src/vendor/go.opentelemetry.io/otel/semconv/v1.7.0/resource.go rename to src/vendor/go.opentelemetry.io/otel/semconv/v1.10.0/resource.go index aab6daf3c..593017eea 100644 --- a/src/vendor/go.opentelemetry.io/otel/semconv/v1.7.0/resource.go +++ b/src/vendor/go.opentelemetry.io/otel/semconv/v1.10.0/resource.go @@ -14,7 +14,7 @@ // Code generated from semantic convention specification. DO NOT EDIT. -package semconv // import "go.opentelemetry.io/otel/semconv/v1.7.0" +package semconv // import "go.opentelemetry.io/otel/semconv/v1.10.0" import "go.opentelemetry.io/otel/attribute" @@ -33,18 +33,19 @@ const ( // Stability: stable // Examples: '111111111111', 'opentelemetry' CloudAccountIDKey = attribute.Key("cloud.account.id") - // The geographical region the resource is running. Refer to your provider's docs - // to see the available regions, for example [Alibaba Cloud - // regions](https://www.alibabacloud.com/help/doc-detail/40654.htm), [AWS - // regions](https://aws.amazon.com/about-aws/global-infrastructure/regions_az/), - // [Azure regions](https://azure.microsoft.com/en-us/global- - // infrastructure/geographies/), or [Google Cloud - // regions](https://cloud.google.com/about/locations). + // The geographical region the resource is running. // // Type: string // Required: No // Stability: stable // Examples: 'us-central1', 'us-east-1' + // Note: Refer to your provider's docs to see the available regions, for example + // [Alibaba Cloud regions](https://www.alibabacloud.com/help/doc- + // detail/40654.htm), [AWS regions](https://aws.amazon.com/about-aws/global- + // infrastructure/regions_az/), [Azure regions](https://azure.microsoft.com/en- + // us/global-infrastructure/geographies/), [Google Cloud + // regions](https://cloud.google.com/about/locations), or [Tencent Cloud + // regions](https://intl.cloud.tencent.com/document/product/213/6091). CloudRegionKey = attribute.Key("cloud.region") // Cloud regions often have multiple, isolated locations known as zones to // increase availability. Availability zone represents the zone where the resource @@ -75,6 +76,8 @@ var ( CloudProviderAzure = CloudProviderKey.String("azure") // Google Cloud Platform CloudProviderGCP = CloudProviderKey.String("gcp") + // Tencent Cloud + CloudProviderTencentCloud = CloudProviderKey.String("tencent_cloud") ) var ( @@ -92,6 +95,8 @@ var ( CloudPlatformAWSLambda = CloudPlatformKey.String("aws_lambda") // AWS Elastic Beanstalk CloudPlatformAWSElasticBeanstalk = CloudPlatformKey.String("aws_elastic_beanstalk") + // AWS App Runner + CloudPlatformAWSAppRunner = CloudPlatformKey.String("aws_app_runner") // Azure Virtual Machines CloudPlatformAzureVM = CloudPlatformKey.String("azure_vm") // Azure Container Instances @@ -112,6 +117,12 @@ var ( CloudPlatformGCPCloudFunctions = CloudPlatformKey.String("gcp_cloud_functions") // Google Cloud App Engine (GAE) CloudPlatformGCPAppEngine = CloudPlatformKey.String("gcp_app_engine") + // Tencent Cloud Cloud Virtual Machine (CVM) + CloudPlatformTencentCloudCvm = CloudPlatformKey.String("tencent_cloud_cvm") + // Tencent Cloud Elastic Kubernetes Service (EKS) + CloudPlatformTencentCloudEKS = CloudPlatformKey.String("tencent_cloud_eks") + // Tencent Cloud Serverless Cloud Function (SCF) + CloudPlatformTencentCloudScf = CloudPlatformKey.String("tencent_cloud_scf") ) // Resources used by AWS Elastic Container Service (ECS). @@ -229,7 +240,7 @@ const ( // A container instance. const ( - // Container name. + // Container name used by container runtime. // // Type: string // Required: No @@ -320,6 +331,16 @@ const ( // Note: It's recommended this value represents a human readable version of the // device model rather than a machine readable alternative. DeviceModelNameKey = attribute.Key("device.model.name") + // The name of the device manufacturer + // + // Type: string + // Required: No + // Stability: stable + // Examples: 'Apple', 'Samsung' + // Note: The Android OS provides this field via + // [Build](https://developer.android.com/reference/android/os/Build#MANUFACTURER). + // iOS apps SHOULD hardcode the value `Apple`. + DeviceManufacturerKey = attribute.Key("device.manufacturer") ) // A serverless instance. @@ -476,6 +497,8 @@ var ( HostArchPPC32 = HostArchKey.String("ppc32") // 64-bit PowerPC HostArchPPC64 = HostArchKey.String("ppc64") + // IBM z/Architecture + HostArchS390x = HostArchKey.String("s390x") // 32-bit x86 HostArchX86 = HostArchKey.String("x86") ) @@ -540,13 +563,23 @@ const ( // A container in a [PodTemplate](https://kubernetes.io/docs/concepts/workloads/pods/#pod-templates). const ( - // The name of the Container in a Pod template. + // The name of the Container from Pod specification, must be unique within a Pod. + // Container runtime usually uses different globally unique name + // (`container.name`). // // Type: string // Required: No // Stability: stable // Examples: 'redis' K8SContainerNameKey = attribute.Key("k8s.container.name") + // Number of times the container was restarted. This attribute can be used to + // identify a particular container (running or stopped) within a container spec. + // + // Type: int + // Required: No + // Stability: stable + // Examples: 0, 2 + K8SContainerRestartCountKey = attribute.Key("k8s.container.restart_count") ) // A Kubernetes ReplicaSet object. @@ -917,6 +950,8 @@ var ( TelemetrySDKLanguageRuby = TelemetrySDKLanguageKey.String("ruby") // webjs TelemetrySDKLanguageWebjs = TelemetrySDKLanguageKey.String("webjs") + // swift + TelemetrySDKLanguageSwift = TelemetrySDKLanguageKey.String("swift") ) // Resource describing the packaged software running the application code. Web engines are typically executed using process.runtime. diff --git a/src/vendor/go.opentelemetry.io/otel/semconv/v1.7.0/schema.go b/src/vendor/go.opentelemetry.io/otel/semconv/v1.10.0/schema.go similarity index 86% rename from src/vendor/go.opentelemetry.io/otel/semconv/v1.7.0/schema.go rename to src/vendor/go.opentelemetry.io/otel/semconv/v1.10.0/schema.go index ec8b463d9..74f2e1d50 100644 --- a/src/vendor/go.opentelemetry.io/otel/semconv/v1.7.0/schema.go +++ b/src/vendor/go.opentelemetry.io/otel/semconv/v1.10.0/schema.go @@ -12,9 +12,9 @@ // See the License for the specific language governing permissions and // limitations under the License. -package semconv // import "go.opentelemetry.io/otel/semconv/v1.7.0" +package semconv // import "go.opentelemetry.io/otel/semconv/v1.10.0" // SchemaURL is the schema URL that matches the version of the semantic conventions // that this package defines. Semconv packages starting from v1.4.0 must declare // non-empty schema URL in the form https://opentelemetry.io/schemas/ -const SchemaURL = "https://opentelemetry.io/schemas/v1.7.0" +const SchemaURL = "https://opentelemetry.io/schemas/1.10.0" diff --git a/src/vendor/go.opentelemetry.io/otel/semconv/v1.7.0/trace.go b/src/vendor/go.opentelemetry.io/otel/semconv/v1.10.0/trace.go similarity index 88% rename from src/vendor/go.opentelemetry.io/otel/semconv/v1.7.0/trace.go rename to src/vendor/go.opentelemetry.io/otel/semconv/v1.10.0/trace.go index 9b75bd77a..faa3d9c86 100644 --- a/src/vendor/go.opentelemetry.io/otel/semconv/v1.7.0/trace.go +++ b/src/vendor/go.opentelemetry.io/otel/semconv/v1.10.0/trace.go @@ -14,7 +14,7 @@ // Code generated from semantic convention specification. DO NOT EDIT. -package semconv // import "go.opentelemetry.io/otel/semconv/v1.7.0" +package semconv // import "go.opentelemetry.io/otel/semconv/v1.10.0" import "go.opentelemetry.io/otel/attribute" @@ -32,6 +32,71 @@ const ( AWSLambdaInvokedARNKey = attribute.Key("aws.lambda.invoked_arn") ) +// This document defines attributes for CloudEvents. CloudEvents is a specification on how to define event data in a standard way. These attributes can be attached to spans when performing operations with CloudEvents, regardless of the protocol being used. +const ( + // The [event_id](https://github.com/cloudevents/spec/blob/v1.0.2/cloudevents/spec + // .md#id) uniquely identifies the event. + // + // Type: string + // Required: Always + // Stability: stable + // Examples: '123e4567-e89b-12d3-a456-426614174000', '0001' + CloudeventsEventIDKey = attribute.Key("cloudevents.event_id") + // The [source](https://github.com/cloudevents/spec/blob/v1.0.2/cloudevents/spec.m + // d#source-1) identifies the context in which an event happened. + // + // Type: string + // Required: Always + // Stability: stable + // Examples: 'https://github.com/cloudevents', '/cloudevents/spec/pull/123', 'my- + // service' + CloudeventsEventSourceKey = attribute.Key("cloudevents.event_source") + // The [version of the CloudEvents specification](https://github.com/cloudevents/s + // pec/blob/v1.0.2/cloudevents/spec.md#specversion) which the event uses. + // + // Type: string + // Required: Always + // Stability: stable + // Examples: '1.0' + CloudeventsEventSpecVersionKey = attribute.Key("cloudevents.event_spec_version") + // The [event_type](https://github.com/cloudevents/spec/blob/v1.0.2/cloudevents/sp + // ec.md#type) contains a value describing the type of event related to the + // originating occurrence. + // + // Type: string + // Required: Always + // Stability: stable + // Examples: 'com.github.pull_request.opened', 'com.example.object.deleted.v2' + CloudeventsEventTypeKey = attribute.Key("cloudevents.event_type") + // The [subject](https://github.com/cloudevents/spec/blob/v1.0.2/cloudevents/spec. + // md#subject) of the event in the context of the event producer (identified by + // source). + // + // Type: string + // Required: No + // Stability: stable + // Examples: 'mynewfile.jpg' + CloudeventsEventSubjectKey = attribute.Key("cloudevents.event_subject") +) + +// This document defines semantic conventions for the OpenTracing Shim +const ( + // Parent-child Reference type + // + // Type: Enum + // Required: No + // Stability: stable + // Note: The causal relationship between a child Span and a parent Span. + OpentracingRefTypeKey = attribute.Key("opentracing.ref_type") +) + +var ( + // The parent Span depends on the child Span in some capacity + OpentracingRefTypeChildOf = OpentracingRefTypeKey.String("child_of") + // The parent Span does not depend in any way on the result of the child Span + OpentracingRefTypeFollowsFrom = OpentracingRefTypeKey.String("follows_from") +) + // This document defines the attributes used to perform database client calls. const ( // An identifier for the database management system (DBMS) product being used. See @@ -66,17 +131,18 @@ const ( // Examples: 'org.postgresql.Driver', // 'com.microsoft.sqlserver.jdbc.SQLServerDriver' DBJDBCDriverClassnameKey = attribute.Key("db.jdbc.driver_classname") - // If no [tech-specific attribute](#call-level-attributes-for-specific- - // technologies) is defined, this attribute is used to report the name of the - // database being accessed. For commands that switch the database, this should be - // set to the target database (even if the command fails). + // This attribute is used to report the name of the database being accessed. For + // commands that switch the database, this should be set to the target database + // (even if the command fails). // // Type: string - // Required: Required, if applicable and no more-specific attribute is defined. + // Required: Required, if applicable. // Stability: stable // Examples: 'customers', 'main' // Note: In some SQL databases, the database name to be used is called "schema - // name". + // name". In case there are multiple layers that could be considered for database + // name (e.g. Oracle instance name and schema name), the database name to be used + // is the more specific layer (e.g. Oracle schema name). DBNameKey = attribute.Key("db.name") // The database statement being executed. // @@ -217,14 +283,6 @@ const ( // Call-level attributes for Cassandra const ( - // The name of the keyspace being accessed. To be used instead of the generic - // `db.name` attribute. - // - // Type: string - // Required: Always - // Stability: stable - // Examples: 'mykeyspace' - DBCassandraKeyspaceKey = attribute.Key("db.cassandra.keyspace") // The fetch size used for paging, i.e. how many rows will be returned at once. // // Type: int @@ -241,7 +299,7 @@ const ( // Stability: stable DBCassandraConsistencyLevelKey = attribute.Key("db.cassandra.consistency_level") // The name of the primary table that the operation is acting upon, including the - // schema name (if applicable). + // keyspace name (if applicable). // // Type: string // Required: Recommended if available. @@ -308,18 +366,6 @@ var ( DBCassandraConsistencyLevelLocalSerial = DBCassandraConsistencyLevelKey.String("local_serial") ) -// Call-level attributes for Apache HBase -const ( - // The [HBase namespace](https://hbase.apache.org/book.html#_namespace) being - // accessed. To be used instead of the generic `db.name` attribute. - // - // Type: string - // Required: Always - // Stability: stable - // Examples: 'default' - DBHBaseNamespaceKey = attribute.Key("db.hbase.namespace") -) - // Call-level attributes for Redis const ( // The index of the database being accessed as used in the [`SELECT` @@ -344,10 +390,10 @@ const ( DBMongoDBCollectionKey = attribute.Key("db.mongodb.collection") ) -// Call-level attrbiutes for SQL databases +// Call-level attributes for SQL databases const ( // The name of the primary table that the operation is acting upon, including the - // schema name (if applicable). + // database name (if applicable). // // Type: string // Required: Recommended if available. @@ -408,7 +454,7 @@ const ( // whether it will escape the scope of a span. // However, it is trivial to know that an exception // will escape, if one checks for an active exception just before ending the span, - // as done in the [example above](#exception-end-example). + // as done in the [example above](#recording-an-exception). // It follows that an exception may still escape the scope of the span // even if the `exception.escaped` attribute was not set or set to false, @@ -419,15 +465,20 @@ const ( // This semantic convention describes an instance of a function that runs without provisioning or managing of servers (also known as serverless functions or Function as a Service (FaaS)) with spans. const ( - // Type of the trigger on which the function is executed. + // Type of the trigger which caused this function execution. // // Type: Enum - // Required: On FaaS instances, faas.trigger MUST be set on incoming invocations. - // Clients invoking FaaS instances MUST set `faas.trigger` on outgoing - // invocations, if it is known to the client. This is, for example, not the case, - // when the transport layer is abstracted in a FaaS client framework without - // access to its configuration. + // Required: No // Stability: stable + // Note: For the server/consumer span on the incoming side, + // `faas.trigger` MUST be set. + + // Clients invoking FaaS instances usually cannot set `faas.trigger`, + // since they would typically need to look in the payload to determine + // the event type. If clients set it, it should be the same as the + // trigger that corresponding incoming would have (i.e., this has + // nothing to do with the underlying transport used to make the API + // call to invoke the lambda, which is often HTTP). FaaSTriggerKey = attribute.Key("faas.trigger") // The execution ID of the current function execution. // @@ -572,6 +623,8 @@ var ( FaaSInvokedProviderAzure = FaaSInvokedProviderKey.String("azure") // Google Cloud Platform FaaSInvokedProviderGCP = FaaSInvokedProviderKey.String("gcp") + // Tencent Cloud + FaaSInvokedProviderTencentCloud = FaaSInvokedProviderKey.String("tencent_cloud") ) // These attributes may be used for any network related operation. @@ -957,6 +1010,13 @@ const ( // Stability: stable // Examples: 5493 HTTPResponseContentLengthUncompressedKey = attribute.Key("http.response_content_length_uncompressed") + // The ordinal number of request re-sending attempt. + // + // Type: int + // Required: If and only if a request was retried. + // Stability: stable + // Examples: 3 + HTTPRetryCountKey = attribute.Key("http.retry_count") ) var ( @@ -1224,7 +1284,7 @@ const ( // Type: string // Required: Always // Stability: stable - // Examples: 'kafka', 'rabbitmq', 'activemq', 'AmazonSQS' + // Examples: 'kafka', 'rabbitmq', 'rocketmq', 'activemq', 'AmazonSQS' MessagingSystemKey = attribute.Key("messaging.system") // The message destination name. This might be equal to the span name but is // required nevertheless. @@ -1396,14 +1456,85 @@ const ( MessagingKafkaTombstoneKey = attribute.Key("messaging.kafka.tombstone") ) -// This document defines semantic conventions for remote procedure calls. +// Attributes for Apache RocketMQ const ( - // A string identifying the remoting system. + // Namespace of RocketMQ resources, resources in different namespaces are + // individual. // // Type: string // Required: Always // Stability: stable - // Examples: 'grpc', 'java_rmi', 'wcf' + // Examples: 'myNamespace' + MessagingRocketmqNamespaceKey = attribute.Key("messaging.rocketmq.namespace") + // Name of the RocketMQ producer/consumer group that is handling the message. The + // client type is identified by the SpanKind. + // + // Type: string + // Required: Always + // Stability: stable + // Examples: 'myConsumerGroup' + MessagingRocketmqClientGroupKey = attribute.Key("messaging.rocketmq.client_group") + // The unique identifier for each client. + // + // Type: string + // Required: Always + // Stability: stable + // Examples: 'myhost@8742@s8083jm' + MessagingRocketmqClientIDKey = attribute.Key("messaging.rocketmq.client_id") + // Type of message. + // + // Type: Enum + // Required: No + // Stability: stable + MessagingRocketmqMessageTypeKey = attribute.Key("messaging.rocketmq.message_type") + // The secondary classifier of message besides topic. + // + // Type: string + // Required: No + // Stability: stable + // Examples: 'tagA' + MessagingRocketmqMessageTagKey = attribute.Key("messaging.rocketmq.message_tag") + // Key(s) of message, another way to mark message besides message id. + // + // Type: string[] + // Required: No + // Stability: stable + // Examples: 'keyA', 'keyB' + MessagingRocketmqMessageKeysKey = attribute.Key("messaging.rocketmq.message_keys") + // Model of message consumption. This only applies to consumer spans. + // + // Type: Enum + // Required: No + // Stability: stable + MessagingRocketmqConsumptionModelKey = attribute.Key("messaging.rocketmq.consumption_model") +) + +var ( + // Normal message + MessagingRocketmqMessageTypeNormal = MessagingRocketmqMessageTypeKey.String("normal") + // FIFO message + MessagingRocketmqMessageTypeFifo = MessagingRocketmqMessageTypeKey.String("fifo") + // Delay message + MessagingRocketmqMessageTypeDelay = MessagingRocketmqMessageTypeKey.String("delay") + // Transaction message + MessagingRocketmqMessageTypeTransaction = MessagingRocketmqMessageTypeKey.String("transaction") +) + +var ( + // Clustering consumption model + MessagingRocketmqConsumptionModelClustering = MessagingRocketmqConsumptionModelKey.String("clustering") + // Broadcasting consumption model + MessagingRocketmqConsumptionModelBroadcasting = MessagingRocketmqConsumptionModelKey.String("broadcasting") +) + +// This document defines semantic conventions for remote procedure calls. +const ( + // A string identifying the remoting system. See below for a list of well-known + // identifiers. + // + // Type: Enum + // Required: Always + // Stability: stable RPCSystemKey = attribute.Key("rpc.system") // The full (logical) name of the service being called, including its package // name, if applicable. @@ -1434,6 +1565,17 @@ const ( RPCMethodKey = attribute.Key("rpc.method") ) +var ( + // gRPC + RPCSystemGRPC = RPCSystemKey.String("grpc") + // Java RMI + RPCSystemJavaRmi = RPCSystemKey.String("java_rmi") + // .NET WCF + RPCSystemDotnetWcf = RPCSystemKey.String("dotnet_wcf") + // Apache Dubbo + RPCSystemApacheDubbo = RPCSystemKey.String("apache_dubbo") +) + // Tech-specific attributes for gRPC. const ( // The [numeric status diff --git a/src/vendor/go.opentelemetry.io/otel/semconv/v1.4.0/http.go b/src/vendor/go.opentelemetry.io/otel/semconv/v1.4.0/http.go index d9537ff39..8d814edc2 100644 --- a/src/vendor/go.opentelemetry.io/otel/semconv/v1.4.0/http.go +++ b/src/vendor/go.opentelemetry.io/otel/semconv/v1.4.0/http.go @@ -15,14 +15,11 @@ package semconv // import "go.opentelemetry.io/otel/semconv/v1.4.0" import ( - "fmt" - "net" "net/http" - "strconv" - "strings" "go.opentelemetry.io/otel/attribute" "go.opentelemetry.io/otel/codes" + "go.opentelemetry.io/otel/semconv/internal" "go.opentelemetry.io/otel/trace" ) @@ -32,163 +29,60 @@ var ( HTTPSchemeHTTPS = HTTPSchemeKey.String("https") ) +var sc = &internal.SemanticConventions{ + EnduserIDKey: EnduserIDKey, + HTTPClientIPKey: HTTPClientIPKey, + HTTPFlavorKey: HTTPFlavorKey, + HTTPHostKey: HTTPHostKey, + HTTPMethodKey: HTTPMethodKey, + HTTPRequestContentLengthKey: HTTPRequestContentLengthKey, + HTTPRouteKey: HTTPRouteKey, + HTTPSchemeHTTP: HTTPSchemeHTTP, + HTTPSchemeHTTPS: HTTPSchemeHTTPS, + HTTPServerNameKey: HTTPServerNameKey, + HTTPStatusCodeKey: HTTPStatusCodeKey, + HTTPTargetKey: HTTPTargetKey, + HTTPURLKey: HTTPURLKey, + HTTPUserAgentKey: HTTPUserAgentKey, + NetHostIPKey: NetHostIPKey, + NetHostNameKey: NetHostNameKey, + NetHostPortKey: NetHostPortKey, + NetPeerIPKey: NetPeerIPKey, + NetPeerNameKey: NetPeerNameKey, + NetPeerPortKey: NetPeerPortKey, + NetTransportIP: NetTransportIP, + NetTransportOther: NetTransportOther, + NetTransportTCP: NetTransportTCP, + NetTransportUDP: NetTransportUDP, + NetTransportUnix: NetTransportUnix, +} + // NetAttributesFromHTTPRequest generates attributes of the net // namespace as specified by the OpenTelemetry specification for a // span. The network parameter is a string that net.Dial function // from standard library can understand. func NetAttributesFromHTTPRequest(network string, request *http.Request) []attribute.KeyValue { - attrs := []attribute.KeyValue{} - - switch network { - case "tcp", "tcp4", "tcp6": - attrs = append(attrs, NetTransportTCP) - case "udp", "udp4", "udp6": - attrs = append(attrs, NetTransportUDP) - case "ip", "ip4", "ip6": - attrs = append(attrs, NetTransportIP) - case "unix", "unixgram", "unixpacket": - attrs = append(attrs, NetTransportUnix) - default: - attrs = append(attrs, NetTransportOther) - } - - peerIP, peerName, peerPort := hostIPNamePort(request.RemoteAddr) - if peerIP != "" { - attrs = append(attrs, NetPeerIPKey.String(peerIP)) - } - if peerName != "" { - attrs = append(attrs, NetPeerNameKey.String(peerName)) - } - if peerPort != 0 { - attrs = append(attrs, NetPeerPortKey.Int(peerPort)) - } - - hostIP, hostName, hostPort := "", "", 0 - for _, someHost := range []string{request.Host, request.Header.Get("Host"), request.URL.Host} { - hostIP, hostName, hostPort = hostIPNamePort(someHost) - if hostIP != "" || hostName != "" || hostPort != 0 { - break - } - } - if hostIP != "" { - attrs = append(attrs, NetHostIPKey.String(hostIP)) - } - if hostName != "" { - attrs = append(attrs, NetHostNameKey.String(hostName)) - } - if hostPort != 0 { - attrs = append(attrs, NetHostPortKey.Int(hostPort)) - } - - return attrs -} - -// hostIPNamePort extracts the IP address, name and (optional) port from hostWithPort. -// It handles both IPv4 and IPv6 addresses. If the host portion is not recognized -// as a valid IPv4 or IPv6 address, the `ip` result will be empty and the -// host portion will instead be returned in `name`. -func hostIPNamePort(hostWithPort string) (ip string, name string, port int) { - var ( - hostPart, portPart string - parsedPort uint64 - err error - ) - if hostPart, portPart, err = net.SplitHostPort(hostWithPort); err != nil { - hostPart, portPart = hostWithPort, "" - } - if parsedIP := net.ParseIP(hostPart); parsedIP != nil { - ip = parsedIP.String() - } else { - name = hostPart - } - if parsedPort, err = strconv.ParseUint(portPart, 10, 16); err == nil { - port = int(parsedPort) - } - return + return sc.NetAttributesFromHTTPRequest(network, request) } // EndUserAttributesFromHTTPRequest generates attributes of the // enduser namespace as specified by the OpenTelemetry specification // for a span. func EndUserAttributesFromHTTPRequest(request *http.Request) []attribute.KeyValue { - if username, _, ok := request.BasicAuth(); ok { - return []attribute.KeyValue{EnduserIDKey.String(username)} - } - return nil + return sc.EndUserAttributesFromHTTPRequest(request) } // HTTPClientAttributesFromHTTPRequest generates attributes of the // http namespace as specified by the OpenTelemetry specification for // a span on the client side. func HTTPClientAttributesFromHTTPRequest(request *http.Request) []attribute.KeyValue { - attrs := []attribute.KeyValue{} - - if request.Method != "" { - attrs = append(attrs, HTTPMethodKey.String(request.Method)) - } else { - attrs = append(attrs, HTTPMethodKey.String(http.MethodGet)) - } - - // remove any username/password info that may be in the URL - // before adding it to the attributes - userinfo := request.URL.User - request.URL.User = nil - - attrs = append(attrs, HTTPURLKey.String(request.URL.String())) - - // restore any username/password info that was removed - request.URL.User = userinfo - - return append(attrs, httpCommonAttributesFromHTTPRequest(request)...) -} - -func httpCommonAttributesFromHTTPRequest(request *http.Request) []attribute.KeyValue { - attrs := []attribute.KeyValue{} - if ua := request.UserAgent(); ua != "" { - attrs = append(attrs, HTTPUserAgentKey.String(ua)) - } - if request.ContentLength > 0 { - attrs = append(attrs, HTTPRequestContentLengthKey.Int64(request.ContentLength)) - } - - return append(attrs, httpBasicAttributesFromHTTPRequest(request)...) -} - -func httpBasicAttributesFromHTTPRequest(request *http.Request) []attribute.KeyValue { - // as these attributes are used by HTTPServerMetricAttributesFromHTTPRequest, they should be low-cardinality - attrs := []attribute.KeyValue{} - - if request.TLS != nil { - attrs = append(attrs, HTTPSchemeHTTPS) - } else { - attrs = append(attrs, HTTPSchemeHTTP) - } - - if request.Host != "" { - attrs = append(attrs, HTTPHostKey.String(request.Host)) - } - - flavor := "" - if request.ProtoMajor == 1 { - flavor = fmt.Sprintf("1.%d", request.ProtoMinor) - } else if request.ProtoMajor == 2 { - flavor = "2" - } - if flavor != "" { - attrs = append(attrs, HTTPFlavorKey.String(flavor)) - } - - return attrs + return sc.HTTPClientAttributesFromHTTPRequest(request) } // HTTPServerMetricAttributesFromHTTPRequest generates low-cardinality attributes // to be used with server-side HTTP metrics. func HTTPServerMetricAttributesFromHTTPRequest(serverName string, request *http.Request) []attribute.KeyValue { - attrs := []attribute.KeyValue{} - if serverName != "" { - attrs = append(attrs, HTTPServerNameKey.String(serverName)) - } - return append(attrs, httpBasicAttributesFromHTTPRequest(request)...) + return sc.HTTPServerMetricAttributesFromHTTPRequest(serverName, request) } // HTTPServerAttributesFromHTTPRequest generates attributes of the @@ -196,116 +90,25 @@ func HTTPServerMetricAttributesFromHTTPRequest(serverName string, request *http. // a span on the server side. Currently, only basic authentication is // supported. func HTTPServerAttributesFromHTTPRequest(serverName, route string, request *http.Request) []attribute.KeyValue { - attrs := []attribute.KeyValue{ - HTTPMethodKey.String(request.Method), - HTTPTargetKey.String(request.RequestURI), - } - - if serverName != "" { - attrs = append(attrs, HTTPServerNameKey.String(serverName)) - } - if route != "" { - attrs = append(attrs, HTTPRouteKey.String(route)) - } - if values, ok := request.Header["X-Forwarded-For"]; ok && len(values) > 0 { - if addresses := strings.SplitN(values[0], ",", 2); len(addresses) > 0 { - attrs = append(attrs, HTTPClientIPKey.String(addresses[0])) - } - } - - return append(attrs, httpCommonAttributesFromHTTPRequest(request)...) + return sc.HTTPServerAttributesFromHTTPRequest(serverName, route, request) } // HTTPAttributesFromHTTPStatusCode generates attributes of the http // namespace as specified by the OpenTelemetry specification for a // span. func HTTPAttributesFromHTTPStatusCode(code int) []attribute.KeyValue { - attrs := []attribute.KeyValue{ - HTTPStatusCodeKey.Int(code), - } - return attrs -} - -type codeRange struct { - fromInclusive int - toInclusive int -} - -func (r codeRange) contains(code int) bool { - return r.fromInclusive <= code && code <= r.toInclusive -} - -var validRangesPerCategory = map[int][]codeRange{ - 1: { - {http.StatusContinue, http.StatusEarlyHints}, - }, - 2: { - {http.StatusOK, http.StatusAlreadyReported}, - {http.StatusIMUsed, http.StatusIMUsed}, - }, - 3: { - {http.StatusMultipleChoices, http.StatusUseProxy}, - {http.StatusTemporaryRedirect, http.StatusPermanentRedirect}, - }, - 4: { - {http.StatusBadRequest, http.StatusTeapot}, // yes, teapot is so useful… - {http.StatusMisdirectedRequest, http.StatusUpgradeRequired}, - {http.StatusPreconditionRequired, http.StatusTooManyRequests}, - {http.StatusRequestHeaderFieldsTooLarge, http.StatusRequestHeaderFieldsTooLarge}, - {http.StatusUnavailableForLegalReasons, http.StatusUnavailableForLegalReasons}, - }, - 5: { - {http.StatusInternalServerError, http.StatusLoopDetected}, - {http.StatusNotExtended, http.StatusNetworkAuthenticationRequired}, - }, + return sc.HTTPAttributesFromHTTPStatusCode(code) } // SpanStatusFromHTTPStatusCode generates a status code and a message // as specified by the OpenTelemetry specification for a span. func SpanStatusFromHTTPStatusCode(code int) (codes.Code, string) { - spanCode, valid := validateHTTPStatusCode(code) - if !valid { - return spanCode, fmt.Sprintf("Invalid HTTP status code %d", code) - } - return spanCode, "" + return internal.SpanStatusFromHTTPStatusCode(code) } // SpanStatusFromHTTPStatusCodeAndSpanKind generates a status code and a message // as specified by the OpenTelemetry specification for a span. // Exclude 4xx for SERVER to set the appropriate status. func SpanStatusFromHTTPStatusCodeAndSpanKind(code int, spanKind trace.SpanKind) (codes.Code, string) { - spanCode, valid := validateHTTPStatusCode(code) - if !valid { - return spanCode, fmt.Sprintf("Invalid HTTP status code %d", code) - } - category := code / 100 - if spanKind == trace.SpanKindServer && category == 4 { - return codes.Unset, "" - } - return spanCode, "" -} - -// Validates the HTTP status code and returns corresponding span status code. -// If the `code` is not a valid HTTP status code, returns span status Error -// and false. -func validateHTTPStatusCode(code int) (codes.Code, bool) { - category := code / 100 - ranges, ok := validRangesPerCategory[category] - if !ok { - return codes.Error, false - } - ok = false - for _, crange := range ranges { - ok = crange.contains(code) - if ok { - break - } - } - if !ok { - return codes.Error, false - } - if category > 0 && category < 4 { - return codes.Unset, true - } - return codes.Error, true + return internal.SpanStatusFromHTTPStatusCodeAndSpanKind(code, spanKind) } diff --git a/src/vendor/go.opentelemetry.io/otel/semconv/v1.4.0/schema.go b/src/vendor/go.opentelemetry.io/otel/semconv/v1.4.0/schema.go index 467a30b9e..a78f1bf40 100644 --- a/src/vendor/go.opentelemetry.io/otel/semconv/v1.4.0/schema.go +++ b/src/vendor/go.opentelemetry.io/otel/semconv/v1.4.0/schema.go @@ -17,4 +17,4 @@ package semconv // import "go.opentelemetry.io/otel/semconv/v1.4.0" // SchemaURL is the schema URL that matches the version of the semantic conventions // that this package defines. Semconv packages starting from v1.4.0 must declare // non-empty schema URL in the form https://opentelemetry.io/schemas/ -const SchemaURL = "https://opentelemetry.io/schemas/v1.4.0" +const SchemaURL = "https://opentelemetry.io/schemas/1.4.0" diff --git a/src/vendor/go.opentelemetry.io/otel/tag.sh b/src/vendor/go.opentelemetry.io/otel/tag.sh deleted file mode 100644 index 70767c703..000000000 --- a/src/vendor/go.opentelemetry.io/otel/tag.sh +++ /dev/null @@ -1,178 +0,0 @@ -#!/usr/bin/env bash - -# Copyright The OpenTelemetry 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. - -readonly PROGNAME=$(basename "$0") -readonly PROGDIR=$(readlink -m "$(dirname "$0")") - -readonly EXCLUDE_PACKAGES="internal/tools" -readonly SEMVER_REGEX="v(0|[1-9][0-9]*)\\.(0|[1-9][0-9]*)\\.(0|[1-9][0-9]*)(\\-[0-9A-Za-z-]+(\\.[0-9A-Za-z-]+)*)?(\\+[0-9A-Za-z-]+(\\.[0-9A-Za-z-]+)*)?" - -usage() { - cat <<- EOF -Usage: $PROGNAME [OPTIONS] SEMVER_TAG COMMIT_HASH - -Creates git tag for all Go packages in project. - -OPTIONS: - -h --help Show this help. - -ARGUMENTS: - SEMVER_TAG Semantic version to tag with. - COMMIT_HASH Git commit hash to tag. -EOF -} - -cmdline() { - local arg commit - - for arg - do - local delim="" - case "$arg" in - # Translate long form options to short form. - --help) args="${args}-h ";; - # Pass through for everything else. - *) [[ "${arg:0:1}" == "-" ]] || delim="\"" - args="${args}${delim}${arg}${delim} ";; - esac - done - - # Reset and process short form options. - eval set -- "$args" - - while getopts "h" OPTION - do - case $OPTION in - h) - usage - exit 0 - ;; - *) - echo "unknown option: $OPTION" - usage - exit 1 - ;; - esac - done - - # Positional arguments. - shift $((OPTIND-1)) - readonly TAG="$1" - if [ -z "$TAG" ] - then - echo "missing SEMVER_TAG" - usage - exit 1 - fi - if [[ ! "$TAG" =~ $SEMVER_REGEX ]] - then - printf "invalid semantic version: %s\n" "$TAG" - exit 2 - fi - if [[ "$( git tag --list "$TAG" )" ]] - then - printf "tag already exists: %s\n" "$TAG" - exit 2 - fi - - shift - commit="$1" - if [ -z "$commit" ] - then - echo "missing COMMIT_HASH" - usage - exit 1 - fi - # Verify rev is for a commit and unify hashes into a complete SHA1. - readonly SHA="$( git rev-parse --quiet --verify "${commit}^{commit}" )" - if [ -z "$SHA" ] - then - printf "invalid commit hash: %s\n" "$commit" - exit 2 - fi - if [ "$( git merge-base "$SHA" HEAD )" != "$SHA" ] - then - printf "commit '%s' not found on this branch\n" "$commit" - exit 2 - fi -} - -package_dirs() { - # Return a list of package directories in the form: - # - # package/directory/a - # package/directory/b - # deeper/package/directory/a - # ... - # - # Making sure to exclude any packages in the EXCLUDE_PACKAGES regexp. - find . -mindepth 2 -type f -name 'go.mod' -exec dirname {} \; \ - | grep -E -v "$EXCLUDE_PACKAGES" \ - | sed 's/^\.\///' \ - | sort -} - -git_tag() { - local tag="$1" - local commit="$2" - - git tag -a "$tag" -s -m "Version $tag" "$commit" -} - -previous_version() { - local current="$1" - - # Requires git > 2.0 - git tag -l --sort=v:refname \ - | grep -E "^${SEMVER_REGEX}$" \ - | grep -v "$current" \ - | tail -1 -} - -print_changes() { - local tag="$1" - local previous - - previous="$( previous_version "$tag" )" - if [ -n "$previous" ] - then - printf "\nRaw changes made between %s and %s\n" "$previous" "$tag" - printf "======================================\n" - git --no-pager log --pretty=oneline "${previous}..$tag" - fi -} - -main() { - local dir - - cmdline "$@" - - cd "$PROGDIR" || exit 3 - - # Create tag for root package. - git_tag "$TAG" "$SHA" - printf "created tag: %s\n" "$TAG" - - # Create tag for all sub-packages. - for dir in $( package_dirs ) - do - git_tag "${dir}/$TAG" "$SHA" - printf "created tag: %s\n" "${dir}/$TAG" - done - - print_changes "$TAG" -} -main "$@" diff --git a/src/vendor/go.opentelemetry.io/otel/trace/config.go b/src/vendor/go.opentelemetry.io/otel/trace/config.go index 8461a15cc..f058cc781 100644 --- a/src/vendor/go.opentelemetry.io/otel/trace/config.go +++ b/src/vendor/go.opentelemetry.io/otel/trace/config.go @@ -41,20 +41,20 @@ func (t *TracerConfig) SchemaURL() string { func NewTracerConfig(options ...TracerOption) TracerConfig { var config TracerConfig for _, option := range options { - option.apply(&config) + config = option.apply(config) } return config } // TracerOption applies an option to a TracerConfig. type TracerOption interface { - apply(*TracerConfig) + apply(TracerConfig) TracerConfig } -type tracerOptionFunc func(*TracerConfig) +type tracerOptionFunc func(TracerConfig) TracerConfig -func (fn tracerOptionFunc) apply(cfg *TracerConfig) { - fn(cfg) +func (fn tracerOptionFunc) apply(cfg TracerConfig) TracerConfig { + return fn(cfg) } // SpanConfig is a group of options for a Span. @@ -106,7 +106,7 @@ func (cfg *SpanConfig) SpanKind() SpanKind { func NewSpanStartConfig(options ...SpanStartOption) SpanConfig { var c SpanConfig for _, option := range options { - option.applySpanStart(&c) + c = option.applySpanStart(c) } return c } @@ -118,27 +118,27 @@ func NewSpanStartConfig(options ...SpanStartOption) SpanConfig { func NewSpanEndConfig(options ...SpanEndOption) SpanConfig { var c SpanConfig for _, option := range options { - option.applySpanEnd(&c) + c = option.applySpanEnd(c) } return c } // SpanStartOption applies an option to a SpanConfig. These options are applicable -// only when the span is created +// only when the span is created. type SpanStartOption interface { - applySpanStart(*SpanConfig) + applySpanStart(SpanConfig) SpanConfig } -type spanOptionFunc func(*SpanConfig) +type spanOptionFunc func(SpanConfig) SpanConfig -func (fn spanOptionFunc) applySpanStart(cfg *SpanConfig) { - fn(cfg) +func (fn spanOptionFunc) applySpanStart(cfg SpanConfig) SpanConfig { + return fn(cfg) } // SpanEndOption applies an option to a SpanConfig. These options are // applicable only when the span is ended. type SpanEndOption interface { - applySpanEnd(*SpanConfig) + applySpanEnd(SpanConfig) SpanConfig } // EventConfig is a group of options for an Event. @@ -170,7 +170,7 @@ func (cfg *EventConfig) StackTrace() bool { func NewEventConfig(options ...EventOption) EventConfig { var c EventConfig for _, option := range options { - option.applyEvent(&c) + c = option.applyEvent(c) } if c.timestamp.IsZero() { c.timestamp = time.Now() @@ -180,7 +180,7 @@ func NewEventConfig(options ...EventOption) EventConfig { // EventOption applies span event options to an EventConfig. type EventOption interface { - applyEvent(*EventConfig) + applyEvent(EventConfig) EventConfig } // SpanOption are options that can be used at both the beginning and end of a span. @@ -203,12 +203,14 @@ type SpanEndEventOption interface { type attributeOption []attribute.KeyValue -func (o attributeOption) applySpan(c *SpanConfig) { +func (o attributeOption) applySpan(c SpanConfig) SpanConfig { c.attributes = append(c.attributes, []attribute.KeyValue(o)...) + return c } -func (o attributeOption) applySpanStart(c *SpanConfig) { o.applySpan(c) } -func (o attributeOption) applyEvent(c *EventConfig) { +func (o attributeOption) applySpanStart(c SpanConfig) SpanConfig { return o.applySpan(c) } +func (o attributeOption) applyEvent(c EventConfig) EventConfig { c.attributes = append(c.attributes, []attribute.KeyValue(o)...) + return c } var _ SpanStartEventOption = attributeOption{} @@ -234,10 +236,16 @@ type SpanEventOption interface { type timestampOption time.Time -func (o timestampOption) applySpan(c *SpanConfig) { c.timestamp = time.Time(o) } -func (o timestampOption) applySpanStart(c *SpanConfig) { o.applySpan(c) } -func (o timestampOption) applySpanEnd(c *SpanConfig) { o.applySpan(c) } -func (o timestampOption) applyEvent(c *EventConfig) { c.timestamp = time.Time(o) } +func (o timestampOption) applySpan(c SpanConfig) SpanConfig { + c.timestamp = time.Time(o) + return c +} +func (o timestampOption) applySpanStart(c SpanConfig) SpanConfig { return o.applySpan(c) } +func (o timestampOption) applySpanEnd(c SpanConfig) SpanConfig { return o.applySpan(c) } +func (o timestampOption) applyEvent(c EventConfig) EventConfig { + c.timestamp = time.Time(o) + return c +} var _ SpanEventOption = timestampOption{} @@ -249,9 +257,15 @@ func WithTimestamp(t time.Time) SpanEventOption { type stackTraceOption bool -func (o stackTraceOption) applyEvent(c *EventConfig) { c.stackTrace = bool(o) } -func (o stackTraceOption) applySpan(c *SpanConfig) { c.stackTrace = bool(o) } -func (o stackTraceOption) applySpanEnd(c *SpanConfig) { o.applySpan(c) } +func (o stackTraceOption) applyEvent(c EventConfig) EventConfig { + c.stackTrace = bool(o) + return c +} +func (o stackTraceOption) applySpan(c SpanConfig) SpanConfig { + c.stackTrace = bool(o) + return c +} +func (o stackTraceOption) applySpanEnd(c SpanConfig) SpanConfig { return o.applySpan(c) } // WithStackTrace sets the flag to capture the error with stack trace (e.g. true, false). func WithStackTrace(b bool) SpanEndEventOption { @@ -261,8 +275,9 @@ func WithStackTrace(b bool) SpanEndEventOption { // WithLinks adds links to a Span. The links are added to the existing Span // links, i.e. this does not overwrite. Links with invalid span context are ignored. func WithLinks(links ...Link) SpanStartOption { - return spanOptionFunc(func(cfg *SpanConfig) { + return spanOptionFunc(func(cfg SpanConfig) SpanConfig { cfg.links = append(cfg.links, links...) + return cfg }) } @@ -270,28 +285,32 @@ func WithLinks(links ...Link) SpanStartOption { // existing parent span context will be ignored when defining the Span's trace // identifiers. func WithNewRoot() SpanStartOption { - return spanOptionFunc(func(cfg *SpanConfig) { + return spanOptionFunc(func(cfg SpanConfig) SpanConfig { cfg.newRoot = true + return cfg }) } // WithSpanKind sets the SpanKind of a Span. func WithSpanKind(kind SpanKind) SpanStartOption { - return spanOptionFunc(func(cfg *SpanConfig) { + return spanOptionFunc(func(cfg SpanConfig) SpanConfig { cfg.spanKind = kind + return cfg }) } // WithInstrumentationVersion sets the instrumentation version. func WithInstrumentationVersion(version string) TracerOption { - return tracerOptionFunc(func(cfg *TracerConfig) { + return tracerOptionFunc(func(cfg TracerConfig) TracerConfig { cfg.instrumentationVersion = version + return cfg }) } // WithSchemaURL sets the schema URL for the Tracer. func WithSchemaURL(schemaURL string) TracerOption { - return tracerOptionFunc(func(cfg *TracerConfig) { + return tracerOptionFunc(func(cfg TracerConfig) TracerConfig { cfg.schemaURL = schemaURL + return cfg }) } diff --git a/src/vendor/go.opentelemetry.io/otel/trace/noop.go b/src/vendor/go.opentelemetry.io/otel/trace/noop.go index ad9a9fc5b..73950f207 100644 --- a/src/vendor/go.opentelemetry.io/otel/trace/noop.go +++ b/src/vendor/go.opentelemetry.io/otel/trace/noop.go @@ -85,5 +85,5 @@ func (noopSpan) AddEvent(string, ...EventOption) {} // SetName does nothing. func (noopSpan) SetName(string) {} -// TracerProvider returns a no-op TracerProvider +// TracerProvider returns a no-op TracerProvider. func (noopSpan) TracerProvider() TracerProvider { return noopTracerProvider{} } diff --git a/src/vendor/go.opentelemetry.io/otel/trace/trace.go b/src/vendor/go.opentelemetry.io/otel/trace/trace.go index 0923ceb98..e1f61e073 100644 --- a/src/vendor/go.opentelemetry.io/otel/trace/trace.go +++ b/src/vendor/go.opentelemetry.io/otel/trace/trace.go @@ -63,7 +63,7 @@ func (t TraceID) MarshalJSON() ([]byte, error) { return json.Marshal(t.String()) } -// String returns the hex string representation form of a TraceID +// String returns the hex string representation form of a TraceID. func (t TraceID) String() string { return hex.EncodeToString(t[:]) } @@ -86,7 +86,7 @@ func (s SpanID) MarshalJSON() ([]byte, error) { return json.Marshal(s.String()) } -// String returns the hex string representation form of a SpanID +// String returns the hex string representation form of a SpanID. func (s SpanID) String() string { return hex.EncodeToString(s[:]) } @@ -151,7 +151,7 @@ func decodeHex(h string, b []byte) error { return nil } -// TraceFlags contains flags that can be set on a SpanContext +// TraceFlags contains flags that can be set on a SpanContext. type TraceFlags byte //nolint:revive // revive complains about stutter of `trace.TraceFlags`. // IsSampled returns if the sampling bit is set in the TraceFlags. @@ -160,7 +160,7 @@ func (tf TraceFlags) IsSampled() bool { } // WithSampled sets the sampling bit in a new copy of the TraceFlags. -func (tf TraceFlags) WithSampled(sampled bool) TraceFlags { +func (tf TraceFlags) WithSampled(sampled bool) TraceFlags { // nolint:revive // sampled is not a control flag. if sampled { return tf | FlagsSampled } @@ -174,7 +174,7 @@ func (tf TraceFlags) MarshalJSON() ([]byte, error) { return json.Marshal(tf.String()) } -// String returns the hex string representation form of TraceFlags +// String returns the hex string representation form of TraceFlags. func (tf TraceFlags) String() string { return hex.EncodeToString([]byte{byte(tf)}[:]) } diff --git a/src/vendor/go.opentelemetry.io/otel/trace/tracestate.go b/src/vendor/go.opentelemetry.io/otel/trace/tracestate.go index 9fad16fd2..5e775ce5f 100644 --- a/src/vendor/go.opentelemetry.io/otel/trace/tracestate.go +++ b/src/vendor/go.opentelemetry.io/otel/trace/tracestate.go @@ -58,7 +58,7 @@ func newMember(key, value string) (member, error) { return member{Key: key, Value: value}, nil } -func parseMemeber(m string) (member, error) { +func parseMember(m string) (member, error) { matches := memberRe.FindStringSubmatch(m) if len(matches) != 5 { return member{}, fmt.Errorf("%w: %s", errInvalidMember, m) @@ -68,7 +68,6 @@ func parseMemeber(m string) (member, error) { Key: matches[1], Value: matches[4], }, nil - } // String encodes member into a string compliant with the W3C Trace Context @@ -114,7 +113,7 @@ func ParseTraceState(tracestate string) (TraceState, error) { continue } - m, err := parseMemeber(memberStr) + m, err := parseMember(memberStr) if err != nil { return TraceState{}, wrapErr(err) } @@ -171,7 +170,8 @@ func (ts TraceState) Get(key string) string { // specification an error is returned with the original TraceState. // // If adding a new list-member means the TraceState would have more members -// than is allowed an error is returned instead with the original TraceState. +// then is allowed, the new list-member will be inserted and the right-most +// list-member will be dropped in the returned TraceState. func (ts TraceState) Insert(key, value string) (TraceState, error) { m, err := newMember(key, value) if err != nil { @@ -179,17 +179,10 @@ func (ts TraceState) Insert(key, value string) (TraceState, error) { } cTS := ts.Delete(key) - if cTS.Len()+1 > maxListMembers { - // TODO (MrAlias): When the second version of the Trace Context - // specification is published this needs to not return an error. - // Instead it should drop the "right-most" member and insert the new - // member at the front. - // - // https://github.com/w3c/trace-context/pull/448 - return ts, fmt.Errorf("failed to insert: %w", errMemberNumber) + if cTS.Len()+1 <= maxListMembers { + cTS.list = append(cTS.list, member{}) } - - cTS.list = append(cTS.list, member{}) + // When the number of members exceeds capacity, drop the "right-most". copy(cTS.list[1:], cTS.list) cTS.list[0] = m diff --git a/src/vendor/go.opentelemetry.io/otel/version.go b/src/vendor/go.opentelemetry.io/otel/version.go index e62acd66e..ddc0abb27 100644 --- a/src/vendor/go.opentelemetry.io/otel/version.go +++ b/src/vendor/go.opentelemetry.io/otel/version.go @@ -16,5 +16,5 @@ package otel // import "go.opentelemetry.io/otel" // Version is the current release version of OpenTelemetry in use. func Version() string { - return "1.3.0" + return "1.8.0" } diff --git a/src/vendor/go.opentelemetry.io/otel/versions.yaml b/src/vendor/go.opentelemetry.io/otel/versions.yaml index 3e1384998..1469512be 100644 --- a/src/vendor/go.opentelemetry.io/otel/versions.yaml +++ b/src/vendor/go.opentelemetry.io/otel/versions.yaml @@ -14,7 +14,7 @@ module-sets: stable-v1: - version: v1.3.0 + version: v1.8.0 modules: - go.opentelemetry.io/otel - go.opentelemetry.io/otel/bridge/opentracing @@ -34,7 +34,7 @@ module-sets: - go.opentelemetry.io/otel/trace - go.opentelemetry.io/otel/sdk experimental-metrics: - version: v0.26.0 + version: v0.31.0 modules: - go.opentelemetry.io/otel/example/prometheus - go.opentelemetry.io/otel/exporters/otlp/otlpmetric @@ -42,16 +42,14 @@ module-sets: - go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp - go.opentelemetry.io/otel/exporters/prometheus - go.opentelemetry.io/otel/exporters/stdout/stdoutmetric - - go.opentelemetry.io/otel/internal/metric - go.opentelemetry.io/otel/metric - - go.opentelemetry.io/otel/sdk/export/metric - go.opentelemetry.io/otel/sdk/metric experimental-schema: - version: v0.0.1 + version: v0.0.2 modules: - go.opentelemetry.io/otel/schema bridge: - version: v0.26.0 + version: v0.31.0 modules: - go.opentelemetry.io/otel/bridge/opencensus - go.opentelemetry.io/otel/bridge/opencensus/test diff --git a/src/vendor/modules.txt b/src/vendor/modules.txt index 77762c525..c4f2e28c4 100644 --- a/src/vendor/modules.txt +++ b/src/vendor/modules.txt @@ -125,21 +125,26 @@ github.com/aws/aws-sdk-go/service/s3 github.com/aws/aws-sdk-go/service/s3/internal/arn github.com/aws/aws-sdk-go/service/sts github.com/aws/aws-sdk-go/service/sts/stsiface -# github.com/beego/beego v1.12.11 -## explicit; go 1.13 -github.com/beego/beego -github.com/beego/beego/cache -github.com/beego/beego/cache/redis -github.com/beego/beego/config -github.com/beego/beego/context -github.com/beego/beego/context/param -github.com/beego/beego/grace -github.com/beego/beego/logs -github.com/beego/beego/orm -github.com/beego/beego/session -github.com/beego/beego/toolbox -github.com/beego/beego/utils -github.com/beego/beego/validation +# github.com/beego/beego/v2 v2.0.6 +## explicit; go 1.18 +github.com/beego/beego/v2 +github.com/beego/beego/v2/client/cache +github.com/beego/beego/v2/client/cache/redis +github.com/beego/beego/v2/client/orm +github.com/beego/beego/v2/client/orm/clauses +github.com/beego/beego/v2/client/orm/clauses/order_clause +github.com/beego/beego/v2/client/orm/hints +github.com/beego/beego/v2/core/admin +github.com/beego/beego/v2/core/berror +github.com/beego/beego/v2/core/config +github.com/beego/beego/v2/core/logs +github.com/beego/beego/v2/core/utils +github.com/beego/beego/v2/core/validation +github.com/beego/beego/v2/server/web +github.com/beego/beego/v2/server/web/context +github.com/beego/beego/v2/server/web/context/param +github.com/beego/beego/v2/server/web/grace +github.com/beego/beego/v2/server/web/session # github.com/beego/i18n v0.0.0-20140604031826-e87155e8f0c0 ## explicit github.com/beego/i18n @@ -153,11 +158,12 @@ github.com/bmatcuk/doublestar ## explicit # github.com/bugsnag/panicwrap v1.2.0 ## explicit -# github.com/casbin/casbin v1.7.0 +# github.com/casbin/casbin v1.9.1 ## explicit github.com/casbin/casbin github.com/casbin/casbin/config github.com/casbin/casbin/effect +github.com/casbin/casbin/errors github.com/casbin/casbin/log github.com/casbin/casbin/model github.com/casbin/casbin/persist @@ -380,7 +386,7 @@ github.com/go-redis/redis/v8/internal/pool github.com/go-redis/redis/v8/internal/proto github.com/go-redis/redis/v8/internal/rand github.com/go-redis/redis/v8/internal/util -# github.com/go-sql-driver/mysql v1.5.0 +# github.com/go-sql-driver/mysql v1.6.0 ## explicit; go 1.10 github.com/go-sql-driver/mysql # github.com/go-stack/stack v1.8.0 @@ -579,7 +585,7 @@ github.com/miekg/pkcs11 # github.com/mitchellh/copystructure v1.2.0 ## explicit; go 1.15 github.com/mitchellh/copystructure -# github.com/mitchellh/mapstructure v1.4.1 +# github.com/mitchellh/mapstructure v1.4.3 ## explicit; go 1.14 github.com/mitchellh/mapstructure # github.com/mitchellh/reflectwalk v1.0.2 @@ -666,7 +672,7 @@ github.com/robfig/cron/v3 # github.com/satori/go.uuid v1.2.0 ## explicit github.com/satori/go.uuid -# github.com/shiena/ansicolor v0.0.0-20151119151921-a422bbe96644 +# github.com/shiena/ansicolor v0.0.0-20200904210342-c7312218db18 ## explicit github.com/shiena/ansicolor # github.com/sirupsen/logrus v1.8.1 @@ -765,8 +771,8 @@ go.opentelemetry.io/contrib/instrumentation/github.com/gorilla/mux/otelmux # go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.22.0 ## explicit; go 1.15 go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp -# go.opentelemetry.io/otel v1.3.0 -## explicit; go 1.16 +# go.opentelemetry.io/otel v1.8.0 +## explicit; go 1.17 go.opentelemetry.io/otel go.opentelemetry.io/otel/attribute go.opentelemetry.io/otel/baggage @@ -775,8 +781,9 @@ go.opentelemetry.io/otel/internal go.opentelemetry.io/otel/internal/baggage go.opentelemetry.io/otel/internal/global go.opentelemetry.io/otel/propagation +go.opentelemetry.io/otel/semconv/internal +go.opentelemetry.io/otel/semconv/v1.10.0 go.opentelemetry.io/otel/semconv/v1.4.0 -go.opentelemetry.io/otel/semconv/v1.7.0 # go.opentelemetry.io/otel/exporters/jaeger v1.0.0 ## explicit; go 1.15 go.opentelemetry.io/otel/exporters/jaeger @@ -805,14 +812,15 @@ go.opentelemetry.io/otel/metric/global go.opentelemetry.io/otel/metric/number go.opentelemetry.io/otel/metric/registry go.opentelemetry.io/otel/metric/unit -# go.opentelemetry.io/otel/sdk v1.3.0 -## explicit; go 1.16 +# go.opentelemetry.io/otel/sdk v1.8.0 +## explicit; go 1.17 go.opentelemetry.io/otel/sdk/instrumentation go.opentelemetry.io/otel/sdk/internal +go.opentelemetry.io/otel/sdk/internal/env go.opentelemetry.io/otel/sdk/resource go.opentelemetry.io/otel/sdk/trace -# go.opentelemetry.io/otel/trace v1.3.0 -## explicit; go 1.16 +# go.opentelemetry.io/otel/trace v1.8.0 +## explicit; go 1.17 go.opentelemetry.io/otel/trace # go.opentelemetry.io/proto/otlp v0.11.0 ## explicit; go 1.14 diff --git a/tests/ci/ut_install.sh b/tests/ci/ut_install.sh index 0684aab71..b1ae45dfc 100755 --- a/tests/ci/ut_install.sh +++ b/tests/ci/ut_install.sh @@ -10,10 +10,12 @@ go get github.com/docker/libtrust go get golang.org/x/lint/golint go get github.com/GeertJohan/fgt go get github.com/dghubble/sling +set +e go get github.com/stretchr/testify go get golang.org/x/tools/cmd/cover go get github.com/mattn/goveralls go get -u github.com/client9/misspell/cmd/misspell +set -e # binary will be $(go env GOPATH)/bin/golangci-lint # go install/go get installation aren't guaranteed to work. We recommend using binary installation. curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s -- -b $(go env GOPATH)/bin v1.50.1