forked from kubernetes/kubernetes
-
Notifications
You must be signed in to change notification settings - Fork 1
/
checkpoint_store.go
156 lines (137 loc) · 4.06 KB
/
checkpoint_store.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
/*
Copyright 2017 The Kubernetes 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 dockershim
import (
"fmt"
"io"
"io/ioutil"
"os"
"path/filepath"
"regexp"
"strings"
"k8s.io/kubernetes/pkg/kubelet/dockershim/errors"
)
const (
tmpPrefix = "."
tmpSuffix = ".tmp"
keyMaxLength = 250
)
var keyRegex = regexp.MustCompile("^[a-zA-Z0-9]+$")
// CheckpointStore provides the interface for checkpoint storage backend.
// CheckpointStore must be thread-safe
type CheckpointStore interface {
// key must contain one or more characters in [A-Za-z0-9]
// Write persists a checkpoint with key
Write(key string, data []byte) error
// Read retrieves a checkpoint with key
// Read must return CheckpointNotFoundError if checkpoint is not found
Read(key string) ([]byte, error)
// Delete deletes a checkpoint with key
// Delete must not return error if checkpoint does not exist
Delete(key string) error
// List lists all keys of existing checkpoints
List() ([]string, error)
}
// FileStore is an implementation of CheckpointStore interface which stores checkpoint in files.
type FileStore struct {
// path to the base directory for storing checkpoint files
path string
}
func NewFileStore(path string) (CheckpointStore, error) {
if err := ensurePath(path); err != nil {
return nil, err
}
return &FileStore{path: path}, nil
}
// writeFileAndSync is copied from ioutil.WriteFile, with the extra File.Sync
// at the end to ensure file is written on the disk.
func writeFileAndSync(filename string, data []byte, perm os.FileMode) error {
f, err := os.OpenFile(filename, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, perm)
if err != nil {
return err
}
n, err := f.Write(data)
if err == nil && n < len(data) {
err = io.ErrShortWrite
}
if err == nil {
// Only sync if the Write completed successfully.
err = f.Sync()
}
if err1 := f.Close(); err == nil {
err = err1
}
return err
}
func (fstore *FileStore) Write(key string, data []byte) error {
if err := validateKey(key); err != nil {
return err
}
if err := ensurePath(fstore.path); err != nil {
return err
}
tmpfile := filepath.Join(fstore.path, fmt.Sprintf("%s%s%s", tmpPrefix, key, tmpSuffix))
if err := writeFileAndSync(tmpfile, data, 0644); err != nil {
return err
}
return os.Rename(tmpfile, fstore.getCheckpointPath(key))
}
func (fstore *FileStore) Read(key string) ([]byte, error) {
if err := validateKey(key); err != nil {
return nil, err
}
bytes, err := ioutil.ReadFile(fstore.getCheckpointPath(key))
if os.IsNotExist(err) {
return bytes, errors.CheckpointNotFoundError
}
return bytes, err
}
func (fstore *FileStore) Delete(key string) error {
if err := validateKey(key); err != nil {
return err
}
if err := os.Remove(fstore.getCheckpointPath(key)); err != nil && !os.IsNotExist(err) {
return err
}
return nil
}
func (fstore *FileStore) List() ([]string, error) {
keys := make([]string, 0)
files, err := ioutil.ReadDir(fstore.path)
if err != nil {
return keys, err
}
for _, f := range files {
if !strings.HasPrefix(f.Name(), tmpPrefix) {
keys = append(keys, f.Name())
}
}
return keys, nil
}
func (fstore *FileStore) getCheckpointPath(key string) string {
return filepath.Join(fstore.path, key)
}
// ensurePath creates input directory if it does not exist
func ensurePath(path string) error {
if _, err := os.Stat(path); err != nil {
// MkdirAll returns nil if directory already exists
return os.MkdirAll(path, 0755)
}
return nil
}
func validateKey(key string) error {
if len(key) <= keyMaxLength && keyRegex.MatchString(key) {
return nil
}
return fmt.Errorf("checkpoint key %q is not valid.", key)
}