forked from rkt/rkt
/
kvm.go
257 lines (215 loc) · 7.71 KB
/
kvm.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
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
// Copyright 2014 The rkt 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.
//+build linux
package main
import (
"errors"
"fmt"
"os"
"path/filepath"
"strings"
"syscall"
"github.com/appc/spec/schema"
"github.com/coreos/go-systemd/util"
"github.com/coreos/rkt/common"
"github.com/coreos/rkt/networking"
"github.com/coreos/rkt/pkg/mountinfo"
stage1commontypes "github.com/coreos/rkt/stage1/common/types"
stage1initcommon "github.com/coreos/rkt/stage1/init/common"
"github.com/coreos/rkt/stage1/init/kvm"
"github.com/hashicorp/errwrap"
)
const journalDir = "/var/log/journal"
// Supported hypervisors
var hypervisors = [...]string{"lkvm", "qemu"}
// KvmNetworkingToSystemd generates systemd unit files for a pod according to network configuration
func KvmNetworkingToSystemd(p *stage1commontypes.Pod, n *networking.Networking) error {
podRoot := common.Stage1RootfsPath(p.Root)
// networking
netDescriptions := kvm.GetNetworkDescriptions(n)
if err := kvm.GenerateNetworkInterfaceUnits(filepath.Join(podRoot, stage1initcommon.UnitsDir), netDescriptions); err != nil {
return errwrap.Wrap(errors.New("failed to transform networking to units"), err)
}
return nil
}
func mountSharedVolumes(p *stage1commontypes.Pod, ra *schema.RuntimeApp) error {
appName := ra.Name
sharedVolPath, err := common.CreateSharedVolumesPath(p.Root)
if err != nil {
return err
}
imageManifest := p.Images[appName.String()]
mounts, err := stage1initcommon.GenerateMounts(ra, p.Manifest.Volumes, stage1initcommon.ConvertedFromDocker(imageManifest))
if err != nil {
return err
}
for _, m := range mounts {
absRoot, err := filepath.Abs(p.Root) // Absolute path to the pod's rootfs.
if err != nil {
return errwrap.Wrap(errors.New("could not get pod's root absolute path"), err)
}
absAppRootfs := common.AppRootfsPath(absRoot, appName)
if err != nil {
return fmt.Errorf(`could not evaluate absolute path for application rootfs in app: %v`, appName)
}
mntPath, err := stage1initcommon.EvaluateSymlinksInsideApp(absAppRootfs, m.Mount.Path)
if err != nil {
return errwrap.Wrap(fmt.Errorf("could not evaluate path %v", m.Mount.Path), err)
}
absDestination := filepath.Join(absAppRootfs, mntPath)
shPath := filepath.Join(sharedVolPath, m.Volume.Name.String())
if err := stage1initcommon.PrepareMountpoints(shPath, absDestination, &m.Volume, m.DockerImplicit); err != nil {
return err
}
source := m.Source(p.Root)
if cleanedSource, err := filepath.EvalSymlinks(source); err != nil {
return errwrap.Wrap(fmt.Errorf("could not resolve symlink for source: %v", source), err)
} else if err := ensureDestinationExists(cleanedSource, absDestination); err != nil {
return errwrap.Wrap(fmt.Errorf("could not create destination mount point: %v", absDestination), err)
} else if err := doBindMount(cleanedSource, absDestination, m.ReadOnly, m.Volume.Recursive); err != nil {
return errwrap.Wrap(fmt.Errorf("could not bind mount path %v (s: %v, d: %v)", m.Mount.Path, source, absDestination), err)
}
}
return nil
}
func doBindMount(source, destination string, readOnly bool, recursive *bool) error {
var flags uintptr = syscall.MS_BIND
// Enable recursive by default and remove it if explicitly requested
recursiveBool := recursive == nil || *recursive == true
if recursiveBool {
flags |= syscall.MS_REC
}
if err := syscall.Mount(source, destination, "bind", flags, ""); err != nil {
return errwrap.Wrap(fmt.Errorf("error mounting %s", destination), err)
}
// Linux can't bind-mount with readonly in a single operation, so remount +ro
if readOnly {
if err := syscall.Mount("", destination, "none", syscall.MS_REMOUNT|syscall.MS_RDONLY|syscall.MS_BIND, ""); err != nil {
return errwrap.Wrap(fmt.Errorf("error remounting read-only %s", destination), err)
}
}
if readOnly && recursiveBool {
// Sub-mounts are still read-write, so find them and remount them read-only
mnts, err := mountinfo.ParseMounts(0)
if err != nil {
return errwrap.Wrap(fmt.Errorf("error getting mounts under %q from mountinfo", source), err)
}
mnts = mnts.Filter(mountinfo.HasPrefix(source + "/"))
for _, mnt := range mnts {
innerAbsPath := destination + strings.Replace(mnt.MountPoint, source, "", -1)
if err := syscall.Mount("", innerAbsPath, "none", syscall.MS_REMOUNT|syscall.MS_RDONLY|syscall.MS_BIND, ""); err != nil {
return errwrap.Wrap(fmt.Errorf("error remounting child mount %s read-only", innerAbsPath), err)
}
}
}
return nil
}
func ensureDestinationExists(source, destination string) error {
fileInfo, err := os.Stat(source)
if err != nil {
return errwrap.Wrap(fmt.Errorf("could not stat source location: %v", source), err)
}
targetPathParent, _ := filepath.Split(destination)
if err := os.MkdirAll(targetPathParent, common.SharedVolumePerm); err != nil {
return errwrap.Wrap(fmt.Errorf("could not create parent directory: %v", targetPathParent), err)
}
if fileInfo.IsDir() {
if err := os.Mkdir(destination, common.SharedVolumePerm); !os.IsExist(err) {
return err
}
} else {
if file, err := os.OpenFile(destination, os.O_CREATE, common.SharedVolumePerm); err != nil {
return err
} else {
file.Close()
}
}
return nil
}
func prepareMountsForApp(p *stage1commontypes.Pod, ra *schema.RuntimeApp) error {
// bind mount all shared volumes (we don't use mechanism for bind-mounting given by nspawn)
if err := mountSharedVolumes(p, ra); err != nil {
return errwrap.Wrap(errors.New("failed to prepare mount point"), err)
}
return nil
}
func KvmPrepareMounts(p *stage1commontypes.Pod) error {
for i := range p.Manifest.Apps {
ra := &p.Manifest.Apps[i]
if err := prepareMountsForApp(p, ra); err != nil {
return errwrap.Wrap(fmt.Errorf("failed prepare mounts for app %q", ra.Name), err)
}
}
return nil
}
func KvmCheckHypervisor(s1Root string) (string, error) {
for _, hv := range hypervisors {
if _, err := os.Stat(filepath.Join(s1Root, hv)); err == nil {
return hv, nil
}
}
return "", fmt.Errorf("unrecognized hypervisor")
}
func linkJournal(s1Root, machineID string) error {
if !util.IsRunningSystemd() {
return nil
}
absS1Root, err := filepath.Abs(s1Root)
if err != nil {
return err
}
// /var/log/journal doesn't exist on the host, don't do anything
if _, err := os.Stat(journalDir); os.IsNotExist(err) {
return nil
}
machineJournalDir := filepath.Join(journalDir, machineID)
podJournalDir := filepath.Join(absS1Root, machineJournalDir)
hostMachineID, err := util.GetMachineID()
if err != nil {
return err
}
// unlikely, machine ID is random (== pod UUID)
if hostMachineID == machineID {
return fmt.Errorf("host and pod machine IDs are equal (%s)", machineID)
}
fi, err := os.Lstat(machineJournalDir)
switch {
case os.IsNotExist(err):
// good, we'll create the symlink
case err != nil:
return err
// unlikely, machine ID is random (== pod UUID)
default:
if fi.IsDir() {
if err := os.Remove(machineJournalDir); err != nil {
return err
}
}
link, err := os.Readlink(machineJournalDir)
if err != nil {
return err
}
if link == podJournalDir {
return nil
} else {
if err := os.Remove(machineJournalDir); err != nil {
return err
}
}
}
if err := os.Symlink(podJournalDir, machineJournalDir); err != nil {
return err
}
return nil
}