4 Commits

Author SHA1 Message Date
  Antoine GIRARD 14e718695a
Update Dockerfile (#12922) 1 month ago
  zeripath 7f8e3192cd
Allow common redis and leveldb connections (#12385) 1 month ago
  GiteaBot f404bdde9b [skip ci] Updated translations via Crowdin 1 month ago
  Tait Hoyem c85c9d40c2
Add config option to make create-on-push repositories public by default (#12936) 1 month ago
76 changed files with 4896 additions and 3090 deletions
Split View
  1. +1
    -2
      Dockerfile
  2. +5
    -1
      custom/conf/app.example.ini
  3. +8
    -9
      docs/content/doc/advanced/config-cheat-sheet.en-us.md
  4. +1
    -0
      docs/content/doc/advanced/config-cheat-sheet.zh-cn.md
  5. +2
    -1
      go.mod
  6. +7
    -0
      go.sum
  7. +0
    -1
      modules/cache/cache.go
  8. +15
    -53
      modules/cache/cache_redis.go
  9. +25
    -0
      modules/nosql/leveldb.go
  10. +71
    -0
      modules/nosql/manager.go
  11. +151
    -0
      modules/nosql/manager_leveldb.go
  12. +205
    -0
      modules/nosql/manager_redis.go
  13. +102
    -0
      modules/nosql/redis.go
  14. +35
    -0
      modules/nosql/redis_test.go
  15. +24
    -7
      modules/queue/queue_disk.go
  16. +5
    -24
      modules/queue/queue_redis.go
  17. +24
    -7
      modules/queue/unique_queue_disk.go
  18. +1
    -1
      modules/queue/unique_queue_redis.go
  19. +13
    -36
      modules/session/redis.go
  20. +1
    -2
      modules/session/virtual.go
  21. +2
    -0
      modules/setting/repository.go
  22. +11
    -0
      options/locale/locale_es-ES.ini
  23. +2
    -1
      services/repository/repository.go
  24. +0
    -1
      vendor/gitea.com/macaron/cache/redis/redis.goconvey
  25. +0
    -1
      vendor/gitea.com/macaron/session/redis/redis.goconvey
  26. +0
    -19
      vendor/github.com/go-redis/redis/.travis.yml
  27. +0
    -25
      vendor/github.com/go-redis/redis/CHANGELOG.md
  28. +0
    -89
      vendor/github.com/go-redis/redis/internal/error.go
  29. +0
    -15
      vendor/github.com/go-redis/redis/internal/log.go
  30. +0
    -93
      vendor/github.com/go-redis/redis/internal/pool/conn.go
  31. +0
    -53
      vendor/github.com/go-redis/redis/internal/pool/pool_single.go
  32. +0
    -29
      vendor/github.com/go-redis/redis/internal/util.go
  33. +0
    -580
      vendor/github.com/go-redis/redis/redis.go
  34. +0
    -0
      vendor/github.com/go-redis/redis/v7/.gitignore
  35. +15
    -0
      vendor/github.com/go-redis/redis/v7/.golangci.yml
  36. +22
    -0
      vendor/github.com/go-redis/redis/v7/.travis.yml
  37. +46
    -0
      vendor/github.com/go-redis/redis/v7/CHANGELOG.md
  38. +0
    -0
      vendor/github.com/go-redis/redis/v7/LICENSE
  39. +3
    -5
      vendor/github.com/go-redis/redis/v7/Makefile
  40. +24
    -42
      vendor/github.com/go-redis/redis/v7/README.md
  41. +464
    -416
      vendor/github.com/go-redis/redis/v7/cluster.go
  42. +1
    -1
      vendor/github.com/go-redis/redis/v7/cluster_commands.go
  43. +665
    -537
      vendor/github.com/go-redis/redis/v7/command.go
  44. +631
    -571
      vendor/github.com/go-redis/redis/v7/commands.go
  45. +0
    -0
      vendor/github.com/go-redis/redis/v7/doc.go
  46. +108
    -0
      vendor/github.com/go-redis/redis/v7/error.go
  47. +15
    -0
      vendor/github.com/go-redis/redis/v7/go.mod
  48. +47
    -0
      vendor/github.com/go-redis/redis/v7/go.sum
  49. +0
    -0
      vendor/github.com/go-redis/redis/v7/internal/consistenthash/consistenthash.go
  50. +0
    -0
      vendor/github.com/go-redis/redis/v7/internal/hashtag/hashtag.go
  51. +0
    -0
      vendor/github.com/go-redis/redis/v7/internal/internal.go
  52. +8
    -0
      vendor/github.com/go-redis/redis/v7/internal/log.go
  53. +0
    -0
      vendor/github.com/go-redis/redis/v7/internal/once.go
  54. +118
    -0
      vendor/github.com/go-redis/redis/v7/internal/pool/conn.go
  55. +121
    -80
      vendor/github.com/go-redis/redis/v7/internal/pool/pool.go
  56. +208
    -0
      vendor/github.com/go-redis/redis/v7/internal/pool/pool_single.go
  57. +12
    -9
      vendor/github.com/go-redis/redis/v7/internal/pool/pool_sticky.go
  58. +38
    -14
      vendor/github.com/go-redis/redis/v7/internal/proto/reader.go
  59. +1
    -1
      vendor/github.com/go-redis/redis/v7/internal/proto/scan.go
  60. +9
    -3
      vendor/github.com/go-redis/redis/v7/internal/proto/writer.go
  61. +56
    -0
      vendor/github.com/go-redis/redis/v7/internal/util.go
  62. +0
    -0
      vendor/github.com/go-redis/redis/v7/internal/util/safe.go
  63. +0
    -0
      vendor/github.com/go-redis/redis/v7/internal/util/strconv.go
  64. +0
    -0
      vendor/github.com/go-redis/redis/v7/internal/util/unsafe.go
  65. +6
    -4
      vendor/github.com/go-redis/redis/v7/iterator.go
  66. +39
    -16
      vendor/github.com/go-redis/redis/v7/options.go
  67. +32
    -10
      vendor/github.com/go-redis/redis/v7/pipeline.go
  68. +194
    -72
      vendor/github.com/go-redis/redis/v7/pubsub.go
  69. +758
    -0
      vendor/github.com/go-redis/redis/v7/redis.go
  70. +57
    -17
      vendor/github.com/go-redis/redis/v7/result.go
  71. +190
    -122
      vendor/github.com/go-redis/redis/v7/ring.go
  72. +1
    -1
      vendor/github.com/go-redis/redis/v7/script.go
  73. +186
    -76
      vendor/github.com/go-redis/redis/v7/sentinel.go
  74. +70
    -21
      vendor/github.com/go-redis/redis/v7/tx.go
  75. +30
    -11
      vendor/github.com/go-redis/redis/v7/universal.go
  76. +10
    -11
      vendor/modules.txt

+ 1
- 2
Dockerfile View File

@@ -8,7 +8,7 @@ ENV GOPROXY ${GOPROXY:-direct}

ARG GITEA_VERSION
ARG TAGS="sqlite sqlite_unlock_notify"
ENV TAGS "bindata $TAGS"
ENV TAGS "bindata timetzdata $TAGS"
ARG CGO_EXTRA_CFLAGS

#Build deps
@@ -38,7 +38,6 @@ RUN apk --no-cache add \
s6 \
sqlite \
su-exec \
tzdata \
gnupg

RUN addgroup \


+ 5
- 1
custom/conf/app.example.ini View File

@@ -30,6 +30,8 @@ ANSI_CHARSET =
FORCE_PRIVATE = false
; Default privacy setting when creating a new repository, allowed values: last, private, public. Default is last which means the last setting used.
DEFAULT_PRIVATE = last
; Default private when using push-to-create
DEFAULT_PUSH_CREATE_PRIVATE = true
; Global limit of repositories per user, applied at creation time. -1 means no limit
MAX_CREATION_LIMIT = -1
; Mirror sync queue length, increase if mirror syncing starts hanging
@@ -465,8 +467,10 @@ LENGTH = 20
BATCH_LENGTH = 20
; Connection string for redis queues this will store the redis connection string.
CONN_STR = "addrs=127.0.0.1:6379 db=0"
; Provide the suffix of the default redis queue name - specific queues can be overriden within in their [queue.name] sections.
; Provides the suffix of the default redis/disk queue name - specific queues can be overriden within in their [queue.name] sections.
QUEUE_NAME = "_queue"
; Provides the suffix of the default redis/disk unique queue set name - specific queues can be overriden within in their [queue.name] sections.
SET_NAME = "_unique"
; If the queue cannot be created at startup - level queues may need a timeout at startup - wrap the queue:
WRAP_IF_NECESSARY = true
; Attempt to create the wrapped queue at max


+ 8
- 9
docs/content/doc/advanced/config-cheat-sheet.en-us.md View File

@@ -51,6 +51,7 @@ Values containing `#` or `;` must be quoted using `` ` `` or `"""`.
- `FORCE_PRIVATE`: **false**: Force every new repository to be private.
- `DEFAULT_PRIVATE`: **last**: Default private when creating a new repository.
\[last, private, public\]
- `DEFAULT_PUSH_CREATE_PRIVATE`: **true**: Default private when creating a new repository with push-to-create.
- `MAX_CREATION_LIMIT`: **-1**: Global maximum creation limit of repositories per user,
`-1` means no limit.
- `PULL_REQUEST_QUEUE_LENGTH`: **1000**: Length of pull request patch test queue, make it
@@ -307,15 +308,13 @@ relation to port exhaustion.
## Queue (`queue` and `queue.*`)

- `TYPE`: **persistable-channel**: General queue type, currently support: `persistable-channel`, `channel`, `level`, `redis`, `dummy`
- `DATADIR`: **queues/**: Base DataDir for storing persistent and level queues. `DATADIR` for inidividual queues can be set in `queue.name` sections but will default to `DATADIR/`**`name`**.
- `DATADIR`: **queues/**: Base DataDir for storing persistent and level queues. `DATADIR` for individual queues can be set in `queue.name` sections but will default to `DATADIR/`**`name`**.
- `LENGTH`: **20**: Maximal queue size before channel queues block
- `BATCH_LENGTH`: **20**: Batch data before passing to the handler
- `CONN_STR`: **addrs=127.0.0.1:6379 db=0**: Connection string for the redis queue type.
- `QUEUE_NAME`: **_queue**: The suffix for default redis queue name. Individual queues will default to **`name`**`QUEUE_NAME` but can be overriden in the specific `queue.name` section.
- `SET_NAME`: **_unique**: The suffix that will added to the default redis
set name for unique queues. Individual queues will default to
**`name`**`QUEUE_NAME`_`SET_NAME`_ but can be overridden in the specific
`queue.name` section.
- `CONN_STR`: **redis://127.0.0.1:6379/0**: Connection string for the redis queue type. Options can be set using query params. Similarly LevelDB options can also be set using: **leveldb://relative/path?option=value** or **leveldb:///absolute/path?option=value**
- `QUEUE_NAME`: **_queue**: The suffix for default redis and disk queue name. Individual queues will default to **`name`**`QUEUE_NAME` but can be overriden in the specific `queue.name` section.
- `SET_NAME`: **_unique**: The suffix that will be added to the default redis and disk queue `set` name for unique queues. Individual queues will default to
**`name`**`QUEUE_NAME`_`SET_NAME`_ but can be overridden in the specific `queue.name` section.
- `WRAP_IF_NECESSARY`: **true**: Will wrap queues with a timeoutable queue if the selected queue is not ready to be created - (Only relevant for the level queue.)
- `MAX_ATTEMPTS`: **10**: Maximum number of attempts to create the wrapped queue
- `TIMEOUT`: **GRACEFUL_HAMMER_TIME + 30s**: Timeout the creation of the wrapped queue if it takes longer than this to create.
@@ -458,7 +457,7 @@ set name for unique queues. Individual queues will default to
- `ADAPTER`: **memory**: Cache engine adapter, either `memory`, `redis`, or `memcache`.
- `INTERVAL`: **60**: Garbage Collection interval (sec), for memory cache only.
- `HOST`: **\<empty\>**: Connection string for `redis` and `memcache`.
- Redis: `network=tcp,addr=127.0.0.1:6379,password=macaron,db=0,pool_size=100,idle_timeout=180`
- Redis: `redis://:macaron@127.0.0.1:6379/0?pool_size=100&idle_timeout=180s`
- Memcache: `127.0.0.1:9090;127.0.0.1:9091`
- `ITEM_TTL`: **16h**: Time to keep items in cache if not used, Setting it to 0 disables caching.

@@ -707,7 +706,7 @@ Task queue configuration has been moved to `queue.task`. However, the below conf

- `QUEUE_TYPE`: **channel**: Task queue type, could be `channel` or `redis`.
- `QUEUE_LENGTH`: **1000**: Task queue length, available only when `QUEUE_TYPE` is `channel`.
- `QUEUE_CONN_STR`: **addrs=127.0.0.1:6379 db=0**: Task queue connection string, available only when `QUEUE_TYPE` is `redis`. If redis needs a password, use `addrs=127.0.0.1:6379 password=123 db=0`.
- `QUEUE_CONN_STR`: **redis://127.0.0.1:6379/0**: Task queue connection string, available only when `QUEUE_TYPE` is `redis`. If redis needs a password, use `redis://123@127.0.0.1:6379/0`.

## Migrations (`migrations`)



+ 1
- 0
docs/content/doc/advanced/config-cheat-sheet.zh-cn.md View File

@@ -30,6 +30,7 @@ menu:
- `ANSI_CHARSET`: 默认字符编码。
- `FORCE_PRIVATE`: 强制所有git工程必须私有。
- `DEFAULT_PRIVATE`: 默认创建的git工程为私有。 可以是`last`, `private` 或 `public`。默认值是 `last`表示用户最后创建的Repo的选择。
- `DEFAULT_PUSH_CREATE_PRIVATE`: **true**: 通过 ``push-to-create`` 方式创建的仓库是否默认为私有仓库.
- `MAX_CREATION_LIMIT`: 全局最大每个用户创建的git工程数目, `-1` 表示没限制。
- `PULL_REQUEST_QUEUE_LENGTH`: 小心:合并请求测试队列的长度,尽量放大。



+ 2
- 1
go.mod View File

@@ -38,7 +38,7 @@ require (
github.com/go-enry/go-enry/v2 v2.5.2
github.com/go-git/go-billy/v5 v5.0.0
github.com/go-git/go-git/v5 v5.1.0
github.com/go-redis/redis v6.15.2+incompatible
github.com/go-redis/redis/v7 v7.4.0
github.com/go-sql-driver/mysql v1.5.0
github.com/go-swagger/go-swagger v0.25.0
github.com/go-testfixtures/testfixtures/v3 v3.4.0
@@ -88,6 +88,7 @@ require (
github.com/shurcooL/httpfs v0.0.0-20190527155220-6a4d4a70508b // indirect
github.com/shurcooL/vfsgen v0.0.0-20181202132449-6a9ea43bcacd
github.com/stretchr/testify v1.6.1
github.com/syndtr/goleveldb v1.0.0
github.com/tecbot/gorocksdb v0.0.0-20181010114359-8752a9433481 // indirect
github.com/tinylib/msgp v1.1.2 // indirect
github.com/tstranex/u2f v1.0.0


+ 7
- 0
go.sum View File

@@ -342,6 +342,8 @@ github.com/go-openapi/validate v0.19.10 h1:tG3SZ5DC5KF4cyt7nqLVcQXGj5A7mpaYkAcNP
github.com/go-openapi/validate v0.19.10/go.mod h1:RKEZTUWDkxKQxN2jDT7ZnZi2bhZlbNMAuKvKB+IaGx8=
github.com/go-redis/redis v6.15.2+incompatible h1:9SpNVG76gr6InJGxoZ6IuuxaCOQwDAhzyXg+Bs+0Sb4=
github.com/go-redis/redis v6.15.2+incompatible/go.mod h1:NAIEuMOZ/fxfXJIrKDQDz8wamY7mA7PouImQ2Jvg6kA=
github.com/go-redis/redis/v7 v7.4.0 h1:7obg6wUoj05T0EpY0o8B59S9w5yeMWql7sw2kwNW1x4=
github.com/go-redis/redis/v7 v7.4.0/go.mod h1:JDNMw23GTyLNC4GZu9njt15ctBQVn7xjRfnwdHj/Dcg=
github.com/go-sql-driver/mysql v1.4.1 h1:g24URVg0OFbNUTx9qqY1IRZ9D9z3iPyi5zKhQZpNwpA=
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=
@@ -730,9 +732,13 @@ github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+W
github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/ginkgo v1.8.0 h1:VkHVNpR4iVnU8XQR6DBm8BqYjN7CRzw+xKUbVVbbW9w=
github.com/onsi/ginkgo v1.8.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/ginkgo v1.10.1 h1:q/mM8GF/n0shIN8SaAZ0V+jnLPzen6WIVZdiwrRlMlo=
github.com/onsi/ginkgo v1.10.1/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
github.com/onsi/gomega v1.5.0 h1:izbySO9zDPmjJ8rDjLvkA2zJHIo+HkYXHnf7eN7SSyo=
github.com/onsi/gomega v1.5.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
github.com/onsi/gomega v1.7.0 h1:XPnZz8VVBHjVsy1vzJmRwIcSwiUO+JFfrv/xGiigmME=
github.com/onsi/gomega v1.7.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o=
github.com/openzipkin/zipkin-go v0.1.6/go.mod h1:QgAqvLzwWbR/WpD4A3cGpPtJrZXNIiJc5AZX7/PBEpw=
github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
@@ -1014,6 +1020,7 @@ golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLL
golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20190923162816-aa69164e4478/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=


+ 0
- 1
modules/cache/cache.go View File

@@ -13,7 +13,6 @@ import (
mc "gitea.com/macaron/cache"

_ "gitea.com/macaron/cache/memcache" // memcache plugin for cache
_ "gitea.com/macaron/cache/redis"
)

var (


vendor/gitea.com/macaron/cache/redis/redis.go → modules/cache/cache_redis.go View File

@@ -1,35 +1,23 @@
// Copyright 2013 Beego Authors
// Copyright 2014 The Macaron 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.
// Copyright 2020 The Gitea Authors. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.

package cache

import (
"fmt"
"strings"
"time"

"github.com/go-redis/redis"
"github.com/unknwon/com"
"gopkg.in/ini.v1"
"code.gitea.io/gitea/modules/nosql"

"gitea.com/macaron/cache"
"github.com/go-redis/redis/v7"
"github.com/unknwon/com"
)

// RedisCacher represents a redis cache adapter implementation.
type RedisCacher struct {
c *redis.Client
c redis.UniversalClient
prefix string
hsetName string
occupyMode bool
@@ -112,7 +100,7 @@ func (c *RedisCacher) IsExist(key string) bool {
// Flush deletes all cached data.
func (c *RedisCacher) Flush() error {
if c.occupyMode {
return c.c.FlushDb().Err()
return c.c.FlushDB().Err()
}

keys, err := c.c.HKeys(c.hsetName).Result()
@@ -131,46 +119,20 @@ func (c *RedisCacher) StartAndGC(opts cache.Options) error {
c.hsetName = "MacaronCache"
c.occupyMode = opts.OccupyMode

cfg, err := ini.Load([]byte(strings.Replace(opts.AdapterConfig, ",", "\n", -1)))
if err != nil {
return err
}
uri := nosql.ToRedisURI(opts.AdapterConfig)

opt := &redis.Options{
Network: "tcp",
}
for k, v := range cfg.Section("").KeysHash() {
c.c = nosql.GetManager().GetRedisClient(uri.String())

for k, v := range uri.Query() {
switch k {
case "network":
opt.Network = v
case "addr":
opt.Addr = v
case "password":
opt.Password = v
case "db":
opt.DB = com.StrTo(v).MustInt()
case "pool_size":
opt.PoolSize = com.StrTo(v).MustInt()
case "idle_timeout":
opt.IdleTimeout, err = time.ParseDuration(v + "s")
if err != nil {
return fmt.Errorf("error parsing idle timeout: %v", err)
}
case "hset_name":
c.hsetName = v
c.hsetName = v[0]
case "prefix":
c.prefix = v
default:
return fmt.Errorf("session/redis: unsupported option '%s'", k)
c.prefix = v[0]
}
}

c.c = redis.NewClient(opt)
if err = c.c.Ping().Err(); err != nil {
return err
}

return nil
return c.c.Ping().Err()
}

func init() {

+ 25
- 0
modules/nosql/leveldb.go View File

@@ -0,0 +1,25 @@
// Copyright 2020 The Gitea Authors. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.

package nosql

import "net/url"

// ToLevelDBURI converts old style connections to a LevelDBURI
//
// A LevelDBURI matches the pattern:
//
// leveldb://path[?[option=value]*]
//
// We have previously just provided the path but this prevent other options
func ToLevelDBURI(connection string) *url.URL {
uri, err := url.Parse(connection)
if err == nil && uri.Scheme == "leveldb" {
return uri
}
uri, _ = url.Parse("leveldb://common")
uri.Host = ""
uri.Path = connection
return uri
}

+ 71
- 0
modules/nosql/manager.go View File

@@ -0,0 +1,71 @@
// Copyright 2020 The Gitea Authors. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.

package nosql

import (
"strconv"
"sync"
"time"

"github.com/go-redis/redis/v7"
"github.com/syndtr/goleveldb/leveldb"
)

var manager *Manager

// Manager is the nosql connection manager
type Manager struct {
mutex sync.Mutex

RedisConnections map[string]*redisClientHolder
LevelDBConnections map[string]*levelDBHolder
}

type redisClientHolder struct {
redis.UniversalClient
name []string
count int64
}

func (r *redisClientHolder) Close() error {
return manager.CloseRedisClient(r.name[0])
}

type levelDBHolder struct {
name []string
count int64
db *leveldb.DB
}

func init() {
_ = GetManager()
}

// GetManager returns a Manager and initializes one as singleton is there's none yet
func GetManager() *Manager {
if manager == nil {
manager = &Manager{
RedisConnections: make(map[string]*redisClientHolder),
LevelDBConnections: make(map[string]*levelDBHolder),
}
}
return manager
}

func valToTimeDuration(vs []string) (result time.Duration) {
var err error
for _, v := range vs {
result, err = time.ParseDuration(v)
if err != nil {
var val int
val, err = strconv.Atoi(v)
result = time.Duration(val)
}
if err == nil {
return
}
}
return
}

+ 151
- 0
modules/nosql/manager_leveldb.go View File

@@ -0,0 +1,151 @@
// Copyright 2020 The Gitea Authors. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.

package nosql

import (
"path"
"strconv"
"strings"

"github.com/syndtr/goleveldb/leveldb"
"github.com/syndtr/goleveldb/leveldb/errors"
"github.com/syndtr/goleveldb/leveldb/opt"
)

// CloseLevelDB closes a levelDB
func (m *Manager) CloseLevelDB(connection string) error {
m.mutex.Lock()
defer m.mutex.Unlock()
db, ok := m.LevelDBConnections[connection]
if !ok {
connection = ToLevelDBURI(connection).String()
db, ok = m.LevelDBConnections[connection]
}
if !ok {
return nil
}

db.count--
if db.count > 0 {
return nil
}

for _, name := range db.name {
delete(m.LevelDBConnections, name)
}
return db.db.Close()
}

// GetLevelDB gets a levelDB for a particular connection
func (m *Manager) GetLevelDB(connection string) (*leveldb.DB, error) {
m.mutex.Lock()
defer m.mutex.Unlock()
db, ok := m.LevelDBConnections[connection]
if ok {
db.count++

return db.db, nil
}
dataDir := connection
uri := ToLevelDBURI(connection)
db = &levelDBHolder{
name: []string{connection, uri.String()},
}

dataDir = path.Join(uri.Host, uri.Path)
opts := &opt.Options{}
for k, v := range uri.Query() {
switch replacer.Replace(strings.ToLower(k)) {
case "blockcachecapacity":
opts.BlockCacheCapacity, _ = strconv.Atoi(v[0])
case "blockcacheevictremoved":
opts.BlockCacheEvictRemoved, _ = strconv.ParseBool(v[0])
case "blockrestartinterval":
opts.BlockRestartInterval, _ = strconv.Atoi(v[0])
case "blocksize":
opts.BlockSize, _ = strconv.Atoi(v[0])
case "compactionexpandlimitfactor":
opts.CompactionExpandLimitFactor, _ = strconv.Atoi(v[0])
case "compactiongpoverlapsfactor":
opts.CompactionGPOverlapsFactor, _ = strconv.Atoi(v[0])
case "compactionl0trigger":
opts.CompactionL0Trigger, _ = strconv.Atoi(v[0])
case "compactionsourcelimitfactor":
opts.CompactionSourceLimitFactor, _ = strconv.Atoi(v[0])
case "compactiontablesize":
opts.CompactionTableSize, _ = strconv.Atoi(v[0])
case "compactiontablesizemultiplier":
opts.CompactionTableSizeMultiplier, _ = strconv.ParseFloat(v[0], 64)
case "compactiontablesizemultiplierperlevel":
for _, val := range v {
f, _ := strconv.ParseFloat(val, 64)
opts.CompactionTableSizeMultiplierPerLevel = append(opts.CompactionTableSizeMultiplierPerLevel, f)
}
case "compactiontotalsize":
opts.CompactionTotalSize, _ = strconv.Atoi(v[0])
case "compactiontotalsizemultiplier":
opts.CompactionTotalSizeMultiplier, _ = strconv.ParseFloat(v[0], 64)
case "compactiontotalsizemultiplierperlevel":
for _, val := range v {
f, _ := strconv.ParseFloat(val, 64)
opts.CompactionTotalSizeMultiplierPerLevel = append(opts.CompactionTotalSizeMultiplierPerLevel, f)
}
case "compression":
val, _ := strconv.Atoi(v[0])
opts.Compression = opt.Compression(val)
case "disablebufferpool":
opts.DisableBufferPool, _ = strconv.ParseBool(v[0])
case "disableblockcache":
opts.DisableBlockCache, _ = strconv.ParseBool(v[0])
case "disablecompactionbackoff":
opts.DisableCompactionBackoff, _ = strconv.ParseBool(v[0])
case "disablelargebatchtransaction":
opts.DisableLargeBatchTransaction, _ = strconv.ParseBool(v[0])
case "errorifexist":
opts.ErrorIfExist, _ = strconv.ParseBool(v[0])
case "errorifmissing":
opts.ErrorIfMissing, _ = strconv.ParseBool(v[0])
case "iteratorsamplingrate":
opts.IteratorSamplingRate, _ = strconv.Atoi(v[0])
case "nosync":
opts.NoSync, _ = strconv.ParseBool(v[0])
case "nowritemerge":
opts.NoWriteMerge, _ = strconv.ParseBool(v[0])
case "openfilescachecapacity":
opts.OpenFilesCacheCapacity, _ = strconv.Atoi(v[0])
case "readonly":
opts.ReadOnly, _ = strconv.ParseBool(v[0])
case "strict":
val, _ := strconv.Atoi(v[0])
opts.Strict = opt.Strict(val)
case "writebuffer":
opts.WriteBuffer, _ = strconv.Atoi(v[0])
case "writel0pausetrigger":
opts.WriteL0PauseTrigger, _ = strconv.Atoi(v[0])
case "writel0slowdowntrigger":
opts.WriteL0SlowdownTrigger, _ = strconv.Atoi(v[0])
case "clientname":
db.name = append(db.name, v[0])
}
}

var err error
db.db, err = leveldb.OpenFile(dataDir, opts)
if err != nil {
if !errors.IsCorrupted(err) {
return nil, err
}
db.db, err = leveldb.RecoverFile(dataDir, opts)
if err != nil {
return nil, err
}
}

for _, name := range db.name {
m.LevelDBConnections[name] = db
}
db.count++
return db.db, nil
}

+ 205
- 0
modules/nosql/manager_redis.go View File

@@ -0,0 +1,205 @@
// Copyright 2020 The Gitea Authors. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.

package nosql

import (
"crypto/tls"
"path"
"strconv"
"strings"

"github.com/go-redis/redis/v7"
)

var replacer = strings.NewReplacer("_", "", "-", "")

// CloseRedisClient closes a redis client
func (m *Manager) CloseRedisClient(connection string) error {
m.mutex.Lock()
defer m.mutex.Unlock()
client, ok := m.RedisConnections[connection]
if !ok {
connection = ToRedisURI(connection).String()
client, ok = m.RedisConnections[connection]
}
if !ok {
return nil
}

client.count--
if client.count > 0 {
return nil
}

for _, name := range client.name {
delete(m.RedisConnections, name)
}
return client.UniversalClient.Close()
}

// GetRedisClient gets a redis client for a particular connection
func (m *Manager) GetRedisClient(connection string) redis.UniversalClient {
m.mutex.Lock()
defer m.mutex.Unlock()
client, ok := m.RedisConnections[connection]
if ok {
client.count++
return client
}

uri := ToRedisURI(connection)
client, ok = m.RedisConnections[uri.String()]
if ok {
client.count++
return client
}
client = &redisClientHolder{
name: []string{connection, uri.String()},
}

opts := &redis.UniversalOptions{}
tlsConfig := &tls.Config{}

// Handle username/password
if password, ok := uri.User.Password(); ok {
opts.Password = password
// Username does not appear to be handled by redis.Options
opts.Username = uri.User.Username()
} else if uri.User.Username() != "" {
// assume this is the password
opts.Password = uri.User.Username()
}

// Now handle the uri query sets
for k, v := range uri.Query() {
switch replacer.Replace(strings.ToLower(k)) {
case "addr":
opts.Addrs = append(opts.Addrs, v...)
case "addrs":
opts.Addrs = append(opts.Addrs, strings.Split(v[0], ",")...)
case "username":
opts.Username = v[0]
case "password":
opts.Password = v[0]
case "database":
fallthrough
case "db":
opts.DB, _ = strconv.Atoi(v[0])
case "maxretries":
opts.MaxRetries, _ = strconv.Atoi(v[0])
case "minretrybackoff":
opts.MinRetryBackoff = valToTimeDuration(v)
case "maxretrybackoff":
opts.MaxRetryBackoff = valToTimeDuration(v)
case "timeout":
timeout := valToTimeDuration(v)
if timeout != 0 {
if opts.DialTimeout == 0 {
opts.DialTimeout = timeout
}
if opts.ReadTimeout == 0 {
opts.ReadTimeout = timeout
}
}
case "dialtimeout":
opts.DialTimeout = valToTimeDuration(v)
case "readtimeout":
opts.ReadTimeout = valToTimeDuration(v)
case "writetimeout":
opts.WriteTimeout = valToTimeDuration(v)
case "poolsize":
opts.PoolSize, _ = strconv.Atoi(v[0])
case "minidleconns":
opts.MinIdleConns, _ = strconv.Atoi(v[0])
case "pooltimeout":
opts.PoolTimeout = valToTimeDuration(v)
case "idletimeout":
opts.IdleTimeout = valToTimeDuration(v)
case "idlecheckfrequency":
opts.IdleCheckFrequency = valToTimeDuration(v)
case "maxredirects":
opts.MaxRedirects, _ = strconv.Atoi(v[0])
case "readonly":
opts.ReadOnly, _ = strconv.ParseBool(v[0])
case "routebylatency":
opts.RouteByLatency, _ = strconv.ParseBool(v[0])
case "routerandomly":
opts.RouteRandomly, _ = strconv.ParseBool(v[0])
case "sentinelmasterid":
fallthrough
case "mastername":
opts.MasterName = v[0]
case "skipverify":
fallthrough
case "insecureskipverify":
insecureSkipVerify, _ := strconv.ParseBool(v[0])
tlsConfig.InsecureSkipVerify = insecureSkipVerify
case "clientname":
client.name = append(client.name, v[0])
}
}

switch uri.Scheme {
case "redis+sentinels":
fallthrough
case "rediss+sentinel":
opts.TLSConfig = tlsConfig
fallthrough
case "redis+sentinel":
if uri.Host != "" {
opts.Addrs = append(opts.Addrs, strings.Split(uri.Host, ",")...)
}
if uri.Path != "" {
if db, err := strconv.Atoi(uri.Path); err == nil {
opts.DB = db
}
}

client.UniversalClient = redis.NewFailoverClient(opts.Failover())
case "redis+clusters":
fallthrough
case "rediss+cluster":
opts.TLSConfig = tlsConfig
fallthrough
case "redis+cluster":
if uri.Host != "" {
opts.Addrs = append(opts.Addrs, strings.Split(uri.Host, ",")...)
}
if uri.Path != "" {
if db, err := strconv.Atoi(uri.Path); err == nil {
opts.DB = db
}
}
client.UniversalClient = redis.NewClusterClient(opts.Cluster())
case "redis+socket":
simpleOpts := opts.Simple()
simpleOpts.Network = "unix"
simpleOpts.Addr = path.Join(uri.Host, uri.Path)
client.UniversalClient = redis.NewClient(simpleOpts)
case "rediss":
opts.TLSConfig = tlsConfig
fallthrough
case "redis":
if uri.Host != "" {
opts.Addrs = append(opts.Addrs, strings.Split(uri.Host, ",")...)
}
if uri.Path != "" {
if db, err := strconv.Atoi(uri.Path); err == nil {
opts.DB = db
}
}
client.UniversalClient = redis.NewClient(opts.Simple())
default:
return nil
}

for _, name := range client.name {
m.RedisConnections[name] = client
}

client.count++

return client
}

+ 102
- 0
modules/nosql/redis.go View File

@@ -0,0 +1,102 @@
// Copyright 2020 The Gitea Authors. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.

package nosql

import (
"net/url"
"strconv"
"strings"
)

// The file contains common redis connection functions

// ToRedisURI converts old style connections to a RedisURI
//
// A RedisURI matches the pattern:
//
// redis://[username:password@]host[:port][/database][?[option=value]*]
// rediss://[username:password@]host[:port][/database][?[option=value]*]
// redis+socket://[username:password@]path[/database][?[option=value]*]
// redis+sentinel://[password@]host1 [: port1][, host2 [:port2]][, hostN [:portN]][/ database][?[option=value]*]
// redis+cluster://[password@]host1 [: port1][, host2 [:port2]][, hostN [:portN]][/ database][?[option=value]*]
//
// We have previously used a URI like:
// addrs=127.0.0.1:6379 db=0
// network=tcp,addr=127.0.0.1:6379,password=macaron,db=0,pool_size=100,idle_timeout=180
//
// We need to convert this old style to the new style
func ToRedisURI(connection string) *url.URL {
uri, err := url.Parse(connection)
if err == nil && strings.HasPrefix(uri.Scheme, "redis") {
// OK we're going to assume that this is a reasonable redis URI
return uri
}

// Let's set a nice default
uri, _ = url.Parse("redis://127.0.0.1:6379/0")
network := "tcp"
query := uri.Query()

// OK so there are two types: Space delimited and Comma delimited
// Let's assume that we have a space delimited string - as this is the most common
fields := strings.Fields(connection)
if len(fields) == 1 {
// It's a comma delimited string, then...
fields = strings.Split(connection, ",")

}
for _, f := range fields {
items := strings.SplitN(f, "=", 2)
if len(items) < 2 {
continue
}
switch strings.ToLower(items[0]) {
case "network":
if items[1] == "unix" {
uri.Scheme = "redis+socket"
}
network = items[1]
case "addrs":
uri.Host = items[1]
// now we need to handle the clustering
if strings.Contains(items[1], ",") && network == "tcp" {
uri.Scheme = "redis+cluster"
}
case "addr":
uri.Host = items[1]
case "password":
uri.User = url.UserPassword(uri.User.Username(), items[1])
case "username":
password, set := uri.User.Password()
if !set {
uri.User = url.User(items[1])
} else {
uri.User = url.UserPassword(items[1], password)
}
case "db":
uri.Path = "/" + items[1]
case "idle_timeout":
_, err := strconv.Atoi(items[1])
if err == nil {
query.Add("idle_timeout", items[1]+"s")
} else {
query.Add("idle_timeout", items[1])
}
default:
// Other options become query params
query.Add(items[0], items[1])
}
}

// Finally we need to fix up the Host if we have a unix port
if uri.Scheme == "redis+socket" {
query.Set("db", uri.Path)
uri.Path = uri.Host
uri.Host = ""
}
uri.RawQuery = query.Encode()

return uri
}

+ 35
- 0
modules/nosql/redis_test.go View File

@@ -0,0 +1,35 @@
// Copyright 2020 The Gitea Authors. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.

package nosql

import (
"testing"
)

func TestToRedisURI(t *testing.T) {
tests := []struct {
name string
connection string
want string
}{
{
name: "old_default",
connection: "addrs=127.0.0.1:6379 db=0",
want: "redis://127.0.0.1:6379/0",
},
{
name: "old_macaron_session_default",
connection: "network=tcp,addr=127.0.0.1:6379,password=macaron,db=0,pool_size=100,idle_timeout=180",
want: "redis://:macaron@127.0.0.1:6379/0?idle_timeout=180s&pool_size=100",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if got := ToRedisURI(tt.connection); got == nil || got.String() != tt.want {
t.Errorf(`ToRedisURI(%q) = %s, want %s`, tt.connection, got.String(), tt.want)
}
})
}
}

+ 24
- 7
modules/queue/queue_disk.go View File

@@ -5,6 +5,8 @@
package queue

import (
"code.gitea.io/gitea/modules/nosql"

"gitea.com/lunny/levelqueue"
)

@@ -14,7 +16,9 @@ const LevelQueueType Type = "level"
// LevelQueueConfiguration is the configuration for a LevelQueue
type LevelQueueConfiguration struct {
ByteFIFOQueueConfiguration
DataDir string
DataDir string
ConnectionString string
QueueName string
}

// LevelQueue implements a disk library queue
@@ -30,7 +34,11 @@ func NewLevelQueue(handle HandlerFunc, cfg, exemplar interface{}) (Queue, error)
}
config := configInterface.(LevelQueueConfiguration)

byteFIFO, err := NewLevelQueueByteFIFO(config.DataDir)
if len(config.ConnectionString) == 0 {
config.ConnectionString = config.DataDir
}

byteFIFO, err := NewLevelQueueByteFIFO(config.ConnectionString, config.QueueName)
if err != nil {
return nil, err
}
@@ -51,18 +59,25 @@ var _ (ByteFIFO) = &LevelQueueByteFIFO{}

// LevelQueueByteFIFO represents a ByteFIFO formed from a LevelQueue
type LevelQueueByteFIFO struct {
internal *levelqueue.Queue
internal *levelqueue.Queue
connection string
}

// NewLevelQueueByteFIFO creates a ByteFIFO formed from a LevelQueue
func NewLevelQueueByteFIFO(dataDir string) (*LevelQueueByteFIFO, error) {
internal, err := levelqueue.Open(dataDir)
func NewLevelQueueByteFIFO(connection, prefix string) (*LevelQueueByteFIFO, error) {
db, err := nosql.GetManager().GetLevelDB(connection)
if err != nil {
return nil, err
}

internal, err := levelqueue.NewQueue(db, []byte(prefix), false)
if err != nil {
return nil, err
}

return &LevelQueueByteFIFO{
internal: internal,
connection: connection,
internal: internal,
}, nil
}

@@ -87,7 +102,9 @@ func (fifo *LevelQueueByteFIFO) Pop() ([]byte, error) {

// Close this fifo
func (fifo *LevelQueueByteFIFO) Close() error {
return fifo.internal.Close()
err := fifo.internal.Close()
_ = nosql.GetManager().CloseLevelDB(fifo.connection)
return err
}

// Len returns the length of the fifo


+ 5
- 24
modules/queue/queue_redis.go View File

@@ -5,12 +5,10 @@
package queue

import (
"errors"
"strings"

"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/nosql"

"github.com/go-redis/redis"
"github.com/go-redis/redis/v7"
)

// RedisQueueType is the type for redis queue
@@ -75,11 +73,8 @@ type RedisByteFIFO struct {

// RedisByteFIFOConfiguration is the configuration for the RedisByteFIFO
type RedisByteFIFOConfiguration struct {
Network string
Addresses string
Password string
DBIndex int
QueueName string
ConnectionString string
QueueName string
}

// NewRedisByteFIFO creates a ByteFIFO formed from a redisClient
@@ -87,21 +82,7 @@ func NewRedisByteFIFO(config RedisByteFIFOConfiguration) (*RedisByteFIFO, error)
fifo := &RedisByteFIFO{
queueName: config.QueueName,
}
dbs := strings.Split(config.Addresses, ",")
if len(dbs) == 0 {
return nil, errors.New("no redis host specified")
} else if len(dbs) == 1 {
fifo.client = redis.NewClient(&redis.Options{
Network: config.Network,
Addr: strings.TrimSpace(dbs[0]), // use default Addr
Password: config.Password, // no password set
DB: config.DBIndex, // use default DB
})
} else {
fifo.client = redis.NewClusterClient(&redis.ClusterOptions{
Addrs: dbs,
})
}
fifo.client = nosql.GetManager().GetRedisClient(config.ConnectionString)
if err := fifo.client.Ping().Err(); err != nil {
return nil, err
}


+ 24
- 7
modules/queue/unique_queue_disk.go View File

@@ -5,6 +5,8 @@
package queue

import (
"code.gitea.io/gitea/modules/nosql"

"gitea.com/lunny/levelqueue"
)

@@ -14,7 +16,9 @@ const LevelUniqueQueueType Type = "unique-level"
// LevelUniqueQueueConfiguration is the configuration for a LevelUniqueQueue
type LevelUniqueQueueConfiguration struct {
ByteFIFOQueueConfiguration
DataDir string
DataDir string
ConnectionString string
QueueName string
}

// LevelUniqueQueue implements a disk library queue
@@ -34,7 +38,11 @@ func NewLevelUniqueQueue(handle HandlerFunc, cfg, exemplar interface{}) (Queue,
}
config := configInterface.(LevelUniqueQueueConfiguration)

byteFIFO, err := NewLevelUniqueQueueByteFIFO(config.DataDir)
if len(config.ConnectionString) == 0 {
config.ConnectionString = config.DataDir
}

byteFIFO, err := NewLevelUniqueQueueByteFIFO(config.ConnectionString, config.QueueName)
if err != nil {
return nil, err
}
@@ -55,18 +63,25 @@ var _ (UniqueByteFIFO) = &LevelUniqueQueueByteFIFO{}

// LevelUniqueQueueByteFIFO represents a ByteFIFO formed from a LevelUniqueQueue
type LevelUniqueQueueByteFIFO struct {
internal *levelqueue.UniqueQueue
internal *levelqueue.UniqueQueue
connection string
}

// NewLevelUniqueQueueByteFIFO creates a new ByteFIFO formed from a LevelUniqueQueue
func NewLevelUniqueQueueByteFIFO(dataDir string) (*LevelUniqueQueueByteFIFO, error) {
internal, err := levelqueue.OpenUnique(dataDir)
func NewLevelUniqueQueueByteFIFO(connection, prefix string) (*LevelUniqueQueueByteFIFO, error) {
db, err := nosql.GetManager().GetLevelDB(connection)
if err != nil {
return nil, err
}

internal, err := levelqueue.NewUniqueQueue(db, []byte(prefix), []byte(prefix+"-unique"), false)
if err != nil {
return nil, err
}

return &LevelUniqueQueueByteFIFO{
internal: internal,
connection: connection,
internal: internal,
}, nil
}

@@ -96,7 +111,9 @@ func (fifo *LevelUniqueQueueByteFIFO) Has(data []byte) (bool, error) {

// Close this fifo
func (fifo *LevelUniqueQueueByteFIFO) Close() error {
return fifo.internal.Close()
err := fifo.internal.Close()
_ = nosql.GetManager().CloseLevelDB(fifo.connection)
return err
}

func init() {


+ 1
- 1
modules/queue/unique_queue_redis.go View File

@@ -4,7 +4,7 @@

package queue

import "github.com/go-redis/redis"
import "github.com/go-redis/redis/v7"

// RedisUniqueQueueType is the type for redis queue
const RedisUniqueQueueType Type = "unique-redis"


vendor/gitea.com/macaron/session/redis/redis.go → modules/session/redis.go View File

@@ -1,5 +1,6 @@
// Copyright 2013 Beego Authors
// Copyright 2014 The Macaron Authors
// Copyright 2020 The Gitea 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
@@ -17,19 +18,18 @@ package session

import (
"fmt"
"strings"
"sync"
"time"

"code.gitea.io/gitea/modules/nosql"

"gitea.com/macaron/session"
"github.com/go-redis/redis"
"github.com/unknwon/com"
"gopkg.in/ini.v1"
"github.com/go-redis/redis/v7"
)

// RedisStore represents a redis session store implementation.
type RedisStore struct {
c *redis.Client
c redis.UniversalClient
prefix, sid string
duration time.Duration
lock sync.RWMutex
@@ -37,7 +37,7 @@ type RedisStore struct {
}

// NewRedisStore creates and returns a redis session store.
func NewRedisStore(c *redis.Client, prefix, sid string, dur time.Duration, kv map[interface{}]interface{}) *RedisStore {
func NewRedisStore(c redis.UniversalClient, prefix, sid string, dur time.Duration, kv map[interface{}]interface{}) *RedisStore {
return &RedisStore{
c: c,
prefix: prefix,
@@ -104,7 +104,7 @@ func (s *RedisStore) Flush() error {

// RedisProvider represents a redis session provider implementation.
type RedisProvider struct {
c *redis.Client
c redis.UniversalClient
duration time.Duration
prefix string
}
@@ -117,39 +117,16 @@ func (p *RedisProvider) Init(maxlifetime int64, configs string) (err error) {
return err
}

cfg, err := ini.Load([]byte(strings.Replace(configs, ",", "\n", -1)))
if err != nil {
return err
}
uri := nosql.ToRedisURI(configs)

opt := &redis.Options{
Network: "tcp",
}
for k, v := range cfg.Section("").KeysHash() {
for k, v := range uri.Query() {
switch k {
case "network":
opt.Network = v
case "addr":
opt.Addr = v
case "password":
opt.Password = v
case "db":
opt.DB = com.StrTo(v).MustInt()
case "pool_size":
opt.PoolSize = com.StrTo(v).MustInt()
case "idle_timeout":
opt.IdleTimeout, err = time.ParseDuration(v + "s")
if err != nil {
return fmt.Errorf("error parsing idle timeout: %v", err)
}
case "prefix":
p.prefix = v
default:
return fmt.Errorf("session/redis: unsupported option '%s'", k)
p.prefix = v[0]
}
}

p.c = redis.NewClient(opt)
p.c = nosql.GetManager().GetRedisClient(uri.String())
return p.c.Ping().Err()
}

@@ -228,11 +205,11 @@ func (p *RedisProvider) Regenerate(oldsid, sid string) (_ session.RawStore, err

// Count counts and returns number of sessions.
func (p *RedisProvider) Count() int {
return int(p.c.DbSize().Val())
return int(p.c.DBSize().Val())
}

// GC calls GC to clean expired sessions.
func (_ *RedisProvider) GC() {}
func (*RedisProvider) GC() {}

func init() {
session.Register("redis", &RedisProvider{})

+ 1
- 2
modules/session/virtual.go View File

@@ -15,7 +15,6 @@ import (
mysql "gitea.com/macaron/session/mysql"
nodb "gitea.com/macaron/session/nodb"
postgres "gitea.com/macaron/session/postgres"
redis "gitea.com/macaron/session/redis"
)

// VirtualSessionProvider represents a shadowed session provider implementation.
@@ -40,7 +39,7 @@ func (o *VirtualSessionProvider) Init(gclifetime int64, config string) error {
case "file":
o.provider = &session.FileProvider{}
case "redis":
o.provider = &redis.RedisProvider{}
o.provider = &RedisProvider{}
case "mysql":
o.provider = &mysql.MysqlProvider{}
case "postgres":


+ 2
- 0
modules/setting/repository.go View File

@@ -29,6 +29,7 @@ var (
AnsiCharset string
ForcePrivate bool
DefaultPrivate string
DefaultPushCreatePrivate bool
MaxCreationLimit int
MirrorQueueLength int
PullRequestQueueLength int
@@ -134,6 +135,7 @@ var (
AnsiCharset: "",
ForcePrivate: false,
DefaultPrivate: RepoCreatingLastUserVisibility,
DefaultPushCreatePrivate: true,
MaxCreationLimit: -1,
MirrorQueueLength: 1000,
PullRequestQueueLength: 1000,


+ 11
- 0
options/locale/locale_es-ES.ini View File

@@ -939,6 +939,8 @@ issues.new.no_assignees=No asignados
issues.new.no_reviewers=No hay revisores
issues.new.add_reviewer_title=Solicitar revisión
issues.choose.get_started=Comenzar
issues.choose.blank=Predeterminado
issues.choose.blank_about=Crear una incidencia a partir de la plantilla predeterminada.
issues.no_ref=Ninguna Rama/Etiqueta especificada
issues.create=Crear incidencia
issues.new_label=Nueva Etiqueta
@@ -1461,6 +1463,15 @@ settings.transfer_desc=Transferir este repositorio a un usuario o una organizaci
settings.transfer_notices_1=- Perderá el acceso al repositorio si lo transfiere a un usuario individual.
settings.transfer_notices_2=- Mantendrá el acceso al repositorio si lo transfiere a una organización que usted (co-)posee.
settings.transfer_form_title=Escriba el nombre del repositorio como confirmación:
settings.signing_settings=Configuración de verificación de firmas
settings.trust_model=Modelo de confianza de firma
settings.trust_model.default=Modelo de confianza por defecto
settings.trust_model.default.desc=Utilice el modelo de confianza de repositorio por defecto para esta instalación.
settings.trust_model.collaborator=Colaborador
settings.trust_model.collaborator.long=Colaborador: Confiar en firmas de colaboradores
settings.trust_model.collaborator.desc=Las firmas válidas de los colaboradores de este repositorio serán marcadas como "confiables" - (coincidan o no con el committer). De lo contrario, las firmas válidas serán marcadas como "no confiables" si la firma coincide con el committer y "no coincidente" si no lo es.
settings.trust_model.committer=Committer
settings.trust_model.committer.long=Committer: Firmas de confianza que coinciden con los committers (Esto coincide con GitHub y obligará a Gitea a firmar los commits a tener a Gitea como el committer)
settings.wiki_delete=Eliminar datos de Wiki
settings.wiki_delete_desc=Eliminar los datos del wiki del repositorio es permanente y no se puede deshacer.
settings.wiki_delete_notices_1=- Esto eliminará y desactivará permanentemente el wiki del repositorio para %s.


+ 2
- 1
services/repository/repository.go View File

@@ -11,6 +11,7 @@ import (
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/notification"
repo_module "code.gitea.io/gitea/modules/repository"
cfg "code.gitea.io/gitea/modules/setting"
pull_service "code.gitea.io/gitea/services/pull"
)

@@ -88,7 +89,7 @@ func PushCreateRepo(authUser, owner *models.User, repoName string) (*models.Repo

repo, err := CreateRepository(authUser, owner, models.CreateRepoOptions{
Name: repoName,
IsPrivate: true,
IsPrivate: cfg.Repository.DefaultPushCreatePrivate,
})
if err != nil {
return nil, err


+ 0
- 1
vendor/gitea.com/macaron/cache/redis/redis.goconvey View File

@@ -1 +0,0 @@
ignore

+ 0
- 1
vendor/gitea.com/macaron/session/redis/redis.goconvey View File

@@ -1 +0,0 @@
ignore

+ 0
- 19
vendor/github.com/go-redis/redis/.travis.yml View File

@@ -1,19 +0,0 @@
sudo: false
language: go

services:
- redis-server

go:
- 1.9.x
- 1.10.x
- 1.11.x
- tip

matrix:
allow_failures:
- go: tip

install:
- go get github.com/onsi/ginkgo
- go get github.com/onsi/gomega

+ 0
- 25
vendor/github.com/go-redis/redis/CHANGELOG.md View File

@@ -1,25 +0,0 @@
# Changelog

## Unreleased

- Cluster and Ring pipelines process commands for each node in its own goroutine.

## 6.14

- Added Options.MinIdleConns.
- Added Options.MaxConnAge.
- PoolStats.FreeConns is renamed to PoolStats.IdleConns.
- Add Client.Do to simplify creating custom commands.
- Add Cmd.String, Cmd.Int, Cmd.Int64, Cmd.Uint64, Cmd.Float64, and Cmd.Bool helpers.
- Lower memory usage.

## v6.13

- Ring got new options called `HashReplicas` and `Hash`. It is recommended to set `HashReplicas = 1000` for better keys distribution between shards.
- Cluster client was optimized to use much less memory when reloading cluster state.
- PubSub.ReceiveMessage is re-worked to not use ReceiveTimeout so it does not lose data when timeout occurres. In most cases it is recommended to use PubSub.Channel instead.
- Dialer.KeepAlive is set to 5 minutes by default.

## v6.12

- ClusterClient got new option called `ClusterSlots` which allows to build cluster of normal Redis Servers that don't have cluster mode enabled. See https://godoc.org/github.com/go-redis/redis#example-NewClusterClient--ManualSetup

+ 0
- 89
vendor/github.com/go-redis/redis/internal/error.go View File

@@ -1,89 +0,0 @@
package internal

import (
"io"
"net"
"strings"

"github.com/go-redis/redis/internal/proto"
)

func IsRetryableError(err error, retryTimeout bool) bool {
if err == nil {
return false
}
if err == io.EOF {
return true
}
if netErr, ok := err.(net.Error); ok {
if netErr.Timeout() {
return retryTimeout
}
return true
}
s := err.Error()
if s == "ERR max number of clients reached" {
return true
}
if strings.HasPrefix(s, "LOADING ") {
return true
}
if strings.HasPrefix(s, "READONLY ") {
return true
}
if strings.HasPrefix(s, "CLUSTERDOWN ") {
return true
}
return false
}

func IsRedisError(err error) bool {
_, ok := err.(proto.RedisError)
return ok
}

func IsBadConn(err error, allowTimeout bool) bool {
if err == nil {
return false
}
if IsRedisError(err) {
// #790
return IsReadOnlyError(err)
}
if allowTimeout {
if netErr, ok := err.(net.Error); ok && netErr.Timeout() {
return false
}
}
return true
}

func IsMovedError(err error) (moved bool, ask bool, addr string) {
if !IsRedisError(err) {
return
}

s := err.Error()
if strings.HasPrefix(s, "MOVED ") {
moved = true
} else if strings.HasPrefix(s, "ASK ") {
ask = true
} else {
return
}

ind := strings.LastIndex(s, " ")
if ind == -1 {
return false, false, ""
}
addr = s[ind+1:]
return
}

func IsLoadingError(err error) bool {
return strings.HasPrefix(err.Error(), "LOADING ")
}

func IsReadOnlyError(err error) bool {
return strings.HasPrefix(err.Error(), "READONLY ")
}

+ 0
- 15
vendor/github.com/go-redis/redis/internal/log.go View File

@@ -1,15 +0,0 @@
package internal

import (
"fmt"
"log"
)

var Logger *log.Logger

func Logf(s string, args ...interface{}) {
if Logger == nil {
return
}
Logger.Output(2, fmt.Sprintf(s, args...))
}

+ 0
- 93
vendor/github.com/go-redis/redis/internal/pool/conn.go View File

@@ -1,93 +0,0 @@
package pool

import (
"net"
"sync/atomic"
"time"

"github.com/go-redis/redis/internal/proto"
)

var noDeadline = time.Time{}

type Conn struct {
netConn net.Conn

rd *proto.Reader
rdLocked bool
wr *proto.Writer

InitedAt time.Time
pooled bool
usedAt atomic.Value
}

func NewConn(netConn net.Conn) *Conn {
cn := &Conn{
netConn: netConn,
}
cn.rd = proto.NewReader(netConn)
cn.wr = proto.NewWriter(netConn)
cn.SetUsedAt(time.Now())
return cn
}

func (cn *Conn) UsedAt() time.Time {
return cn.usedAt.Load().(time.Time)
}

func (cn *Conn) SetUsedAt(tm time.Time) {
cn.usedAt.Store(tm)
}

func (cn *Conn) SetNetConn(netConn net.Conn) {
cn.netConn = netConn
cn.rd.Reset(netConn)
cn.wr.Reset(netConn)
}

func (cn *Conn) setReadTimeout(timeout time.Duration) error {
now := time.Now()
cn.SetUsedAt(now)
if timeout > 0 {
return cn.netConn.SetReadDeadline(now.Add(timeout))
}
return cn.netConn.SetReadDeadline(noDeadline)
}

func (cn *Conn) setWriteTimeout(timeout time.Duration) error {
now := time.Now()
cn.SetUsedAt(now)
if timeout > 0 {
return cn.netConn.SetWriteDeadline(now.Add(timeout))
}
return cn.netConn.SetWriteDeadline(noDeadline)
}

func (cn *Conn) Write(b []byte) (int, error) {
return cn.netConn.Write(b)
}

func (cn *Conn) RemoteAddr() net.Addr {
return cn.netConn.RemoteAddr()
}

func (cn *Conn) WithReader(timeout time.Duration, fn func(rd *proto.Reader) error) error {
_ = cn.setReadTimeout(timeout)
return fn(cn.rd)
}

func (cn *Conn) WithWriter(timeout time.Duration, fn func(wr *proto.Writer) error) error {
_ = cn.setWriteTimeout(timeout)

firstErr := fn(cn.wr)
err := cn.wr.Flush()
if err != nil && firstErr == nil {
firstErr = err
}
return firstErr
}

func (cn *Conn) Close() error {
return cn.netConn.Close()
}

+ 0
- 53
vendor/github.com/go-redis/redis/internal/pool/pool_single.go View File

@@ -1,53 +0,0 @@
package pool

type SingleConnPool struct {
cn *Conn
}

var _ Pooler = (*SingleConnPool)(nil)

func NewSingleConnPool(cn *Conn) *SingleConnPool {
return &SingleConnPool{
cn: cn,
}
}

func (p *SingleConnPool) NewConn() (*Conn, error) {
panic("not implemented")
}

func (p *SingleConnPool) CloseConn(*Conn) error {
panic("not implemented")
}

func (p *SingleConnPool) Get() (*Conn, error) {
return p.cn, nil
}

func (p *SingleConnPool) Put(cn *Conn) {
if p.cn != cn {
panic("p.cn != cn")
}
}

func (p *SingleConnPool) Remove(cn *Conn) {
if p.cn != cn {
panic("p.cn != cn")
}
}

func (p *SingleConnPool) Len() int {
return 1
}

func (p *SingleConnPool) IdleLen() int {
return 0
}

func (p *SingleConnPool) Stats() *Stats {
return nil
}

func (p *SingleConnPool) Close() error {
return nil
}

+ 0
- 29
vendor/github.com/go-redis/redis/internal/util.go View File

@@ -1,29 +0,0 @@
package internal

import "github.com/go-redis/redis/internal/util"

func ToLower(s string) string {
if isLower(s) {
return s
}

b := make([]byte, len(s))
for i := range b {
c := s[i]
if c >= 'A' && c <= 'Z' {
c += 'a' - 'A'
}
b[i] = c
}
return util.BytesToString(b)
}

func isLower(s string) bool {
for i := 0; i < len(s); i++ {
c := s[i]
if c >= 'A' && c <= 'Z' {
return false
}
}
return true
}

+ 0
- 580
vendor/github.com/go-redis/redis/redis.go View File

@@ -1,580 +0,0 @@
package redis

import (
"context"
"fmt"
"log"
"os"
"time"

"github.com/go-redis/redis/internal"
"github.com/go-redis/redis/internal/pool"
"github.com/go-redis/redis/internal/proto"
)

// Nil reply Redis returns when key does not exist.
const Nil = proto.Nil

func init() {
SetLogger(log.New(os.Stderr, "redis: ", log.LstdFlags|log.Lshortfile))
}

func SetLogger(logger *log.Logger) {
internal.Logger = logger
}

type baseClient struct {
opt *Options
connPool pool.Pooler
limiter Limiter

process func(Cmder) error
processPipeline func([]Cmder) error
processTxPipeline func([]Cmder) error

onClose func() error // hook called when client is closed
}

func (c *baseClient) init() {
c.process = c.defaultProcess
c.processPipeline = c.defaultProcessPipeline
c.processTxPipeline = c.defaultProcessTxPipeline
}

func (c *baseClient) String() string {
return fmt.Sprintf("Redis<%s db:%d>", c.getAddr(), c.opt.DB)
}

func (c *baseClient) newConn() (*pool.Conn, error) {
cn, err := c.connPool.NewConn()
if err != nil {
return nil, err
}

if cn.InitedAt.IsZero() {
if err := c.initConn(cn); err != nil {
_ = c.connPool.CloseConn(cn)
return nil, err
}
}

return cn, nil
}

func (c *baseClient) getConn() (*pool.Conn, error) {
if c.limiter != nil {
err := c.limiter.Allow()
if err != nil {
return nil, err
}
}

cn, err := c._getConn()
if err != nil {
if c.limiter != nil {
c.limiter.ReportResult(err)
}
return nil, err
}
return cn, nil
}

func (c *baseClient) _getConn() (*pool.Conn, error) {
cn, err := c.connPool.Get()
if err != nil {
return nil, err
}

if cn.InitedAt.IsZero() {
err := c.initConn(cn)
if err != nil {
c.connPool.Remove(cn)
return nil, err
}
}

return cn, nil
}

func (c *baseClient) releaseConn(cn *pool.Conn, err error) {
if c.limiter != nil {
c.limiter.ReportResult(err)
}

if internal.IsBadConn(err, false) {
c.connPool.Remove(cn)
} else {
c.connPool.Put(cn)
}
}

func (c *baseClient) releaseConnStrict(cn *pool.Conn, err error) {
if c.limiter != nil {
c.limiter.ReportResult(err)
}

if err == nil || internal.IsRedisError(err) {
c.connPool.Put(cn)
} else {
c.connPool.Remove(cn)
}
}

func (c *baseClient) initConn(cn *pool.Conn) error {
cn.InitedAt = time.Now()

if c.opt.Password == "" &&
c.opt.DB == 0 &&
!c.opt.readOnly &&
c.opt.OnConnect == nil {
return nil
}

conn := newConn(c.opt, cn)
_, err := conn.Pipelined(func(pipe Pipeliner) error {
if c.opt.Password != "" {
pipe.Auth(c.opt.Password)
}

if c.opt.DB > 0 {
pipe.Select(c.opt.DB)
}

if c.opt.readOnly {
pipe.ReadOnly()
}

return nil
})
if err != nil {
return err
}

if c.opt.OnConnect != nil {
return c.opt.OnConnect(conn)
}
return nil
}

// Do creates a Cmd from the args and processes the cmd.
func (c *baseClient) Do(args ...interface{}) *Cmd {
cmd := NewCmd(args...)
_ = c.Process(cmd)
return cmd
}

// WrapProcess wraps function that processes Redis commands.
func (c *baseClient) WrapProcess(
fn func(oldProcess func(cmd Cmder) error) func(cmd Cmder) error,
) {
c.process = fn(c.process)
}

func (c *baseClient) Process(cmd Cmder) error {
return c.process(cmd)
}

func (c *baseClient) defaultProcess(cmd Cmder) error {
for attempt := 0; attempt <= c.opt.MaxRetries; attempt++ {
if attempt > 0 {
time.Sleep(c.retryBackoff(attempt))
}

cn, err := c.getConn()
if err != nil {
cmd.setErr(err)
if internal.IsRetryableError(err, true) {
continue
}
return err
}

err = cn.WithWriter(c.opt.WriteTimeout, func(wr *proto.Writer) error {
return writeCmd(wr, cmd)
})
if err != nil {
c.releaseConn(cn, err)
cmd.setErr(err)
if internal.IsRetryableError(err, true) {
continue
}
return err
}

err = cn.WithReader(c.cmdTimeout(cmd), func(rd *proto.Reader) error {
return cmd.readReply(rd)
})
c.releaseConn(cn, err)
if err != nil && internal.IsRetryableError(err, cmd.readTimeout() == nil) {
continue
}

return err
}

return cmd.Err()
}

func (c *baseClient) retryBackoff(attempt int) time.Duration {
return internal.RetryBackoff(attempt, c.opt.MinRetryBackoff, c.opt.MaxRetryBackoff)
}

func (c *baseClient) cmdTimeout(cmd Cmder) time.Duration {
if timeout := cmd.readTimeout(); timeout != nil {
t := *timeout
if t == 0 {
return 0
}
return t + 10*time.Second
}
return c.opt.ReadTimeout
}

// Close closes the client, releasing any open resources.
//
// It is rare to Close a Client, as the Client is meant to be
// long-lived and shared between many goroutines.
func (c *baseClient) Close() error {
var firstErr error
if c.onClose != nil {
if err := c.onClose(); err != nil && firstErr == nil {
firstErr = err
}
}
if err := c.connPool.Close(); err != nil && firstErr == nil {
firstErr = err
}
return firstErr
}

func (c *baseClient) getAddr() string {
return c.opt.Addr
}

func (c *baseClient) WrapProcessPipeline(
fn func(oldProcess func([]Cmder) error) func([]Cmder) error,
) {
c.processPipeline = fn(c.processPipeline)
c.processTxPipeline = fn(c.processTxPipeline)
}

func (c *baseClient) defaultProcessPipeline(cmds []Cmder) error {
return c.generalProcessPipeline(cmds, c.pipelineProcessCmds)
}

func (c *baseClient) defaultProcessTxPipeline(cmds []Cmder) error {
return c.generalProcessPipeline(cmds, c.txPipelineProcessCmds)
}

type pipelineProcessor func(*pool.Conn, []Cmder) (bool, error)

func (c *baseClient) generalProcessPipeline(cmds []Cmder, p pipelineProcessor) error {
for attempt := 0; attempt <= c.opt.MaxRetries; attempt++ {
if attempt > 0 {
time.Sleep(c.retryBackoff(attempt))
}

cn, err := c.getConn()
if err != nil {
setCmdsErr(cmds, err)
return err
}

canRetry, err := p(cn, cmds)
c.releaseConnStrict(cn, err)

if !canRetry || !internal.IsRetryableError(err, true) {
break
}
}
return cmdsFirstErr(cmds)
}

func (c *baseClient) pipelineProcessCmds(cn *pool.Conn, cmds []Cmder) (bool, error) {
err := cn.WithWriter(c.opt.WriteTimeout, func(wr *proto.Writer) error {
return writeCmd(wr, cmds...)
})
if err != nil {
setCmdsErr(cmds, err)
return true, err
}

err = cn.WithReader(c.opt.ReadTimeout, func(rd *proto.Reader) error {
return pipelineReadCmds(rd, cmds)
})
return true, err
}

func pipelineReadCmds(rd *proto.Reader, cmds []Cmder) error {
for _, cmd := range cmds {
err := cmd.readReply(rd)
if err != nil && !internal.IsRedisError(err) {
return err
}
}
return nil
}

func (c *baseClient) txPipelineProcessCmds(cn *pool.Conn, cmds []Cmder) (bool, error) {
err := cn.WithWriter(c.opt.WriteTimeout, func(wr *proto.Writer) error {
return txPipelineWriteMulti(wr, cmds)
})
if err != nil {
setCmdsErr(cmds, err)
return true, err
}

err = cn.WithReader(c.opt.ReadTimeout, func(rd *proto.Reader) error {
err := txPipelineReadQueued(rd, cmds)
if err != nil {
setCmdsErr(cmds, err)
return err
}
return pipelineReadCmds(rd, cmds)
})
return false, err
}

func txPipelineWriteMulti(wr *proto.Writer, cmds []Cmder) error {
multiExec := make([]Cmder, 0, len(cmds)+2)
multiExec = append(multiExec, NewStatusCmd("MULTI"))
multiExec = append(multiExec, cmds...)
multiExec = append(multiExec, NewSliceCmd("EXEC"))
return writeCmd(wr, multiExec...)
}

func txPipelineReadQueued(rd *proto.Reader, cmds []Cmder) error {
// Parse queued replies.
var statusCmd StatusCmd
err := statusCmd.readReply(rd)