Example #1
0
func (c *client) start() {

	if !config.IsPaired() {
		log.Infof("Client is unpaired. Attempting to pair.")
		if err := c.pair(); err != nil {
			log.Fatalf("An error occurred while pairing. Restarting. error: %s", err)
		}

		log.Infof("Pairing was successful.")
		// We reload the config so the creds can be picked up
		config.MustRefresh()

		if !config.IsPaired() {
			log.Fatalf("Pairing appeared successful, but I did not get the credentials. Restarting.")
		}

	}

	log.Infof("Client is paired. User: %s", config.MustString("userId"))

	if !config.NoCloud() {

		mesh, err := refreshMeshInfo()

		if err == errorUnauthorised {
			log.Warningf("UNAUTHORISED! Unpairing.")
			c.unpair()
			return
		}

		if err != nil {
			log.Warningf("Failed to refresh mesh info: %s", err)
		} else {
			log.Debugf("Got mesh info: %+v", mesh)
		}

		config.MustRefresh()

		if !config.HasString("masterNodeId") {
			log.Warningf("We don't have any mesh information. Which is unlikely. But we can't do anything without it, so restarting client.")
			time.Sleep(time.Second * 10)
			os.Exit(0)
		}

	}

	if config.MustString("masterNodeId") == config.Serial() {
		log.Infof("I am the master, starting HomeCloud.")

		cmd := exec.Command("start", "sphere-homecloud")
		cmd.Output()
		go c.exportNodeDevice()

		c.master = true
	} else {
		log.Infof("I am a slave. The master is %s", config.MustString("masterNodeId"))

		// TODO: Remove this when we are running drivers on slaves
		cmd := exec.Command("stop", "sphere-director")
		cmd.Output()

		c.masterReceiveTimeout = time.AfterFunc(orphanTimeout, func() {
			c.setOrphaned()
		})

	}

	go func() {

		log.Infof("Starting search for peers")

		for {
			c.findPeers()
			time.Sleep(time.Second * 30)
		}
	}()
}
func (m *baseModel) Sync(timeout time.Duration, conn redis.Conn) error {
	m.syncing.Wait()
	m.syncing.Add(1)
	defer m.syncing.Done()

	if config.NoCloud() {
		m.log.Infof("sync: syncing disabled because there is no cloud")
		return nil
	}

	m.log.Infof("sync: Syncing %ss. Save data from cloud?:%s", m.idType, enableSyncFromCloud)

	var diffList SyncDifferenceList

	manifest, err := m.getSyncManifest(conn)
	if err != nil {
		return err
	}

	m.log.Debugf("sync: Sending %d %s local update times", len(*manifest), m.idType)

	calcClient := m.SyncConn.Conn.GetServiceClient("$ninja/services/rpc/modelstore/calculate_sync_items")
	err = calcClient.Call("modelstore.calculate_sync_items", []interface{}{m.idType, manifest}, &diffList, timeout)

	if err != nil {
		return fmt.Errorf("Failed calling calculate_sync_items for model %s error:%s", m.idType, err)
	}

	m.log.Infof("sync: Cloud requires %d %s(s), Node requires %d %s(s)", len(diffList.CloudRequires), m.idType, len(diffList.NodeRequires), m.idType)

	if len(diffList.CloudRequires)+len(diffList.NodeRequires) == 0 {
		// Nothing to do, we're in sync.
		return nil
	}

	requestIds := make([]string, 0)
	for id := range diffList.NodeRequires {
		requestIds = append(requestIds, id)
	}

	requestedData := SyncDataSet{}

	for id := range diffList.CloudRequires {
		obj := reflect.New(m.objType).Interface()

		err = m.fetch(id, obj, true, conn)

		if err != nil && err != RecordNotFound {
			return fmt.Errorf("Failed retrieving requested %s id:%s error:%s", m.idType, id, err)
		}

		if err == RecordNotFound {
			obj = nil
		}

		lastUpdated, err := m.getLastUpdated(id, conn)
		if err != nil {
			return fmt.Errorf("Failed retrieving last updated time for requested %s id:%s error:%s", m.idType, id, err)
		}

		requestedData[id] = SyncObject{obj, lastUpdated.UnixNano() / int64(time.Millisecond)}
	}

	if deleteUnknownFromCloud {
		for id := range diffList.NodeRequires {
			if _, ok := (*manifest)[id]; !ok { // We've never heard of this, so remove it
				m.log.Infof("Removing %s id:%s from cloud.", m.idType, id)
				requestedData[id] = SyncObject{nil, time.Now().UnixNano() / int64(time.Millisecond)}
			}
		}
	}

	syncClient := m.SyncConn.Conn.GetServiceClient("$ninja/services/rpc/modelstore/do_sync_items")

	var syncReply SyncReply

	err = syncClient.Call("modelstore.do_sync_items", []interface{}{m.idType, requestedData, requestIds}, &syncReply, timeout)

	if err != nil {
		return fmt.Errorf("Failed calling do_sync_items for model %s error:%s", m.idType, err)
	}

	defer syncFS()

	if enableSyncFromCloud {

		for id, requestedObj := range syncReply.RequestedObjects {
			obj := reflect.New(m.objType).Interface()

			err := json.Unmarshal(requestedObj.Data, obj)
			if err != nil {
				m.log.Warningf("Failed to unmarshal requested %s id:%s error: %s", m.idType, id, err)
				m.delete(id, conn)
			} else if string(requestedObj.Data) == "null" {
				m.log.Infof("Requested %s id:%s has been remotely deleted", m.idType, id)
				m.delete(id, conn)
			} else {

				updated, err := m.save(id, obj, conn)
				if err != nil {
					return fmt.Errorf("Failed to save requested %s id:%s error: %s", m.idType, id, err)
				}
				if !updated {
					m.log.Warningf("We requested an updated %s id:%s but it was the same as what we had.", m.idType, id)
				}

			}

			err = m.markUpdated(id, time.Unix(0, requestedObj.LastModified*int64(time.Millisecond)), conn)
			if err != nil {
				m.log.Warningf("Failed to update last modified time of requested %s id:%s error: %s", m.idType, id, err)
			}
		}
	} else {
		m.log.Warningf("Ignoring sync data from cloud.")
	}

	if err != nil {

		ts, err := time.Now().MarshalText()
		if err != nil {
			return err
		}

		_, err = conn.Do("SET", m.idType+"s:synced", ts)

	}

	return err
}