-
Notifications
You must be signed in to change notification settings - Fork 4.8k
/
Copy pathmanager.go
148 lines (122 loc) · 3.64 KB
/
manager.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
// Copyright Project Harbor Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package migration
import (
"github.com/Masterminds/semver"
"reflect"
"github.com/gomodule/redigo/redis"
"github.com/goharbor/harbor/src/jobservice/logger"
"github.com/pkg/errors"
)
// Manager for managing the related migrators
type Manager interface {
// Register the specified migrator to the execution chain
Register(migratorFactory MigratorFactory)
// Migrate data
Migrate() error
}
// MigratorChainNode is a wrapper to append the migrator to the chain with a next reference
type MigratorChainNode struct {
// Migrator implementation
migrator RDBMigrator
// Refer the next migration of the chain if existing
next *MigratorChainNode
}
// BasicManager is the default implementation of manager interface
type BasicManager struct {
// The head of migrator chain
head *MigratorChainNode
// Pool for connecting to redis
pool *redis.Pool
// RDB namespace
namespace string
}
// New a basic manager
func New(pool *redis.Pool, ns string) Manager {
return &BasicManager{
pool: pool,
namespace: ns,
}
}
// Register the migrator to the chain
func (bm *BasicManager) Register(migratorFactory MigratorFactory) {
if migratorFactory == nil {
return // ignore, do nothing
}
migrator, err := migratorFactory(bm.pool, bm.namespace)
if err != nil {
logger.Errorf("migrator register error: %s", err)
return
}
newNode := &MigratorChainNode{
migrator: migrator,
next: nil,
}
if bm.head == nil {
bm.head = newNode
return
}
bm.head.next = newNode
}
// Migrate data
func (bm *BasicManager) Migrate() error {
conn := bm.pool.Get()
defer func() {
_ = conn.Close()
}()
// Read schema version first
v, err := redis.String(conn.Do("GET", VersionKey(bm.namespace)))
if err != nil && err != redis.ErrNil {
return errors.Wrap(err, "read schema version failed")
}
if len(v) > 0 {
current, err := semver.NewVersion(v)
if err != nil {
return errors.Wrap(err, "malformed schema version")
}
nowV, _ := semver.NewVersion(SchemaVersion)
diff := nowV.Compare(current)
if diff < 0 {
return errors.Errorf("the schema version of migrator is smaller that the one in the rdb: %s<%s", nowV.String(), current.String())
} else if diff == 0 {
logger.Info("No migration needed")
return nil
}
}
if bm.head == nil {
logger.Warning("No migrator registered, passed migration")
return nil
}
logger.Info("Process for migrating data is started")
h := bm.head
for h != nil {
meta := h.migrator.Metadata()
if meta == nil {
// Make metadata required
return errors.Errorf("no metadata provided for the migrator %s", reflect.TypeOf(h.migrator).String())
}
logger.Infof("Migrate %s from %s to %s", meta.ObjectRef, meta.FromVersion, meta.ToVersion)
if err := h.migrator.Migrate(); err != nil {
return errors.Wrap(err, "migration chain calling failed")
}
// Next one if existing
h = h.next
}
// Set schema version
if _, err = conn.Do("SET", VersionKey(bm.namespace), SchemaVersion); err != nil {
return errors.Wrap(err, "write schema version failed")
}
logger.Infof("Data schema version upgraded to %s", SchemaVersion)
return nil
}