forked from cilium/cilium
-
Notifications
You must be signed in to change notification settings - Fork 0
/
directory.go
157 lines (132 loc) · 5.37 KB
/
directory.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
// Copyright 2018 Authors of Cilium
//
// 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 endpoint
import (
"fmt"
"os"
"path/filepath"
"github.com/cilium/cilium/common"
"github.com/cilium/cilium/pkg/logging/logfields"
"github.com/cilium/cilium/pkg/option"
"github.com/sirupsen/logrus"
)
const (
nextDirectorySuffix = "_next"
nextFailedDirectorySuffix = "_next_fail"
backupDirectorySuffix = "_stale"
)
// DirectoryPath returns the directory name for this endpoint bpf program.
func (e *Endpoint) DirectoryPath() string {
return filepath.Join(".", fmt.Sprintf("%d", e.ID))
}
// FailedDirectoryPath returns the directory name for this endpoint bpf program
// failed builds.
func (e *Endpoint) FailedDirectoryPath() string {
return filepath.Join(".", fmt.Sprintf("%d%s", e.ID, nextFailedDirectorySuffix))
}
// StateDirectoryPath returns the directory name for this endpoint bpf program.
func (e *Endpoint) StateDirectoryPath() string {
return filepath.Join(option.Config.StateDir, e.StringID())
}
// NextDirectoryPath returns the directory name for this endpoint bpf program
// next bpf builds.
func (e *Endpoint) NextDirectoryPath() string {
return filepath.Join(".", fmt.Sprintf("%d%s", e.ID, nextDirectorySuffix))
}
func (e *Endpoint) backupDirectoryPath() string {
return e.DirectoryPath() + backupDirectorySuffix
}
// synchronizeDirectories moves the files related to endpoint BPF program
// compilation to their according directories if compilation of BPF was
// necessary for the endpoint.
// Returns the original regenerationError if regenerationError was non-nil,
// or if any updates to directories for the endpoint's directories fails.
// Must be called with endpoint.Mutex held.
func (e *Endpoint) synchronizeDirectories(origDir string, compilationExecuted bool) error {
scopedLog := e.getLogger()
scopedLog.Debug("synchronizing directories")
tmpDir := e.NextDirectoryPath()
// Check if an existing endpoint directory exists, e.g.
// /var/run/cilium/state/1111
_, err := os.Stat(origDir)
switch {
// An endpoint directory already exists. We need to back it up before attempting
// to move the new directory in its place so we can attempt recovery.
case !os.IsNotExist(err):
scopedLog.Debug("endpoint directory exists; backing it up")
backupDir := e.backupDirectoryPath()
// Remove any eventual old backup directory. This may fail if
// the directory does not exist. The error is deliberately
// ignored.
e.removeDirectory(backupDir)
// Move the current endpoint directory to a backup location
scopedLog.WithFields(logrus.Fields{
"originalDirectory": origDir,
"backupDirectory": backupDir,
}).Debug("moving current directory to backup location")
if err := os.Rename(origDir, backupDir); err != nil {
return fmt.Errorf("unable to rename current endpoint directory: %s", err)
}
// Regarldess of whether the atomic replace succeeds or not,
// ensure that the backup directory is removed when the
// function returns.
defer e.removeDirectory(backupDir)
// Make temporary directory the new endpoint directory
if err := os.Rename(tmpDir, origDir); err != nil {
if err2 := os.Rename(backupDir, origDir); err2 != nil {
scopedLog.WithFields(logrus.Fields{
logfields.Path: backupDir,
}).Warn("restoring directory for endpoint failed, endpoint " +
"is in inconsistent state. Keeping stale directory.")
return err2
}
return fmt.Errorf("restored original endpoint directory, atomic directory move failed: %s", err)
}
// If the compilation was skipped then we need to copy the old
// bpf objects into the new directory
if !compilationExecuted {
scopedLog.Debug("compilation was skipped; moving old BPF objects into new directory")
err := common.MoveNewFilesTo(backupDir, origDir)
if err != nil {
log.WithError(err).Debugf("unable to copy old bpf object "+
"files from %s into the new directory %s.", backupDir, origDir)
}
}
// No existing endpoint directory, synchronizing the directory is a
// simple move
default:
// Make temporary directory the new endpoint directory
scopedLog.WithFields(logrus.Fields{
"temporaryDirectory": tmpDir,
"originalDirectory": origDir,
}).Debug("attempting to make temporary directory new directory for endpoint programs")
if err := os.Rename(tmpDir, origDir); err != nil {
return fmt.Errorf("atomic endpoint directory move failed: %s", err)
}
}
// The build succeeded and is in place, any eventual existing failure
// directory can be removed.
e.removeDirectory(e.FailedDirectoryPath())
return nil
}
func (e *Endpoint) removeDirectory(path string) error {
e.getLogger().WithField("directory", path).Debug("removing directory")
return os.RemoveAll(path)
}
func (e *Endpoint) removeDirectories() {
e.removeDirectory(e.DirectoryPath())
e.removeDirectory(e.FailedDirectoryPath())
e.removeDirectory(e.NextDirectoryPath())
e.removeDirectory(e.backupDirectoryPath())
}