コード例 #1
0
ファイル: daemon.go プロジェクト: contiv/volplugin
func (d *DaemonConfig) handleForceRemoveLock(req *config.VolumeRequest, vc *config.Volume, locks []config.UseLocker) error {
	exists, err := control.ExistsVolume(vc, d.Global.Timeout)
	if err != nil && err != errors.NoActionTaken {
		return errors.RemoveVolume.Combine(errored.New(vc.String())).Combine(err)
	}

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

		d.removeVolumeUse(locks[0], vc)
	}

	if err != nil {
		return errors.RemoveVolume.Combine(errored.New(vc.String())).Combine(err)
	}

	if !exists {
		d.removeVolume(req, vc)
		return errors.RemoveVolume.Combine(errored.New(vc.String())).Combine(errors.NotExists)
	}

	err = d.completeRemove(req, vc)
	if err != nil {
		return errors.RemoveVolume.Combine(errored.New(vc.String())).Combine(errors.NotExists)
	}

	d.removeVolumeUse(locks[0], vc)
	return nil
}
コード例 #2
0
ファイル: utils.go プロジェクト: contiv/volplugin
// SplitName splits a docker volume name from policy/name safely.
func SplitName(name string) (string, string, error) {
	if strings.Count(name, "/") > 1 {
		return "", "", errors.InvalidVolume.Combine(errored.New(name))
	}

	parts := strings.SplitN(name, "/", 2)
	if len(parts) != 2 || parts[0] == "" || parts[1] == "" {
		return "", "", errors.InvalidVolume.Combine(errored.New(name))
	}

	return parts[0], parts[1], nil
}
コード例 #3
0
ファイル: validation.go プロジェクト: contiv/volplugin
// Combines array of errors into a single error
func combineErrors(resultErrors []gojson.ResultError) error {
	var errors []string
	for _, err := range resultErrors {
		errors = append(errors, fmt.Sprintf("%s\n", err))
	}
	return errored.New(strings.Join(errors, "\n"))
}
コード例 #4
0
ファイル: ceph.go プロジェクト: contiv/volplugin
// CopySnapshot copies a snapshot into a new volume. Takes a DriverOptions,
// snap and volume name (string). Returns error on failure.
func (c *Driver) CopySnapshot(do storage.DriverOptions, snapName, newName string) error {
	intOrigName, err := c.internalName(do.Volume.Name)
	if err != nil {
		return err
	}

	intNewName, err := c.internalName(newName)
	if err != nil {
		return err
	}

	poolName := do.Volume.Params["pool"]

	list, err := c.List(storage.ListOptions{Params: storage.Params{"pool": poolName}})
	for _, vol := range list {
		if intNewName == vol.Name {
			return errored.Errorf("Volume %q already exists", vol.Name)
		}
	}

	errChan := make(chan error, 1)

	cmd := exec.Command("rbd", "snap", "protect", mkpool(poolName, intOrigName), "--snap", snapName)
	er, err := runWithTimeout(cmd, do.Timeout)

	// EBUSY indicates that the snapshot is already protected.
	if err != nil && er.ExitStatus != 0 && er.ExitStatus != int(unix.EBUSY) {
		if er.ExitStatus == int(unix.EEXIST) {
			err = errored.Errorf("Volume %q or snapshot name %q already exists. Snapshots cannot share the same name as the target volume.", do.Volume.Name, snapName).Combine(errors.Exists).Combine(errors.SnapshotProtect)
		}
		errChan <- err
		return err
	}

	defer c.cleanupCopy(snapName, newName, do, errChan)

	cmd = exec.Command("rbd", "clone", mkpool(poolName, intOrigName), mkpool(poolName, intNewName), "--snap", snapName)
	er, err = runWithTimeout(cmd, do.Timeout)
	if err != nil && er.ExitStatus == 0 {
		var err2 *errored.Error
		var ok bool

		err2, ok = err.(*errored.Error)
		if !ok {
			err2 = errored.New(err.Error())
		}
		errChan <- err2.Combine(errors.SnapshotCopy)
		return err2
	}

	if er.ExitStatus != 0 {
		newerr := errored.Errorf("Cloning snapshot to volume (volume %q, snapshot %q): %v", intOrigName, snapName, err).Combine(errors.SnapshotCopy).Combine(errors.SnapshotProtect)
		if er.ExitStatus != int(unix.EEXIST) {
			errChan <- newerr
		}
		return err
	}

	return nil
}
コード例 #5
0
ファイル: daemon.go プロジェクト: contiv/volplugin
func (d *DaemonConfig) completeRemove(req *config.VolumeRequest, vc *config.Volume) error {
	if err := control.RemoveVolume(vc, d.Global.Timeout); err != nil && err != errors.NoActionTaken {
		logrus.Warn(errors.RemoveImage.Combine(errored.New(vc.String())).Combine(err))
	}

	return d.removeVolume(req, vc)
}
コード例 #6
0
ファイル: daemon.go プロジェクト: contiv/volplugin
func (d *DaemonConfig) removeVolume(req *config.VolumeRequest, vc *config.Volume) error {
	if err := d.Config.RemoveVolume(req.Policy, req.Name); err != nil {
		return errors.ClearVolume.Combine(errored.New(vc.String())).Combine(err)
	}

	return nil
}
コード例 #7
0
ファイル: daemon.go プロジェクト: contiv/volplugin
func (d *DaemonConfig) handleListAll(w http.ResponseWriter, r *http.Request) {
	vols, err := d.Config.ListAllVolumes()
	if err != nil {
		api.RESTHTTPError(w, errors.ListVolume.Combine(err))
		return
	}

	response := []*config.Volume{}
	for _, vol := range vols {
		parts := strings.SplitN(vol, "/", 2)
		if len(parts) != 2 {
			api.RESTHTTPError(w, errors.InvalidVolume.Combine(errored.New(vol)))
			return
		}
		// FIXME make this take a single string and not a split one
		volConfig, err := d.Config.GetVolume(parts[0], parts[1])
		if err != nil {
			api.RESTHTTPError(w, errors.ListVolume.Combine(err))
			return
		}

		response = append(response, volConfig)
	}

	content, err := json.Marshal(response)
	if err != nil {
		api.RESTHTTPError(w, errors.ListVolume.Combine(err))
		return
	}

	w.Write(content)
}
コード例 #8
0
ファイル: utils.go プロジェクト: unclejack/volplugin
// CombineError is a simplication of errored.Combine
func CombineError(err error, format string, args ...interface{}) error {
	if erd, ok := err.(*errored.Error); ok {
		erd.Combine(errored.Errorf(format, args...))
	}

	return errored.New(err.Error()).Combine(errored.Errorf(format, args...))
}
コード例 #9
0
ファイル: entity_test.go プロジェクト: unclejack/volplugin
func (t *testEntity) Validate() error {
	if t.FailsValidation {
		return errored.New("failed validation")
	}

	return nil
}
コード例 #10
0
ファイル: volume.go プロジェクト: contiv/volplugin
// Path provides the path to this volumes data store.
func (v *Volume) Path() (string, error) {
	if v.PolicyName == "" || v.VolumeName == "" {
		return "", errors.InvalidVolume.Combine(errored.New("Volume or policy name is missing"))
	}

	return strings.Join([]string{v.Prefix(), v.PolicyName, v.VolumeName}, "/"), nil
}
コード例 #11
0
ファイル: use.go プロジェクト: unclejack/volplugin
// Validate does nothing on use locks.
func (m *Use) Validate() error {
	parts := strings.Split(m.Volume, "/")
	if len(parts) != 2 || parts[0] == "" || parts[1] == "" {
		return errors.InvalidVolume.Combine(errored.New(m.Volume))
	}

	return nil
}
コード例 #12
0
ファイル: daemon.go プロジェクト: contiv/volplugin
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
	}
}
コード例 #13
0
ファイル: daemon.go プロジェクト: contiv/volplugin
func unmarshalRequest(r *http.Request) (*config.VolumeRequest, error) {
	cfg := &config.VolumeRequest{}

	content, err := ioutil.ReadAll(r.Body)
	if err != nil {
		return cfg, err
	}

	if err := json.Unmarshal(content, cfg); err != nil {
		return cfg, err
	}

	if cfg.Policy == "" {
		return cfg, errored.New("Policy was blank")
	}

	if cfg.Name == "" {
		return cfg, errored.New("volume was blank")
	}

	return cfg, nil
}
コード例 #14
0
ファイル: policy.go プロジェクト: contiv/volplugin
// SetKey implements the SetKey entity interface.
func (p *Policy) SetKey(key string) error {
	suffix := strings.Trim(strings.TrimPrefix(key, rootPolicy), "/")
	if strings.Contains(suffix, "/") {
		return errors.InvalidDBPath.Combine(errored.Errorf("Policy name %q contains invalid characters", suffix))
	}

	if suffix == "" {
		return errors.InvalidDBPath.Combine(errored.New("Policy name is empty"))
	}

	p.Name = suffix
	return nil
}
コード例 #15
0
ファイル: client.go プロジェクト: contiv/volplugin
// WatchStopPath stops a watch given a path to stop the watch on.
func (c *Client) watchStopPath(path string) error {
	c.watcherMutex.Lock()
	defer c.watcherMutex.Unlock()

	stopChan, ok := c.watchers[path]
	if !ok {
		return errors.InvalidDBPath.Combine(errored.New("missing key during watch"))
	}

	close(stopChan)
	delete(c.watchers, path)

	return nil
}
コード例 #16
0
ファイル: handlers.go プロジェクト: contiv/volplugin
// 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
	}
}
コード例 #17
0
ファイル: lock.go プロジェクト: unclejack/volplugin
// Free a lock. Passing true as the second parameter will force the removal.
func (c *Client) Free(obj db.Lock, force bool) error {
	path, err := obj.Path()
	if err != nil {
		return errors.LockFailed.Combine(err)
	}

	logrus.Debugf("Attempting to free %q by %v", path, obj)

	mylock, ok := c.getLock(path)
	if !ok {
		return errors.LockFailed.Combine(errored.New("Could not locate lock"))
	}

	if !reflect.DeepEqual(obj, mylock.obj) {
		if force {
			goto free
		}

		return errors.LockFailed.Combine(errored.New("invalid lock requested to be freed (wrong host?)"))
	}

free:
	select {
	case <-mylock.monitorChan:
	default:
		mylock.lock.Unlock()
	}

	c.lockMutex.Lock()
	if _, ok := c.locks[path]; ok {
		delete(c.locks, path)
	}
	c.Delete(mylock.obj)
	c.lockMutex.Unlock()

	return nil
}
コード例 #18
0
ファイル: client.go プロジェクト: unclejack/volplugin
// Get retrieves an object from consul, returns error on any problems.
func (c *Client) Get(obj db.Entity) error {
	return helpers.WrapGet(c, obj, func(path string) (string, []byte, error) {
		pair, _, err := c.client.KV().Get(c.qualified(path), nil)

		if err != nil {
			return "", nil, err
		}

		if pair == nil {
			return "", nil, errors.NotExists.Combine(errored.New(c.qualified(path)))
		}

		return pair.Key, pair.Value, nil
	})
}
コード例 #19
0
ファイル: validate_json.go プロジェクト: contiv/volplugin
func validateJSON(schema string, obj Entity) error {
	schemaObj := gojson.NewStringLoader(schema)
	doc := gojson.NewGoLoader(obj)

	if result, err := gojson.Validate(schemaObj, doc); err != nil {
		return err
	} else if !result.Valid() {
		var errors []string
		for _, err := range result.Errors() {
			errors = append(errors, fmt.Sprintf("%s\n", err))
		}
		return errored.New(strings.Join(errors, "\n"))
	}

	return nil
}
コード例 #20
0
ファイル: handlers.go プロジェクト: contiv/volplugin
// triggered on any failure during call into mount.
func (a *API) clearMount(ms mountState) {
	logrus.Errorf("MOUNT FAILURE: %v", ms.err)

	if err := ms.driver.Unmount(ms.driverOpts); err != nil {
		// literally can't do anything about this situation. Log.
		logrus.Errorf("Failure during unmount after failed mount: %v %v", err, ms.err)
	}

	if err := a.Lock.ClearLock(ms.ut, (*a.Global).Timeout); err != nil {
		a.HTTPError(ms.w, errors.RefreshMount.Combine(errored.New(ms.volConfig.String())).Combine(err).Combine(ms.err))
		return
	}

	a.HTTPError(ms.w, errors.MountFailed.Combine(ms.err))
	return
}
コード例 #21
0
ファイル: client.go プロジェクト: unclejack/volplugin
// NewClient creates a new Client with connections to etcd already established.
func NewClient(hosts []string, prefix string) (*Client, error) {
	ec, err := client.New(client.Config{Endpoints: hosts})
	if err != nil {
		return nil, err
	}

	c := &Client{
		client:   client.NewKeysAPI(ec),
		prefix:   prefix,
		watchers: map[string]chan struct{}{},
	}

	if _, err := c.client.Set(context.Background(), c.prefix, "", &client.SetOptions{Dir: true, PrevExist: client.PrevNoExist}); err != nil {
		if err != nil {
			er, ok := errors.EtcdToErrored(err).(*errored.Error)
			if !ok || !er.Contains(errors.Exists) {
				return nil, errored.New("Initial setup").Combine(err)
			}
		}
	}

	return c, nil
}
コード例 #22
0
ファイル: daemon.go プロジェクト: contiv/volplugin
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)
}
コード例 #23
0
ファイル: daemon.go プロジェクト: contiv/volplugin
// this cleans up uses when forcing the removal
func (d *DaemonConfig) removeVolumeUse(lock config.UseLocker, vc *config.Volume) {
	// locks[0] is the usemount lock
	if err := d.Config.RemoveUse(lock, true); err != nil {
		logrus.Warn(errors.RemoveImage.Combine(errored.New(vc.String())).Combine(err))
	}
}
コード例 #24
0
ファイル: daemon.go プロジェクト: contiv/volplugin
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
	}
}
コード例 #25
0
ファイル: errors.go プロジェクト: contiv/volplugin
package errors

import "github.com/contiv/errored"

// service-level errors
var (
	// Unknown is for those times when we just. don't. know.
	Unknown = errored.New("Unknown error")

	// Exists is used to exit in situations where duplicated data would be written.
	Exists = errored.New("Already exists")
	// NotExists is used to exit in situations where no data would be read.
	NotExists = errored.New("Does not exist")

	// LockFailed is for when locks fails to acquire.
	LockFailed = errored.New("Locking Operation Failed")

	// LockMismatch is when our compare/swap operations fail.
	LockMismatch = errored.New("Compare/swap lock operation failed. Perhaps it's mounted on a different host?")

	// NoActionTaken signifies that the requested operation was ignored.
	NoActionTaken = errored.New("No action taken")

	// ErrPublish is an error for when use locks cannot be published
	ErrLockPublish = errored.New("Could not publish use lock")

	// ErrRemove is an error for when use locks cannot be removed
	ErrLockRemove = errored.New("Could not remove use lock")

	// VolmasterDown signifies that the apiserver could not be reached.
	VolmasterDown = errored.New("apiserver could not be contacted")