Beispiel #1
0
// Create creates a disk without a parent (and doesn't attach it).
func (m *Manager) Create(op trace.Operation, newDiskURI string,
	capacityKB int64) (*VirtualDisk, error) {

	defer trace.End(trace.Begin(newDiskURI))

	vdm := object.NewVirtualDiskManager(m.vm.Vim25())

	d, err := NewVirtualDisk(newDiskURI)
	if err != nil {
		return nil, errors.Trace(err)
	}

	spec := &types.FileBackedVirtualDiskSpec{
		VirtualDiskSpec: types.VirtualDiskSpec{
			DiskType:    string(types.VirtualDiskTypeThin),
			AdapterType: string(types.VirtualDiskAdapterTypeLsiLogic),
		},

		CapacityKb: capacityKB,
	}

	op.Infof("Creating vmdk for layer or volume %s", d.DatastoreURI)
	err = tasks.Wait(op, func(ctx context.Context) (tasks.Task, error) {
		return vdm.CreateVirtualDisk(ctx, d.DatastoreURI, nil, spec)
	})

	if err != nil {
		return nil, errors.Trace(err)
	}

	return d, nil
}
Beispiel #2
0
func (m *Manager) Attach(op trace.Operation, disk *types.VirtualDisk) error {
	deviceList := object.VirtualDeviceList{}
	deviceList = append(deviceList, disk)

	changeSpec, err := deviceList.ConfigSpec(types.VirtualDeviceConfigSpecOperationAdd)
	if err != nil {
		return err
	}

	machineSpec := types.VirtualMachineConfigSpec{}
	machineSpec.DeviceChange = append(machineSpec.DeviceChange, changeSpec...)

	m.reconfig.Lock()
	_, err = m.vm.WaitForResult(op, func(ctx context.Context) (tasks.Task, error) {
		t, er := m.vm.Reconfigure(ctx, machineSpec)

		op.Debugf("Attach reconfigure task=%s", t.Reference())

		return t, er
	})
	m.reconfig.Unlock()

	if err != nil {
		op.Errorf("vmdk storage driver failed to attach disk: %s", errors.ErrorStack(err))
		return errors.Trace(err)
	}
	return nil
}
Beispiel #3
0
func (v *ImageStore) deleteImage(op trace.Operation, storeName, ID string) error {
	// Delete in order of manifest (the done file), the vmdk (because VC honors
	// the deletable flag in the vmdk file), then the directory to get
	// everything else.
	paths := []string{
		v.manifestPath(storeName, ID),
		v.imageDiskPath(storeName, ID),
		v.imageDirPath(storeName, ID),
	}

	for _, pth := range paths {
		err := v.ds.Rm(op, pth)

		// not exist is ok
		if err == nil || types.IsFileNotFound(err) {
			continue
		}

		// something isn't right.  bale.
		op.Errorf("ImageStore: delete image error: %s", err.Error())
		return err
	}

	return nil
}
Beispiel #4
0
// DeleteImage deletes an image from the image store.  If the image is in
// use either by way of inheritance or because it's attached to a
// container, this will return an error.
func (v *ImageStore) DeleteImage(op trace.Operation, image *portlayer.Image) error {
	//  check if the image is in use.
	if err := imagesInUse(op, image.ID); err != nil {
		op.Errorf("ImageStore: delete image error: %s", err.Error())
		return err
	}

	storeName, err := util.ImageStoreName(image.Store)
	if err != nil {
		return err
	}

	return v.deleteImage(op, storeName, image.ID)
}
Beispiel #5
0
func (v *ImageStore) scratch(op trace.Operation, storeName string) error {

	// Create the image directory in the store.
	imageDir := v.imageDirPath(storeName, portlayer.Scratch.ID)
	if _, err := v.ds.Mkdir(op, false, imageDir); err != nil {
		return err
	}

	// Write the metadata to the datastore
	metaDataDir := v.imageMetadataDirPath(storeName, portlayer.Scratch.ID)
	if err := writeMetadata(op, v.ds, metaDataDir, nil); err != nil {
		return err
	}

	imageDiskDsURI := v.imageDiskDSPath(storeName, portlayer.Scratch.ID)
	op.Infof("Creating image %s (%s)", portlayer.Scratch.ID, imageDiskDsURI)

	var size int64
	size = defaultDiskSize
	if portlayer.Config.ScratchSize != 0 {
		size = portlayer.Config.ScratchSize
	}

	// Create the disk
	vmdisk, err := v.dm.CreateAndAttach(op, imageDiskDsURI, "", size, os.O_RDWR)
	if err != nil {
		return err
	}
	defer func() {
		if vmdisk.Attached() {
			v.dm.Detach(op, vmdisk)
		}
	}()
	op.Debugf("Scratch disk created with size %d", portlayer.Config.ScratchSize)

	// Make the filesystem and set its label to defaultDiskLabel
	if err = vmdisk.Mkfs(defaultDiskLabel); err != nil {
		return err
	}

	if err = v.dm.Detach(op, vmdisk); err != nil {
		return err
	}

	if err = v.writeManifest(op, storeName, portlayer.Scratch.ID, nil); err != nil {
		return err
	}

	return nil
}
Beispiel #6
0
func (m *Manager) Detach(op trace.Operation, d *VirtualDisk) error {
	defer trace.End(trace.Begin(d.DevicePath))
	op.Infof("Detaching disk %s", d.DevicePath)

	d.lock()
	defer d.unlock()

	if !d.Attached() {
		op.Infof("Disk %s is already detached", d.DevicePath)
		return nil
	}

	if err := d.canBeDetached(); err != nil {
		return errors.Trace(err)
	}

	spec := types.VirtualMachineConfigSpec{}

	disk, err := findDisk(op, m.vm, d.DatastoreURI)
	if err != nil {
		return errors.Trace(err)
	}

	config := []types.BaseVirtualDeviceConfigSpec{
		&types.VirtualDeviceConfigSpec{
			Device:    disk,
			Operation: types.VirtualDeviceConfigSpecOperationRemove,
		},
	}

	spec.DeviceChange = config

	m.reconfig.Lock()
	_, err = m.vm.WaitForResult(op, func(ctx context.Context) (tasks.Task, error) {
		t, er := m.vm.Reconfigure(ctx, spec)

		op.Debugf("Detach reconfigure task=%s", t.Reference())

		return t, er
	})
	m.reconfig.Unlock()

	if err != nil {
		op.Errorf(err.Error())
		log.Warnf("detach for %s failed with %s", d.DevicePath, errors.ErrorStack(err))
		return errors.Trace(err)
	}

	func() {
		select {
		case <-m.maxAttached:
		default:
		}
	}()

	return d.setDetached()
}
Beispiel #7
0
func (v *ImageStore) GetImage(op trace.Operation, store *url.URL, ID string) (*portlayer.Image, error) {

	defer trace.End(trace.Begin(store.String()))
	storeName, err := util.ImageStoreName(store)
	if err != nil {
		return nil, err
	}

	imageURL, err := util.ImageURL(storeName, ID)
	if err != nil {
		return nil, err
	}

	if err = v.verifyImage(op, storeName, ID); err != nil {
		return nil, err
	}

	// get the metadata
	metaDataDir := v.imageMetadataDirPath(storeName, ID)
	meta, err := getMetadata(op, v.ds, metaDataDir)
	if err != nil {
		return nil, err
	}

	var s = *store
	var parentURL *url.URL

	parentID := v.parents.Get(ID)
	if parentID != "" {
		parentURL, _ = util.ImageURL(storeName, parentID)
	}

	newImage := &portlayer.Image{
		ID:       ID,
		SelfLink: imageURL,
		// We're relying on the parent map for this since we don't currently have a
		// way to get the disk's spec.  See VIC #482 for details.  Parent:
		// parent.SelfLink,
		Store:      &s,
		ParentLink: parentURL,
		Metadata:   meta,
	}

	op.Debugf("Returning image from location %s with parent url %s", newImage.SelfLink, newImage.Parent())
	return newImage, nil
}
Beispiel #8
0
// GetImage gets the specified image from the given store by retreiving it from the cache.
func (c *NameLookupCache) GetImage(op trace.Operation, store *url.URL, ID string) (*Image, error) {

	op.Debugf("Getting image %s from %s", ID, store.String())

	storeName, err := util.ImageStoreName(store)
	if err != nil {
		return nil, err
	}

	// Check the store exists
	if _, err = c.GetImageStore(op, storeName); err != nil {
		return nil, err
	}

	c.storeCacheLock.Lock()
	indx := c.storeCache[*store]
	c.storeCacheLock.Unlock()

	imgURL, err := util.ImageURL(storeName, ID)
	if err != nil {
		return nil, err
	}
	node, err := c.storeCache[*store].Get(imgURL.String())

	var img *Image
	if err != nil {
		if err == index.ErrNodeNotFound {
			debugf("Image %s not in cache, retreiving from datastore", ID)
			// Not in the cache.  Try to load it.
			img, err = c.DataStore.GetImage(op, store, ID)
			if err != nil {
				return nil, err
			}

			if err = indx.Insert(img); err != nil {
				return nil, err
			}
		} else {
			return nil, err
		}
	} else {
		img, _ = node.(*Image)
	}

	return img, nil
}
Beispiel #9
0
func waitForPath(op trace.Operation, path string) error {
	defer trace.End(trace.Begin(path))
	timeout := time.Duration(pathTimeout)

	op, _ = trace.WithTimeout(&op, timeout, path)
	done := make(chan struct{})

	go func() {
		t := time.NewTicker(200 * time.Microsecond)
		defer t.Stop()
		for range t.C {
			if _, err := os.Stat(path); err == nil {
				close(done)
				break
			}

			// We've timed out.
			if op.Err() != nil {
				break
			}
		}
	}()

	log.Debugf("Waiting for attached disk to appear in /dev/disk/by-path, or timeout")
	select {
	case <-done:
		log.Infof("Attached disk present at %s", path)
	case <-op.Done():
		if op.Err() != nil {
			return errors.Errorf("timeout waiting for layer to present as %s", path)
		}
	}

	return nil
}
Beispiel #10
0
// Find any image directories without the manifest file and remove them.
func (v *ImageStore) cleanup(op trace.Operation, store *url.URL) error {
	op.Infof("Checking for inconsistent images on %s", store.String())

	storeName, err := util.ImageStoreName(store)
	if err != nil {
		return err
	}

	res, err := v.ds.Ls(op, v.imageStorePath(storeName))
	if err != nil {
		return err
	}

	// We could call v.ListImages here but that results in calling GetImage,
	// which pulls and unmarshalls the metadata.  We don't need that.
	for _, f := range res.File {
		file, ok := f.(*types.FileInfo)
		if !ok {
			continue
		}

		ID := file.Path

		if ID == portlayer.Scratch.ID {
			continue
		}

		if err := v.verifyImage(op, storeName, ID); err != nil {

			if err = v.deleteImage(op, storeName, ID); err != nil {
				// deleteImage logs the error in the event there is one.
				return err
			}
		}
	}

	return nil
}
Beispiel #11
0
// CreateAndAttach creates a new vmdk child from parent of the given size.
// Returns a VirtualDisk corresponding to the created and attached disk.  The
// newDiskURI and parentURI are both Datastore URI paths in the form of
// [datastoreN] /path/to/disk.vmdk.
func (m *Manager) CreateAndAttach(op trace.Operation, newDiskURI,
	parentURI string,
	capacity int64, flags int) (*VirtualDisk, error) {
	defer trace.End(trace.Begin(newDiskURI))

	// ensure we abide by max attached disks limits
	m.maxAttached <- true

	d, err := NewVirtualDisk(newDiskURI)
	if err != nil {
		return nil, errors.Trace(err)
	}

	spec := m.createDiskSpec(newDiskURI, parentURI, capacity, flags)

	op.Infof("Create/attach vmdk %s from parent %s", newDiskURI, parentURI)

	err = m.Attach(op, spec)
	if err != nil {
		return nil, errors.Trace(err)
	}

	op.Debugf("Mapping vmdk to pci device %s", newDiskURI)
	devicePath, err := m.devicePathByURI(op, newDiskURI)
	if err != nil {
		return nil, errors.Trace(err)
	}

	d.setAttached(devicePath)

	if err := waitForPath(op, devicePath); err != nil {
		op.Infof("waitForPath failed for %s with %s", newDiskURI, errors.ErrorStack(err))
		// ensure that the disk is detached if it's the publish that's failed

		if detachErr := m.Detach(op, d); detachErr != nil {
			op.Debugf("detach(%s) failed with %s", newDiskURI, errors.ErrorStack(detachErr))
		}

		return nil, errors.Trace(err)
	}

	return d, nil
}
Beispiel #12
0
// Create the image directory, create a temp vmdk in this directory,
// attach/mount the disk, unpack the tar, check the checksum.  If the data
// doesn't match the expected checksum, abort by nuking the image directory.
// If everything matches, move the tmp vmdk to ID.vmdk.  The unwind path is a
// bit convoluted here;  we need to clean up on the way out in the error case
func (v *ImageStore) writeImage(op trace.Operation, storeName, parentID, ID string, meta map[string][]byte,
	sum string, r io.Reader) error {

	// Create a temp image directory in the store.
	imageDir := v.imageDirPath(storeName, ID)
	_, err := v.ds.Mkdir(op, true, imageDir)
	if err != nil {
		return err
	}

	// Write the metadata to the datastore
	metaDataDir := v.imageMetadataDirPath(storeName, ID)
	err = writeMetadata(op, v.ds, metaDataDir, meta)
	if err != nil {
		return err
	}

	// datastore path to the parent
	parentDiskDsURI := v.imageDiskDSPath(storeName, parentID)

	// datastore path to the disk we're creating
	diskDsURI := v.imageDiskDSPath(storeName, ID)
	op.Infof("Creating image %s (%s)", ID, diskDsURI)

	var vmdisk *disk.VirtualDisk

	// On error, unmount if mounted, detach if attached, and nuke the image directory
	defer func() {
		if err != nil {
			op.Errorf("Cleaning up failed WriteImage directory %s", imageDir)

			if vmdisk != nil {
				if vmdisk.Mounted() {
					op.Debugf("Unmounting abandoned disk")
					vmdisk.Unmount()
				}

				if vmdisk.Attached() {
					op.Debugf("Detaching abandoned disk")
					v.dm.Detach(op, vmdisk)
				}
			}

			v.ds.Rm(op, imageDir)
		}
	}()

	// Create the disk
	vmdisk, err = v.dm.CreateAndAttach(op, diskDsURI, parentDiskDsURI, 0, os.O_RDWR)
	if err != nil {
		return err
	}
	// tmp dir to mount the disk
	dir, err := ioutil.TempDir("", "mnt-"+ID)
	if err != nil {
		return err
	}
	defer os.RemoveAll(dir)

	if err := vmdisk.Mount(dir, nil); err != nil {
		return err
	}

	h := sha256.New()
	t := io.TeeReader(r, h)

	// Untar the archive
	var n int64
	if n, err = archive.ApplyLayer(dir, t); err != nil {
		return err
	}

	op.Debugf("%s wrote %d bytes", ID, n)

	actualSum := fmt.Sprintf("sha256:%x", h.Sum(nil))
	if actualSum != sum {
		err = fmt.Errorf("Failed to validate image checksum. Expected %s, got %s", sum, actualSum)
		return err
	}

	if err = vmdisk.Unmount(); err != nil {
		return err
	}

	if err = v.dm.Detach(op, vmdisk); err != nil {
		return err
	}

	// Write our own bookkeeping manifest file to the image's directory.  We
	// treat the manifest file like a done file.  Its existence means this vmdk
	// is consistent.  Previously we were writing the vmdk to a tmp vmdk file
	// then moving it (using the MoveDatastoreFile or MoveVirtualDisk calls).
	// However(!!) this flattens the vmdk.  Also mkdir foo && ls -l foo fails
	// on VSAN (see
	// https://github.com/vmware/vic/pull/1764#issuecomment-237093424 for
	// detail).  We basically can't trust any of the datastore calls to help us
	// with atomic operations.  Touching an empty file seems to work well
	// enough.
	if err = v.writeManifest(op, storeName, ID, nil); err != nil {
		return err
	}

	return nil
}