forked from gravitational/teleport
-
Notifications
You must be signed in to change notification settings - Fork 0
/
migrate.go
168 lines (142 loc) · 4.31 KB
/
migrate.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
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
/*
Copyright 2018 Gravitational, Inc.
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 dir
import (
"io/ioutil"
"os"
"path/filepath"
"strings"
"time"
"github.com/gravitational/teleport/lib/backend"
"github.com/gravitational/trace"
)
// DELETE IN: 2.8.0
// migrate old directory backend to the new flat keyspace.
func migrate(rootDir string, b *Backend) error {
// Check if the directory structure is the old bucket format.
ok, err := isOld(rootDir)
if err != nil {
return trace.Wrap(err)
}
// Found the new flat keyspace directory backend, nothing to do.
if !ok {
b.log.Debugf("Found new flat keyspace, skipping migration.")
return nil
}
// The old directory backend was found, make a backup in-case is needs to
// be restored.
backupDir := rootDir + ".backup-" + time.Now().Format(time.RFC3339)
err = os.Rename(rootDir, backupDir)
if err != nil {
return trace.Wrap(err)
}
err = os.MkdirAll(rootDir, defaultDirMode)
if err != nil {
return trace.ConvertSystemError(err)
}
b.log.Infof("Migrating directory backend to new flat keyspace. Backup in %v.", backupDir)
// Go over every file in the backend. If the key is not expired upsert
// into the new backend.
err = filepath.Walk(backupDir, func(path string, info os.FileInfo, err error) error {
if err != nil {
return trace.ConvertSystemError(err)
}
// Skip the locks directory completely.
if info.IsDir() && info.Name() == ".locks" {
return filepath.SkipDir
}
// Skip over directories themselves, but not their content.
if info.IsDir() {
return nil
}
// Skip over TTL files (they are handled by readTTL function).
if strings.HasPrefix(info.Name(), ".") {
return nil
}
// Construct key and flat bucket.
key := info.Name()
flatbucket := strings.TrimPrefix(path, backupDir)
flatbucket = strings.TrimSuffix(flatbucket, string(filepath.Separator)+key)
// Read in TTL for key. If the key is expired, skip over it.
ttl, expired, err := readTTL(backupDir, flatbucket, key)
if err != nil {
return trace.Wrap(err)
}
if expired {
b.log.Infof("Skipping migration of expired bucket %q and key %q.", flatbucket, info.Name())
return nil
}
// Read in the value of the key.
value, err := ioutil.ReadFile(path)
if err != nil {
return trace.Wrap(err)
}
// Upsert key and value (with TTL) into new flat keyspace backend.
bucket := strings.Split(flatbucket, string(filepath.Separator))
err = b.UpsertVal(bucket, key, value, ttl)
if err != nil {
return trace.Wrap(err)
}
b.log.Infof("Migrated bucket %q and key %q with TTL %v.", flatbucket, key, ttl)
return nil
})
if err != nil {
return trace.Wrap(err)
}
b.log.Infof("Migration successful.")
return nil
}
// DELETE IN: 2.8.0
// readTTL reads in TTL for the given key. If no TTL key is found,
// backend.Forever is returned.
func readTTL(rootDir string, bucket string, key string) (time.Duration, bool, error) {
filename := filepath.Join(rootDir, bucket, "."+key+".ttl")
bytes, err := ioutil.ReadFile(filename)
if err != nil {
if os.IsNotExist(err) {
return backend.Forever, false, nil
}
return backend.Forever, false, trace.Wrap(err)
}
if len(bytes) == 0 {
return backend.Forever, false, nil
}
var expiryTime time.Time
if err = expiryTime.UnmarshalText(bytes); err != nil {
return backend.Forever, false, trace.Wrap(err)
}
ttl := expiryTime.Sub(time.Now())
if ttl < 0 {
return backend.Forever, true, nil
}
return ttl, false, nil
}
// DELETE IN: 2.8.0
// isOld checks if the directory backend is in the old format or not.
func isOld(rootDir string) (bool, error) {
d, err := os.Open(rootDir)
if err != nil {
return false, trace.ConvertSystemError(err)
}
defer d.Close()
files, err := d.Readdir(0)
if err != nil {
return false, trace.ConvertSystemError(err)
}
for _, fi := range files {
if fi.IsDir() {
return true, nil
}
}
return false, nil
}