func (c *client) downloadExecutor(ctx types.Context) error { if c.isController() { return utils.NewUnsupportedForClientTypeError( c.clientType, "downloadExecutor") } ctx.Debug("downloading executor") f, err := os.OpenFile( types.LSX.String(), os.O_CREATE|os.O_RDWR|os.O_TRUNC, 0755) if err != nil { return err } defer f.Close() rdr, err := c.APIClient.ExecutorGet(ctx, types.LSX.Name()) n, err := io.Copy(f, rdr) if err != nil { return err } if err := f.Sync(); err != nil { return err } ctx.WithField("bytes", n).Debug("downloaded executor") return nil }
func (c *client) getExecutorChecksum(ctx types.Context) (string, error) { if c.isController() { return "", utils.NewUnsupportedForClientTypeError( c.clientType, "getExecutorChecksum") } ctx.Debug("getting executor checksum") f, err := os.Open(types.LSX.String()) if err != nil { return "", err } defer f.Close() h := md5.New() buf := make([]byte, 1024) for { n, err := f.Read(buf) if err == io.EOF { break } if err != nil { return "", err } if _, err := h.Write(buf[:n]); err != nil { return "", err } } sum := fmt.Sprintf("%x", h.Sum(nil)) ctx.WithField("localChecksum", sum).Debug("got local executor checksum") return sum, nil }
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) }
func (c *client) lsxMutexSignal() error { if c.isController() { return utils.NewUnsupportedForClientTypeError( c.clientType, "lsxMutexSignal") } return os.RemoveAll(lsxMutex) }
func (c *client) NextDevice( ctx types.Context, opts types.Store) (string, error) { if c.isController() { return "", utils.NewUnsupportedForClientTypeError( c.clientType, "NextDevice") } if supported, _ := c.Supported(ctx, opts); !supported { return "", errExecutorNotSupported } ctx = context.RequireTX(ctx.Join(c.ctx)) serviceName, ok := context.ServiceName(ctx) if !ok { return "", goof.New("missing service name") } si, err := c.getServiceInfo(serviceName) if err != nil { return "", err } driverName := si.Driver.Name out, err := c.runExecutor(ctx, driverName, types.LSXCmdNextDevice) if err != nil { return "", err } ctx.Debug("xli nextdevice success") return gotil.Trim(string(out)), nil }
func (c *client) Instances( ctx types.Context) (map[string]*types.Instance, error) { if c.isController() { return nil, utils.NewUnsupportedForClientTypeError( c.clientType, "Instances") } ctx = c.withAllInstanceIDs(c.requireCtx(ctx)) return c.APIClient.Instances(ctx) }
func (c *client) ExecutorGet( ctx types.Context, name string) (io.ReadCloser, error) { if c.isController() { return nil, utils.NewUnsupportedForClientTypeError( c.clientType, "ExecutorGet") } ctx = c.requireCtx(ctx) return c.APIClient.ExecutorGet(ctx, name) }
func (c *client) Supported( ctx types.Context, opts types.Store) (bool, error) { if c.isController() { return false, utils.NewUnsupportedForClientTypeError( c.clientType, "Supported") } ctx = context.RequireTX(ctx.Join(c.ctx)) serviceName, ok := context.ServiceName(ctx) if !ok { return false, goof.New("missing service name") } si, err := c.getServiceInfo(serviceName) if err != nil { return false, err } driverName := strings.ToLower(si.Driver.Name) // check to see if the driver's executor is supported on this host if ok := c.supportedCache.IsSet(driverName); ok { return c.supportedCache.GetBool(driverName), nil } out, err := c.runExecutor(ctx, driverName, types.LSXCmdSupported) if err != nil { if err == types.ErrNotImplemented { ctx.WithField("serviceDriver", driverName).Warn( "supported cmd not implemented") c.supportedCache.Set(driverName, true) ctx.WithField("supported", true).Debug("cached supported flag") return true, nil } return false, err } if len(out) == 0 { return false, nil } out = bytes.TrimSpace(out) b, err := strconv.ParseBool(string(out)) if err != nil { return false, err } c.supportedCache.Set(driverName, b) ctx.WithField("supported", b).Debug("cached supported flag") return b, nil }
func (c *client) runExecutor( ctx types.Context, args ...string) ([]byte, error) { if c.isController() { return nil, utils.NewUnsupportedForClientTypeError( c.clientType, "runExecutor") } ctx.Debug("waiting on executor lock") if err := c.lsxMutexWait(); err != nil { return nil, err } defer func() { ctx.Debug("signalling executor lock") if err := c.lsxMutexSignal(); err != nil { panic(err) } }() lsxBin := types.LSX.String() cmd := exec.Command(lsxBin, args...) cmd.Env = os.Environ() configEnvVars := c.config.EnvVars() for _, cev := range configEnvVars { // ctx.WithField("value", cev).Debug("set executor env var") cmd.Env = append(cmd.Env, cev) } out, err := cmd.Output() if exitError, ok := err.(*exec.ExitError); ok { exitCode := exitError.Sys().(syscall.WaitStatus).ExitStatus() switch exitCode { case types.LSXExitCodeNotImplemented: return nil, types.ErrNotImplemented case types.LSXExitCodeTimedOut: return nil, types.ErrTimedOut } return nil, goof.WithFieldsE( map[string]interface{}{ "lsx": lsxBin, "args": args, }, "error executing xcli", err) } return out, err }
func (c *client) InstanceInspect( ctx types.Context, service string) (*types.Instance, error) { if c.isController() { return nil, utils.NewUnsupportedForClientTypeError( c.clientType, "InstanceInspect") } ctx = c.withInstanceID(c.requireCtx(ctx), service) i, err := c.APIClient.InstanceInspect(ctx, service) if err != nil { return nil, err } return i, nil }
func (c *client) lsxMutexWait() error { if c.isController() { return utils.NewUnsupportedForClientTypeError( c.clientType, "lsxMutexWait") } for { f, err := os.OpenFile(lsxMutex, os.O_CREATE|os.O_EXCL, 0644) if err != nil { time.Sleep(time.Millisecond * 500) continue } return f.Close() } }
func (d *driver) InstanceInspect( ctx types.Context, opts types.Store) (*types.Instance, error) { if d.isController() { return nil, utils.NewUnsupportedForClientTypeError( d.clientType, "InstanceInspect") } serviceName, ok := context.ServiceName(ctx) if !ok { return nil, goof.New("missing service name") } return d.client.InstanceInspect(ctx, serviceName) }
func (c *client) updateExecutor(ctx types.Context) error { if c.isController() { return utils.NewUnsupportedForClientTypeError( c.clientType, "updateExecutor") } ctx.Debug("updating executor") lsxi := c.lsxCache.GetExecutorInfo(types.LSX.Name()) if lsxi == nil { return goof.WithField("lsx", types.LSX, "unknown executor") } ctx.Debug("waiting on executor lock") if err := c.lsxMutexWait(); err != nil { return err } defer func() { ctx.Debug("signalling executor lock") if err := c.lsxMutexSignal(); err != nil { panic(err) } }() if !types.LSX.Exists() { ctx.Debug("executor does not exist, download executor") return c.downloadExecutor(ctx) } ctx.Debug("executor exists, getting local checksum") checksum, err := c.getExecutorChecksum(ctx) if err != nil { return err } if lsxi.MD5Checksum != checksum { ctx.WithFields(log.Fields{ "remoteChecksum": lsxi.MD5Checksum, "localChecksum": checksum, }).Debug("executor checksums do not match, download executor") return c.downloadExecutor(ctx) } return nil }
func (c *client) VolumeDetachAll( ctx types.Context, request *types.VolumeDetachRequest) (types.ServiceVolumeMap, error) { if c.isController() { return nil, utils.NewUnsupportedForClientTypeError( c.clientType, "VolumeDetachAll") } ctx = c.withAllInstanceIDs(c.requireCtx(ctx)) ctxA, err := c.withAllLocalDevices(ctx) if err != nil { return nil, err } ctx = ctxA return c.APIClient.VolumeDetachAll(ctx, request) }
func (c *client) Executors( ctx types.Context) (map[string]*types.ExecutorInfo, error) { if c.isController() { return nil, utils.NewUnsupportedForClientTypeError( c.clientType, "Executors") } ctx = c.requireCtx(ctx) lsxInfo, err := c.APIClient.Executors(ctx) if err != nil { return nil, err } for k, v := range lsxInfo { c.lsxCache.Set(k, v) } return lsxInfo, nil }
func (c *client) WaitForDevice( ctx types.Context, opts *types.WaitForDeviceOpts) (bool, *types.LocalDevices, error) { if c.isController() { return false, nil, utils.NewUnsupportedForClientTypeError( c.clientType, "WaitForDevice") } if supported, _ := c.Supported(ctx, opts.Opts); !supported { return false, nil, errExecutorNotSupported } ctx = context.RequireTX(ctx.Join(c.ctx)) serviceName, ok := context.ServiceName(ctx) if !ok { return false, nil, goof.New("missing service name") } si, err := c.getServiceInfo(serviceName) if err != nil { return false, nil, err } driverName := si.Driver.Name out, err := c.runExecutor( ctx, driverName, types.LSXCmdWaitForDevice, opts.ScanType.String(), opts.Token, opts.Timeout.String()) if err != types.ErrTimedOut { return false, nil, err } matched := err == nil ld, err := unmarshalLocalDevices(ctx, out) if err != nil { return false, nil, err } ctx.Debug("xli waitfordevice success") return matched, ld, nil }
func (c *client) VolumeDetach( ctx types.Context, service string, volumeID string, request *types.VolumeDetachRequest) (*types.Volume, error) { if c.isController() { return nil, utils.NewUnsupportedForClientTypeError( c.clientType, "VolumeDetach") } ctx = c.withInstanceID(c.requireCtx(ctx), service) ctxA, err := c.withAllLocalDevices(ctx) if err != nil { return nil, err } ctx = ctxA return c.APIClient.VolumeDetach(ctx, service, volumeID, request) }
func (c *client) LocalDevices( ctx types.Context, opts *types.LocalDevicesOpts) (*types.LocalDevices, error) { if c.isController() { return nil, utils.NewUnsupportedForClientTypeError( c.clientType, "LocalDevices") } if supported, _ := c.Supported(ctx, opts.Opts); !supported { return nil, errExecutorNotSupported } ctx = context.RequireTX(ctx.Join(c.ctx)) serviceName, ok := context.ServiceName(ctx) if !ok { return nil, goof.New("missing service name") } si, err := c.getServiceInfo(serviceName) if err != nil { return nil, err } driverName := si.Driver.Name out, err := c.runExecutor( ctx, driverName, types.LSXCmdLocalDevices, opts.ScanType.String()) if err != nil { return nil, err } ld, err := unmarshalLocalDevices(ctx, out) if err != nil { return nil, err } ctx.Debug("xli localdevices success") return ld, nil }
func (d *driver) VolumeDetach( ctx types.Context, volumeID string, opts *types.VolumeDetachOpts) (*types.Volume, error) { if d.isController() { return nil, utils.NewUnsupportedForClientTypeError( d.clientType, "VolumeDetach") } ctx = d.requireCtx(ctx) serviceName, ok := context.ServiceName(ctx) if !ok { return nil, goof.New("missing service name") } req := &types.VolumeDetachRequest{ Force: opts.Force, Opts: opts.Opts.Map(), } return d.client.VolumeDetach(ctx, serviceName, volumeID, req) }
func (c *client) InstanceID( ctx types.Context, opts types.Store) (*types.InstanceID, error) { if c.isController() { return nil, utils.NewUnsupportedForClientTypeError( c.clientType, "InstanceID") } if supported, _ := c.Supported(ctx, opts); !supported { return nil, errExecutorNotSupported } ctx = context.RequireTX(ctx.Join(c.ctx)) serviceName, ok := context.ServiceName(ctx) if !ok { return nil, goof.New("missing service name") } si, err := c.getServiceInfo(serviceName) if err != nil { return nil, err } driverName := strings.ToLower(si.Driver.Name) // check to see if the driver's instance ID is cached if iid := c.instanceIDCache.GetInstanceID(driverName); iid != nil { return iid, nil } out, err := c.runExecutor(ctx, driverName, types.LSXCmdInstanceID) if err != nil { return nil, err } iid := &types.InstanceID{} if err := iid.UnmarshalText(out); err != nil { return nil, err } ctx = ctx.WithValue(context.InstanceIDKey, iid) if iid.HasMetadata() { ctx.Debug("sending instanceID in API.InstanceInspect call") instance, err := c.InstanceInspect(ctx, serviceName) if err != nil { return nil, err } ctx.Debug("received instanceID from API.InstanceInspect call") iid.ID = instance.InstanceID.ID iid.Fields = instance.InstanceID.Fields iid.DeleteMetadata() } c.instanceIDCache.Set(driverName, iid) ctx.Debug("cached instanceID") ctx.Debug("xli instanceID success") return iid, nil }