Example #1
0
func (d *DaemonConfig) handleCreate(w http.ResponseWriter, r *http.Request) {
	content, err := ioutil.ReadAll(r.Body)
	if err != nil {
		api.RESTHTTPError(w, errors.ReadBody.Combine(err))
		return
	}

	req := &config.VolumeRequest{}

	if err := json.Unmarshal(content, req); err != nil {
		api.RESTHTTPError(w, errors.UnmarshalRequest.Combine(err))
		return
	}

	if req.Policy == "" {
		api.RESTHTTPError(w, errors.GetPolicy.Combine(errored.Errorf("policy was blank")))
		return
	}

	if req.Name == "" {
		api.RESTHTTPError(w, errors.GetVolume.Combine(errored.Errorf("volume was blank")))
		return
	}

	hostname, err := os.Hostname()
	if err != nil {
		api.RESTHTTPError(w, errors.GetHostname.Combine(err))
		return
	}

	policy, err := d.Config.GetPolicy(req.Policy)
	if err != nil {
		api.RESTHTTPError(w, errors.GetPolicy.Combine(errored.New(req.Policy).Combine(err)))
		return
	}

	uc := &config.UseMount{
		Volume:   strings.Join([]string{req.Policy, req.Name}, "/"),
		Reason:   lock.ReasonCreate,
		Hostname: hostname,
	}

	snapUC := &config.UseSnapshot{
		Volume: strings.Join([]string{req.Policy, req.Name}, "/"),
		Reason: lock.ReasonCreate,
	}

	err = lock.NewDriver(d.Config).ExecuteWithMultiUseLock(
		[]config.UseLocker{uc, snapUC},
		d.Global.Timeout,
		d.createVolume(w, req, policy),
	)
	if err != nil && err != errors.Exists {
		api.RESTHTTPError(w, errors.CreateVolume.Combine(err))
		return
	}
}
Example #2
0
func (dc *DaemonConfig) pruneSnapshots(val *config.Volume) {
	logrus.Infof("starting snapshot prune for %q", val.VolumeName)
	if val.Backends.Snapshot == "" {
		logrus.Debugf("Snapshot driver for volume %v was empty, not snapshotting.", val)
		return
	}

	uc := &config.UseSnapshot{
		Volume: val.String(),
		Reason: lock.ReasonSnapshotPrune,
	}

	stopChan, err := lock.NewDriver(dc.Config).AcquireWithTTLRefresh(uc, dc.Global.TTL, dc.Global.Timeout)
	if err != nil {
		logrus.Error(errors.LockFailed.Combine(err))
		return
	}

	defer func() { stopChan <- struct{}{} }()

	driver, err := backend.NewSnapshotDriver(val.Backends.Snapshot)
	if err != nil {
		logrus.Errorf("failed to get driver: %v", err)
		return
	}

	driverOpts := storage.DriverOptions{
		Volume: storage.Volume{
			Name: val.String(),
			Params: storage.Params{
				"pool": val.DriverOptions["pool"],
			},
		},
		Timeout: dc.Global.Timeout,
	}

	list, err := driver.ListSnapshots(driverOpts)
	if err != nil {
		logrus.Errorf("Could not list snapshots for volume %q: %v", val.VolumeName, err)
		return
	}

	logrus.Debugf("Volume %q: keeping %d snapshots", val, val.RuntimeOptions.Snapshot.Keep)

	toDeleteCount := len(list) - int(val.RuntimeOptions.Snapshot.Keep)
	if toDeleteCount < 0 {
		return
	}

	for i := 0; i < toDeleteCount; i++ {
		logrus.Infof("Removing snapshot %q for volume %q", list[i], val.VolumeName)
		if err := driver.RemoveSnapshot(list[i], driverOpts); err != nil {
			logrus.Errorf("Removing snapshot %q for volume %q failed: %v", list[i], val.VolumeName, err)
		}
	}
}
Example #3
0
// NewAPI returns an *API
func NewAPI(volplugin Volplugin, hostname string, client *config.Client, global **config.Global) *API {
	return &API{
		Volplugin:       volplugin,
		Hostname:        hostname,
		Client:          client,
		Global:          global,
		Lock:            lock.NewDriver(client),
		MountCollection: mount.NewCollection(),
		MountCounter:    mount.NewCounter(),
		lockStopChans:   map[string]chan struct{}{},
	}
}
Example #4
0
// Daemon is the top-level entrypoint for the volsupervisor from the CLI.
func Daemon(ctx *cli.Context) {
	cfg, err := config.NewClient(ctx.String("prefix"), ctx.StringSlice("etcd"))
	if err != nil {
		logrus.Fatal(err)
	}

retry:
	global, err := cfg.GetGlobal()
	if err != nil {
		logrus.Errorf("Could not retrieve global configuration: %v. Retrying in 1 second", err)
		time.Sleep(time.Second)
		goto retry
	}

	dc := &DaemonConfig{Config: cfg, Global: global, Hostname: ctx.String("host-label")}
	dc.setDebug()

	globalChan := make(chan *watch.Watch)
	dc.Config.WatchGlobal(globalChan)
	go dc.watchAndSetGlobal(globalChan)
	go info.HandleDebugSignal()

	stopChan, err := lock.NewDriver(dc.Config).AcquireWithTTLRefresh(&config.UseVolsupervisor{Hostname: dc.Hostname}, dc.Global.TTL, dc.Global.Timeout)
	if err != nil {
		logrus.Fatal("Could not start volsupervisor: already in use")
	}

	sigChan := make(chan os.Signal, 1)

	go func() {
		<-sigChan
		logrus.Infof("Removing volsupervisor global lock; waiting %v for lock to clear", dc.Global.TTL)
		stopChan <- struct{}{}
		time.Sleep(wait.Jitter(dc.Global.TTL+time.Second, 0)) // give us enough time to try to clear the lock
		os.Exit(0)
	}()

	signal.Notify(sigChan, syscall.SIGTERM, syscall.SIGINT)

	dc.signalSnapshot()
	dc.updateVolumes()
	// doing it here ensures the goroutine is created when the first poll completes.
	go func() {
		for {
			time.Sleep(wait.Jitter(time.Second, 0))
			dc.updateVolumes()
		}
	}()

	dc.loop()
}
Example #5
0
// Create fully creates a volume
func (a *API) Create(w http.ResponseWriter, r *http.Request) {
	volume, err := a.ReadCreate(r)
	if err != nil {
		a.HTTPError(w, err)
		return
	}

	if vol, err := a.Client.GetVolume(volume.Policy, volume.Name); err == nil && vol != nil {
		a.HTTPError(w, errors.Exists)
		return
	}

	logrus.Infof("Creating volume %s", volume)

	hostname, err := os.Hostname()
	if err != nil {
		a.HTTPError(w, errors.GetHostname.Combine(err))
		return
	}

	policyObj, err := a.Client.GetPolicy(volume.Policy)
	if err != nil {
		a.HTTPError(w, errors.GetPolicy.Combine(errored.New(volume.Policy)).Combine(err))
		return
	}

	uc := &config.UseMount{
		Volume:   volume.String(),
		Reason:   lock.ReasonCreate,
		Hostname: hostname,
	}

	snapUC := &config.UseSnapshot{
		Volume: volume.String(),
		Reason: lock.ReasonCreate,
	}

	global := *a.Global

	err = lock.NewDriver(a.Client).ExecuteWithMultiUseLock(
		[]config.UseLocker{uc, snapUC},
		global.Timeout,
		a.createVolume(w, volume, policyObj),
	)

	if err != nil && err != errors.Exists {
		a.HTTPError(w, errors.CreateVolume.Combine(err))
		return
	}
}
Example #6
0
func (dc *DaemonConfig) createSnapshot(val *config.Volume) {
	logrus.Infof("Snapshotting %q.", val)

	uc := &config.UseSnapshot{
		Volume: val.String(),
		Reason: lock.ReasonSnapshot,
	}

	stopChan, err := lock.NewDriver(dc.Config).AcquireWithTTLRefresh(uc, dc.Global.TTL, dc.Global.Timeout)
	if err != nil {
		logrus.Error(err)
		return
	}

	defer func() { stopChan <- struct{}{} }()

	driver, err := backend.NewSnapshotDriver(val.Backends.Snapshot)
	if err != nil {
		logrus.Errorf("Error establishing driver backend %q; cannot snapshot", val.Backends.Snapshot)
		return
	}

	driverOpts := storage.DriverOptions{
		Volume: storage.Volume{
			Name: val.String(),
			Params: storage.Params{
				"pool": val.DriverOptions["pool"],
			},
		},
		Timeout: dc.Global.Timeout,
	}

	if err := driver.CreateSnapshot(time.Now().String(), driverOpts); err != nil {
		logrus.Errorf("Error creating snapshot for volume %q: %v", val, err)
	}
}
Example #7
0
func (d *DaemonConfig) handleRemove(w http.ResponseWriter, r *http.Request) {
	// set a default timeout if none is specified
	timeout := d.Global.Timeout
	req, err := unmarshalRequest(r)
	if err != nil {
		api.RESTHTTPError(w, errors.UnmarshalRequest.Combine(err))
		return
	}

	if req.Options["timeout"] != "" {
		var t time.Duration
		if t, err = time.ParseDuration(req.Options["timeout"]); err != nil {
			api.RESTHTTPError(w, errors.RemoveVolume.Combine(err))
			return
		}
		timeout = t
	}

	vc, err := d.Config.GetVolume(req.Policy, req.Name)
	if err != nil {
		api.RESTHTTPError(w, errors.GetVolume.Combine(err))
		return
	}

	locks, err := d.createRemoveLocks(vc)
	if err != nil {
		api.RESTHTTPError(w, err)
		return
	}

	if req.Options["force"] == "true" {
		if err := d.handleForceRemoveLock(req, vc, locks); err != nil {
			api.RESTHTTPError(w, err)
			return
		}
	}

	err = lock.NewDriver(d.Config).ExecuteWithMultiUseLock(locks, timeout, func(ld *lock.Driver, ucs []config.UseLocker) error {
		exists, err := control.ExistsVolume(vc, timeout)
		if err != nil && err != errors.NoActionTaken {
			return err
		}

		if err == errors.NoActionTaken {
			return d.completeRemove(req, vc)
		}

		if !exists {
			d.removeVolume(req, vc)
			return errors.NotExists
		}

		return d.completeRemove(req, vc)
	})

	if err == errors.NotExists {
		w.WriteHeader(404)
		return
	}

	if err != nil {
		api.RESTHTTPError(w, errors.RemoveVolume.Combine(errored.New(vc.String())).Combine(err))
		return
	}
}
Example #8
0
func (d *DaemonConfig) handleCopy(w http.ResponseWriter, r *http.Request) {
	req, err := unmarshalRequest(r)
	if err != nil {
		api.RESTHTTPError(w, errors.UnmarshalRequest.Combine(err))
		return
	}

	if _, ok := req.Options["snapshot"]; !ok {
		api.RESTHTTPError(w, errors.MissingSnapshotOption)
		return
	}

	if _, ok := req.Options["target"]; !ok {
		api.RESTHTTPError(w, errors.MissingTargetOption)
		return
	}

	if strings.Contains(req.Options["target"], "/") {
		api.RESTHTTPError(w, errors.InvalidVolume.Combine(errored.New("/")))
		return
	}

	volConfig, err := d.Config.GetVolume(req.Policy, req.Name)
	if err != nil {
		api.RESTHTTPError(w, errors.GetVolume.Combine(err))
		return
	}

	if volConfig.Backends.Snapshot == "" {
		api.RESTHTTPError(w, errors.SnapshotsUnsupported.Combine(errored.New(volConfig.Backends.Snapshot)))
		return
	}

	driver, err := backend.NewSnapshotDriver(volConfig.Backends.Snapshot)
	if err != nil {
		api.RESTHTTPError(w, errors.GetDriver.Combine(err))
		return
	}

	newVolConfig, err := d.Config.GetVolume(req.Policy, req.Name)
	if err != nil {
		api.RESTHTTPError(w, errors.GetVolume.Combine(err))
		return
	}

	newVolConfig.VolumeName = req.Options["target"]

	do := storage.DriverOptions{
		Volume: storage.Volume{
			Name:   volConfig.String(),
			Params: volConfig.DriverOptions,
		},
		Timeout: d.Global.Timeout,
	}

	host, err := os.Hostname()
	if err != nil {
		api.RESTHTTPError(w, errors.GetHostname.Combine(err))
		return
	}

	if volConfig.VolumeName == newVolConfig.VolumeName {
		api.RESTHTTPError(w, errors.CannotCopyVolume.Combine(errored.Errorf("You cannot copy volume %q onto itself.", volConfig.VolumeName)))
		return
	}

	snapUC := &config.UseSnapshot{
		Volume: volConfig.String(),
		Reason: lock.ReasonCopy,
	}

	newUC := &config.UseMount{
		Volume:   newVolConfig.String(),
		Reason:   lock.ReasonCopy,
		Hostname: host,
	}

	newSnapUC := &config.UseSnapshot{
		Volume: newVolConfig.String(),
		Reason: lock.ReasonCopy,
	}

	err = lock.NewDriver(d.Config).ExecuteWithMultiUseLock([]config.UseLocker{newUC, newSnapUC, snapUC}, d.Global.Timeout, func(ld *lock.Driver, ucs []config.UseLocker) error {
		if err := d.Config.PublishVolume(newVolConfig); err != nil {
			return err
		}

		if err := driver.CopySnapshot(do, req.Options["snapshot"], newVolConfig.String()); err != nil {
			return err
		}
		return nil
	})

	if err != nil {
		api.RESTHTTPError(w, errors.PublishVolume.Combine(errored.Errorf(
			"Creating new volume %q from volume %q, snapshot %q",
			req.Options["target"],
			volConfig.String(),
			req.Options["snapshot"],
		)).Combine(err))
		return
	}

	content, err := json.Marshal(newVolConfig)
	if err != nil {
		api.RESTHTTPError(w, errors.PublishVolume.Combine(errored.Errorf(
			"Creating new volume %q from volume %q, snapshot %q",
			req.Options["target"],
			volConfig.String(),
			req.Options["snapshot"],
		)).Combine(err))
	}

	w.Write(content)
}