示例#1
0
func TestMountCommandRemoteCache(t *testing.T) {
	Convey("Given a process error is returned", t, func() {
		fakeKlient := &testutil.FakeKlient{
			ReturnRemoteCacheErr: util.KiteErrorf(kiteerrortypes.ProcessError, "Err msg"),
		}
		var b bytes.Buffer
		c := MountCommand{
			Stdout: &b,
			Klient: fakeKlient,
		}

		Convey("It should print RemoteProcessFailed", func() {
			err := c.callRemoteCache(req.Cache{}, func(*dnode.Partial) {})
			So(err, ShouldNotBeNil)
			So(b.String(), ShouldContainSubstring, "A requested process on the remote")
		})
	})

	Convey("Given a non-process error is returned", t, func() {
		fakeKlient := &testutil.FakeKlient{
			ReturnRemoteCacheErr: errors.New("Err msg"),
		}
		var b bytes.Buffer
		c := MountCommand{
			Stdout: &b,
			Klient: fakeKlient,
		}

		Convey("It should not print RemoteProcessFailed", func() {
			err := c.callRemoteCache(req.Cache{}, func(*dnode.Partial) {})
			So(err, ShouldNotBeNil)
			So(b.String(), ShouldNotContainSubstring, "A requested process on the remote")
		})
	})
}
示例#2
0
文件: machine.go 项目: koding/koding
// Dial dials the internal dialer.
func (m *Machine) Dial() (err error) {
	// set the resulting dial based on the success of the Dial method.
	// Note that repeated calls to Dial creates a new XHR transport, so failing
	// dial on an existing sets a new local client transport session.
	// In otherwords, a failed dial will result in a not-connected sessuin. Due to
	// this, we track the state of the dialed by result, regardless of original state.
	defer func() {
		m.hasDialed = err == nil
	}()

	if m.Transport == nil {
		m.Log.Error("Dial was attempted with a nil Transport")
		return util.KiteErrorf(
			kiteerrortypes.MachineNotValidYet, "Machine.Transport is nil",
		)
	}

	// Log the failure here, because this logger has machine context.
	if err := m.Transport.Dial(); err != nil {
		m.Log.Error("Dialer returned error. err:%s", err)
		return util.NewKiteError(kiteerrortypes.DialingFailed, err)
	}

	return nil
}
示例#3
0
文件: status.go 项目: koding/koding
// MachineStatus dials the given machine name, pings it, and returns ok or not.
// Custom type errors for any problems encountered.
func (s *Status) MachineStatus(name string) error {
	machine, err := s.MachineGetter.GetMachine(name)
	if err != nil {
		return err
	}

	// Try and ping it directly via http. This lets us know if the machine is
	// reachable, without dealing with any kite issues.
	if _, err := s.HTTPClient.Get(fmt.Sprintf("http://%s:56789/kite", machine.IP)); err != nil {
		return util.KiteErrorf(
			kiteerrortypes.MachineUnreachable,
			"Machine unreachable. host:%s, err:%s",
			machine.IP, err,
		)
	}

	if err := machine.DialOnce(); err != nil {
		return s.handleKiteErr(err)
	}

	// We have to use klient.info here, because we're trying to catch the
	// TokenIsNotValidYet error. kite.ping does not use auth, and does not show
	// that error as a result. klient.info does use auth, and doesn't reset klient
	// usage. So it seems like an okay method for this.
	//
	// In the future, we should probably add auth'd ping to klient, to avoid side
	// effects.
	if _, err := machine.Tell("klient.info"); err != nil {
		return s.handleKiteErr(err)
	}

	return nil
}
示例#4
0
文件: machine.go 项目: koding/koding
// CheckValid checks if this Machine is missing any required fields. Fields can be
// missing because we store all machines, online or offline, but Kontrol doesn't
// return any information about offline machines. We don't have Kites, for offline
// machines. Because of this, a Machine may exist, but not be usable.
//
// Eg, you could attempt to mount an offline machine - if it hasn't connected
// to kontrol since klient restarted, it won't be valid.
//
// This is a common check, and should be performed before using a machine.
func (m *Machine) CheckValid() error {
	if m.Transport == nil {
		return util.KiteErrorf(
			kiteerrortypes.MachineNotValidYet, "Machine.Transport is nil",
		)
	}

	if m.KiteTracker == nil {
		return util.KiteErrorf(
			kiteerrortypes.MachineNotValidYet, "Machine.KiteTracker is nil",
		)
	}

	if m.Log == nil {
		return util.KiteErrorf(kiteerrortypes.MachineNotValidYet, "Machine.Log is nil.")
	}

	return nil
}
示例#5
0
文件: machine.go 项目: koding/koding
// TellWithTimeout uses the Kite protocol (with a dnode response) to communicate
// with this machine.
func (m *Machine) TellWithTimeout(method string, timeout time.Duration, args ...interface{}) (*dnode.Partial, error) {
	if m.Transport == nil {
		m.Log.Error("TellWithTimeout was attempted with a nil Transport")
		return nil, util.KiteErrorf(
			kiteerrortypes.MachineNotValidYet, "Machine.Transport is nil",
		)
	}

	if err := m.DialOnce(); err != nil {
		return nil, err
	}

	return m.Transport.TellWithTimeout(method, timeout, args...)
}
示例#6
0
文件: mounter.go 项目: koding/koding
// IsConfigured checks the Mounter fields to ensure (as best as it can) that
// there are no missing required fields, such as mock fields and etc.
func (m *Mounter) IsConfigured() error {
	if m.Options.Name == "" {
		return util.KiteErrorf(kiteerrortypes.MissingArgument, "Missing Name")
	}

	if m.Options.LocalPath == "" {
		return util.KiteErrorf(kiteerrortypes.MissingArgument, "Missing LocalPath")
	}

	if m.IP == "" {
		return util.KiteErrorf(kiteerrortypes.MissingArgument, "Missing IP")
	}

	if m.Options.PrefetchAll && m.Options.CachePath == "" {
		return util.KiteErrorf(kiteerrortypes.MissingArgument,
			"Using PrefetchAll but missing CachePath")
	}

	if m.Machine == nil {
		return util.KiteErrorf(kiteerrortypes.MissingArgument, "Missing Machine")
	}

	if m.KiteTracker == nil {
		return util.KiteErrorf(kiteerrortypes.MissingArgument, "Missing KiteTracker")
	}

	if m.Transport == nil {
		return util.KiteErrorf(kiteerrortypes.MissingArgument, "Missing Transport")
	}

	if m.PathUnmounter == nil {
		return util.KiteErrorf(kiteerrortypes.MissingArgument, "Missing Unmounter")
	}

	if m.Log == nil {
		return util.KiteErrorf(kiteerrortypes.MissingArgument, "Missing Log")
	}

	return nil
}
示例#7
0
文件: client.go 项目: koding/koding
// Publish method takes arbitrary event data, and passes it to
// any functions which have subscribed via `client.Subscribe`. The
// only required value is a single `eventName` value. Only listeners
// of the given eventName will be called back with the data.
//
// Examples:
//
//		{
//			"eventName": "fullscreen"
//		}
//
// 		{
// 			"eventName": "openFiles",
// 			"files": ["file1.txt", "file2.txt"]
// 		}
//
// The only response is an error, if any.
func (c *PubSub) Publish(r *kite.Request) (interface{}, error) {
	// Parse the eventName from the incoming data. Note that this method
	// accepts any data beyond eventName, so that this method is as generic
	// as possible.
	var params PublishRequest

	if r.Args == nil {
		return nil, errors.New("client.Publish: Arguments are not passed")
	}

	// The raw response that we'll be publishing to the Client. We're
	// sending the Raw response because, as seen in the params struct,
	// we don't know the data format being passed to client.Publish.
	resp := r.Args.One()

	err := r.Args.One().Unmarshal(&params)
	if err != nil || params.EventName == "" {
		c.Log.Info("client.Publish: Unknown param format %q\n", resp)
		return nil, errors.New("client.Publish: eventName is required")
	}

	c.subMu.Lock()
	defer c.subMu.Unlock()

	subs, ok := c.Subscriptions[params.EventName]
	if !ok {
		return nil, util.KiteErrorf(kiteerrortypes.NoSubscribers,
			"client.Publish: No client.Subscribers found for %q",
			params.EventName)
	}

	// This condition should never occur - Subscription() should remove
	// all of the subs manually. If it doesn't, something wrong occured
	// during the removal attempt.
	if len(subs) == 0 {
		c.Log.Info("client.Publish: The event %q was found empty, when it should have  been removed\n", params.EventName)
		return nil, fmt.Errorf("client.Publish: No client.Subscribers found for %q", params.EventName)
	}

	c.Log.Info("client.Publish: Publishing data for event %q\n", params.EventName)
	for _, sub := range subs {
		sub.Call(resp)
	}

	return nil, nil
}
示例#8
0
文件: mounter.go 项目: koding/koding
func (m *Mounter) Mount() (*Mount, error) {
	if err := m.IsConfigured(); err != nil {
		m.Log.Error("Mounter improperly configured. err:%s", err)
		return nil, err
	}

	// Mount() requires a MountAdder
	if m.MountAdder == nil {
		return nil, util.KiteErrorf(kiteerrortypes.MissingArgument, "Missing MountAdder")
	}

	var syncOpts rsync.SyncIntervalOpts
	if m.Intervaler != nil {
		syncOpts = m.Intervaler.SyncIntervalOpts()
	} else {
		m.Log.Warning("Unable to locate Intervaler")
	}

	mount := &Mount{
		MountFolder:      m.Options,
		MountName:        m.Options.Name,
		IP:               m.IP,
		SyncIntervalOpts: syncOpts,
		EventSub:         m.EventSub,
	}

	if m.Options.OneWaySyncMount {
		mount.Type = SyncMount
	} else {
		mount.Type = FuseMount
	}

	mount.Log = MountLogger(mount, m.Log)

	if err := m.MountExisting(mount); err != nil {
		return nil, err
	}

	if err := m.MountAdder.AddMount(mount); err != nil {
		return nil, err
	}

	return mount, nil
}
示例#9
0
文件: mounter.go 项目: koding/koding
	//
	// If left too large, the Kernel (in OSX, for example) will fail fs ops by printing
	// "Socket is not connected" to the user. So if that message is seen, this value
	// likely needs to be lowered.
	fuseTellTimeout = 55 * time.Second

	// Messages displayed to the user about the machines status.
	autoRemountFailed = "Error auto-mounting. Please unmount & mount again."
	autoRemounting    = "Remounting after extended disconnect. Please wait..."
)

