Exemple #1
0
func TestVolumeAttach(t *testing.T) {
	tf := func(config gofig.Config, client types.Client, t *testing.T) {

		nextDevice, err := client.Executor().NextDevice(
			context.Background().WithValue(context.ServiceKey, vfs.Name),
			utils.NewStore())
		assert.NoError(t, err)
		if err != nil {
			t.FailNow()
		}

		request := &types.VolumeAttachRequest{
			NextDeviceName: &nextDevice,
		}

		reply, attTokn, err := client.API().VolumeAttach(
			nil, vfs.Name, "vfs-002", request)
		assert.NoError(t, err)
		if reply == nil {
			t.FailNow()
		}
		assert.Equal(t, "/dev/xvdc", attTokn)
		assert.Equal(t, "vfs-002", reply.ID)
		assert.Equal(t, "/dev/xvdc", reply.Attachments[0].DeviceName)

	}
	apitests.Run(t, vfs.Name, newTestConfig(t), tf)
}
func (d *driver) VolumeAttach(
	ctx types.Context,
	volumeID string,
	opts *types.VolumeAttachOpts) (*types.Volume, string, error) {

	if d.isController() {
		return nil, "", utils.NewUnsupportedForClientTypeError(
			d.clientType, "VolumeAttach")
	}

	ctx = d.requireCtx(ctx)
	serviceName, ok := context.ServiceName(ctx)
	if !ok {
		return nil, "", goof.New("missing service name")
	}

	nextDevice, err := d.NextDevice(ctx, utils.NewStore())
	if err != nil {
		return nil, "", err
	}

	var nextDevicePtr *string
	if nextDevice != "" {
		nextDevicePtr = &nextDevice
	}

	req := &types.VolumeAttachRequest{
		NextDeviceName: nextDevicePtr,
		Force:          opts.Force,
		Opts:           opts.Opts.Map(),
	}

	return d.client.VolumeAttach(ctx, serviceName, volumeID, req)
}
Exemple #3
0
// Test volume attachment by volume ID
func volumeAttach(
	t *testing.T, client types.Client, volumeID string) *types.Volume {
	log.WithField("volumeID", volumeID).Info("attaching volume")
	// Get next device name from executor
	nextDevice, err := client.Executor().NextDevice(context.Background().WithValue(context.ServiceKey, ebs.Name),
		utils.NewStore())
	assert.NoError(t, err)
	if err != nil {
		t.Error("error getting next device name from executor")
		t.FailNow()
	}

	reply, token, err := client.API().VolumeAttach(
		nil, ebs.Name, volumeID, &types.VolumeAttachRequest{
			NextDeviceName: &nextDevice,
		})

	assert.NoError(t, err)
	if err != nil {
		t.Error("failed volumeAttach")
		t.FailNow()
	}
	apitests.LogAsJSON(reply, t)
	assert.NotEqual(t, token, "")

	return reply
}
Exemple #4
0
// Test is the APITestFunc for the NextDeviceTest.
func (tt *NextDeviceTest) Test(
	config gofig.Config,
	client types.Client,
	t *testing.T) {

	ctx := context.Background().WithValue(context.ServiceKey, tt.Driver)
	val, err := client.Executor().NextDevice(ctx, utils.NewStore())
	assert.NoError(t, err)
	assert.Equal(t, tt.Expected, val)
}
func (c *client) dial(ctx types.Context) error {

	ctx.WithField("path", lsxMutex).Info("lsx lock file path")

	svcInfos, err := c.Services(ctx)
	if err != nil {
		return err
	}

	// controller clients do not have any additional dialer logic
	if c.isController() {
		return nil
	}

	store := utils.NewStore()
	c.ctx = c.ctx.WithValue(context.ServerKey, c.ServerName())

	if !c.config.GetBool(types.ConfigExecutorNoDownload) {

		ctx.Info("initializing executors cache")
		if _, err := c.Executors(ctx); err != nil {
			return err
		}

		if err := c.updateExecutor(ctx); err != nil {
			return err
		}
	}

	for service, _ := range svcInfos {
		ctx := c.ctx.WithValue(context.ServiceKey, service)
		ctx.Info("initializing supported cache")
		supported, err := c.Supported(ctx, store)
		if err != nil {
			return goof.WithError("error initializing supported cache", err)
		}

		if !supported {
			ctx.Warn("executor not supported")
			continue
		}

		ctx.Info("initializing instance ID cache")
		if _, err := c.InstanceID(ctx, store); err != nil {
			if err == types.ErrNotImplemented {
				ctx.WithError(err).Warn("cannot get instance ID")
				continue
			}
			return goof.WithError("error initializing instance ID cache", err)
		}
	}

	return nil
}
Exemple #6
0
func TestClient(t *testing.T) {
	apitests.Run(t, vfs.Name, newTestConfig(t),
		func(config gofig.Config, client types.Client, t *testing.T) {
			ctx := context.Background()
			iid, err := client.Executor().InstanceID(
				ctx.WithValue(context.ServiceKey, vfs.Name),
				utils.NewStore())
			assert.NoError(t, err)
			assert.NotNil(t, iid)
		})
}
Exemple #7
0
func TestStorageDriverVolumes(t *testing.T) {
	apitests.Run(t, vfs.Name, newTestConfig(t),
		func(config gofig.Config, client types.Client, t *testing.T) {

			vols, err := client.Storage().Volumes(
				context.Background().WithValue(
					context.ServiceKey, vfs.Name),
				&types.VolumesOpts{
					Attachments: types.VolAttReqTrue,
					Opts:        utils.NewStore()})
			assert.NoError(t, err)
			assert.Len(t, vols, 2)
		})
}
Exemple #8
0
func TestVolumeAttachWithControllerClient(t *testing.T) {
	tf := func(config gofig.Config, client types.Client, t *testing.T) {

		_, err := client.Executor().NextDevice(
			context.Background().WithValue(context.ServiceKey, vfs.Name),
			utils.NewStore())
		assert.Error(t, err)
		assert.Equal(t, "unsupported op for client type", err.Error())

		_, _, err = client.API().VolumeAttach(
			nil, vfs.Name, "vfs-002", &types.VolumeAttachRequest{})
		assert.Error(t, err)
		assert.Equal(t, "unsupported op for client type", err.Error())
	}

	apitests.RunWithClientType(
		t, types.ControllerClient, vfs.Name, newTestConfig(t), tf)
}
Exemple #9
0
// Test is the APITestFunc for the InstanceTest.
func (tt *InstanceTest) Test(
	config gofig.Config,
	client types.Client,
	t *testing.T) {

	expectedBuf, err := json.Marshal(tt.Expected)
	assert.NoError(t, err)
	expectedJSON := string(expectedBuf)

	ctx := context.Background().WithValue(context.ServiceKey, tt.Driver)
	iid, err := client.Storage().InstanceInspect(ctx, utils.NewStore())
	assert.NoError(t, err)

	iidBuf, err := json.Marshal(iid)
	assert.NoError(t, err)
	iidJSON := string(iidBuf)

	assert.Equal(t, expectedJSON, iidJSON)
}
Exemple #10
0
// Check if InstanceID metadata is properly returned by executor
// and InstanceID.ID is filled out by InstanceInspect
func TestInstanceID(t *testing.T) {
	if skipTests() {
		t.SkipNow()
	}

	// create storage driver
	sd, err := registry.NewStorageDriver(ebs.Name)
	if err != nil {
		t.Fatal(err)
	}

	// initialize storage driver
	ctx := context.Background()
	if err := sd.Init(ctx, registry.NewConfig()); err != nil {
		t.Fatal(err)
	}
	// Get Instance ID metadata from executor
	iid, err := ebsUtils.InstanceID(ctx)
	assert.NoError(t, err)
	if err != nil {
		t.Fatal(err)
	}

	// Fill in Instance ID's ID field with InstanceInspect
	ctx = ctx.WithValue(context.InstanceIDKey, iid)
	i, err := sd.InstanceInspect(ctx, utils.NewStore())
	if err != nil {
		t.Fatal(err)
	}

	iid = i.InstanceID

	// test resulting InstanceID
	apitests.Run(
		t, ebs.Name, nil,
		(&apitests.InstanceIDTest{
			Driver:   ebs.Name,
			Expected: iid,
		}).Test)

}
Exemple #11
0
func TestInstanceID(t *testing.T) { //PASSES lowercase hidden for testing other stuff
	if skipTests() {
		t.SkipNow()
	}

	sd, err := registry.NewStorageDriver(rackspace.Name)
	if err != nil {
		t.Fatal(err)
	}

	ctx := context.Background()
	configR := registry.NewConfig()
	if err := configR.ReadConfig(bytes.NewReader(configYAML)); err != nil {
		panic(err)
	}
	if err := sd.Init(ctx, configR); err != nil {
		t.Fatal(err)
	}
	iid, err := rackspacex.InstanceID(configR)
	if err != nil {
		t.Fatal(err)
	}

	ctx = ctx.WithValue(context.InstanceIDKey, iid)
	i, err := sd.InstanceInspect(ctx, utils.NewStore())
	if err != nil {
		t.Fatal(err)
	}

	iid = i.InstanceID

	apitests.Run(
		t, rackspace.Name, configYAML,
		(&apitests.InstanceIDTest{
			Driver:   rackspace.Name,
			Expected: iid,
		}).Test)
}
Exemple #12
0
func TestInstanceID(t *testing.T) {
	if skipTests() {
		t.SkipNow()
	}

	sd, err := registry.NewStorageDriver(efs.Name)
	if err != nil {
		t.Fatal(err)
	}

	ctx := context.Background()
	if err := sd.Init(ctx, registry.NewConfig()); err != nil {
		t.Fatal(err)
	}

	iid, err := efsx.InstanceID()
	assert.NoError(t, err)
	if err != nil {
		t.Error("failed TestInstanceID")
		t.FailNow()
	}
	assert.NotEqual(t, iid, "")

	ctx = ctx.WithValue(context.InstanceIDKey, iid)
	i, err := sd.InstanceInspect(ctx, utils.NewStore())
	if err != nil {
		t.Fatal(err)
	}

	iid = i.InstanceID

	apitests.Run(
		t, efs.Name, configYAML,
		(&apitests.InstanceIDTest{
			Driver:   efs.Name,
			Expected: iid,
		}).Test)
}
Exemple #13
0
// Test is the APITestFunc for the LocalDevicesTest.
func (tt *LocalDevicesTest) Test(
	config gofig.Config,
	client types.Client,
	t *testing.T) {

	expectedBuf, err := tt.Expected.MarshalText()
	assert.NoError(t, err)
	expectedText := string(expectedBuf)

	ctx := context.Background().WithValue(context.ServiceKey, tt.Driver)

	val, err := client.Executor().LocalDevices(ctx, &types.LocalDevicesOpts{
		ScanType: types.DeviceScanQuick,
		Opts:     utils.NewStore(),
	})
	assert.NoError(t, err)

	buf, err := val.MarshalText()
	assert.NoError(t, err)
	actualText := string(buf)

	assert.Equal(t, expectedText, actualText)
}
Exemple #14
0
// Mount will return a mount point path when specifying either a volumeName
// or volumeID.  If a overwriteFs boolean is specified it will overwrite
// the FS based on newFsType if it is detected that there is no FS present.
func (d *driver) Mount(
	ctx types.Context,
	volumeID, volumeName string,
	opts *types.VolumeMountOpts) (string, *types.Volume, error) {

	ctx.WithFields(log.Fields{
		"volumeName": volumeName,
		"volumeID":   volumeID,
		"opts":       opts}).Info("mounting volume")

	vol, err := d.volumeInspectByIDOrName(
		ctx, volumeID, volumeName,
		types.VolAttReqWithDevMapOnlyVolsAttachedToInstanceOrUnattachedVols,
		opts.Opts)
	if isErrNotFound(err) && d.volumeCreateImplicit() {
		var err error
		if vol, err = d.Create(ctx, volumeName, &types.VolumeCreateOpts{
			Opts: utils.NewStore(),
		}); err != nil {
			return "", nil, goof.WithError(
				"problem creating volume implicitly", err)
		}
	} else if err != nil {
		return "", nil, err
	}

	if vol == nil {
		return "", nil, goof.New("no volume returned or created")
	}

	client := context.MustClient(ctx)
	if len(vol.Attachments) == 0 || opts.Preempt {
		mp, err := d.getVolumeMountPath(vol.Name)
		if err != nil {
			return "", nil, err
		}

		ctx.Debug("performing precautionary unmount")
		_ = client.OS().Unmount(ctx, mp, opts.Opts)

		var token string
		vol, token, err = client.Storage().VolumeAttach(
			ctx, vol.ID, &types.VolumeAttachOpts{
				Force: opts.Preempt,
				Opts:  utils.NewStore(),
			})
		if err != nil {
			return "", nil, err
		}

		if token != "" {
			opts := &types.WaitForDeviceOpts{
				LocalDevicesOpts: types.LocalDevicesOpts{
					ScanType: apiconfig.DeviceScanType(d.config),
					Opts:     opts.Opts,
				},
				Token:   token,
				Timeout: apiconfig.DeviceAttachTimeout(d.config),
			}

			_, _, err = client.Executor().WaitForDevice(ctx, opts)
			if err != nil {
				return "", nil, goof.WithError(
					"problem with device discovery", err)
			}
		}

		vol, err = d.volumeInspectByIDOrName(
			ctx, vol.ID, "", types.VolAttReqTrue, opts.Opts)
		if err != nil {
			return "", nil, err
		}

	}

	if len(vol.Attachments) == 0 {
		return "", nil, goof.New("volume did not attach")
	}

	inst, err := client.Storage().InstanceInspect(ctx, utils.NewStore())
	if err != nil {
		return "", nil, goof.New("problem getting instance ID")
	}
	var ma *types.VolumeAttachment
	for _, att := range vol.Attachments {
		if att.InstanceID.ID == inst.InstanceID.ID {
			ma = att
			break
		}
	}

	if ma == nil {
		return "", nil, goof.New("no local attachment found")
	}

	if ma.DeviceName == "" {
		return "", nil, goof.New("no device name returned")
	}

	mounts, err := client.OS().Mounts(
		ctx, ma.DeviceName, "", opts.Opts)
	if err != nil {
		return "", nil, err
	}

	if len(mounts) > 0 {
		return d.volumeMountPath(mounts[0].MountPoint), vol, nil
	}

	if opts.NewFSType == "" {
		opts.NewFSType = d.fsType()
	}

	if err := client.OS().Format(
		ctx,
		ma.DeviceName,
		&types.DeviceFormatOpts{
			NewFSType:   opts.NewFSType,
			OverwriteFS: opts.OverwriteFS,
		}); err != nil {
		return "", nil, err
	}

	mountPath, err := d.getVolumeMountPath(vol.Name)
	if err != nil {
		return "", nil, err
	}

	if err := os.MkdirAll(mountPath, 0755); err != nil {
		return "", nil, err
	}

	if err := client.OS().Mount(
		ctx,
		ma.DeviceName,
		mountPath,
		&types.DeviceMountOpts{}); err != nil {
		return "", nil, err
	}

	mntPath := d.volumeMountPath(mountPath)

	fields := log.Fields{
		"vol":     vol,
		"mntPath": mntPath,
	}
	ctx.WithFields(fields).Info("volume mounted")

	return mntPath, vol, nil
}
Exemple #15
0
func store() apitypes.Store {
	return apiutils.NewStore()
}
Exemple #16
0
func (m *mod) buildMux() *http.ServeMux {

	mux := http.NewServeMux()
	// m.ctx.WithServiceName(m.ctx.ServiceName())

	mux.HandleFunc("/Plugin.Activate", func(w http.ResponseWriter, r *http.Request) {
		w.Header().Set("Content-Type", "application/vnd.docker.plugins.v1.2+json")
		fmt.Fprintln(w, `{"Implements": ["VolumeDriver"]}`)
		m.ctx.Debug("/Plugin.Activate")
	})

	mux.HandleFunc("/VolumeDriver.Create", func(w http.ResponseWriter, r *http.Request) {
		var pr pluginRequest
		if err := json.NewDecoder(r.Body).Decode(&pr); err != nil {
			http.Error(w, fmt.Sprintf("{\"Error\":\"%s\"}", err.Error()), 500)
			m.ctx.WithError(err).Error("/VolumeDriver.Create: error decoding json")
			return
		}

		m.ctx.WithField("pluginResponse", pr).Debug("/VolumeDriver.Create")
		store := apiutils.NewStoreWithVars(pr.Opts)
		vtype := store.GetStringPtr("type")
		if vtype == nil {
			vtype = store.GetStringPtr("volumetype")
		}
		_, err := m.lsc.Integration().Create(
			m.ctx,
			pr.Name,
			&apitypes.VolumeCreateOpts{
				AvailabilityZone: store.GetStringPtr("availabilityZone"),
				IOPS:             store.GetInt64Ptr("iops"),
				Size:             store.GetInt64Ptr("size"),
				Type:             vtype,
				Opts:             store,
			})

		if err != nil {
			http.Error(w, fmt.Sprintf("{\"Error\":\"%s\"}", err.Error()), 500)
			m.ctx.WithError(err).Error("/VolumeDriver.Create: error creating volume")
			return
		}

		w.Header().Set("Content-Type", "application/vnd.docker.plugins.v1.2+json")
		fmt.Fprintln(w, `{}`)
	})

	mux.HandleFunc("/VolumeDriver.Remove", func(w http.ResponseWriter, r *http.Request) {
		var pr pluginRequest
		if err := json.NewDecoder(r.Body).Decode(&pr); err != nil {
			http.Error(w, fmt.Sprintf("{\"Error\":\"%s\"}", err.Error()), 500)
			m.ctx.WithError(err).Error("/VolumeDriver.Remove: error decoding json")
			return
		}

		m.ctx.WithField("pluginResponse", pr).Debug("/VolumeDriver.Remove")

		// TODO We need the service name
		err := m.lsc.Integration().Remove(m.ctx, pr.Name, apiutils.NewStore())
		if err != nil {
			http.Error(w, fmt.Sprintf("{\"Error\":\"%s\"}", err.Error()), 500)
			m.ctx.WithError(err).Error("/VolumeDriver.Remove: error removing volume")
			return
		}

		w.Header().Set("Content-Type", "application/vnd.docker.plugins.v1.2+json")
		fmt.Fprintln(w, `{}`)
	})

	mux.HandleFunc("/VolumeDriver.Path", func(w http.ResponseWriter, r *http.Request) {
		var pr pluginRequest
		if err := json.NewDecoder(r.Body).Decode(&pr); err != nil {
			http.Error(w, fmt.Sprintf("{\"Error\":\"%s\"}", err.Error()), 500)
			m.ctx.WithError(err).Error("/VolumeDriver.Path: error decoding json")
			return
		}

		m.ctx.WithField("pluginResponse", pr).Debug("/VolumeDriver.Path")

		mountPath, err := m.lsc.Integration().Path(
			m.ctx, "", pr.Name, apiutils.NewStore())
		if err != nil {
			http.Error(w, fmt.Sprintf("{\"Error\":\"%s\"}", err.Error()), 500)
			m.ctx.WithError(err).Error("/VolumeDriver.Path: error returning path")
			return
		}

		w.Header().Set("Content-Type", "application/vnd.docker.plugins.v1.2+json")
		fmt.Fprintln(w, fmt.Sprintf("{\"Mountpoint\": \"%s\"}", mountPath))
	})

	mux.HandleFunc("/VolumeDriver.Mount", func(w http.ResponseWriter, r *http.Request) {
		var pr pluginRequest
		if err := json.NewDecoder(r.Body).Decode(&pr); err != nil {
			http.Error(w, fmt.Sprintf("{\"Error\":\"%s\"}", err.Error()), 500)
			m.ctx.WithError(err).Error("/VolumeDriver.Mount: error decoding json")
			return
		}

		m.ctx.WithField("pluginResponse", pr).Debug("/VolumeDriver.Mount")

		mountPath, _, err := m.lsc.Integration().Mount(
			m.ctx, "", pr.Name, &apitypes.VolumeMountOpts{})
		if err != nil {
			http.Error(w, fmt.Sprintf("{\"Error\":\"%s\"}", err.Error()), 500)
			m.ctx.WithError(err).Error("/VolumeDriver.Mount: error mounting volume")
			return
		}

		w.Header().Set("Content-Type", "application/vnd.docker.plugins.v1.2+json")
		fmt.Fprintln(w, fmt.Sprintf("{\"Mountpoint\": \"%s\"}", mountPath))
	})

	mux.HandleFunc("/VolumeDriver.Unmount", func(w http.ResponseWriter, r *http.Request) {
		var pr pluginRequest
		if err := json.NewDecoder(r.Body).Decode(&pr); err != nil {
			http.Error(w, fmt.Sprintf("{\"Error\":\"%s\"}", err.Error()), 500)
			m.ctx.WithError(err).Error("/VolumeDriver.Unmount: error decoding json")
			return
		}

		m.ctx.WithField("pluginResponse", pr).Debug("/VolumeDriver.Unmount")

		_, err := m.lsc.Integration().Unmount(
			m.ctx, "", pr.Name, apiutils.NewStore())
		if err != nil {
			http.Error(w, fmt.Sprintf("{\"Error\":\"%s\"}", err.Error()), 500)
			m.ctx.WithError(err).Error("/VolumeDriver.Unmount: error unmounting volume")
			return
		}

		w.Header().Set("Content-Type", "application/vnd.docker.plugins.v1.2+json")
		fmt.Fprintln(w, `{}`)
	})

	mux.HandleFunc("/VolumeDriver.Get", func(w http.ResponseWriter, r *http.Request) {
		var pr pluginRequest
		if err := json.NewDecoder(r.Body).Decode(&pr); err != nil {
			http.Error(w, fmt.Sprintf("{\"Error\":\"%s\"}", err.Error()), 500)
			m.ctx.WithError(err).Error("/VolumeDriver.Get: error decoding json")
			return
		}

		m.ctx.WithField("pluginResponse", pr).Debug("/VolumeDriver.Get")

		volMapping, err := m.lsc.Integration().Inspect(
			m.ctx, pr.Name, apiutils.NewStore())
		if err != nil {
			http.Error(w, fmt.Sprintf("{\"Error\":\"%s\"}", err.Error()), 500)
			m.ctx.WithError(err).Error("/VolumeDriver.Get: error getting volume")
			return
		}

		w.Header().Set(
			"Content-Type", "application/vnd.docker.plugins.v1.2+json")
		json.NewEncoder(w).Encode(map[string]apitypes.VolumeMapping{
			"Volume": volMapping,
		})
	})

	mux.HandleFunc("/VolumeDriver.List", func(w http.ResponseWriter, r *http.Request) {
		var pr pluginRequest
		if err := json.NewDecoder(r.Body).Decode(&pr); err != nil {
			http.Error(w, fmt.Sprintf("{\"Error\":\"%s\"}", err.Error()), 500)
			m.ctx.WithError(err).Error("/VolumeDriver.List: error decoding json")
			return
		}

		m.ctx.WithField("pluginResponse", pr).Debug("/VolumeDriver.List")

		volMappings, err := m.lsc.Integration().List(m.ctx, apiutils.NewStore())
		if err != nil {
			http.Error(w, fmt.Sprintf("{\"Error\":\"%s\"}", err.Error()), 500)
			m.ctx.WithError(err).Error("/VolumeDriver.List: error listing volumes")
			return
		}

		w.Header().Set("Content-Type", "application/vnd.docker.plugins.v1.2+json")
		json.NewEncoder(w).Encode(
			map[string][]apitypes.VolumeMapping{"Volumes": volMappings})
	})

	mux.HandleFunc("/VolumeDriver.Capabilities", func(w http.ResponseWriter, r *http.Request) {
		var pr pluginRequest
		if err := json.NewDecoder(r.Body).Decode(&pr); err != nil {
			http.Error(w, fmt.Sprintf("{\"Error\":\"%s\"}", err.Error()), 500)
			m.ctx.WithError(err).Error("/VolumeDriver.Capabilities: error decoding json")
			return
		}

		m.ctx.WithField("pluginResponse", pr).Debug("/VolumeDriver.Capabilities")

		w.Header().Set("Content-Type", "application/vnd.docker.plugins.v1.2+json")
		fmt.Fprintln(w, `{"Capabilities": { "Scope": "global" }}`)
	})

	return mux
}
Exemple #17
0
// Unmount will unmount the specified volume by volumeName or volumeID.
func (d *driver) Unmount(
	ctx types.Context,
	volumeID, volumeName string,
	opts types.Store) (*types.Volume, error) {

	ctx.WithFields(log.Fields{
		"volumeName": volumeName,
		"volumeID":   volumeID,
		"opts":       opts}).Info("unmounting volume")

	if volumeName == "" && volumeID == "" {
		return nil, goof.New("missing volume name or ID")
	}

	vol, err := d.volumeInspectByIDOrName(
		ctx, volumeID, volumeName,
		types.VolAttReqWithDevMapOnlyVolsAttachedToInstance, opts)
	if err != nil {
		return nil, err
	}

	if len(vol.Attachments) == 0 {
		return nil, nil
	}

	client := context.MustClient(ctx)

	inst, err := client.Storage().InstanceInspect(ctx, utils.NewStore())
	if err != nil {
		return nil, goof.New("problem getting instance ID")
	}
	var ma *types.VolumeAttachment
	for _, att := range vol.Attachments {
		if att.InstanceID.ID == inst.InstanceID.ID {
			ma = att
			break
		}
	}

	if ma == nil {
		return nil, goof.New("no attachment found for instance")
	}

	if ma.DeviceName == "" {
		return nil, goof.New("no device name found for attachment")
	}

	mounts, err := client.OS().Mounts(
		ctx, ma.DeviceName, "", opts)
	if err != nil {
		return nil, err
	}

	for _, mount := range mounts {
		ctx.WithField("mount", mount).Debug("retrieved mount")
	}

	if len(mounts) > 0 {
		for _, mount := range mounts {
			ctx.WithField("mount", mount).Debug("unmounting mount point")
			err = client.OS().Unmount(ctx, mount.MountPoint, opts)
			if err != nil {
				return nil, err
			}
		}
	}

	vol, err = client.Storage().VolumeDetach(ctx, vol.ID,
		&types.VolumeDetachOpts{
			Force: opts.GetBool("force"),
			Opts:  utils.NewStore(),
		})
	if err != nil {
		return nil, err
	}

	ctx.WithFields(log.Fields{
		"vol": vol}).Info("unmounted and detached volume")

	return vol, nil
}
Exemple #18
0
// Run runs the executor CLI.
func Run() {

	args := os.Args
	if len(args) < 3 {
		printUsageAndExit()
	}

	d, err := registry.NewStorageExecutor(args[1])
	if err != nil {
		fmt.Fprintf(os.Stderr, "error: %v\n", err)
		os.Exit(1)
	}

	driverName := strings.ToLower(d.Name())

	config, err := apiconfig.NewConfig()
	if err != nil {
		fmt.Fprintf(os.Stderr, "error: %v\n", err)
		os.Exit(1)
	}

	apiconfig.UpdateLogLevel(config)
	ctx := context.Background()

	if err := d.Init(ctx, config); err != nil {
		fmt.Fprintf(os.Stderr, "error: %v\n", err)
		os.Exit(1)
	}

	cmd := cmdRx.FindString(args[2])
	if cmd == "" {
		printUsageAndExit()
	}
	store := utils.NewStore()

	var (
		result   interface{}
		op       string
		exitCode int
	)

	if strings.EqualFold(cmd, apitypes.LSXCmdSupported) {
		op = "supported"
		if dws, ok := d.(apitypes.StorageExecutorWithSupported); ok {
			opResult, opErr := dws.Supported(ctx, store)
			if opErr != nil {
				err = opErr
			} else {
				result = opResult
			}
		} else {
			err = apitypes.ErrNotImplemented
		}
	} else if strings.EqualFold(cmd, apitypes.LSXCmdInstanceID) {
		op = "instance ID"
		opResult, opErr := d.InstanceID(ctx, store)
		if opErr != nil {
			err = opErr
		} else {
			opResult.Driver = driverName
			result = opResult
		}
	} else if strings.EqualFold(cmd, apitypes.LSXCmdNextDevice) {
		op = "next device"
		opResult, opErr := d.NextDevice(ctx, store)
		if opErr != nil && opErr != apitypes.ErrNotImplemented {
			err = opErr
		} else {
			result = opResult
		}
	} else if strings.EqualFold(cmd, apitypes.LSXCmdLocalDevices) {
		if len(args) < 4 {
			printUsageAndExit()
		}
		op = "local devices"
		opResult, opErr := d.LocalDevices(ctx, &apitypes.LocalDevicesOpts{
			ScanType: apitypes.ParseDeviceScanType(args[3]),
			Opts:     store,
		})
		opResult.Driver = driverName
		if opErr != nil {
			err = opErr
		} else {
			result = opResult
		}
	} else if strings.EqualFold(cmd, apitypes.LSXCmdWaitForDevice) {
		if len(args) < 5 {
			printUsageAndExit()
		}
		op = "wait"
		opts := &apitypes.WaitForDeviceOpts{
			LocalDevicesOpts: apitypes.LocalDevicesOpts{
				ScanType: apitypes.ParseDeviceScanType(args[3]),
				Opts:     store,
			},
			Token:   strings.ToLower(args[4]),
			Timeout: utils.DeviceAttachTimeout(args[5]),
		}

		ldl := func() (bool, *apitypes.LocalDevices, error) {
			ldm, err := d.LocalDevices(ctx, &opts.LocalDevicesOpts)
			if err != nil {
				return false, nil, err
			}
			for k := range ldm.DeviceMap {
				if strings.ToLower(k) == opts.Token {
					return true, ldm, nil
				}
			}
			return false, ldm, nil
		}

		var (
			found    bool
			opErr    error
			opResult *apitypes.LocalDevices
			timeoutC = time.After(opts.Timeout)
			tick     = time.Tick(500 * time.Millisecond)
		)

	TimeoutLoop:

		for {
			select {
			case <-timeoutC:
				exitCode = apitypes.LSXExitCodeTimedOut
				break TimeoutLoop
			case <-tick:
				if found, opResult, opErr = ldl(); found || opErr != nil {
					break TimeoutLoop
				}
			}
		}

		if opErr != nil {
			err = opErr
		} else {
			opResult.Driver = driverName
			result = opResult
		}
	}

	if err != nil {
		// if the function is not implemented then exit with
		// apitypes.LSXExitCodeNotImplemented to let callers
		// know that the function is unsupported on this system
		exitCode = 1
		if strings.EqualFold(err.Error(), apitypes.ErrNotImplemented.Error()) {
			exitCode = apitypes.LSXExitCodeNotImplemented
		}
		fmt.Fprintf(os.Stderr,
			"error: error getting %s: %v\n", op, err)
		os.Exit(exitCode)
	}

	switch tr := result.(type) {
	case bool:
		fmt.Fprintf(os.Stdout, "%v", result)
	case string:
		fmt.Fprintln(os.Stdout, result)
	case encoding.TextMarshaler:
		buf, err := tr.MarshalText()
		if err != nil {
			fmt.Fprintf(os.Stderr, "error: error encoding %s: %v\n", op, err)
			os.Exit(1)
		}
		os.Stdout.Write(buf)
	default:
		buf, err := json.Marshal(result)
		if err != nil {
			fmt.Fprintf(os.Stderr, "error: error encoding %s: %v\n", op, err)
			os.Exit(1)
		}
		if isNullBuf(buf) {
			os.Stdout.Write(emptyJSONBuff)
		} else {
			os.Stdout.Write(buf)
		}
	}

	os.Exit(exitCode)
}
func (d *driver) Init(ctx types.Context, config gofig.Config) error {
	logFields := log.Fields{}

	addr := config.GetString(types.ConfigHost)
	d.ctx = ctx.WithValue(context.HostKey, addr)
	d.ctx.Debug("got configured host address")

	proto, lAddr, err := gotil.ParseAddress(addr)
	if err != nil {
		return err
	}

	tlsConfig, err := utils.ParseTLSConfig(
		config, logFields, "libstorage.client")
	if err != nil {
		return err
	}

	host := getHost(proto, lAddr, tlsConfig)
	lsxPath := config.GetString(types.ConfigExecutorPath)
	cliType := types.ParseClientType(config.GetString(types.ConfigClientType))
	disableKeepAlive := config.GetBool(types.ConfigHTTPDisableKeepAlive)

	logFields["host"] = host
	logFields["lsxPath"] = lsxPath
	logFields["clientType"] = cliType
	logFields["disableKeepAlive"] = disableKeepAlive

	httpTransport := &http.Transport{
		Dial: func(string, string) (net.Conn, error) {
			if tlsConfig == nil {
				return net.Dial(proto, lAddr)
			}
			return tls.Dial(proto, lAddr, tlsConfig)
		},
		DisableKeepAlives: disableKeepAlive,
	}

	apiClient := apiclient.New(host, httpTransport)
	logReq := config.GetBool(types.ConfigLogHTTPRequests)
	logRes := config.GetBool(types.ConfigLogHTTPResponses)
	apiClient.LogRequests(logReq)
	apiClient.LogResponses(logRes)

	logFields["enableInstanceIDHeaders"] = EnableInstanceIDHeaders
	logFields["enableLocalDevicesHeaders"] = EnableLocalDevicesHeaders
	logFields["logRequests"] = logReq
	logFields["logResponses"] = logRes

	d.client = client{
		APIClient:    apiClient,
		ctx:          ctx,
		config:       config,
		clientType:   cliType,
		serviceCache: &lss{Store: utils.NewStore()},
	}

	if d.clientType == types.IntegrationClient {

		newIIDCache := utils.NewStore
		dur, err := time.ParseDuration(
			config.GetString(types.ConfigClientCacheInstanceID))
		if err != nil {
			logFields["iidCacheDuration"] = dur.String()
			newIIDCache = func() types.Store {
				return utils.NewTTLStore(dur, true)
			}
		}

		d.lsxCache = &lss{Store: utils.NewStore()}
		d.supportedCache = utils.NewStore()
		d.instanceIDCache = &lss{Store: newIIDCache()}
	}

	d.ctx.WithFields(logFields).Info("created libStorage client")

	if err := d.dial(ctx); err != nil {
		return err
	}

	d.ctx.Info("successefully dialed libStorage server")
	return nil
}