github.com/mirantis/virtlet@v1.5.2-0.20191204181327-1659b8a48e9b/pkg/libvirttools/persistentroot_volumesource.go (about) 1 /* 2 Copyright 2018 Mirantis 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 package libvirttools 18 19 import ( 20 "crypto/sha256" 21 "encoding/hex" 22 "fmt" 23 24 "github.com/golang/glog" 25 libvirtxml "github.com/libvirt/libvirt-go-xml" 26 digest "github.com/opencontainers/go-digest" 27 "golang.org/x/sys/unix" 28 29 "github.com/Mirantis/virtlet/pkg/blockdev" 30 "github.com/Mirantis/virtlet/pkg/metadata/types" 31 ) 32 33 // persistentRootVolume represents a root volume that can survive the 34 // deletion of its pod 35 type persistentRootVolume struct { 36 volumeBase 37 dev types.VMVolumeDevice 38 } 39 40 var _ VMVolume = &persistentRootVolume{} 41 42 func (v *persistentRootVolume) devHandler() *blockdev.LogicalDeviceHandler { 43 return blockdev.NewLogicalDeviceHandler(v.owner.Commander(), "", "") 44 } 45 46 func (v *persistentRootVolume) IsDisk() bool { return true } 47 48 func (v *persistentRootVolume) UUID() string { 49 return v.dev.UUID() 50 } 51 52 func (v *persistentRootVolume) dmName() string { 53 return "virtlet-dm-" + v.config.DomainUUID 54 } 55 56 func (v *persistentRootVolume) dmPath() string { 57 return "/dev/mapper/" + v.dmName() 58 } 59 60 func (v *persistentRootVolume) copyImageToDev(imagePath string) error { 61 syncFiles(imagePath, v.dev.HostPath, v.dmPath()) 62 if _, err := v.owner.Commander().Command("qemu-img", "convert", "-O", "raw", imagePath, v.dmPath()).Run(nil); err != nil { 63 return err 64 } 65 syncFiles(v.dmPath()) 66 return nil 67 } 68 69 func (v *persistentRootVolume) Setup() (*libvirtxml.DomainDisk, *libvirtxml.DomainFilesystem, error) { 70 glog.V(4).Infof("Persistent rootfs setup on %q", v.dev.HostPath) 71 imagePath, imageDigest, imageSize, err := v.owner.ImageManager().GetImagePathDigestAndVirtualSize(v.config.Image) 72 if err != nil { 73 glog.V(4).Infof("Persistent rootfs setup on %q: image info error: %v", v.dev.HostPath, err) 74 return nil, nil, err 75 } 76 77 if imageDigest.Algorithm() != digest.SHA256 { 78 glog.V(4).Infof("Persistent rootfs setup on %q: image info error: %v", v.dev.HostPath, err) 79 return nil, nil, fmt.Errorf("unsupported digest algorithm %q", imageDigest.Algorithm()) 80 } 81 imageHash, err := hex.DecodeString(imageDigest.Hex()) 82 if err != nil { 83 glog.V(4).Infof("Persistent rootfs setup on %q: bad digest hex: %q", v.dev.HostPath, imageDigest.Hex()) 84 return nil, nil, fmt.Errorf("bad digest hex: %q", imageDigest.Hex()) 85 } 86 if len(imageHash) != sha256.Size { 87 glog.V(4).Infof("Persistent rootfs setup on %q: bad digest size: %q", v.dev.HostPath, imageDigest.Hex()) 88 return nil, nil, fmt.Errorf("bad digest size: %q", imageDigest.Hex()) 89 } 90 91 var hash [sha256.Size]byte 92 copy(hash[:], imageHash) 93 ldh := v.devHandler() 94 headerMatches, err := ldh.EnsureDevHeaderMatches(v.dev.HostPath, hash) 95 96 if err == nil { 97 glog.V(4).Infof("Persistent rootfs setup on %q: headerMatches: %v", v.dev.HostPath, headerMatches) 98 err = ldh.Map(v.dev.HostPath, v.dmName(), imageSize) 99 } 100 101 if err == nil { 102 if headerMatches { 103 glog.V(4).Infof("Persistent rootfs setup on %q: header matches image %q, not overwriting", v.dev.HostPath, imagePath) 104 } else { 105 glog.V(4).Infof("Persistent rootfs setup on %q: writing image from %q", v.dev.HostPath, imagePath) 106 err = v.copyImageToDev(imagePath) 107 } 108 } 109 110 if err != nil { 111 glog.V(4).Infof("Persistent rootfs setup on %q: error: %v", v.dev.HostPath, err) 112 return nil, nil, err 113 } 114 115 if len(v.config.ParsedAnnotations.InjectedFiles) > 0 { 116 if err := v.owner.StorageConnection().PutFiles(v.dmPath(), v.config.ParsedAnnotations.InjectedFiles); err != nil { 117 return nil, nil, fmt.Errorf("error adding files to rootfs: %v", err) 118 } 119 } 120 121 return &libvirtxml.DomainDisk{ 122 Device: "disk", 123 Source: &libvirtxml.DomainDiskSource{Block: &libvirtxml.DomainDiskSourceBlock{Dev: v.dmPath()}}, 124 Driver: &libvirtxml.DomainDiskDriver{Name: "qemu", Type: "raw"}, 125 }, nil, nil 126 } 127 128 func (v *persistentRootVolume) Teardown() error { 129 return v.devHandler().Unmap(v.dmName()) 130 } 131 132 func syncFiles(paths ...string) error { 133 // https://www.redhat.com/archives/libguestfs/2012-July/msg00009.html 134 unix.Sync() 135 for _, p := range paths { 136 fd, err := unix.Open(p, unix.O_RDWR|unix.O_SYNC, 0) 137 if err != nil { 138 return err 139 } 140 if err := unix.Fsync(fd); err != nil { 141 unix.Close(fd) 142 return err 143 } 144 if err := unix.Close(fd); err != nil { 145 return err 146 } 147 } 148 return nil 149 }