var (
	// ErrRemotePathDoesNotExist is returned when the remote path does not exist,
	// or is not a dir.
	ErrRemotePathDoesNotExist = util.KiteErrorf(
		kiteerrortypes.RemotePathDoesNotExist,
		"The RemotePath either does not exist, or is not a dir",
	)
)

// MounterTransport is the transport that the Mounter uses to communicate with
// the remote machine.
type MounterTransport interface {
	Tell(string, ...interface{}) (*dnode.Partial, error)
	TellWithTimeout(string, time.Duration, ...interface{}) (*dnode.Partial, error)
}

// Mounter is responsible for actually mounting fuse mounts from Klient.
type Mounter struct {
	Log logging.Logger

	// The options for this Mounter, such as LocalFolder, etc.
示例#10
0
func TestSSHCommand(t *testing.T) {
	Convey("", t, func() {
		tempSSHDir, err := ioutil.TempDir("", "")
		So(err, ShouldBeNil)
		defer os.Remove(tempSSHDir)

		teller := newFakeTransport()
		s := ssh.SSHCommand{
			SSHKey: &ssh.SSHKey{
				Log:     testutil.DiscardLogger,
				KeyPath: tempSSHDir,
				KeyName: "key",
				// Create a klient, with the fake transport to satisfy the Teller interface.
				Klient: &klient.Klient{
					Teller: teller,
				},
			},
		}

		Convey("Given PrepareForSSH is called", func() {
			Convey("When it returns a dialing error", func() {
				kiteErr := util.KiteErrorf(
					kiteerrortypes.DialingFailed, "Failed to dial.",
				)
				teller.TripErrors["remote.sshKeysAdd"] = kiteErr
				teller.TripResponses["remote.currentUsername"] = &dnode.Partial{
					Raw: []byte(`"foo"`),
				}

				Convey("It should return ErrRemoteDialingFailed", func() {
					So(s.PrepareForSSH("foo"), ShouldEqual, kiteErr)
				})
			})
		})

		Convey("It should return public key path", func() {
			key := s.PublicKeyPath()
			So(key, ShouldEqual, path.Join(tempSSHDir, "key.pub"))
		})

		Convey("It should return private key path", func() {
			key := s.PrivateKeyPath()
			So(key, ShouldEqual, path.Join(tempSSHDir, "key"))
		})

		Convey("It should return error if invalid key exists", func() {
			err := ioutil.WriteFile(s.PrivateKeyPath(), []byte("a"), 0700)
			So(err, ShouldBeNil)

			err = ioutil.WriteFile(s.PublicKeyPath(), []byte("a"), 0700)
			So(err, ShouldBeNil)

			err = s.PrepareForSSH("name")
			So(err, ShouldNotBeNil)
			So(os.IsExist(err), ShouldBeFalse)
		})

		Convey("It should create ssh folder if it doesn't exist", func() {
			teller.TripResponses["remote.currentUsername"] = &dnode.Partial{
				Raw: []byte(`"foo"`),
			}
			So(s.PrepareForSSH("name"), ShouldBeNil)

			_, err := os.Stat(s.KeyPath)
			So(err, ShouldBeNil)
		})

		Convey("It generates and saves key to remote if key doesn't exist", func() {
			teller.TripResponses["remote.currentUsername"] = &dnode.Partial{
				Raw: []byte(`"foo"`),
			}
			err := s.PrepareForSSH("name")
			So(err, ShouldBeNil)

			firstContents, err := ioutil.ReadFile(s.PublicKeyPath())
			So(err, ShouldBeNil)

			publicExists := s.PublicKeyExists()
			So(publicExists, ShouldBeTrue)

			privateExists := s.PrivateKeyExists()
			So(privateExists, ShouldBeTrue)

			Convey("It returns key if it exists", func() {
				err := s.PrepareForSSH("name")
				So(err, ShouldBeNil)

				secondContents, err := ioutil.ReadFile(s.PublicKeyPath())
				So(err, ShouldBeNil)

				So(string(firstContents), ShouldEqual, string(secondContents))
			})
		})
	})
}
示例#11
0
文件: client.go 项目: koding/koding
import (
	"errors"
	"fmt"
	"sync"

	"koding/klient/kiteerrortypes"
	"koding/klient/util"

	"github.com/koding/kite"
	"github.com/koding/kite/dnode"
)

var (
	// ErrSubNotFound is returned from Unsubscribe if the given sub id cannot be found.
	ErrSubNotFound = util.KiteErrorf(
		kiteerrortypes.SubNotFound, "The given subscription id cannot be found.",
	)
)

// SubscribeResponse is the response type of the `client.Subscribe` method.
type SubscribeResponse struct {
	ID int `json:"id"`
}

// SubscribeRequest is the request type for the `client.Subscribe` method.
type SubscribeRequest struct {
	EventName string         `json:"eventName"`
	OnPublish dnode.Function `json:"onPublish"`
}

// UnsubscribeRequest is the request type for the `client.Unsubscribe` method.
示例#12
0
文件: machine.go 项目: koding/koding
	// The machine is remounting
	//
	// TODO: Move this type to a mount specific status, once we support multiple
	// mounts.
	MachineRemounting
)

