github.com/fanux/shipyard@v0.0.0-20161009071005-6515ce223235/controller/manager/manager.go (about)

     1  package manager
     2  
     3  import (
     4  	"bytes"
     5  	"encoding/json"
     6  	"errors"
     7  	"fmt"
     8  	"net"
     9  	"net/http"
    10  	"strings"
    11  	"sync"
    12  	"time"
    13  
    14  	log "github.com/Sirupsen/logrus"
    15  	"github.com/gorilla/sessions"
    16  	"github.com/samalba/dockerclient"
    17  	"github.com/shipyard/shipyard"
    18  	"github.com/shipyard/shipyard/auth"
    19  	"github.com/shipyard/shipyard/dockerhub"
    20  	"github.com/shipyard/shipyard/version"
    21  	r "gopkg.in/dancannon/gorethink.v2"
    22  )
    23  
    24  const (
    25  	tblNameConfig      = "config"
    26  	tblNameEvents      = "events"
    27  	tblNameAccounts    = "accounts"
    28  	tblNameRoles       = "roles"
    29  	tblNameServiceKeys = "service_keys"
    30  	tblNameExtensions  = "extensions"
    31  	tblNameWebhookKeys = "webhook_keys"
    32  	tblNameRegistries  = "registries"
    33  	tblNameConsole     = "console"
    34  	storeKey           = "shipyard"
    35  	trackerHost        = "http://tracker.shipyard-project.com"
    36  	NodeHealthUp       = "up"
    37  	NodeHealthDown     = "down"
    38  )
    39  
    40  var (
    41  	ErrLoginFailure               = errors.New("invalid username or password")
    42  	ErrAccountExists              = errors.New("account already exists")
    43  	ErrAccountDoesNotExist        = errors.New("account does not exist")
    44  	ErrRoleDoesNotExist           = errors.New("role does not exist")
    45  	ErrNodeDoesNotExist           = errors.New("node does not exist")
    46  	ErrServiceKeyDoesNotExist     = errors.New("service key does not exist")
    47  	ErrInvalidAuthToken           = errors.New("invalid auth token")
    48  	ErrExtensionDoesNotExist      = errors.New("extension does not exist")
    49  	ErrWebhookKeyDoesNotExist     = errors.New("webhook key does not exist")
    50  	ErrRegistryDoesNotExist       = errors.New("registry does not exist")
    51  	ErrConsoleSessionDoesNotExist = errors.New("console session does not exist")
    52  	store                         = sessions.NewCookieStore([]byte(storeKey))
    53  )
    54  
    55  type (
    56  	DefaultManager struct {
    57  		storeKey         string
    58  		database         string
    59  		authKey          string
    60  		session          *r.Session
    61  		authenticator    auth.Authenticator
    62  		store            *sessions.CookieStore
    63  		client           *dockerclient.DockerClient
    64  		disableUsageInfo bool
    65  	}
    66  
    67  	ScaleResult struct {
    68  		Scaled []string
    69  		Errors []string
    70  	}
    71  
    72  	Manager interface {
    73  		Accounts() ([]*auth.Account, error)
    74  		Account(username string) (*auth.Account, error)
    75  		Authenticate(username, password string) (bool, error)
    76  		GetAuthenticator() auth.Authenticator
    77  		SaveAccount(account *auth.Account) error
    78  		DeleteAccount(account *auth.Account) error
    79  		Roles() ([]*auth.ACL, error)
    80  		Role(name string) (*auth.ACL, error)
    81  		Store() *sessions.CookieStore
    82  		StoreKey() string
    83  		Container(id string) (*dockerclient.ContainerInfo, error)
    84  		ScaleContainer(id string, numInstances int) ScaleResult
    85  		SaveServiceKey(key *auth.ServiceKey) error
    86  		RemoveServiceKey(key string) error
    87  		SaveEvent(event *shipyard.Event) error
    88  		Events(limit int) ([]*shipyard.Event, error)
    89  		PurgeEvents() error
    90  		ServiceKey(key string) (*auth.ServiceKey, error)
    91  		ServiceKeys() ([]*auth.ServiceKey, error)
    92  		NewAuthToken(username string, userAgent string) (*auth.AuthToken, error)
    93  		VerifyAuthToken(username, token string) error
    94  		VerifyServiceKey(key string) error
    95  		NewServiceKey(description string) (*auth.ServiceKey, error)
    96  		ChangePassword(username, password string) error
    97  		WebhookKey(key string) (*dockerhub.WebhookKey, error)
    98  		WebhookKeys() ([]*dockerhub.WebhookKey, error)
    99  		NewWebhookKey(image string) (*dockerhub.WebhookKey, error)
   100  		SaveWebhookKey(key *dockerhub.WebhookKey) error
   101  		DeleteWebhookKey(id string) error
   102  		DockerClient() *dockerclient.DockerClient
   103  
   104  		Nodes() ([]*shipyard.Node, error)
   105  		Node(name string) (*shipyard.Node, error)
   106  
   107  		AddRegistry(registry *shipyard.Registry) error
   108  		RemoveRegistry(registry *shipyard.Registry) error
   109  		Registries() ([]*shipyard.Registry, error)
   110  		Registry(name string) (*shipyard.Registry, error)
   111  
   112  		CreateConsoleSession(c *shipyard.ConsoleSession) error
   113  		RemoveConsoleSession(c *shipyard.ConsoleSession) error
   114  		ConsoleSession(token string) (*shipyard.ConsoleSession, error)
   115  		ValidateConsoleSessionToken(containerId, token string) bool
   116  	}
   117  )
   118  
   119  func NewManager(addr string, database string, authKey string, client *dockerclient.DockerClient, disableUsageInfo bool, authenticator auth.Authenticator) (Manager, error) {
   120  	log.Debug("setting up rethinkdb session")
   121  	session, err := r.Connect(r.ConnectOpts{
   122  		Address:  addr,
   123  		Database: database,
   124  		AuthKey:  authKey,
   125  	})
   126  	if err != nil {
   127  		return nil, err
   128  	}
   129  	log.Info("checking database")
   130  
   131  	r.DBCreate(database).Run(session)
   132  	m := &DefaultManager{
   133  		database:         database,
   134  		authKey:          authKey,
   135  		session:          session,
   136  		authenticator:    authenticator,
   137  		store:            store,
   138  		client:           client,
   139  		storeKey:         storeKey,
   140  		disableUsageInfo: disableUsageInfo,
   141  	}
   142  	m.initdb()
   143  	m.init()
   144  	return m, nil
   145  }
   146  
   147  func (m DefaultManager) Store() *sessions.CookieStore {
   148  	return m.store
   149  }
   150  
   151  func (m DefaultManager) DockerClient() *dockerclient.DockerClient {
   152  	return m.client
   153  }
   154  
   155  func (m DefaultManager) StoreKey() string {
   156  	return m.storeKey
   157  }
   158  
   159  func (m DefaultManager) initdb() {
   160  	// create tables if needed
   161  	tables := []string{tblNameConfig, tblNameEvents, tblNameAccounts, tblNameRoles, tblNameConsole, tblNameServiceKeys, tblNameRegistries, tblNameExtensions, tblNameWebhookKeys}
   162  	for _, tbl := range tables {
   163  		_, err := r.Table(tbl).Run(m.session)
   164  		if err != nil {
   165  			if _, err := r.DB(m.database).TableCreate(tbl).Run(m.session); err != nil {
   166  				log.Fatalf("error creating table: %s", err)
   167  			}
   168  		}
   169  	}
   170  }
   171  
   172  func (m DefaultManager) init() error {
   173  	// anonymous usage info
   174  	go m.usageReport()
   175  	return nil
   176  }
   177  
   178  func (m DefaultManager) logEvent(eventType, message string, tags []string) {
   179  	evt := &shipyard.Event{
   180  		Type:    eventType,
   181  		Time:    time.Now(),
   182  		Message: message,
   183  		Tags:    tags,
   184  	}
   185  
   186  	if err := m.SaveEvent(evt); err != nil {
   187  		log.Errorf("error logging event: %s", err)
   188  	}
   189  }
   190  
   191  func (m DefaultManager) usageReport() {
   192  	if m.disableUsageInfo {
   193  		return
   194  	}
   195  	m.uploadUsage()
   196  	t := time.NewTicker(1 * time.Hour).C
   197  	for {
   198  		select {
   199  		case <-t:
   200  			go m.uploadUsage()
   201  		}
   202  	}
   203  }
   204  
   205  func (m DefaultManager) uploadUsage() {
   206  	id := "anon"
   207  	ifaces, err := net.Interfaces()
   208  	if err == nil {
   209  		for _, iface := range ifaces {
   210  			if iface.Name != "lo" {
   211  				hw := iface.HardwareAddr.String()
   212  				id = strings.Replace(hw, ":", "", -1)
   213  				break
   214  			}
   215  		}
   216  	}
   217  	usage := &shipyard.Usage{
   218  		ID:      id,
   219  		Version: version.Version,
   220  	}
   221  	b, err := json.Marshal(usage)
   222  	if err != nil {
   223  		log.Warnf("error serializing usage info: %s", err)
   224  	}
   225  	buf := bytes.NewBuffer(b)
   226  	if _, err := http.Post(fmt.Sprintf("%s/update", trackerHost), "application/json", buf); err != nil {
   227  		log.Warnf("error sending usage info: %s", err)
   228  	}
   229  }
   230  
   231  func (m DefaultManager) Container(id string) (*dockerclient.ContainerInfo, error) {
   232  	return m.client.InspectContainer(id)
   233  }
   234  
   235  func (m DefaultManager) ScaleContainer(id string, numInstances int) ScaleResult {
   236  	var (
   237  		errChan = make(chan (error))
   238  		resChan = make(chan (string))
   239  		result  = ScaleResult{Scaled: make([]string, 0), Errors: make([]string, 0)}
   240  	)
   241  
   242  	var lock sync.Mutex
   243  
   244  	containerInfo, err := m.Container(id)
   245  	if err != nil {
   246  		result.Errors = append(result.Errors, err.Error())
   247  		return result
   248  	}
   249  
   250  	fmt.Println("scale container without threads!!!!!")
   251  	//fmt.Println("scale container with sleep")
   252  	fmt.Println("try to add mutex")
   253  
   254  	for i := 0; i < numInstances; i++ {
   255  		go func(instance int) {
   256  			log.Debugf("scaling: id=%s #=%d", containerInfo.Id, instance)
   257  			config := containerInfo.Config
   258  			// clear hostname to get a newly generated
   259  			config.Hostname = ""
   260  			hostConfig := containerInfo.HostConfig
   261  			config.HostConfig = *hostConfig // sending hostconfig via the Start-endpoint is deprecated starting with docker-engine 1.12
   262  
   263  			lock.Lock()
   264  			id, err := m.client.CreateContainer(config, "", nil)
   265  			if err != nil {
   266  				errChan <- err
   267  				return
   268  			}
   269  			if err := m.client.StartContainer(id, hostConfig); err != nil {
   270  				errChan <- err
   271  				return
   272  			}
   273  			lock.Unlock()
   274  			resChan <- id
   275  		}(i)
   276  
   277  		//time.Sleep(time.Second * 10)
   278  	}
   279  
   280  	for i := 0; i < numInstances; i++ {
   281  		select {
   282  		case id := <-resChan:
   283  			result.Scaled = append(result.Scaled, id)
   284  		case err := <-errChan:
   285  			log.Errorf("error scaling container: err=%s", strings.TrimSpace(err.Error()))
   286  			result.Errors = append(result.Errors, strings.TrimSpace(err.Error()))
   287  		}
   288  	}
   289  
   290  	return result
   291  }
   292  
   293  func (m DefaultManager) SaveServiceKey(key *auth.ServiceKey) error {
   294  	if _, err := r.Table(tblNameServiceKeys).Insert(key).RunWrite(m.session); err != nil {
   295  		return err
   296  	}
   297  
   298  	m.logEvent("add-service-key", fmt.Sprintf("description=%s", key.Description), []string{"security"})
   299  
   300  	return nil
   301  }
   302  
   303  func (m DefaultManager) RemoveServiceKey(key string) error {
   304  	if _, err := r.Table(tblNameServiceKeys).Filter(map[string]string{"key": key}).Delete().RunWrite(m.session); err != nil {
   305  		return err
   306  	}
   307  
   308  	m.logEvent("delete-service-key", fmt.Sprintf("key=%s", key), []string{"security"})
   309  
   310  	return nil
   311  }
   312  
   313  func (m DefaultManager) SaveEvent(event *shipyard.Event) error {
   314  	if _, err := r.Table(tblNameEvents).Insert(event).RunWrite(m.session); err != nil {
   315  		return err
   316  	}
   317  
   318  	return nil
   319  }
   320  
   321  func (m DefaultManager) Events(limit int) ([]*shipyard.Event, error) {
   322  	t := r.Table(tblNameEvents).OrderBy(r.Desc("Time"))
   323  	if limit > -1 {
   324  		t.Limit(limit)
   325  	}
   326  	res, err := t.Run(m.session)
   327  	if err != nil {
   328  		return nil, err
   329  	}
   330  	events := []*shipyard.Event{}
   331  	if err := res.All(&events); err != nil {
   332  		return nil, err
   333  	}
   334  	return events, nil
   335  }
   336  
   337  func (m DefaultManager) PurgeEvents() error {
   338  	if _, err := r.Table(tblNameEvents).Delete().RunWrite(m.session); err != nil {
   339  		return err
   340  	}
   341  	return nil
   342  }
   343  
   344  func (m DefaultManager) ServiceKey(key string) (*auth.ServiceKey, error) {
   345  	res, err := r.Table(tblNameServiceKeys).Filter(map[string]string{"key": key}).Run(m.session)
   346  	if err != nil {
   347  		return nil, err
   348  
   349  	}
   350  	if res.IsNil() {
   351  		return nil, ErrServiceKeyDoesNotExist
   352  	}
   353  	var k *auth.ServiceKey
   354  	if err := res.One(&k); err != nil {
   355  		return nil, err
   356  	}
   357  	return k, nil
   358  }
   359  
   360  func (m DefaultManager) ServiceKeys() ([]*auth.ServiceKey, error) {
   361  	res, err := r.Table(tblNameServiceKeys).Run(m.session)
   362  	if err != nil {
   363  		return nil, err
   364  	}
   365  	keys := []*auth.ServiceKey{}
   366  	if err := res.All(&keys); err != nil {
   367  		return nil, err
   368  	}
   369  	return keys, nil
   370  }
   371  
   372  func (m DefaultManager) Accounts() ([]*auth.Account, error) {
   373  	res, err := r.Table(tblNameAccounts).OrderBy(r.Asc("username")).Run(m.session)
   374  	if err != nil {
   375  		return nil, err
   376  	}
   377  	accounts := []*auth.Account{}
   378  	if err := res.All(&accounts); err != nil {
   379  		return nil, err
   380  	}
   381  	return accounts, nil
   382  }
   383  
   384  func (m DefaultManager) Account(username string) (*auth.Account, error) {
   385  	res, err := r.Table(tblNameAccounts).Filter(map[string]string{"username": username}).Run(m.session)
   386  	if err != nil {
   387  		return nil, err
   388  
   389  	}
   390  	if res.IsNil() {
   391  		return nil, ErrAccountDoesNotExist
   392  	}
   393  	var account *auth.Account
   394  	if err := res.One(&account); err != nil {
   395  		return nil, err
   396  	}
   397  	return account, nil
   398  }
   399  
   400  func (m DefaultManager) SaveAccount(account *auth.Account) error {
   401  	var (
   402  		hash      string
   403  		eventType string
   404  	)
   405  	if account.Password != "" {
   406  		h, err := auth.Hash(account.Password)
   407  		if err != nil {
   408  			return err
   409  		}
   410  
   411  		hash = h
   412  	}
   413  	// check if exists; if so, update
   414  	acct, err := m.Account(account.Username)
   415  	if err != nil && err != ErrAccountDoesNotExist {
   416  		return err
   417  	}
   418  
   419  	// update
   420  	if acct != nil {
   421  		updates := map[string]interface{}{
   422  			"first_name": account.FirstName,
   423  			"last_name":  account.LastName,
   424  			"roles":      account.Roles,
   425  		}
   426  		if account.Password != "" {
   427  			updates["password"] = hash
   428  		}
   429  
   430  		if _, err := r.Table(tblNameAccounts).Filter(map[string]string{"username": account.Username}).Update(updates).RunWrite(m.session); err != nil {
   431  			return err
   432  		}
   433  
   434  		eventType = "update-account"
   435  	} else {
   436  		account.Password = hash
   437  		if _, err := r.Table(tblNameAccounts).Insert(account).RunWrite(m.session); err != nil {
   438  			return err
   439  		}
   440  
   441  		eventType = "add-account"
   442  	}
   443  
   444  	m.logEvent(eventType, fmt.Sprintf("username=%s", account.Username), []string{"security"})
   445  
   446  	return nil
   447  }
   448  
   449  func (m DefaultManager) DeleteAccount(account *auth.Account) error {
   450  	res, err := r.Table(tblNameAccounts).Filter(map[string]string{"id": account.ID}).Delete().Run(m.session)
   451  	if err != nil {
   452  		return err
   453  	}
   454  
   455  	if res.IsNil() {
   456  		return ErrAccountDoesNotExist
   457  	}
   458  
   459  	m.logEvent("delete-account", fmt.Sprintf("username=%s", account.Username), []string{"security"})
   460  
   461  	return nil
   462  }
   463  
   464  func (m DefaultManager) Roles() ([]*auth.ACL, error) {
   465  	roles := auth.DefaultACLs()
   466  	return roles, nil
   467  }
   468  
   469  func (m DefaultManager) Role(name string) (*auth.ACL, error) {
   470  	acls, err := m.Roles()
   471  	if err != nil {
   472  		return nil, err
   473  	}
   474  
   475  	for _, r := range acls {
   476  		if r.RoleName == name {
   477  			return r, nil
   478  		}
   479  	}
   480  
   481  	return nil, nil
   482  }
   483  
   484  func (m DefaultManager) GetAuthenticator() auth.Authenticator {
   485  	return m.authenticator
   486  }
   487  
   488  func (m DefaultManager) Authenticate(username, password string) (bool, error) {
   489  	// only get the account to get the hashed password if using the builtin auth
   490  	passwordHash := ""
   491  	if m.authenticator.Name() == "builtin" {
   492  		acct, err := m.Account(username)
   493  		if err != nil {
   494  			log.Error(err)
   495  			return false, ErrLoginFailure
   496  		}
   497  
   498  		passwordHash = acct.Password
   499  	}
   500  
   501  	a, err := m.authenticator.Authenticate(username, password, passwordHash)
   502  	if !a || err != nil {
   503  		log.Error(ErrLoginFailure)
   504  		return false, ErrLoginFailure
   505  	}
   506  
   507  	return true, nil
   508  }
   509  
   510  func (m DefaultManager) NewAuthToken(username string, userAgent string) (*auth.AuthToken, error) {
   511  	tk, err := m.authenticator.GenerateToken()
   512  	if err != nil {
   513  		return nil, err
   514  	}
   515  	acct, err := m.Account(username)
   516  	if err != nil {
   517  		return nil, err
   518  	}
   519  	token := &auth.AuthToken{}
   520  	tokens := acct.Tokens
   521  	found := false
   522  	for _, t := range tokens {
   523  		if t.UserAgent == userAgent {
   524  			found = true
   525  			t.Token = tk
   526  			token = t
   527  			break
   528  		}
   529  	}
   530  	if !found {
   531  		token = &auth.AuthToken{
   532  			UserAgent: userAgent,
   533  			Token:     tk,
   534  		}
   535  		tokens = append(tokens, token)
   536  	}
   537  	// delete token
   538  	if _, err := r.Table(tblNameAccounts).Filter(map[string]string{"username": username}).Filter(r.Row.Field("user_agent").Eq(userAgent)).Delete().Run(m.session); err != nil {
   539  		return nil, err
   540  	}
   541  	// add
   542  	if _, err := r.Table(tblNameAccounts).Filter(map[string]string{"username": username}).Update(map[string]interface{}{"tokens": tokens}).RunWrite(m.session); err != nil {
   543  		return nil, err
   544  	}
   545  	return token, nil
   546  }
   547  
   548  func (m DefaultManager) VerifyAuthToken(username, token string) error {
   549  	acct, err := m.Account(username)
   550  	if err != nil {
   551  		return err
   552  	}
   553  	found := false
   554  	for _, t := range acct.Tokens {
   555  		if token == t.Token {
   556  			found = true
   557  			break
   558  		}
   559  	}
   560  	if !found {
   561  		return ErrInvalidAuthToken
   562  	}
   563  	return nil
   564  }
   565  
   566  func (m DefaultManager) VerifyServiceKey(key string) error {
   567  	if _, err := m.ServiceKey(key); err != nil {
   568  		return err
   569  	}
   570  	return nil
   571  }
   572  
   573  func (m DefaultManager) NewServiceKey(description string) (*auth.ServiceKey, error) {
   574  	k, err := m.authenticator.GenerateToken()
   575  	if err != nil {
   576  		return nil, err
   577  	}
   578  	key := &auth.ServiceKey{
   579  		Key:         k[24:],
   580  		Description: description,
   581  	}
   582  	if err := m.SaveServiceKey(key); err != nil {
   583  		return nil, err
   584  	}
   585  	return key, nil
   586  }
   587  
   588  func (m DefaultManager) ChangePassword(username, password string) error {
   589  	if !m.authenticator.IsUpdateSupported() {
   590  		return fmt.Errorf("not supported for authenticator: %s", m.authenticator.Name())
   591  	}
   592  
   593  	hash, err := auth.Hash(password)
   594  	if err != nil {
   595  		return err
   596  	}
   597  
   598  	if _, err := r.Table(tblNameAccounts).Filter(map[string]string{"username": username}).Update(map[string]string{"password": hash}).Run(m.session); err != nil {
   599  		return err
   600  	}
   601  
   602  	m.logEvent("change-password", username, []string{"security"})
   603  
   604  	return nil
   605  }
   606  
   607  func (m DefaultManager) WebhookKey(key string) (*dockerhub.WebhookKey, error) {
   608  	res, err := r.Table(tblNameWebhookKeys).Filter(map[string]string{"key": key}).Run(m.session)
   609  	if err != nil {
   610  		return nil, err
   611  
   612  	}
   613  
   614  	if res.IsNil() {
   615  		return nil, ErrWebhookKeyDoesNotExist
   616  
   617  	}
   618  
   619  	var k *dockerhub.WebhookKey
   620  	if err := res.One(&k); err != nil {
   621  		return nil, err
   622  
   623  	}
   624  
   625  	return k, nil
   626  }
   627  
   628  func (m DefaultManager) WebhookKeys() ([]*dockerhub.WebhookKey, error) {
   629  	res, err := r.Table(tblNameWebhookKeys).OrderBy(r.Asc("image")).Run(m.session)
   630  	if err != nil {
   631  		return nil, err
   632  	}
   633  	keys := []*dockerhub.WebhookKey{}
   634  	if err := res.All(&keys); err != nil {
   635  		return nil, err
   636  	}
   637  	return keys, nil
   638  }
   639  
   640  func (m DefaultManager) NewWebhookKey(image string) (*dockerhub.WebhookKey, error) {
   641  	k := generateId(16)
   642  	key := &dockerhub.WebhookKey{
   643  		Key:   k,
   644  		Image: image,
   645  	}
   646  
   647  	if err := m.SaveWebhookKey(key); err != nil {
   648  		return nil, err
   649  	}
   650  
   651  	return key, nil
   652  }
   653  
   654  func (m DefaultManager) SaveWebhookKey(key *dockerhub.WebhookKey) error {
   655  	if _, err := r.Table(tblNameWebhookKeys).Insert(key).RunWrite(m.session); err != nil {
   656  		return err
   657  
   658  	}
   659  
   660  	m.logEvent("add-webhook-key", fmt.Sprintf("image=%s", key.Image), []string{"webhook"})
   661  
   662  	return nil
   663  }
   664  
   665  func (m DefaultManager) DeleteWebhookKey(id string) error {
   666  	key, err := m.WebhookKey(id)
   667  	if err != nil {
   668  		return err
   669  
   670  	}
   671  	res, err := r.Table(tblNameWebhookKeys).Get(key.ID).Delete().Run(m.session)
   672  	if err != nil {
   673  		return err
   674  
   675  	}
   676  
   677  	if res.IsNil() {
   678  		return ErrWebhookKeyDoesNotExist
   679  
   680  	}
   681  
   682  	m.logEvent("delete-webhook-key", fmt.Sprintf("image=%s", key.Image), []string{"webhook"})
   683  
   684  	return nil
   685  }
   686  
   687  func (m DefaultManager) Nodes() ([]*shipyard.Node, error) {
   688  	info, err := m.client.Info()
   689  	if err != nil {
   690  		return nil, err
   691  	}
   692  
   693  	nodes, err := parseClusterNodes(info.DriverStatus)
   694  	if err != nil {
   695  		return nil, err
   696  	}
   697  
   698  	return nodes, nil
   699  }
   700  
   701  func (m DefaultManager) Node(name string) (*shipyard.Node, error) {
   702  	nodes, err := m.Nodes()
   703  	if err != nil {
   704  		return nil, err
   705  	}
   706  
   707  	for _, node := range nodes {
   708  		if node.Name == name {
   709  			return node, nil
   710  		}
   711  	}
   712  
   713  	return nil, nil
   714  }
   715  
   716  func (m DefaultManager) AddRegistry(registry *shipyard.Registry) error {
   717  	resp, err := http.Get(fmt.Sprintf("%s/v1/search", registry.Addr))
   718  	if err != nil {
   719  		return err
   720  	}
   721  	if resp.StatusCode != 200 {
   722  		return errors.New(resp.Status)
   723  	}
   724  
   725  	if _, err := r.Table(tblNameRegistries).Insert(registry).RunWrite(m.session); err != nil {
   726  		return err
   727  	}
   728  
   729  	m.logEvent("add-registry", fmt.Sprintf("name=%s endpoint=%s", registry.Name, registry.Addr), []string{"registry"})
   730  
   731  	return nil
   732  }
   733  
   734  func (m DefaultManager) RemoveRegistry(registry *shipyard.Registry) error {
   735  	res, err := r.Table(tblNameRegistries).Get(registry.ID).Delete().Run(m.session)
   736  	if err != nil {
   737  		return err
   738  	}
   739  
   740  	if res.IsNil() {
   741  		return ErrRegistryDoesNotExist
   742  	}
   743  
   744  	m.logEvent("delete-registry", fmt.Sprintf("name=%s endpoint=%s", registry.Name, registry.Addr), []string{"registry"})
   745  
   746  	return nil
   747  }
   748  
   749  func (m DefaultManager) Registries() ([]*shipyard.Registry, error) {
   750  	res, err := r.Table(tblNameRegistries).OrderBy(r.Asc("name")).Run(m.session)
   751  	if err != nil {
   752  		return nil, err
   753  	}
   754  
   755  	regs := []*shipyard.Registry{}
   756  	if err := res.All(&regs); err != nil {
   757  		return nil, err
   758  	}
   759  
   760  	registries := []*shipyard.Registry{}
   761  	for _, r := range regs {
   762  		reg, err := shipyard.NewRegistry(r.ID, r.Name, r.Addr)
   763  		if err != nil {
   764  			return nil, err
   765  		}
   766  
   767  		registries = append(registries, reg)
   768  	}
   769  
   770  	return registries, nil
   771  }
   772  
   773  func (m DefaultManager) Registry(name string) (*shipyard.Registry, error) {
   774  	res, err := r.Table(tblNameRegistries).Filter(map[string]string{"name": name}).Run(m.session)
   775  	if err != nil {
   776  		return nil, err
   777  
   778  	}
   779  	if res.IsNil() {
   780  		return nil, ErrRegistryDoesNotExist
   781  	}
   782  	var reg *shipyard.Registry
   783  	if err := res.One(&reg); err != nil {
   784  		return nil, err
   785  	}
   786  
   787  	registry, err := shipyard.NewRegistry(reg.ID, reg.Name, reg.Addr)
   788  	if err != nil {
   789  		return nil, err
   790  	}
   791  
   792  	return registry, nil
   793  }
   794  
   795  func (m DefaultManager) CreateConsoleSession(c *shipyard.ConsoleSession) error {
   796  	if _, err := r.Table(tblNameConsole).Insert(c).RunWrite(m.session); err != nil {
   797  		return err
   798  	}
   799  
   800  	m.logEvent("create-console-session", fmt.Sprintf("container=%s", c.ContainerID), []string{"console"})
   801  
   802  	return nil
   803  }
   804  
   805  func (m DefaultManager) RemoveConsoleSession(c *shipyard.ConsoleSession) error {
   806  	res, err := r.Table(tblNameConsole).Get(c.ID).Delete().Run(m.session)
   807  	if err != nil {
   808  		return err
   809  	}
   810  
   811  	if res.IsNil() {
   812  		return ErrConsoleSessionDoesNotExist
   813  	}
   814  
   815  	return nil
   816  }
   817  
   818  func (m DefaultManager) ConsoleSession(token string) (*shipyard.ConsoleSession, error) {
   819  	res, err := r.Table(tblNameConsole).Filter(map[string]string{"token": token}).Run(m.session)
   820  	if err != nil {
   821  		return nil, err
   822  	}
   823  
   824  	if res.IsNil() {
   825  		return nil, ErrConsoleSessionDoesNotExist
   826  	}
   827  
   828  	var c *shipyard.ConsoleSession
   829  	if err := res.One(&c); err != nil {
   830  		return nil, err
   831  	}
   832  
   833  	return c, nil
   834  }
   835  
   836  func (m DefaultManager) ValidateConsoleSessionToken(containerId string, token string) bool {
   837  	cs, err := m.ConsoleSession(token)
   838  	if err != nil {
   839  		log.Errorf("error validating console session token: %s", err)
   840  		return false
   841  	}
   842  
   843  	if cs == nil || cs.ContainerID != containerId {
   844  		log.Warnf("unauthorized token request: %s", token)
   845  		return false
   846  	}
   847  
   848  	if err := m.RemoveConsoleSession(cs); err != nil {
   849  		log.Error(err)
   850  		return false
   851  	}
   852  
   853  	return true
   854  }