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  }