const (
	// The duration between IsConnected() checks performed by WaitUntilOnline()
	waitUntilOnlinePause = 5 * time.Second
)

var (
	// Returned by various methods if the requested machine cannot be found.
	ErrMachineNotFound error = util.KiteErrorf(
		kiteerrortypes.MachineNotFound, "Machine not found",
	)

	// Returned by various methods if the requested action is locked.
	ErrMachineActionIsLocked error = util.KiteErrorf(
		kiteerrortypes.MachineActionIsLocked, "Machine action is locked",
	)
)

// Transport is a Kite compatible interface for Machines.
type Transport interface {
	Dial() error
	Tell(string, ...interface{}) (*dnode.Partial, error)
	TellWithTimeout(string, time.Duration, ...interface{}) (*dnode.Partial, error)
}
示例#13
0
文件: mount.go 项目: koding/koding
import (
	"koding/fuseklient"
	"koding/klient/kiteerrortypes"
	"koding/klient/remote/kitepinger"
	"koding/klient/remote/req"
	"koding/klient/remote/rsync"
	"koding/klient/util"

	"github.com/koding/logging"
)

var (
	// Returned by various methods if the requested mount cannot be found.
	ErrMountNotFound error = util.KiteErrorf(
		kiteerrortypes.MountNotFound, "Mount not found",
	)
)

type MountType int

const (
	UnknownMount MountType = iota
	FuseMount
	SyncMount
)

// Mount stores information about mounted folders, and is both with
// to various Remote.* kite methods as well as being saved in
// klient's storage.
type Mount struct {
示例#14
0
文件: machines.go 项目: koding/koding
	// The key used to store machines in the database, allowing machine / machinemeta
	// to persist between Klient restarts.
	machinesStorageKey = "machines"
)

// 15 names to be used to identify machines.
var sourceMachineNames = []string{
	"apple", "orange", "banana", "grape", "coconut", "peach", "mango", "date",
	"kiwi", "lemon", "squash", "jackfruit", "raisin", "tomato", "quince",
}

var (
	// ErrMachineAlreadyAdded is returned if the given machine instance is already
	// added to this Machines struct.
	ErrMachineAlreadyAdded = util.KiteErrorf(
		kiteerrortypes.MachineAlreadyAdded, "Machine already added.",
	)

	// ErrMachineDuplicate is returned if the given machine has unique fields
	// matching the given machine, such as kite url or name, meaning that the
	// machines could get confused in future usage.
	ErrMachineDuplicate error = util.KiteErrorf(
		kiteerrortypes.MachineDuplicate, "Machine has unique fields that match another machine.",
	)
)

// Machines is responsible for storing and querying the *Machine(s)
type Machines struct {
	Log logging.Logger

	// The internal list of mounts