<commit_before>// Copyright 2017 Liam Breck
//
// This file is part of the "mnm" software. Anyone may redistribute mnm and/or modify
// it under the terms of the GNU Lesser General Public License version 3, as published
// by the Free Software Foundation. See www.gnu.org/licenses/
// Mnm is distributed WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See said License for details.

package main

import (
   "fmt"
   "io/ioutil"
   "encoding/json"
   "net"
   "os"
   "qlib"
   "strconv"
)

const kVersionA, kVersionB, kVersionC = 0, 0, 0
const kVersionDate = "(unreleased)"
const kConfigFile = "mnm.config"

var sConfig tConfig

func main() { os.Exit(mainResult()) }

func mainResult() int {
   // return 2 reserved for use by Go internals
   var err error

   aTcNum := 10
   if len(os.Args) == 2 {
      aTcNum, err = strconv.Atoi(os.Args[1])
      if err != nil || aTcNum < 2 || aTcNum > 1000 {
         fmt.Fprintf(os.Stderr, "testclient count must be 2-1000\n")
         return 1
      }
   } else {
      err = sConfig.load()
      if err != nil {
         if !os.IsNotExist(err) {
            fmt.Fprintf(os.Stderr, "config load: %s\n", err.Error())
            return 1
         }
      } else {
         aTcNum = 0
      }
   }

   fmt.Printf("mnm tmtp server v%d.%d.%d %s\n", kVersionA, kVersionB, kVersionC, kVersionDate)

   aDbName := "userdb"; if aTcNum != 0 { aDbName += "-test-qlib" }
   qlib.UDb, err = NewUserDb(aDbName)
   if err != nil {
      fmt.Fprintf(os.Stderr, "%s\n", err.Error())
      return 1
   }

   qlib.Init("qstore")

   if aTcNum != 0 {
      fmt.Printf("Starting Test Pass\n")
      TestUserDb("userdb-test-unit")
      qlib.LocalTest(aTcNum)
   } else {
      err = startServer(&sConfig)
      if err != nil {
         fmt.Fprintf(os.Stderr, "server exit: %s\n", err.Error())
         return 1
      }
   }

   return 0
}

type tConfig struct {
   Listen struct {
      Net string
      Laddr string
   }
}

func (o *tConfig) load() error {
   aBuf, err := ioutil.ReadFile(kConfigFile)
   if err != nil { return err }
   err = json.Unmarshal(aBuf, o)
   return err
}

func startServer(iConf *tConfig) error {
   aListener, err := net.Listen(iConf.Listen.Net, iConf.Listen.Laddr)
   if err != nil { return err }
   defer aListener.Close()

   for {
      var aConn net.Conn
      aConn, err = aListener.Accept()
      if err != nil { return err }
      qlib.NewLink(aConn)
   }
}

/* moved to qlib/testclient.go
const ( _=iota; eRegister; eAddNode; eLogin; eListEdit; ePost; ePing; eAck )

var sTestClientId chan int

type tTestClient struct {
   id, to int // who i am, who i send to
   count int // msg number
   deferLogin bool // test login timeout feature
   ack chan int // writer tells reader to issue ack to qlib
   closed bool // when about to shut down
   readDeadline time.Time // set by qlib
}

func InitTestClient(i int) {
   sTestClientId = make(chan int, i)
   for a:=1; a <= i; a++ {
      sTestClientId <- 111111 * a
   }
}

func NewTestClient(iDawdle bool) *tTestClient {
   a := <-sTestClientId
   return &tTestClient{
      id: a, to: a+111111,
      deferLogin: iDawdle,
      ack: make(chan int,10),
   }
}

func (o *tTestClient) Read(buf []byte) (int, error) {
   if o.count % 10 == 9 {
      return 0, &net.OpError{Op:"read", Err:tTestClientError("log out")}
   }

   var aDlC <-chan time.Time
   if !o.readDeadline.IsZero() {
      aDl := time.NewTimer(o.readDeadline.Sub(time.Now()))
      defer aDl.Stop()
      aDlC = aDl.C
   }

   aUnit := 200 * time.Millisecond; if o.deferLogin { aUnit = 6 * time.Second }
   aTmr := time.NewTimer(aUnit)
   defer aTmr.Stop()

   var aHead map[string]interface{}
   var aData string

   select {
   case <-o.ack:
      aHead = tMsg{"Op":eAck, "Id":"n", "Type":"n"}
   case <-aTmr.C:
      o.count++
      if o.deferLogin {
         aHead = tMsg{}
      } else if o.count == 1 {
         aHead = tMsg{"Op":eLogin, "Uid":"u"+fmt.Sprint(o.id), "NodeId":fmt.Sprint(o.id)}
      } else {
         aHead = tMsg{"Op":ePost, "Id":"n", "For":[]string{"u"+fmt.Sprint(o.to)}}
         aData = fmt.Sprintf(" |msg %d|", o.count)
      }
   case <-aDlC:
      return 0, &net.OpError{Op:"read", Err:&tTimeoutError{}}
   }

   aMsg := qlib.PackMsg(aHead, []byte(aData))
   fmt.Printf("%d testclient.read %s\n", o.id, string(aMsg))
   return copy(buf, aMsg), nil
}

func (o *tTestClient) Write(buf []byte) (int, error) {
   if o.closed {
      fmt.Printf("%d testclient.write was closed\n", o.id)
      return 0, &net.OpError{Op:"write", Err:tTestClientError("closed")}
   }

   aTmr := time.NewTimer(2 * time.Second)

   select {
   case o.ack <- 1:
      aTmr.Stop()
   case <-aTmr.C:
      fmt.Printf("%d testclient.write timed out on ack\n", o.id)
      return 0, &net.OpError{Op:"write", Err:tTestClientError("noack")}
   }

   fmt.Printf("%d testclient.write got %s\n", o.id, string(buf))
   return len(buf), nil
}

func (o *tTestClient) SetReadDeadline(i time.Time) error {
   o.readDeadline = i
   return nil
}

func (o *tTestClient) Close() error {
   o.closed = true;
   time.AfterFunc(10*time.Millisecond, func(){ sTestClientId <- o.id })
   return nil
}

func (o *tTestClient) LocalAddr() net.Addr { return &net.UnixAddr{"e", "a"} }
func (o *tTestClient) RemoteAddr() net.Addr { return &net.UnixAddr{"e", "a"} }
func (o *tTestClient) SetDeadline(time.Time) error { return nil }
func (o *tTestClient) SetWriteDeadline(time.Time) error { return nil }

type tTimeoutError struct{}
func (o *tTimeoutError) Error() string   { return "i/o timeout" }
func (o *tTimeoutError) Timeout() bool   { return true }
func (o *tTimeoutError) Temporary() bool { return true }

type tTestClientError string
func (o tTestClientError) Error() string { return string(o) }

type tMsg map[string]interface{}
*/

/* moved to tudb
//: these are instructions/guidance comments
//: you'll implement the public api to add/edit userdb records
//: for all ops, you look up a record in cache,
//:   and if not there call getRecord and cache the result
//:   lookups are done with aObj := o.user[Uid] (or o.alias, o.group)
//: for add/edit ops, you then modify the cache object, then call putRecord
//: locking
//:   cache read ops are done inside o.xyzDoor.RLock/RUnlock()
//:   cache add/delete ops are done inside o.xyzDoor.Lock/Unlock()
//:   tUser and tGroup object updates are done inside aObj.door.Lock/Unlock()
//: records are stored as files in subdirectories of o.root: user, alias, group
//:   user/* & group/* files are json format
//:   alias/* files are symlinks to Uid

type tUserDb struct {
   root string // top-level directory
   temp string // temp subdirectory; write files here first

   // cache records here
   userDoor sync.RWMutex
   user map[string]*tUser

   aliasDoor sync.RWMutex
   alias map[string]string // value is Uid

   groupDoor sync.RWMutex
   group map[string]*tGroup
}

type tUser struct {
   door sync.RWMutex
   Nodes map[string]int // value is NodeRef
   Aliases []tAlias // public names for the user
}

type tAlias struct {
   En string // in english
   Nat string // in whatever language
}

type tGroup struct {
   door sync.RWMutex
   Uid map[string]tMember
}

type tMember struct {
   Alias string // invited/joined by this alias
   Joined bool // use a date here?
}

type tUserDbErr string
func (o tUserDbErr) Error() string { return string(o) }

type tType string
const (
   eTuser  tType = "user"
   eTalias tType = "alias"
   eTgroup tType = "group"
)

//: add a crash recovery pass on startup
//: examine temp dir
//:   complete any pending transactions
//: in transaction
//:   sync temp dir instead of data dir
//:   remove temp file in commitDir
//:   drop .tmp files

func NewUserDb(iPath string) (*tUserDb, error) {
   for _, a := range [...]tType{ "temp", eTuser, eTalias, eTgroup } {
      err := os.MkdirAll(iPath + "/" + string(a), 0700)
      if err != nil { return nil, err }
   }

   aDb := new(tUserDb)
   aDb.root = iPath+"/"
   aDb.temp = aDb.root + "temp"
   aDb.user = make(map[string]*tUser)
   aDb.alias = make(map[string]string)
   aDb.group = make(map[string]*tGroup)

   return aDb, nil
}

func (o *tUserDb) Test() error {
   //: exercise the api, print diagnostics
   //: invoke from main() before tTestClient loop; stop program if tests fail
   return nil
}

//: below is the public api
//: if same parameters are retried after success, ie data already exists,
//:   function should do nothing but return success

func (o *tUserDb) AddUser(iUid, iNewNode string) (aQid string, err error) {
   //: add user
   //: iUid not in o.user, or already has iNewNode
   return "", nil
}

func (o *tUserDb) AddNode(iUid, iNode, iNewNode string) (aQid string, err error) {
   //: add node
   //: iUid has iNode
   //: iUid may already have iNewNode
   return "", nil
}

func (o *tUserDb) DropNode(iUid, iNode string) (aQid string, err error) {
   //: mark iNode defunct
   //: iUid has iNode
   return "", nil
}

func (o *tUserDb) AddAlias(iUid, iNode, iNat, iEn string) error {
   //: add aliases to iUid and o.alias
   //: iUid has iNode
   //: iNat != iEn, iNat or iEn != ""
   return nil
}

func (o *tUserDb) DropAlias(iUid, iNode, iAlias string) error {
   //: mark alias defunct in o.alias
   //: iUid has iNode
   //: iAlias for iUid
   return nil
}

//func (o *tUserDb) DropUser(iUid string) error { return nil }

func (o *tUserDb) Verify(iUid, iNode string) (aQid string, err error) {
   //: return Qid of node
   //: iUid has iNode
   // trivial implementation for qlib testing
   if o.user[iUid] != nil && o.user[iUid].Nodes[iNode] != 0 {
      return "q"+iNode, nil
   }
   return "", tUserDbErr("no such user/node")
}

func (o *tUserDb) GetNodes(iUid string) (aQids []string, err error) {
   //: return Qids for iUid
   // trivial implementation for qlib testing
   for aN,_ := range o.user[iUid].Nodes {
      aQids = append(aQids, aN)
   }
   return aQids, nil
}

func (o *tUserDb) Lookup(iAlias string) (aUid string, err error) {
   //: return uid for iAlias
   // trivial implementation for qlib testing
   aUid = o.alias[iAlias]
   if aUid == "" {
      return "", tUserDbErr(iAlias+" not known")
   }
   return aUid, nil
}

func (o *tUserDb) GroupInvite(iGid, iAlias, iByAlias, iByUid string) error {
   //: add member to group, possibly create group
   //: iAlias exists
   //: iGid exists, iByUid in group, iByAlias ignored
   //: iGid !exists, make iGid and add iByUid with iByAlias
   //: iByAlias for iByUid
   return nil
}

func (o *tUserDb) GroupJoin(iGid, iUid, iNewAlias string) error {
   //: set joined status for member
   //: iUid in group
   //: iNewAlias optional for iUid
   return nil
}

func (o *tUserDb) GroupAlias(iGid, iUid, iNewAlias string) error {
   //: update member alias
   //: iUid in group
   //: iNewAlias for iUid
   return nil
}

func (o *tUserDb) GroupDrop(iGid, iUid, iByUid string) error {
   //: change member status of member with iUid
   //: iUid in group, iByUid same or in group
   //: iUid == iByUid, status=invited
   //: iUid != iByUid, if iUid status==joined, status=barred else delete member
   return nil
}

func (o *tUserDb) GroupGetUsers(iGid, iByUid string) (aUids []string, err error) {
   //: return uids in iGid
   //: iByUid is member
   for a,_ := range o.group["g1"].Uid {
      aUids = append(aUids, a)
   }
   return aUids, nil
}

type tFetch bool
const eFetchCheck, eFetchMake tFetch = false, true

// retrieve user from cache or disk
func (o *tUserDb) fetchUser(iUid string, iMake tFetch) (*tUser, error) {
   o.userDoor.RLock() // read-lock user map
   aUser := o.user[iUid] // lookup user in map
   o.userDoor.RUnlock()

   if aUser == nil { // user not in cache
      aObj, err := o.getRecord(eTuser, iUid) // lookup user on disk
      if err != nil { return nil, err }
      aUser = aObj.(*tUser) // "type assertion" extracts *tUser from interface{}

      if aUser.Nodes == nil { // user not on disk
         if !iMake {
            return nil, nil
         }
         aUser.Nodes = make(map[string]int) // initialize user
      }

      o.userDoor.Lock() // write-lock user map
      if aTemp := o.user[iUid]; aTemp != nil { // recheck the map
         aUser = aTemp
      } else {
         o.user[iUid] = aUser // add user to map
      }
      o.userDoor.Unlock()
   }
   return aUser, nil // do .door.[R]Lock() on return value before use
}

// pull a file into a cache object
func (o *tUserDb) getRecord(iType tType, iId string) (interface{}, error) {
   var err error
   var aObj interface{}
   aPath := o.root + string(iType) + "/" + iId

   // in case putRecord was interrupted
   err = os.Link(aPath + ".tmp", aPath)
   if err != nil {
      if !os.IsExist(err) && !os.IsNotExist(err) { return nil, err }
   } else {
      fmt.Println("getRecord: finished transaction for "+aPath)
   }

   switch iType {
   default:
      panic("getRecord: unexpected type "+iType)
   case eTalias:
      aLn, err := os.Readlink(aPath)
      if err != nil {
         if os.IsNotExist(err) { return nil, nil }
         return nil, err
      }
      return &aLn, nil
   case eTuser:  aObj = &tUser{}
   case eTgroup: aObj = &tGroup{}
   }

   aBuf, err := ioutil.ReadFile(aPath)
   if err != nil {
      if os.IsNotExist(err) { return aObj, nil }
      return nil, err
   }

   err = json.Unmarshal(aBuf, aObj)
   return aObj, err
}

// save cache object to disk. getRecord must be called before this
func (o *tUserDb) putRecord(iType tType, iId string, iObj interface{}) error {
   var err error
   aPath := o.root + string(iType) + "/" + iId
   aTemp := o.temp + string(iType) + "_" + iId

   err = os.Remove(aPath + ".tmp")
   if err == nil {
      fmt.Println("putRecord: removed residual .tmp file for "+aPath)
   }

   switch iType {
   default:
      panic("putRecord: unexpected type "+iType)
   case eTalias:
      err = os.Symlink(iObj.(string), aPath + ".tmp")
      if err != nil { return err }
      return o.commitDir(iType, aPath)
   case eTuser, eTgroup:
   }

   aBuf, err := json.Marshal(iObj)
   if err != nil { return err }

   aFd, err := os.OpenFile(aTemp, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0600)
   if err != nil { return err }
   defer aFd.Close()

   for aPos,aLen := 0,0; aPos < len(aBuf); aPos += aLen {
      aLen, err = aFd.Write(aBuf[aPos:])
      if err != nil { return err }
   }

   err = aFd.Sync()
   if err != nil { return err }

   err = os.Link(aTemp, aPath + ".tmp")
   if err != nil { return err }
   err = os.Remove(aTemp)
   if err != nil { return err }

   return o.commitDir(iType, aPath)
}

// sync the directory and set the filename
func (o *tUserDb) commitDir(iType tType, iPath string) error {
   aFd, err := os.Open(o.root + string(iType))
   if err != nil { return err }
   defer aFd.Close()
   err = aFd.Sync()
   if err != nil { return err }

   err = os.Remove(iPath)
   if err != nil && !os.IsNotExist(err) { return err }
   err = os.Rename(iPath + ".tmp", iPath)
   return err
}
*/
<commit_msg>main: Drop moved userdb & testclient code<commit_after>// Copyright 2017 Liam Breck
//
// This file is part of the "mnm" software. Anyone may redistribute mnm and/or modify
// it under the terms of the GNU Lesser General Public License version 3, as published
// by the Free Software Foundation. See www.gnu.org/licenses/
// Mnm is distributed WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See said License for details.

package main

import (
   "fmt"
   "io/ioutil"
   "encoding/json"
   "net"
   "os"
   "qlib"
   "strconv"
)

const kVersionA, kVersionB, kVersionC = 0, 0, 0
const kVersionDate = "(unreleased)"
const kConfigFile = "mnm.config"

var sConfig tConfig

func main() { os.Exit(mainResult()) }

func mainResult() int {
   // return 2 reserved for use by Go internals
   var err error

   aTcNum := 10
   if len(os.Args) == 2 {
      aTcNum, err = strconv.Atoi(os.Args[1])
      if err != nil || aTcNum < 2 || aTcNum > 1000 {
         fmt.Fprintf(os.Stderr, "testclient count must be 2-1000\n")
         return 1
      }
   } else {
      err = sConfig.load()
      if err != nil {
         if !os.IsNotExist(err) {
            fmt.Fprintf(os.Stderr, "config load: %s\n", err.Error())
            return 1
         }
      } else {
         aTcNum = 0
      }
   }

   fmt.Printf("mnm tmtp server v%d.%d.%d %s\n", kVersionA, kVersionB, kVersionC, kVersionDate)

   aDbName := "userdb"; if aTcNum != 0 { aDbName += "-test-qlib" }
   qlib.UDb, err = NewUserDb(aDbName)
   if err != nil {
      fmt.Fprintf(os.Stderr, "%s\n", err.Error())
      return 1
   }

   qlib.Init("qstore")

   if aTcNum != 0 {
      fmt.Printf("Starting Test Pass\n")
      TestUserDb("userdb-test-unit")
      qlib.LocalTest(aTcNum)
   } else {
      err = startServer(&sConfig)
      if err != nil {
         fmt.Fprintf(os.Stderr, "server exit: %s\n", err.Error())
         return 1
      }
   }

   return 0
}

type tConfig struct {
   Listen struct {
      Net string
      Laddr string
   }
}

func (o *tConfig) load() error {
   aBuf, err := ioutil.ReadFile(kConfigFile)
   if err != nil { return err }
   err = json.Unmarshal(aBuf, o)
   return err
}

func startServer(iConf *tConfig) error {
   aListener, err := net.Listen(iConf.Listen.Net, iConf.Listen.Laddr)
   if err != nil { return err }
   defer aListener.Close()

   for {
      var aConn net.Conn
      aConn, err = aListener.Accept()
      if err != nil { return err }
      qlib.NewLink(aConn)
   }
}
<|endoftext|>


<commit_before>// Copyright 2015 Canonical Ltd.
// Licensed under the AGPLv3, see LICENCE file for details.

// +build go1.3

package lxdclient

import (
	"github.com/juju/errors"
	"github.com/juju/testing"
	jc "github.com/juju/testing/checkers"
	"github.com/lxc/lxd"
	gc "gopkg.in/check.v1"
)

type ConnectSuite struct {
	testing.IsolationSuite
}

var _ = gc.Suite(&ConnectSuite{})

func (cs ConnectSuite) TestLocalConnectError(c *gc.C) {
	cs.PatchValue(&lxdNewClientFromInfo, fakeNewClientFromInfo)

	cfg, err := Config{
		Remote: Local,
	}.WithDefaults()
	c.Assert(err, jc.ErrorIsNil)
	_, err = Connect(cfg)

	// Yes, the error message actually matters here... this is being displayed
	// to the user.
	c.Assert(err, gc.ErrorMatches, "can't connect to the local LXD server.*")
}

func (cs ConnectSuite) TestRemoteConnectError(c *gc.C) {
	cs.PatchValue(&lxdNewClientFromInfo, fakeNewClientFromInfo)

	cfg, err := Config{
		Remote: Remote{
			Name: "foo",
			Host: "a.b.c",
			Cert: &Cert{
				Name:    "really-valid",
				CertPEM: []byte("kinda-public"),
				KeyPEM:  []byte("super-secret"),
			},
		},
	}.WithDefaults()
	c.Assert(err, jc.ErrorIsNil)
	_, err = Connect(cfg)

	c.Assert(errors.Cause(err), gc.Equals, testerr)
}

func (cs ConnectSuite) TestLXDClientForCloudImagesDefault(c *gc.C) {
	client, err := lxdClientForCloudImages(Config{})
	c.Assert(err, jc.ErrorIsNil)
	c.Check(client.BaseURL, gc.Equals, lxd.UbuntuRemote.Addr)
}

func (cs ConnectSuite) TestLXDClientForCloudImagesDaily(c *gc.C) {
	client, err := lxdClientForCloudImages(Config{
		ImageStream: StreamDaily,
	})
	c.Assert(err, jc.ErrorIsNil)
	c.Check(client.BaseURL, gc.Equals, lxd.UbuntuDailyRemote.Addr)
}

func (cs ConnectSuite) TestLXDClientForCloudImagesReleases(c *gc.C) {
	client, err := lxdClientForCloudImages(Config{
		ImageStream: StreamReleases,
	})
	c.Assert(err, jc.ErrorIsNil)
	c.Check(client.BaseURL, gc.Equals, lxd.UbuntuRemote.Addr)
}

var testerr = errors.Errorf("boo!")

func fakeNewClientFromInfo(info lxd.ConnectInfo) (*lxd.Client, error) {
	return nil, testerr
}
<commit_msg>notes on a test case<commit_after>// Copyright 2015 Canonical Ltd.
// Licensed under the AGPLv3, see LICENCE file for details.

// +build go1.3

package lxdclient

import (
	"github.com/juju/errors"
	"github.com/juju/testing"
	jc "github.com/juju/testing/checkers"
	"github.com/lxc/lxd"
	gc "gopkg.in/check.v1"
)

type ConnectSuite struct {
	testing.IsolationSuite
}

var _ = gc.Suite(&ConnectSuite{})

func (cs ConnectSuite) TestLocalConnectError(c *gc.C) {
	cs.PatchValue(&lxdNewClientFromInfo, fakeNewClientFromInfo)

	cfg, err := Config{
		Remote: Local,
	}.WithDefaults()
	c.Assert(err, jc.ErrorIsNil)
	_, err = Connect(cfg)

	// Yes, the error message actually matters here... this is being displayed
	// to the user.
	c.Assert(err, gc.ErrorMatches, "can't connect to the local LXD server.*")
}

func (cs ConnectSuite) TestRemoteConnectError(c *gc.C) {
	cs.PatchValue(&lxdNewClientFromInfo, fakeNewClientFromInfo)

	cfg, err := Config{
		Remote: Remote{
			Name: "foo",
			Host: "a.b.c",
			Cert: &Cert{
				Name:    "really-valid",
				CertPEM: []byte("kinda-public"),
				KeyPEM:  []byte("super-secret"),
			},
		},
	}.WithDefaults()
	c.Assert(err, jc.ErrorIsNil)
	_, err = Connect(cfg)

	c.Assert(errors.Cause(err), gc.Equals, testerr)
}

func (cs ConnectSuite) TestLXDClientForCloudImagesDefault(c *gc.C) {
	// Note: this assumes current LXD behavior to not actually connect to
	// the remote host until we try to perform an action.
	client, err := lxdClientForCloudImages(Config{})
	c.Assert(err, jc.ErrorIsNil)
	c.Check(client.BaseURL, gc.Equals, lxd.UbuntuRemote.Addr)
}

func (cs ConnectSuite) TestLXDClientForCloudImagesDaily(c *gc.C) {
	client, err := lxdClientForCloudImages(Config{
		ImageStream: StreamDaily,
	})
	c.Assert(err, jc.ErrorIsNil)
	c.Check(client.BaseURL, gc.Equals, lxd.UbuntuDailyRemote.Addr)
}

func (cs ConnectSuite) TestLXDClientForCloudImagesReleases(c *gc.C) {
	client, err := lxdClientForCloudImages(Config{
		ImageStream: StreamReleases,
	})
	c.Assert(err, jc.ErrorIsNil)
	c.Check(client.BaseURL, gc.Equals, lxd.UbuntuRemote.Addr)
}

var testerr = errors.Errorf("boo!")

func fakeNewClientFromInfo(info lxd.ConnectInfo) (*lxd.Client, error) {
	return nil, testerr
}
<|endoftext|>


<commit_before>/*
Copyright 2018 The Kubernetes Authors.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package core

import (
	"fmt"
	"reflect"
	"testing"

	"k8s.io/apimachinery/pkg/api/resource"
	"k8s.io/autoscaler/cluster-autoscaler/cloudprovider"
	testcloudprovider "k8s.io/autoscaler/cluster-autoscaler/cloudprovider/test"
	"k8s.io/autoscaler/cluster-autoscaler/clusterstate/utils"
	"k8s.io/autoscaler/cluster-autoscaler/config"
	"k8s.io/autoscaler/cluster-autoscaler/context"
	"k8s.io/autoscaler/cluster-autoscaler/estimator"
	"k8s.io/autoscaler/cluster-autoscaler/expander/random"
	"k8s.io/autoscaler/cluster-autoscaler/metrics"
	"k8s.io/autoscaler/cluster-autoscaler/processors"
	processor_callbacks "k8s.io/autoscaler/cluster-autoscaler/processors/callbacks"
	"k8s.io/autoscaler/cluster-autoscaler/processors/nodegroups"
	"k8s.io/autoscaler/cluster-autoscaler/processors/nodegroupset"
	"k8s.io/autoscaler/cluster-autoscaler/processors/status"
	"k8s.io/autoscaler/cluster-autoscaler/simulator"
	"k8s.io/autoscaler/cluster-autoscaler/utils/errors"
	kube_util "k8s.io/autoscaler/cluster-autoscaler/utils/kubernetes"
	"k8s.io/autoscaler/cluster-autoscaler/utils/labels"

	"github.com/stretchr/testify/assert"

	apiv1 "k8s.io/api/core/v1"
	"k8s.io/autoscaler/cluster-autoscaler/clusterstate"
	"k8s.io/autoscaler/cluster-autoscaler/utils/backoff"
	kube_client "k8s.io/client-go/kubernetes"
	kube_record "k8s.io/client-go/tools/record"
	schedulernodeinfo "k8s.io/kubernetes/pkg/scheduler/nodeinfo"
)

type nodeConfig struct {
	name   string
	cpu    int64
	memory int64
	gpu    int64
	ready  bool
	group  string
}

type podConfig struct {
	name         string
	cpu          int64
	memory       int64
	gpu          int64
	node         string
	toleratesGpu bool
}

type groupSizeChange struct {
	groupName  string
	sizeChange int
}

type scaleTestConfig struct {
	nodes                   []nodeConfig
	pods                    []podConfig
	extraPods               []podConfig
	options                 config.AutoscalingOptions
	nodeDeletionTracker     *NodeDeletionTracker
	expansionOptionToChoose groupSizeChange // this will be selected by assertingStrategy.BestOption

	expectedScaleDowns []string
}

type scaleTestResults struct {
	expansionOptions []groupSizeChange
	finalOption      groupSizeChange
	noScaleUpReason  string
	finalScaleDowns  []string
	events           []string
	scaleUpStatus    scaleUpStatusInfo
}

// scaleUpStatusInfo is a simplified form of a ScaleUpStatus, to avoid mocking actual NodeGroup and Pod objects in test config.
type scaleUpStatusInfo struct {
	result                  status.ScaleUpResult
	podsTriggeredScaleUp    []string
	podsRemainUnschedulable []string
	podsAwaitEvaluation     []string
}

func (s *scaleUpStatusInfo) WasSuccessful() bool {
	return s.result == status.ScaleUpSuccessful
}

func extractPodNames(pods []*apiv1.Pod) []string {
	podNames := []string{}
	for _, pod := range pods {
		podNames = append(podNames, pod.Name)
	}
	return podNames
}

func simplifyScaleUpStatus(scaleUpStatus *status.ScaleUpStatus) scaleUpStatusInfo {
	remainUnschedulable := []string{}
	for _, nsi := range scaleUpStatus.PodsRemainUnschedulable {
		remainUnschedulable = append(remainUnschedulable, nsi.Pod.Name)
	}
	return scaleUpStatusInfo{
		result:                  scaleUpStatus.Result,
		podsTriggeredScaleUp:    extractPodNames(scaleUpStatus.PodsTriggeredScaleUp),
		podsRemainUnschedulable: remainUnschedulable,
		podsAwaitEvaluation:     extractPodNames(scaleUpStatus.PodsAwaitEvaluation),
	}
}

// NewTestProcessors returns a set of simple processors for use in tests.
func NewTestProcessors() *processors.AutoscalingProcessors {
	return &processors.AutoscalingProcessors{
		PodListProcessor:       NewFilterOutSchedulablePodListProcessor(),
		NodeGroupListProcessor: &nodegroups.NoOpNodeGroupListProcessor{},
		NodeGroupSetProcessor:  &nodegroupset.BalancingNodeGroupSetProcessor{},
		// TODO(bskiba): change scale up test so that this can be a NoOpProcessor
		ScaleUpStatusProcessor:     &status.EventingScaleUpStatusProcessor{},
		ScaleDownStatusProcessor:   &status.NoOpScaleDownStatusProcessor{},
		AutoscalingStatusProcessor: &status.NoOpAutoscalingStatusProcessor{},
		NodeGroupManager:           nodegroups.NewDefaultNodeGroupManager(),
	}
}

// NewScaleTestAutoscalingContext creates a new test autoscaling context for scaling tests.
func NewScaleTestAutoscalingContext(
	options config.AutoscalingOptions, fakeClient kube_client.Interface,
	listers kube_util.ListerRegistry, provider cloudprovider.CloudProvider,
	processorCallbacks processor_callbacks.ProcessorCallbacks) (context.AutoscalingContext, error) {
	fakeRecorder := kube_record.NewFakeRecorder(5)
	fakeLogRecorder, err := utils.NewStatusMapRecorder(fakeClient, "kube-system", fakeRecorder, false)
	if err != nil {
		return context.AutoscalingContext{}, err
	}
	// Ignoring error here is safe - if a test doesn't specify valid estimatorName,
	// it either doesn't need one, or should fail when it turns out to be nil.
	estimatorBuilder, _ := estimator.NewEstimatorBuilder(options.EstimatorName)
	predicateChecker, err := simulator.NewTestPredicateChecker()
	if err != nil {
		return context.AutoscalingContext{}, err
	}
	clusterSnapshot := simulator.NewBasicClusterSnapshot()
	return context.AutoscalingContext{
		AutoscalingOptions: options,
		AutoscalingKubeClients: context.AutoscalingKubeClients{
			ClientSet:      fakeClient,
			Recorder:       fakeRecorder,
			LogRecorder:    fakeLogRecorder,
			ListerRegistry: listers,
		},
		CloudProvider:      provider,
		PredicateChecker:   predicateChecker,
		ClusterSnapshot:    clusterSnapshot,
		ExpanderStrategy:   random.NewStrategy(),
		EstimatorBuilder:   estimatorBuilder,
		ProcessorCallbacks: processorCallbacks,
	}, nil
}

type mockAutoprovisioningNodeGroupManager struct {
	t           *testing.T
	extraGroups int
}

func (p *mockAutoprovisioningNodeGroupManager) CreateNodeGroup(context *context.AutoscalingContext, nodeGroup cloudprovider.NodeGroup) (nodegroups.CreateNodeGroupResult, errors.AutoscalerError) {
	newNodeGroup, err := nodeGroup.Create()
	assert.NoError(p.t, err)
	metrics.RegisterNodeGroupCreation()
	extraGroups := []cloudprovider.NodeGroup{}
	testGroup, ok := nodeGroup.(*testcloudprovider.TestNodeGroup)
	if !ok {
		return nodegroups.CreateNodeGroupResult{}, errors.ToAutoscalerError(errors.InternalError, fmt.Errorf("expected test node group, found %v", reflect.TypeOf(nodeGroup)))
	}
	testCloudProvider, ok := context.CloudProvider.(*testcloudprovider.TestCloudProvider)
	if !ok {
		return nodegroups.CreateNodeGroupResult{}, errors.ToAutoscalerError(errors.InternalError, fmt.Errorf("expected test CloudProvider, found %v", reflect.TypeOf(context.CloudProvider)))
	}
	for i := 0; i < p.extraGroups; i++ {
		extraNodeGroup, err := testCloudProvider.NewNodeGroupWithId(
			testGroup.MachineType(),
			testGroup.Labels(),
			map[string]string{},
			[]apiv1.Taint{},
			map[string]resource.Quantity{},
			fmt.Sprintf("%d", i+1),
		)
		assert.NoError(p.t, err)
		extraGroup, err := extraNodeGroup.Create()
		assert.NoError(p.t, err)
		metrics.RegisterNodeGroupCreation()
		extraGroups = append(extraGroups, extraGroup)
	}
	result := nodegroups.CreateNodeGroupResult{
		MainCreatedNodeGroup:   newNodeGroup,
		ExtraCreatedNodeGroups: extraGroups,
	}
	return result, nil
}

func (p *mockAutoprovisioningNodeGroupManager) RemoveUnneededNodeGroups(context *context.AutoscalingContext) (removedNodeGroups []cloudprovider.NodeGroup, err error) {
	if !context.AutoscalingOptions.NodeAutoprovisioningEnabled {
		return nil, nil
	}
	removedNodeGroups = make([]cloudprovider.NodeGroup, 0)
	nodeGroups := context.CloudProvider.NodeGroups()
	for _, nodeGroup := range nodeGroups {
		if !nodeGroup.Autoprovisioned() {
			continue
		}
		targetSize, err := nodeGroup.TargetSize()
		assert.NoError(p.t, err)
		if targetSize > 0 {
			continue
		}
		nodes, err := nodeGroup.Nodes()
		assert.NoError(p.t, err)
		if len(nodes) > 0 {
			continue
		}
		err = nodeGroup.Delete()
		assert.NoError(p.t, err)
		removedNodeGroups = append(removedNodeGroups, nodeGroup)
	}
	return removedNodeGroups, nil
}

func (p *mockAutoprovisioningNodeGroupManager) CleanUp() {
}

type mockAutoprovisioningNodeGroupListProcessor struct {
	t *testing.T
}

func (p *mockAutoprovisioningNodeGroupListProcessor) Process(context *context.AutoscalingContext, nodeGroups []cloudprovider.NodeGroup, nodeInfos map[string]*schedulernodeinfo.NodeInfo,
	unschedulablePods []*apiv1.Pod) ([]cloudprovider.NodeGroup, map[string]*schedulernodeinfo.NodeInfo, error) {

	machines, err := context.CloudProvider.GetAvailableMachineTypes()
	assert.NoError(p.t, err)

	bestLabels := labels.BestLabelSet(unschedulablePods)
	for _, machineType := range machines {
		nodeGroup, err := context.CloudProvider.NewNodeGroup(machineType, bestLabels, map[string]string{}, []apiv1.Taint{}, map[string]resource.Quantity{})
		assert.NoError(p.t, err)
		nodeInfo, err := nodeGroup.TemplateNodeInfo()
		assert.NoError(p.t, err)
		nodeInfos[nodeGroup.Id()] = nodeInfo
		nodeGroups = append(nodeGroups, nodeGroup)
	}
	return nodeGroups, nodeInfos, nil
}

func (p *mockAutoprovisioningNodeGroupListProcessor) CleanUp() {
}

func newBackoff() backoff.Backoff {
	return backoff.NewIdBasedExponentialBackoff(clusterstate.InitialNodeGroupBackoffDuration, clusterstate.MaxNodeGroupBackoffDuration, clusterstate.NodeGroupBackoffResetTimeout)
}
<commit_msg>increase test event recorder buffer<commit_after>/*
Copyright 2018 The Kubernetes Authors.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package core

import (
	"fmt"
	"reflect"
	"testing"

	"k8s.io/apimachinery/pkg/api/resource"
	"k8s.io/autoscaler/cluster-autoscaler/cloudprovider"
	testcloudprovider "k8s.io/autoscaler/cluster-autoscaler/cloudprovider/test"
	"k8s.io/autoscaler/cluster-autoscaler/clusterstate/utils"
	"k8s.io/autoscaler/cluster-autoscaler/config"
	"k8s.io/autoscaler/cluster-autoscaler/context"
	"k8s.io/autoscaler/cluster-autoscaler/estimator"
	"k8s.io/autoscaler/cluster-autoscaler/expander/random"
	"k8s.io/autoscaler/cluster-autoscaler/metrics"
	"k8s.io/autoscaler/cluster-autoscaler/processors"
	processor_callbacks "k8s.io/autoscaler/cluster-autoscaler/processors/callbacks"
	"k8s.io/autoscaler/cluster-autoscaler/processors/nodegroups"
	"k8s.io/autoscaler/cluster-autoscaler/processors/nodegroupset"
	"k8s.io/autoscaler/cluster-autoscaler/processors/status"
	"k8s.io/autoscaler/cluster-autoscaler/simulator"
	"k8s.io/autoscaler/cluster-autoscaler/utils/errors"
	kube_util "k8s.io/autoscaler/cluster-autoscaler/utils/kubernetes"
	"k8s.io/autoscaler/cluster-autoscaler/utils/labels"

	"github.com/stretchr/testify/assert"

	apiv1 "k8s.io/api/core/v1"
	"k8s.io/autoscaler/cluster-autoscaler/clusterstate"
	"k8s.io/autoscaler/cluster-autoscaler/utils/backoff"
	kube_client "k8s.io/client-go/kubernetes"
	kube_record "k8s.io/client-go/tools/record"
	schedulernodeinfo "k8s.io/kubernetes/pkg/scheduler/nodeinfo"
)

type nodeConfig struct {
	name   string
	cpu    int64
	memory int64
	gpu    int64
	ready  bool
	group  string
}

type podConfig struct {
	name         string
	cpu          int64
	memory       int64
	gpu          int64
	node         string
	toleratesGpu bool
}

type groupSizeChange struct {
	groupName  string
	sizeChange int
}

type scaleTestConfig struct {
	nodes                   []nodeConfig
	pods                    []podConfig
	extraPods               []podConfig
	options                 config.AutoscalingOptions
	nodeDeletionTracker     *NodeDeletionTracker
	expansionOptionToChoose groupSizeChange // this will be selected by assertingStrategy.BestOption

	expectedScaleDowns []string
}

type scaleTestResults struct {
	expansionOptions []groupSizeChange
	finalOption      groupSizeChange
	noScaleUpReason  string
	finalScaleDowns  []string
	events           []string
	scaleUpStatus    scaleUpStatusInfo
}

// scaleUpStatusInfo is a simplified form of a ScaleUpStatus, to avoid mocking actual NodeGroup and Pod objects in test config.
type scaleUpStatusInfo struct {
	result                  status.ScaleUpResult
	podsTriggeredScaleUp    []string
	podsRemainUnschedulable []string
	podsAwaitEvaluation     []string
}

func (s *scaleUpStatusInfo) WasSuccessful() bool {
	return s.result == status.ScaleUpSuccessful
}

func extractPodNames(pods []*apiv1.Pod) []string {
	podNames := []string{}
	for _, pod := range pods {
		podNames = append(podNames, pod.Name)
	}
	return podNames
}

func simplifyScaleUpStatus(scaleUpStatus *status.ScaleUpStatus) scaleUpStatusInfo {
	remainUnschedulable := []string{}
	for _, nsi := range scaleUpStatus.PodsRemainUnschedulable {
		remainUnschedulable = append(remainUnschedulable, nsi.Pod.Name)
	}
	return scaleUpStatusInfo{
		result:                  scaleUpStatus.Result,
		podsTriggeredScaleUp:    extractPodNames(scaleUpStatus.PodsTriggeredScaleUp),
		podsRemainUnschedulable: remainUnschedulable,
		podsAwaitEvaluation:     extractPodNames(scaleUpStatus.PodsAwaitEvaluation),
	}
}

// NewTestProcessors returns a set of simple processors for use in tests.
func NewTestProcessors() *processors.AutoscalingProcessors {
	return &processors.AutoscalingProcessors{
		PodListProcessor:       NewFilterOutSchedulablePodListProcessor(),
		NodeGroupListProcessor: &nodegroups.NoOpNodeGroupListProcessor{},
		NodeGroupSetProcessor:  &nodegroupset.BalancingNodeGroupSetProcessor{},
		// TODO(bskiba): change scale up test so that this can be a NoOpProcessor
		ScaleUpStatusProcessor:     &status.EventingScaleUpStatusProcessor{},
		ScaleDownStatusProcessor:   &status.NoOpScaleDownStatusProcessor{},
		AutoscalingStatusProcessor: &status.NoOpAutoscalingStatusProcessor{},
		NodeGroupManager:           nodegroups.NewDefaultNodeGroupManager(),
	}
}

// NewScaleTestAutoscalingContext creates a new test autoscaling context for scaling tests.
func NewScaleTestAutoscalingContext(
	options config.AutoscalingOptions, fakeClient kube_client.Interface,
	listers kube_util.ListerRegistry, provider cloudprovider.CloudProvider,
	processorCallbacks processor_callbacks.ProcessorCallbacks) (context.AutoscalingContext, error) {
	// Not enough buffer space causes the test to hang without printing any logs.
	// This is not useful.
	fakeRecorder := kube_record.NewFakeRecorder(100)
	fakeLogRecorder, err := utils.NewStatusMapRecorder(fakeClient, "kube-system", fakeRecorder, false)
	if err != nil {
		return context.AutoscalingContext{}, err
	}
	// Ignoring error here is safe - if a test doesn't specify valid estimatorName,
	// it either doesn't need one, or should fail when it turns out to be nil.
	estimatorBuilder, _ := estimator.NewEstimatorBuilder(options.EstimatorName)
	predicateChecker, err := simulator.NewTestPredicateChecker()
	if err != nil {
		return context.AutoscalingContext{}, err
	}
	clusterSnapshot := simulator.NewBasicClusterSnapshot()
	return context.AutoscalingContext{
		AutoscalingOptions: options,
		AutoscalingKubeClients: context.AutoscalingKubeClients{
			ClientSet:      fakeClient,
			Recorder:       fakeRecorder,
			LogRecorder:    fakeLogRecorder,
			ListerRegistry: listers,
		},
		CloudProvider:      provider,
		PredicateChecker:   predicateChecker,
		ClusterSnapshot:    clusterSnapshot,
		ExpanderStrategy:   random.NewStrategy(),
		EstimatorBuilder:   estimatorBuilder,
		ProcessorCallbacks: processorCallbacks,
	}, nil
}

type mockAutoprovisioningNodeGroupManager struct {
	t           *testing.T
	extraGroups int
}

func (p *mockAutoprovisioningNodeGroupManager) CreateNodeGroup(context *context.AutoscalingContext, nodeGroup cloudprovider.NodeGroup) (nodegroups.CreateNodeGroupResult, errors.AutoscalerError) {
	newNodeGroup, err := nodeGroup.Create()
	assert.NoError(p.t, err)
	metrics.RegisterNodeGroupCreation()
	extraGroups := []cloudprovider.NodeGroup{}
	testGroup, ok := nodeGroup.(*testcloudprovider.TestNodeGroup)
	if !ok {
		return nodegroups.CreateNodeGroupResult{}, errors.ToAutoscalerError(errors.InternalError, fmt.Errorf("expected test node group, found %v", reflect.TypeOf(nodeGroup)))
	}
	testCloudProvider, ok := context.CloudProvider.(*testcloudprovider.TestCloudProvider)
	if !ok {
		return nodegroups.CreateNodeGroupResult{}, errors.ToAutoscalerError(errors.InternalError, fmt.Errorf("expected test CloudProvider, found %v", reflect.TypeOf(context.CloudProvider)))
	}
	for i := 0; i < p.extraGroups; i++ {
		extraNodeGroup, err := testCloudProvider.NewNodeGroupWithId(
			testGroup.MachineType(),
			testGroup.Labels(),
			map[string]string{},
			[]apiv1.Taint{},
			map[string]resource.Quantity{},
			fmt.Sprintf("%d", i+1),
		)
		assert.NoError(p.t, err)
		extraGroup, err := extraNodeGroup.Create()
		assert.NoError(p.t, err)
		metrics.RegisterNodeGroupCreation()
		extraGroups = append(extraGroups, extraGroup)
	}
	result := nodegroups.CreateNodeGroupResult{
		MainCreatedNodeGroup:   newNodeGroup,
		ExtraCreatedNodeGroups: extraGroups,
	}
	return result, nil
}

func (p *mockAutoprovisioningNodeGroupManager) RemoveUnneededNodeGroups(context *context.AutoscalingContext) (removedNodeGroups []cloudprovider.NodeGroup, err error) {
	if !context.AutoscalingOptions.NodeAutoprovisioningEnabled {
		return nil, nil
	}
	removedNodeGroups = make([]cloudprovider.NodeGroup, 0)
	nodeGroups := context.CloudProvider.NodeGroups()
	for _, nodeGroup := range nodeGroups {
		if !nodeGroup.Autoprovisioned() {
			continue
		}
		targetSize, err := nodeGroup.TargetSize()
		assert.NoError(p.t, err)
		if targetSize > 0 {
			continue
		}
		nodes, err := nodeGroup.Nodes()
		assert.NoError(p.t, err)
		if len(nodes) > 0 {
			continue
		}
		err = nodeGroup.Delete()
		assert.NoError(p.t, err)
		removedNodeGroups = append(removedNodeGroups, nodeGroup)
	}
	return removedNodeGroups, nil
}

func (p *mockAutoprovisioningNodeGroupManager) CleanUp() {
}

type mockAutoprovisioningNodeGroupListProcessor struct {
	t *testing.T
}

func (p *mockAutoprovisioningNodeGroupListProcessor) Process(context *context.AutoscalingContext, nodeGroups []cloudprovider.NodeGroup, nodeInfos map[string]*schedulernodeinfo.NodeInfo,
	unschedulablePods []*apiv1.Pod) ([]cloudprovider.NodeGroup, map[string]*schedulernodeinfo.NodeInfo, error) {

	machines, err := context.CloudProvider.GetAvailableMachineTypes()
	assert.NoError(p.t, err)

	bestLabels := labels.BestLabelSet(unschedulablePods)
	for _, machineType := range machines {
		nodeGroup, err := context.CloudProvider.NewNodeGroup(machineType, bestLabels, map[string]string{}, []apiv1.Taint{}, map[string]resource.Quantity{})
		assert.NoError(p.t, err)
		nodeInfo, err := nodeGroup.TemplateNodeInfo()
		assert.NoError(p.t, err)
		nodeInfos[nodeGroup.Id()] = nodeInfo
		nodeGroups = append(nodeGroups, nodeGroup)
	}
	return nodeGroups, nodeInfos, nil
}

func (p *mockAutoprovisioningNodeGroupListProcessor) CleanUp() {
}

func newBackoff() backoff.Backoff {
	return backoff.NewIdBasedExponentialBackoff(clusterstate.InitialNodeGroupBackoffDuration, clusterstate.MaxNodeGroupBackoffDuration, clusterstate.NodeGroupBackoffResetTimeout)
}
<|endoftext|>


<commit_before>/*
Copyright 2016 The Kubernetes Authors.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package kubernetes

import (
	"fmt"
	"time"

	apiv1 "k8s.io/kubernetes/pkg/api/v1"
)

// IsNodeReadyAndSchedulable returns true if the node is ready and schedulable.
func IsNodeReadyAndSchedulable(node *apiv1.Node) bool {
	ready, _, _ := GetReadinessState(node)
	if !ready {
		return false
	}
	// Ignore nodes that are marked unschedulable
	if node.Spec.Unschedulable {
		return false
	}
	return true
}

// GetReadinessState gets readiness state for the node
func GetReadinessState(node *apiv1.Node) (isNodeReady bool, lastTransitionTime time.Time, err error) {
	canNodeBeReady, readyFound := true, false
	lastTransitionTime = time.Time{}

	for _, cond := range node.Status.Conditions {
		switch cond.Type {
		case apiv1.NodeReady:
			readyFound = true
			if cond.Status == apiv1.ConditionFalse {
				canNodeBeReady = false
			}
			if lastTransitionTime.Before(cond.LastTransitionTime.Time) {
				lastTransitionTime = cond.LastTransitionTime.Time
			}
		case apiv1.NodeOutOfDisk:
			if cond.Status == apiv1.ConditionTrue {
				canNodeBeReady = false
			}
			if lastTransitionTime.Before(cond.LastTransitionTime.Time) {
				lastTransitionTime = cond.LastTransitionTime.Time
			}
		case apiv1.NodeNetworkUnavailable:
			if cond.Status == apiv1.ConditionTrue {
				canNodeBeReady = false
			}
			if lastTransitionTime.Before(cond.LastTransitionTime.Time) {
				lastTransitionTime = cond.LastTransitionTime.Time
			}
		}
	}
	if !readyFound {
		return false, time.Time{}, fmt.Errorf("feadiness information not found")
	}
	return canNodeBeReady, lastTransitionTime, nil
}
<commit_msg>Cluster-Autoscaler: "unknown" readiness -> unready<commit_after>/*
Copyright 2016 The Kubernetes Authors.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package kubernetes

import (
	"fmt"
	"time"

	apiv1 "k8s.io/kubernetes/pkg/api/v1"
)

// IsNodeReadyAndSchedulable returns true if the node is ready and schedulable.
func IsNodeReadyAndSchedulable(node *apiv1.Node) bool {
	ready, _, _ := GetReadinessState(node)
	if !ready {
		return false
	}
	// Ignore nodes that are marked unschedulable
	if node.Spec.Unschedulable {
		return false
	}
	return true
}

// GetReadinessState gets readiness state for the node
func GetReadinessState(node *apiv1.Node) (isNodeReady bool, lastTransitionTime time.Time, err error) {
	canNodeBeReady, readyFound := true, false
	lastTransitionTime = time.Time{}

	for _, cond := range node.Status.Conditions {
		switch cond.Type {
		case apiv1.NodeReady:
			readyFound = true
			if cond.Status == apiv1.ConditionFalse || cond.Status == apiv1.ConditionUnknown {
				canNodeBeReady = false
			}
			if lastTransitionTime.Before(cond.LastTransitionTime.Time) {
				lastTransitionTime = cond.LastTransitionTime.Time
			}
		case apiv1.NodeOutOfDisk:
			if cond.Status == apiv1.ConditionTrue {
				canNodeBeReady = false
			}
			if lastTransitionTime.Before(cond.LastTransitionTime.Time) {
				lastTransitionTime = cond.LastTransitionTime.Time
			}
		case apiv1.NodeNetworkUnavailable:
			if cond.Status == apiv1.ConditionTrue {
				canNodeBeReady = false
			}
			if lastTransitionTime.Before(cond.LastTransitionTime.Time) {
				lastTransitionTime = cond.LastTransitionTime.Time
			}
		}
	}
	if !readyFound {
		return false, time.Time{}, fmt.Errorf("feadiness information not found")
	}
	return canNodeBeReady, lastTransitionTime, nil
}
<|endoftext|>


<commit_before>package bot

import (
	"sync"

	"github.com/belak/irc"
)

// BasicMux is a simple IRC event multiplexer.
// It matches the command against registered Handlers and calls the correct set.
//
// Handlers will be processed in the order in which they were added.
// Registering a handler with a "*" command will cause it to receive all events.
// Note that even though "*" will match all commands, glob matching is not used.
type BasicMux struct {
	m  map[string][]BotFunc
	mu *sync.Mutex
}

// This will create an initialized BasicMux with no handlers.
func NewBasicMux() *BasicMux {
	return &BasicMux{
		make(map[string][]BotFunc),
		&sync.Mutex{},
	}
}

// BasicMux.Event will register a Handler
func (m *BasicMux) Event(c string, h BotFunc) {
	m.mu.Lock()
	defer m.mu.Unlock()

	m.m[c] = append(m.m[c], h)
}

// HandleEvent allows us to be a Handler so we can nest BasicMuxes
//
// The BasicMux simply dispatches all the Handler commands as needed
func (m *BasicMux) HandleEvent(b *Bot, e *irc.Event) {
	// Lock our handlers so we don't crap bricks if a
	// handler is added or removed from under our feet.
	m.mu.Lock()
	defer m.mu.Unlock()

	// Star means ALL THE THINGS
	// Really, this is only useful for logging
	for _, h := range m.m["*"] {
		h(b, e)
	}

	// Now that we've done the global handlers, we can run
	// the ones specific to this command
	for _, h := range m.m[e.Command] {
		h(b, e)
	}
}
<commit_msg>Fix package declarations by removing mux<commit_after><|endoftext|>


<commit_before>// TODO FIXME name me
package worker

import (
	"container/list"
	"errors"
	"time"

	context "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/go.net/context"
	process "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/goprocess"
	blocks "github.com/jbenet/go-ipfs/blocks"
	exchange "github.com/jbenet/go-ipfs/exchange"
	util "github.com/jbenet/go-ipfs/util"
)

var log = util.Logger("blockservice")

var DefaultConfig = Config{
	NumWorkers:       1,
	ClientBufferSize: 0,
	WorkerBufferSize: 0,
}

type Config struct {
	// NumWorkers sets the number of background workers that provide blocks to
	// the exchange.
	NumWorkers int

	// ClientBufferSize allows clients of HasBlock to send up to
	// |ClientBufferSize| blocks without blocking.
	ClientBufferSize int

	// WorkerBufferSize can be used in conjunction with NumWorkers to reduce
	// communication-coordination within the worker.
	WorkerBufferSize int
}

// TODO FIXME name me
type Worker struct {
	// added accepts blocks from client
	added    chan *blocks.Block
	exchange exchange.Interface

	// workQueue is owned by the client worker
	// process manages life-cycle
	process process.Process
}

func NewWorker(e exchange.Interface, c Config) *Worker {
	if c.NumWorkers < 1 {
		c.NumWorkers = 1 // provide a sane default
	}
	w := &Worker{
		exchange: e,
		added:    make(chan *blocks.Block, c.ClientBufferSize),
		process:  process.WithParent(process.Background()), // internal management
	}
	w.start(c)
	return w
}

func (w *Worker) HasBlock(b *blocks.Block) error {
	select {
	case <-w.process.Closed():
		return errors.New("blockservice worker is closed")
	case w.added <- b:
		return nil
	}
}

func (w *Worker) Close() error {
	log.Debug("blockservice provide worker is shutting down...")
	return w.process.Close()
}

func (w *Worker) start(c Config) {

	workerChan := make(chan *blocks.Block, c.WorkerBufferSize)

	// clientWorker handles incoming blocks from |w.added| and sends to
	// |workerChan|. This will never block the client.
	w.process.Go(func(proc process.Process) {
		defer close(workerChan)

		var workQueue BlockList
		for {

			// take advantage of the fact that sending on nil channel always
			// blocks so that a message is only sent if a block exists
			sendToWorker := workerChan
			nextBlock := workQueue.Pop()
			if nextBlock == nil {
				sendToWorker = nil
			}

			select {

			// if worker is ready and there's a block to process, send the
			// block
			case sendToWorker <- nextBlock:
			case <-time.Tick(5 * time.Second):
				if workQueue.Len() > 0 {
					log.Debugf("%d blocks in blockservice provide queue...", workQueue.Len())
				}
			case block := <-w.added:
				if nextBlock != nil {
					workQueue.Push(nextBlock) // missed the chance to send it
				}
				// if the client sends another block, add it to the queue.
				workQueue.Push(block)
			case <-proc.Closing():
				return
			}
		}
	})

	// reads from |workerChan| until process closes
	w.process.Go(func(proc process.Process) {
		ctx, cancel := context.WithCancel(context.Background())

		// shuts down an in-progress HasBlock operation
		proc.Go(func(proc process.Process) {
			<-proc.Closing()
			cancel()
		})

		for {
			select {
			case <-proc.Closing():
				return
			case block, ok := <-workerChan:
				if !ok {
					return
				}
				if err := w.exchange.HasBlock(ctx, block); err != nil {
					// TODO log event?
				}
			}
		}
	})
}

type BlockList struct {
	list list.List
}

func (s *BlockList) PushFront(b *blocks.Block) {
	// FIXME find figures
	s.list.PushFront(b)
}

func (s *BlockList) Push(b *blocks.Block) {
	s.list.PushBack(b)
}

func (s *BlockList) Pop() *blocks.Block {
	if s.list.Len() == 0 {
		return nil
	}
	e := s.list.Front()
	s.list.Remove(e)
	return e.Value.(*blocks.Block)
}

func (s *BlockList) Len() int {
	return s.list.Len()
}
<commit_msg>use rate-limiter<commit_after>// TODO FIXME name me
package worker

import (
	"container/list"
	"errors"
	"time"

	context "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/go.net/context"
	process "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/goprocess"
	ratelimit "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/goprocess/ratelimit"
	blocks "github.com/jbenet/go-ipfs/blocks"
	exchange "github.com/jbenet/go-ipfs/exchange"
	util "github.com/jbenet/go-ipfs/util"
)

var log = util.Logger("blockservice")

var DefaultConfig = Config{
	NumWorkers:       1,
	ClientBufferSize: 0,
	WorkerBufferSize: 0,
}

type Config struct {
	// NumWorkers sets the number of background workers that provide blocks to
	// the exchange.
	NumWorkers int

	// ClientBufferSize allows clients of HasBlock to send up to
	// |ClientBufferSize| blocks without blocking.
	ClientBufferSize int

	// WorkerBufferSize can be used in conjunction with NumWorkers to reduce
	// communication-coordination within the worker.
	WorkerBufferSize int
}

// TODO FIXME name me
type Worker struct {
	// added accepts blocks from client
	added    chan *blocks.Block
	exchange exchange.Interface

	// workQueue is owned by the client worker
	// process manages life-cycle
	process process.Process
}

func NewWorker(e exchange.Interface, c Config) *Worker {
	if c.NumWorkers < 1 {
		c.NumWorkers = 1 // provide a sane default
	}
	w := &Worker{
		exchange: e,
		added:    make(chan *blocks.Block, c.ClientBufferSize),
		process:  process.WithParent(process.Background()), // internal management
	}
	w.start(c)
	return w
}

func (w *Worker) HasBlock(b *blocks.Block) error {
	select {
	case <-w.process.Closed():
		return errors.New("blockservice worker is closed")
	case w.added <- b:
		return nil
	}
}

func (w *Worker) Close() error {
	log.Debug("blockservice provide worker is shutting down...")
	return w.process.Close()
}

func (w *Worker) start(c Config) {

	workerChan := make(chan *blocks.Block, c.WorkerBufferSize)

	// clientWorker handles incoming blocks from |w.added| and sends to
	// |workerChan|. This will never block the client.
	w.process.Go(func(proc process.Process) {
		defer close(workerChan)

		var workQueue BlockList
		for {

			// take advantage of the fact that sending on nil channel always
			// blocks so that a message is only sent if a block exists
			sendToWorker := workerChan
			nextBlock := workQueue.Pop()
			if nextBlock == nil {
				sendToWorker = nil
			}

			select {

			// if worker is ready and there's a block to process, send the
			// block
			case sendToWorker <- nextBlock:
			case <-time.Tick(5 * time.Second):
				if workQueue.Len() > 0 {
					log.Debugf("%d blocks in blockservice provide queue...", workQueue.Len())
				}
			case block := <-w.added:
				if nextBlock != nil {
					workQueue.Push(nextBlock) // missed the chance to send it
				}
				// if the client sends another block, add it to the queue.
				workQueue.Push(block)
			case <-proc.Closing():
				return
			}
		}
	})

	// reads from |workerChan| until process closes
	w.process.Go(func(proc process.Process) {
		ctx, cancel := context.WithCancel(context.Background())

		// shuts down an in-progress HasBlock operation
		proc.Go(func(proc process.Process) {
			<-proc.Closing()
			cancel()
		})

		limiter := ratelimit.NewRateLimiter(proc, c.NumWorkers)
		for {
			select {
			case <-proc.Closing():
				return
			case block, ok := <-workerChan:
				if !ok {
					return
				}
				limiter.LimitedGo(func(proc process.Process) {
					if err := w.exchange.HasBlock(ctx, block); err != nil {
						// TODO log event?
					}
				})
			}
		}
	})
}

type BlockList struct {
	list list.List
}

func (s *BlockList) PushFront(b *blocks.Block) {
	// FIXME find figures
	s.list.PushFront(b)
}

func (s *BlockList) Push(b *blocks.Block) {
	s.list.PushBack(b)
}

func (s *BlockList) Pop() *blocks.Block {
	if s.list.Len() == 0 {
		return nil
	}
	e := s.list.Front()
	s.list.Remove(e)
	return e.Value.(*blocks.Block)
}

func (s *BlockList) Len() int {
	return s.list.Len()
}
<|endoftext|>


<commit_before>// This file is part of fs1up.
// Copyright (C) 2014 Andreas Klauer <Andreas.Klauer@metamorpher.de>
// License: GPL-2

// Package nbd uses the Linux NBD layer to emulate a block device in user space
package nbd

const (
	// Defined in <linux/nbd.h>:
	NBD_SET_SOCK        = 43776
	NBD_SET_BLKSIZE     = 43777
	NBD_SET_SIZE        = 43778
	NBD_DO_IT           = 43779
	NBD_CLEAR_SOCK      = 43780
	NBD_CLEAR_QUE       = 43781
	NBD_PRINT_DEBUG     = 43782
	NBD_SET_SIZE_BLOCKS = 43782
	NBD_DISCONNECT      = 43783
	NBD_SET_TIMEOUT     = 43784
	NBD_SET_FLAGS       = 43785
	// enum
	NBD_CMD_READ  = 0
	NBD_CMD_WRITE = 1
	NBD_CMD_DISC  = 2
	NBD_CMD_FLUSH = 3
	NBD_CMD_TRIM  = 4
	// values for flags field
	NBD_FLAG_HAS_FLAGS  = (1 << 0) // nbd-server supports flags
	NBD_FLAG_READ_ONLY  = (1 << 1) // device is read-only
	NBD_FLAG_SEND_FLUSH = (1 << 2) // can flush writeback cache
	// there is a gap here to match userspace
	NBD_FLAG_SEND_TRIM = (1 << 5) // send trim/discard
	// These are sent over the network in the request/reply magic fields
	NBD_REQUEST_MAGIC = 0x25609513
	NBD_REPLY_MAGIC   = 0x67446698
	// Do *not* use magics: 0x12560953 0x96744668.
)
<commit_msg>client ioctls<commit_after>// This file is part of fs1up.
// Copyright (C) 2014 Andreas Klauer <Andreas.Klauer@metamorpher.de>
// License: GPL-2

// Package nbd uses the Linux NBD layer to emulate a block device in user space
package nbd

import (
	"os"
	"runtime"
	"syscall"
)

const (
	// Defined in <linux/fs.h>:
	BLKROSET = 4701
	// Defined in <linux/nbd.h>:
	NBD_SET_SOCK        = 43776
	NBD_SET_BLKSIZE     = 43777
	NBD_SET_SIZE        = 43778
	NBD_DO_IT           = 43779
	NBD_CLEAR_SOCK      = 43780
	NBD_CLEAR_QUE       = 43781
	NBD_PRINT_DEBUG     = 43782
	NBD_SET_SIZE_BLOCKS = 43782
	NBD_DISCONNECT      = 43783
	NBD_SET_TIMEOUT     = 43784
	NBD_SET_FLAGS       = 43785
	// enum
	NBD_CMD_READ  = 0
	NBD_CMD_WRITE = 1
	NBD_CMD_DISC  = 2
	NBD_CMD_FLUSH = 3
	NBD_CMD_TRIM  = 4
	// values for flags field
	NBD_FLAG_HAS_FLAGS  = (1 << 0) // nbd-server supports flags
	NBD_FLAG_READ_ONLY  = (1 << 1) // device is read-only
	NBD_FLAG_SEND_FLUSH = (1 << 2) // can flush writeback cache
	// there is a gap here to match userspace
	NBD_FLAG_SEND_TRIM = (1 << 5) // send trim/discard
	// These are sent over the network in the request/reply magic fields
	NBD_REQUEST_MAGIC = 0x25609513
	NBD_REPLY_MAGIC   = 0x67446698
	// Do *not* use magics: 0x12560953 0x96744668.
)

// DeviceInfo interface is a subset of os.FileInfo.
type DeviceInfo interface {
	Size() int64
}

// Device interface is a subset of os.File.
type Device interface {
	Stat() (di DeviceInfo, err error)
	ReadAt(b []byte, off int64) (n int, err error)
	WriteAt(b []byte, off int64) (n int, err error)
}

func Client(b Device) {
	runtime.LockOSThread()
	nbd := os.Open("/dev/nbd0") // TODO: find a free one
	fd, _ := Syscall.Socketpair(SOCK_STREAM, AF_UNIX, 0)
	syscall.Syscall(syscall.SYS_IOCTL, nbd.Fd(), NBD_SET_SOCK, fd[0])
	syscall.Syscall(syscall.SYS_IOCTL, nbd.Fd(), NBD_SET_BLKSIZE, 4096)
	syscall.Syscall(syscall.SYS_IOCTL, nbd.Fd(), NBD_SET_SIZE_BLOCKS, b.Stat().Size()/4096)
	syscall.Syscall(syscall.SYS_IOCTL, nbd.Fd(), NBD_SET_FLAGS, 0)
	syscall.Syscall(syscall.SYS_IOCTL, nbd.Fd(), BLKROSET, 0)  // || 1
	syscall.Syscall(syscall.SYS_IOCTL, nbd.Fd(), NBD_DO_IT, 0) // doesn't return
	syscall.Syscall(syscall.SYS_IOCTL, nbd.Fd(), NBD_DISCONNECT, 0)
	syscall.Syscall(syscall.SYS_IOCTL, nbd.Fd(), NBD_CLEAR_SOCK, 0)
	runtime.UnlockOSThread()
}
<|endoftext|>


<commit_before>package asock

// Copyright (c) 2014,2015 Shawn Boyette <shawn@firepear.net>. All
// rights reserved.  Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.

// Socket code for asock

import (
	"bytes"
	"fmt"
	"net"
	"time"

	"firepear.net/qsplit"
)

// sockAccept monitors the listener socket and spawns connections for
// clients.
func (a *Asock) sockAccept() {
	defer a.w.Done()
	var n uint
	for n = 1; true; n++ {
		c, err := a.l.Accept()
		if err != nil {
			select {
			case <-a.q:
				// a.Quit() was invoked; close up shop
				a.genMsg(0, 0, 199, 1, "closing listener socket", nil)
				return
			default:
				// we've had a networking error
				a.Msgr <- &Msg{0, 0, 599, "read from listener socket failed", err}
				return
			}
		}
		// we have a new client
		a.w.Add(1)
		go a.connHandler(c, n)
	}
}

// connHandler dispatches commands from, and sends reponses to, a client. It
// is launched, per-connection, from sockAccept().
func (a *Asock) connHandler(c net.Conn, n uint) {
	defer a.w.Done()
	defer c.Close()
	b1 := make([]byte, 128) // buffer 1:  network reads go here, 128B at a time
	var b2 []byte           // buffer 2:  data accumulates here; requests pulled from here
	var rs [][]byte         // a request, split by word
	var reqnum uint         // request counter for this connection

	a.genMsg(n, reqnum, 100, 1, "client connected", nil)
	for {
		// check if we're a one-shot connection, and if we're done
		if a.t < 0 && reqnum > 0 {
			a.genMsg(n, reqnum, 197, 1, "ending session", nil)
			return
		}
		// set conn timeout deadline if needed
		if a.t != 0 {
			err := a.setConnTimeout(c)
			if err != nil {
				a.genMsg(n, reqnum, 501, 2, "deadline set failed; disconnecting client", err)
				c.Write([]byte("Sorry, an error occurred. Terminating connection."))
				return
			}
		}
		// get some data from the client
		b, err := c.Read(b1)
		if err != nil {
			if err.Error() == "EOF" {
				a.genMsg(n, reqnum, 198, 1, "client disconnected", err)
			} else {
				a.genMsg(n, reqnum, 197, 1, "ending session", err)
			}
			return
		}
		// append what we read into the b2 slice
		b2 = append(b2, b1[:b]...)
		// scan b2 for an EOM marker. if we don't find it, read again.
		eom := bytes.Index(b2, a.eom)
		if eom == -1 {
			continue
		}
		// we did find it, so we have a request., increment reqnum and
		// create put the req in b3.  then reslice b2 to remove the
		// request.
		reqnum++
		b3 := b2[:eom]
		b2 = b2[eom + len(a.eom):]
		// find the command in b3 and send error and list of known
		// commands if we don't recognize it.
		cl := qsplit.Locations(b3)
		cmd := string(b3[cl[0][0]:cl[0][1]])
		dfunc, ok := a.d[cmd]
		if !ok {
			c.Write([]byte(fmt.Sprintf("Unknown command '%v'\nAvailable commands:\n%v",
				cmd, a.help)))
			a.genMsg(n, reqnum, 400, 0, fmt.Sprintf("bad command '%v'", cmd), nil)
			continue
		}
		// ok, we do know the command and we have its dispatch
		// func. call it and send response
		a.genMsg(n, reqnum, 101, 0, fmt.Sprintf("dispatching [%v]", cmd), nil)
		switch dfunc.Argmode {
		case "split":
			rs = qsplit.ToBytes(b3[cl[1][0]:])
		case "nosplit":
			rs = rs[:0]
			rs = append(rs, b3[cl[1][0]:])
		}
		reply, err := dfunc.Func(rs)
		if err != nil {
			c.Write([]byte("Sorry, an error occurred and your request could not be completed."))
			a.genMsg(n, reqnum, 500, 2, "request failed", err)
			continue
		}
		c.Write(reply)
		a.genMsg(n, reqnum, 200, 0, "reply sent", nil)
	}
}

func (a *Asock) setConnTimeout(c net.Conn) error {
	var t time.Duration
	if a.t > 0 {
		t = time.Duration(a.t)
	} else {
		t = time.Duration(a.t - (a.t * 2))
	}
	err := c.SetReadDeadline(time.Now().Add(t * time.Millisecond))
	if err != nil {
		return err
	}
	return nil
}
<commit_msg>Fixed: a request could end up stuck in the read buffer with no further incoming data from the client<commit_after>package asock

// Copyright (c) 2014,2015 Shawn Boyette <shawn@firepear.net>. All
// rights reserved.  Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.

// Socket code for asock

import (
	"bytes"
	"fmt"
	"net"
	"time"

	"firepear.net/qsplit"
)

// sockAccept monitors the listener socket and spawns connections for
// clients.
func (a *Asock) sockAccept() {
	defer a.w.Done()
	var n uint
	for n = 1; true; n++ {
		c, err := a.l.Accept()
		if err != nil {
			select {
			case <-a.q:
				// a.Quit() was invoked; close up shop
				a.genMsg(0, 0, 199, 1, "closing listener socket", nil)
				return
			default:
				// we've had a networking error
				a.Msgr <- &Msg{0, 0, 599, "read from listener socket failed", err}
				return
			}
		}
		// we have a new client
		a.w.Add(1)
		go a.connHandler(c, n)
	}
}

// connHandler dispatches commands from, and sends reponses to, a client. It
// is launched, per-connection, from sockAccept().
func (a *Asock) connHandler(c net.Conn, n uint) {
	defer a.w.Done()
	defer c.Close()
	b1 := make([]byte, 128) // buffer 1:  network reads go here, 128B at a time
	var b2 []byte           // buffer 2:  data accumulates here; requests pulled from here
	var rs [][]byte         // a request, split by word
	var reqnum uint         // request counter for this connection

	a.genMsg(n, reqnum, 100, 1, "client connected", nil)
	for {
		// check if we're a one-shot connection, and if we're done
		if a.t < 0 && reqnum > 0 {
			a.genMsg(n, reqnum, 197, 1, "ending session", nil)
			return
		}
		// set conn timeout deadline if needed
		if a.t != 0 {
			err := a.setConnTimeout(c)
			if err != nil {
				a.genMsg(n, reqnum, 501, 2, "deadline set failed; disconnecting client", err)
				c.Write([]byte("Sorry, an error occurred. Terminating connection."))
				return
			}
		}
		// get some data from the client
		b, err := c.Read(b1)
		if err != nil {
			if err.Error() == "EOF" {
				a.genMsg(n, reqnum, 198, 1, "client disconnected", err)
			} else {
				a.genMsg(n, reqnum, 197, 1, "ending session", err)
			}
			return
		}
		// append what we read into the b2 slice
		b2 = append(b2, b1[:b]...)
		// and enter the dispatch loop
		for {
			// scan b2 for eom; break from loop if we don't find it.
			eom := bytes.Index(b2, a.eom)
			if eom == -1 {
				break
			}
			// we did find it, so we have a request. increment reqnum
			// and slice the req into b3, then reslice b2 to remove
			// this request.
			reqnum++
			b3 := b2[:eom]
			b2 = b2[eom + len(a.eom):]
			// extract the command from b3; send error and list of
			// known commands if we don't recognize it.
			cl := qsplit.Locations(b3)
			cmd := string(b3[cl[0][0]:cl[0][1]])
			dfunc, ok := a.d[cmd]
			if !ok {
				c.Write([]byte(fmt.Sprintf("Unknown command '%v'\nAvailable commands:\n%v",
					cmd, a.help)))
				a.genMsg(n, reqnum, 400, 0, fmt.Sprintf("bad command '%v'", cmd), nil)
				continue
			}
			// we know the command and we have its dispatch func. call
			// it and send response
			a.genMsg(n, reqnum, 101, 0, fmt.Sprintf("dispatching [%v]", cmd), nil)
			switch dfunc.Argmode {
			case "split":
				rs = qsplit.ToBytes(b3[cl[1][0]:])
			case "nosplit":
				rs = rs[:0]
				rs = append(rs, b3[cl[1][0]:])
			}
			reply, err := dfunc.Func(rs)
			if err != nil {
				c.Write([]byte("Sorry, an error occurred and your request could not be completed."))
				a.genMsg(n, reqnum, 500, 2, "request failed", err)
				continue
			}
			c.Write(reply)
			a.genMsg(n, reqnum, 200, 0, "reply sent", nil)
		}
	}
}

func (a *Asock) setConnTimeout(c net.Conn) error {
	var t time.Duration
	if a.t > 0 {
		t = time.Duration(a.t)
	} else {
		t = time.Duration(a.t - (a.t * 2))
	}
	err := c.SetReadDeadline(time.Now().Add(t * time.Millisecond))
	if err != nil {
		return err
	}
	return nil
}
<|endoftext|>


<commit_before>package main

import (
	"net"
	"strings"
)

func IPv4(keys ...string) string {
	return findIP(keys, true)
}

func IPv6(keys ...string) string {
	return findIP(keys, false)
}

func findIP(keys []string, prefer4 bool) string {
	for _, key := range keys {
		if nic, err := net.InterfaceByName(key); err == nil {
			addrs, err := nic.Addrs()
			if ips := filterByVersion(addrs, err, prefer4); len(ips) > 0 {
				return ips[0]
			}
		}
	}

	addrs, err := net.InterfaceAddrs()
	ips := filterByVersion(addrs, err, prefer4)
	for _, ip := range ips {
		for _, key := range keys {
			if strings.HasPrefix(ip, key) {
				return ip
			}
		}
	}

	return ""
}

func filterByVersion(addrs []net.Addr, err error, prefer4 bool) []string {
	var ips []string
	if err != nil {
		return ips
	}

	for _, addr := range addrs {
		ip, _, err := net.ParseCIDR(addr.String())
		if err == nil && (ip.To4() != nil) == prefer4 {
			ips = append(ips, ip.String())
		}
	}

	return ips
}
<commit_msg>Fix order of IP address resolving<commit_after>package main

import (
	"net"
	"strings"
)

func IPv4(keys ...string) string {
	return findIP(keys, true)
}

func IPv6(keys ...string) string {
	return findIP(keys, false)
}

func findIP(keys []string, prefer4 bool) string {
	addrs, err := net.InterfaceAddrs()
	ips := filterByVersion(addrs, err, prefer4)

	for _, key := range keys {
		if nic, err := net.InterfaceByName(key); err == nil {
			addrs, err := nic.Addrs()
			if ips := filterByVersion(addrs, err, prefer4); len(ips) > 0 {
				return ips[0]
			}
		} else {
			for _, ip := range ips {
				if strings.HasPrefix(ip, key) {
					return ip
				}
			}
		}
	}
	return ""
}

func filterByVersion(addrs []net.Addr, err error, prefer4 bool) []string {
	var ips []string
	if err != nil {
		return ips
	}

	for _, addr := range addrs {
		ip, _, err := net.ParseCIDR(addr.String())
		if err == nil && (ip.To4() != nil) == prefer4 {
			ips = append(ips, ip.String())
		}
	}

	return ips
}
<|endoftext|>


<commit_before>// Copyright 2016 The LUCI Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package rawpresentation

import (
	"fmt"
	"time"

	"golang.org/x/net/context"

	"github.com/luci/luci-go/common/clock"
	"github.com/luci/luci-go/common/proto/google"
	miloProto "github.com/luci/luci-go/common/proto/milo"
	"github.com/luci/luci-go/milo/api/resp"
	"github.com/luci/luci-go/milo/common/model"
)

// URLBuilder constructs URLs for various link types.
type URLBuilder interface {
	// LinkURL returns the URL associated with the supplied Link.
	//
	// If no URL could be built for that Link, nil will be returned.
	BuildLink(l *miloProto.Link) *resp.Link
}

// HACK(hinoka): This should be a part of recipes, but just hardcoding a list
// of unimportant things for now.
var builtIn = map[string]struct{}{
	"recipe bootstrap": {},
	"setup_build":      {},
	"recipe result":    {},
}

// miloBuildStep converts a logdog/milo step to a BuildComponent struct.
// buildCompletedTime must be zero if build did not complete yet.
func miloBuildStep(ub URLBuilder, anno *miloProto.Step, isMain bool, buildCompletedTime,
	now time.Time) []*resp.BuildComponent {

	comp := &resp.BuildComponent{Label: anno.Name}
	switch anno.Status {
	case miloProto.Status_RUNNING:
		comp.Status = model.Running

	case miloProto.Status_SUCCESS:
		comp.Status = model.Success

	case miloProto.Status_FAILURE:
		if fd := anno.GetFailureDetails(); fd != nil {
			switch fd.Type {
			case miloProto.FailureDetails_EXCEPTION, miloProto.FailureDetails_INFRA:
				comp.Status = model.InfraFailure

			case miloProto.FailureDetails_EXPIRED:
				comp.Status = model.Expired

			case miloProto.FailureDetails_DM_DEPENDENCY_FAILED:
				comp.Status = model.DependencyFailure

			default:
				comp.Status = model.Failure
			}

			if fd.Text != "" {
				comp.Text = append(comp.Text, fd.Text)
			}
		} else {
			comp.Status = model.Failure
		}

	case miloProto.Status_PENDING:
		comp.Status = model.NotRun

		// Missing the case of waiting on unfinished dependency...
	default:
		comp.Status = model.NotRun
	}

	if !(buildCompletedTime.IsZero() || comp.Status.Terminal()) {
		// The build has completed, but this step has not. Mark it as an
		// infrastructure failure.
		comp.Status = model.InfraFailure
	}

	// Hide the unimportant steps, highlight the interesting ones.
	switch comp.Status {
	case model.NotRun, model.Running:
		if isMain {
			comp.Verbosity = resp.Hidden
		}

	case model.Success:
		if _, ok := builtIn[anno.Name]; ok || isMain {
			comp.Verbosity = resp.Hidden
		}
	case model.InfraFailure, model.Failure:
		comp.Verbosity = resp.Interesting
	}

	// Main link is a link to the stdout.
	var stdoutLink *miloProto.Link
	if anno.StdoutStream != nil {
		stdoutLink = &miloProto.Link{
			Label: "stdout",
			Value: &miloProto.Link_LogdogStream{
				LogdogStream: anno.StdoutStream,
			},
		}
	}

	if anno.Link != nil {
		comp.MainLink = resp.LinkSet{ub.BuildLink(anno.Link)}

		// If we also have a STDOUT stream, add it to our OtherLinks.
		if stdoutLink != nil {
			anno.OtherLinks = append([]*miloProto.Link{stdoutLink}, anno.OtherLinks...)
		}
	} else if stdoutLink != nil {
		comp.MainLink = resp.LinkSet{ub.BuildLink(stdoutLink)}
	}

	// Add STDERR link, if available.
	if anno.StderrStream != nil {
		anno.OtherLinks = append(anno.OtherLinks, &miloProto.Link{
			Label: "stderr",
			Value: &miloProto.Link_LogdogStream{
				LogdogStream: anno.StderrStream,
			},
		})
	}

	// Sub link is for one link per log that isn't stdout.
	for _, link := range anno.GetOtherLinks() {
		if l := ub.BuildLink(link); l != nil {
			comp.SubLink = append(comp.SubLink, resp.LinkSet{l})
		}
	}

	// This should always be a step.
	comp.Type = resp.Step

	// This should always be 0
	comp.LevelsDeep = 0

	// Timestamps
	comp.Started = google.TimeFromProto(anno.Started)
	comp.Finished = google.TimeFromProto(anno.Ended)

	var till time.Time
	switch {
	case !comp.Finished.IsZero():
		till = comp.Finished
	case comp.Status == model.Running:
		till = now
	case !buildCompletedTime.IsZero():
		till = buildCompletedTime
	}
	if !comp.Started.IsZero() && till.After(comp.Started) {
		comp.Duration = till.Sub(comp.Started)
	}

	// This should be the exact same thing.
	comp.Text = append(comp.Text, anno.Text...)

	ss := anno.GetSubstep()
	results := []*resp.BuildComponent{}
	results = append(results, comp)
	// Process nested steps.
	for _, substep := range ss {
		var subanno *miloProto.Step
		switch s := substep.GetSubstep().(type) {
		case *miloProto.Step_Substep_Step:
			subanno = s.Step
		case *miloProto.Step_Substep_AnnotationStream:
			panic("Non-inline substeps not supported")
		default:
			panic(fmt.Errorf("Unknown type %v", s))
		}
		for _, subcomp := range miloBuildStep(ub, subanno, false, buildCompletedTime, now) {
			results = append(results, subcomp)
		}
	}

	return results
}

// AddLogDogToBuild takes a set of logdog streams and populate a milo build.
// build.Summary.Finished must be set.
func AddLogDogToBuild(
	c context.Context, ub URLBuilder, mainAnno *miloProto.Step, build *resp.MiloBuild) {
	now := clock.Now(c)

	// Now fill in each of the step components.
	// TODO(hinoka): This is totes cachable.
	buildCompletedTime := google.TimeFromProto(mainAnno.Ended)
	build.Summary = *(miloBuildStep(ub, mainAnno, true, buildCompletedTime, now)[0])
	for _, substepContainer := range mainAnno.Substep {
		anno := substepContainer.GetStep()
		if anno == nil {
			// TODO: We ignore non-embedded substeps for now.
			continue
		}

		bss := miloBuildStep(ub, anno, false, buildCompletedTime, now)
		for _, bs := range bss {
			if bs.Status != model.Success {
				build.Summary.Text = append(
					build.Summary.Text, fmt.Sprintf("%s %s", bs.Status, bs.Label))
			}
			build.Components = append(build.Components, bs)
			propGroup := &resp.PropertyGroup{GroupName: bs.Label}
			for _, prop := range anno.Property {
				propGroup.Property = append(propGroup.Property, &resp.Property{
					Key:   prop.Name,
					Value: prop.Value,
				})
			}
			build.PropertyGroup = append(build.PropertyGroup, propGroup)
		}
	}

	// Take care of properties
	propGroup := &resp.PropertyGroup{GroupName: "Main"}
	for _, prop := range mainAnno.Property {
		propGroup.Property = append(propGroup.Property, &resp.Property{
			Key:   prop.Name,
			Value: prop.Value,
		})
	}
	build.PropertyGroup = append(build.PropertyGroup, propGroup)

	return
}
<commit_msg>Milo Swarmbucket: Use got_revision as revision<commit_after>// Copyright 2016 The LUCI Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package rawpresentation

import (
	"encoding/json"
	"fmt"
	"time"

	"golang.org/x/net/context"

	"github.com/luci/luci-go/common/clock"
	"github.com/luci/luci-go/common/proto/google"
	miloProto "github.com/luci/luci-go/common/proto/milo"
	"github.com/luci/luci-go/milo/api/resp"
	"github.com/luci/luci-go/milo/common/model"
)

// URLBuilder constructs URLs for various link types.
type URLBuilder interface {
	// LinkURL returns the URL associated with the supplied Link.
	//
	// If no URL could be built for that Link, nil will be returned.
	BuildLink(l *miloProto.Link) *resp.Link
}

// HACK(hinoka): This should be a part of recipes, but just hardcoding a list
// of unimportant things for now.
var builtIn = map[string]struct{}{
	"recipe bootstrap": {},
	"setup_build":      {},
	"recipe result":    {},
}

// miloBuildStep converts a logdog/milo step to a BuildComponent struct.
// buildCompletedTime must be zero if build did not complete yet.
func miloBuildStep(ub URLBuilder, anno *miloProto.Step, isMain bool, buildCompletedTime,
	now time.Time) []*resp.BuildComponent {

	comp := &resp.BuildComponent{Label: anno.Name}
	switch anno.Status {
	case miloProto.Status_RUNNING:
		comp.Status = model.Running

	case miloProto.Status_SUCCESS:
		comp.Status = model.Success

	case miloProto.Status_FAILURE:
		if fd := anno.GetFailureDetails(); fd != nil {
			switch fd.Type {
			case miloProto.FailureDetails_EXCEPTION, miloProto.FailureDetails_INFRA:
				comp.Status = model.InfraFailure

			case miloProto.FailureDetails_EXPIRED:
				comp.Status = model.Expired

			case miloProto.FailureDetails_DM_DEPENDENCY_FAILED:
				comp.Status = model.DependencyFailure

			default:
				comp.Status = model.Failure
			}

			if fd.Text != "" {
				comp.Text = append(comp.Text, fd.Text)
			}
		} else {
			comp.Status = model.Failure
		}

	case miloProto.Status_PENDING:
		comp.Status = model.NotRun

		// Missing the case of waiting on unfinished dependency...
	default:
		comp.Status = model.NotRun
	}

	if !(buildCompletedTime.IsZero() || comp.Status.Terminal()) {
		// The build has completed, but this step has not. Mark it as an
		// infrastructure failure.
		comp.Status = model.InfraFailure
	}

	// Hide the unimportant steps, highlight the interesting ones.
	switch comp.Status {
	case model.NotRun, model.Running:
		if isMain {
			comp.Verbosity = resp.Hidden
		}

	case model.Success:
		if _, ok := builtIn[anno.Name]; ok || isMain {
			comp.Verbosity = resp.Hidden
		}
	case model.InfraFailure, model.Failure:
		comp.Verbosity = resp.Interesting
	}

	// Main link is a link to the stdout.
	var stdoutLink *miloProto.Link
	if anno.StdoutStream != nil {
		stdoutLink = &miloProto.Link{
			Label: "stdout",
			Value: &miloProto.Link_LogdogStream{
				LogdogStream: anno.StdoutStream,
			},
		}
	}

	if anno.Link != nil {
		comp.MainLink = resp.LinkSet{ub.BuildLink(anno.Link)}

		// If we also have a STDOUT stream, add it to our OtherLinks.
		if stdoutLink != nil {
			anno.OtherLinks = append([]*miloProto.Link{stdoutLink}, anno.OtherLinks...)
		}
	} else if stdoutLink != nil {
		comp.MainLink = resp.LinkSet{ub.BuildLink(stdoutLink)}
	}

	// Add STDERR link, if available.
	if anno.StderrStream != nil {
		anno.OtherLinks = append(anno.OtherLinks, &miloProto.Link{
			Label: "stderr",
			Value: &miloProto.Link_LogdogStream{
				LogdogStream: anno.StderrStream,
			},
		})
	}

	// Sub link is for one link per log that isn't stdout.
	for _, link := range anno.GetOtherLinks() {
		if l := ub.BuildLink(link); l != nil {
			comp.SubLink = append(comp.SubLink, resp.LinkSet{l})
		}
	}

	// This should always be a step.
	comp.Type = resp.Step

	// This should always be 0
	comp.LevelsDeep = 0

	// Timestamps
	comp.Started = google.TimeFromProto(anno.Started)
	comp.Finished = google.TimeFromProto(anno.Ended)

	var till time.Time
	switch {
	case !comp.Finished.IsZero():
		till = comp.Finished
	case comp.Status == model.Running:
		till = now
	case !buildCompletedTime.IsZero():
		till = buildCompletedTime
	}
	if !comp.Started.IsZero() && till.After(comp.Started) {
		comp.Duration = till.Sub(comp.Started)
	}

	// This should be the exact same thing.
	comp.Text = append(comp.Text, anno.Text...)

	ss := anno.GetSubstep()
	results := []*resp.BuildComponent{}
	results = append(results, comp)
	// Process nested steps.
	for _, substep := range ss {
		var subanno *miloProto.Step
		switch s := substep.GetSubstep().(type) {
		case *miloProto.Step_Substep_Step:
			subanno = s.Step
		case *miloProto.Step_Substep_AnnotationStream:
			panic("Non-inline substeps not supported")
		default:
			panic(fmt.Errorf("Unknown type %v", s))
		}
		for _, subcomp := range miloBuildStep(ub, subanno, false, buildCompletedTime, now) {
			results = append(results, subcomp)
		}
	}

	return results
}

// AddLogDogToBuild takes a set of logdog streams and populate a milo build.
// build.Summary.Finished must be set.
func AddLogDogToBuild(
	c context.Context, ub URLBuilder, mainAnno *miloProto.Step, build *resp.MiloBuild) {
	now := clock.Now(c)

	// Now fill in each of the step components.
	// TODO(hinoka): This is totes cachable.
	buildCompletedTime := google.TimeFromProto(mainAnno.Ended)
	build.Summary = *(miloBuildStep(ub, mainAnno, true, buildCompletedTime, now)[0])
	propMap := map[string]string{}
	for _, substepContainer := range mainAnno.Substep {
		anno := substepContainer.GetStep()
		if anno == nil {
			// TODO: We ignore non-embedded substeps for now.
			continue
		}

		bss := miloBuildStep(ub, anno, false, buildCompletedTime, now)
		for _, bs := range bss {
			if bs.Status != model.Success {
				build.Summary.Text = append(
					build.Summary.Text, fmt.Sprintf("%s %s", bs.Status, bs.Label))
			}
			build.Components = append(build.Components, bs)
			propGroup := &resp.PropertyGroup{GroupName: bs.Label}
			for _, prop := range anno.Property {
				propGroup.Property = append(propGroup.Property, &resp.Property{
					Key:   prop.Name,
					Value: prop.Value,
				})
				propMap[prop.Name] = prop.Value
			}
			build.PropertyGroup = append(build.PropertyGroup, propGroup)
		}
	}

	// Take care of properties
	propGroup := &resp.PropertyGroup{GroupName: "Main"}
	for _, prop := range mainAnno.Property {
		propGroup.Property = append(propGroup.Property, &resp.Property{
			Key:   prop.Name,
			Value: prop.Value,
		})
		propMap[prop.Name] = prop.Value
	}
	build.PropertyGroup = append(build.PropertyGroup, propGroup)

	// HACK(hinoka,iannucci): Extract revision out of properties.
	if jrev, ok := propMap["got_revision"]; ok {
		// got_revision is a json string, so it looks like "aaaaaabbcc123..."
		var rev string
		err := json.Unmarshal([]byte(jrev), &rev)
		if err == nil {
			if build.SourceStamp == nil {
				build.SourceStamp = &resp.SourceStamp{}
			}
			build.SourceStamp.Revision = resp.NewLink(
				rev, fmt.Sprintf("https://crrev.com/%s", rev))
		}
	}

	return
}
<|endoftext|>


<commit_before>package main

import (
	"bitbucket.org/davidwallace/go-opc/colorutils"
    "math"
	"bufio"
	"fmt"
	"github.com/davecheney/profile"
	"net"
	"os"
	"strconv"
	"strings"
	"time"
)

// Read locations from JSON file into a slice of floats
func readLocations(fn string) []float64 {
	locations := make([]float64, 0)
	var file *os.File
	var err error
	if file, err = os.Open(fn); err != nil {
		panic(fmt.Sprintf("could not open layout file: %s", fn))
	}
	defer file.Close()
	scanner := bufio.NewScanner(file)
	for scanner.Scan() {
		line := scanner.Text()
		if len(line) == 0 || line[0] == '[' || line[0] == ']' {
			continue
		}
		line = strings.Split(line, "[")[1]
		line = strings.Split(line, "]")[0]
		coordStrings := strings.Split(line, ", ")
		var x, y, z float64
		x, err = strconv.ParseFloat(coordStrings[0], 64)
		y, err = strconv.ParseFloat(coordStrings[1], 64)
		z, err = strconv.ParseFloat(coordStrings[2], 64)
		locations = append(locations, x, y, z)
	}
	fmt.Printf("Read %v pixel locations from %s\n", len(locations), fn)
	return locations
}

// Try to connect a couple of times
// If we fail after several tries, return nil
func getConnection(ipPort string) net.Conn {
	fmt.Printf("connecting to %v...\n", ipPort)
	triesLeft := 1
	var conn net.Conn
	var err error
	for {
		conn, err = net.Dial("tcp", ipPort)
		if err == nil {
			break
		}
		fmt.Println(triesLeft, err)
		time.Sleep(1 * time.Millisecond)
		triesLeft -= 1
		if triesLeft == 0 {
			return nil
		}
	}
	fmt.Println("    connected")
	return conn
}

// Connect to an ip:port and send the values array with an OPC header in front of it.
func networkThread(networkWantsMore chan int, sendThisSlice chan []byte, ipPort string) {
	var conn net.Conn
	var err error

	// loop forever, getting slices to send
	for {

		// indicate we're idle
		networkWantsMore <- 1

		// wait to get a slice to send
		values := <-sendThisSlice

		// if the connection has gone bad, make a new one
		if conn == nil {
			conn = getConnection(ipPort)
		}
		// if that didn't work, wait a second and restart the loop
		if conn == nil {
			time.Sleep(1000 * time.Millisecond)
			continue
		}

		// ok, connection is good

		// make and send header
		channel := byte(0)
		command := byte(0)
		lenLowByte := byte(len(values) % 256)
		lenHighByte := byte(len(values) / 256)
		header := []byte{channel, command, lenHighByte, lenLowByte}
		_, err = conn.Write(header)
		if err != nil {
			// net error -- set conn to nil so we can try to make a new one
			fmt.Println(err)
			conn = nil
			continue
		}

		// send actual pixel values
		_, err = conn.Write(values)
		if err != nil {
			// net error -- set conn to nil so we can try to make a new one
			fmt.Println(err)
			conn = nil
			continue
		}
	}
}

func main() {
	defer profile.Start(profile.CPUProfile).Stop()

	path := "layouts/freespace.json"
	//ipPort := "127.0.0.1:7890"
	ipPort := "192.168.11.11:7890"

	LOCATIONS := readLocations(path)
	N_PIXELS := len(LOCATIONS) / 3
	VALUES := make([][]byte, 2)
	VALUES[0] = make([]byte, N_PIXELS*3)
	VALUES[1] = make([]byte, N_PIXELS*3)

	writing, sending := 0, 1

	networkWantsMore := make(chan int, 0)
	sendThisSlice := make(chan []byte, 0)

	go networkThread(networkWantsMore, sendThisSlice, ipPort)

	// fill in values over and over

    var (
        // how many sine wave cycles are squeezed into our n_pixels
        // 24 happens to create nice diagonal stripes on the wall layout
        freq_r float64 = 24
        freq_g float64 = 24
        freq_b float64 = 24

        // how many seconds the color sine waves take to shift through a complete cycle
        speed_r float64 = 7
        speed_g float64 = -13
        speed_b float64 = 19
    )

	var pct, r, g, b, t float64
	var last_print = float64(time.Now().UnixNano()) / 1.0e9
	var frames = 0
	var start_time = last_print
	t = start_time
	for t < start_time+50 {
		t = float64(time.Now().UnixNano()) / 1.0e9
		// fps bookkeeping
		frames += 1
		if t > last_print+1 {
			last_print = t
			fmt.Printf("%f ms (%d fps)\n", 1000.0/float64(frames), frames)
			frames = 0
		}
		// fill in values array
		for ii := 0; ii < N_PIXELS; ii++ {
			pct = float64(ii) / float64(N_PIXELS)

            // diagonal black stripes
            pct_jittered := math.Mod((pct * 77) + 37, 37)
            blackstripes := colorutils.Cos(pct_jittered, t*0.05, 1, -1.5, 1.5) // offset, period, minn, maxx
            blackstripes_offset := colorutils.Cos(t, 0.9, 60, -0.5, 3)
            blackstripes = colorutils.Clamp(blackstripes + blackstripes_offset, 0, 1)

            // 3 sine waves for r, g, b which are out of sync with each other
            r = blackstripes * colorutils.Remap(math.Cos((t/speed_r + pct*freq_r)*math.Pi*2), -1, 1, 0, 1)
            g = blackstripes * colorutils.Remap(math.Cos((t/speed_g + pct*freq_g)*math.Pi*2), -1, 1, 0, 1)
            b = blackstripes * colorutils.Remap(math.Cos((t/speed_b + pct*freq_b)*math.Pi*2), -1, 1, 0, 1)

			VALUES[writing][ii*3+0] = colorutils.FloatToByte(r)
			VALUES[writing][ii*3+1] = colorutils.FloatToByte(g)
			VALUES[writing][ii*3+2] = colorutils.FloatToByte(b)
		}

        time.Sleep(10 * time.Millisecond)

		// wait until the network is idle
		<-networkWantsMore
		// swap the slices
		writing, sending = sending, writing
		// tell the network to start sending the one we just finished making
		sendThisSlice <- VALUES[sending]
		// and now we can start writing to the other one
	}
}
<commit_msg>Refactor opc.go to make it easier to split into library<commit_after>package main

import (
	"bitbucket.org/davidwallace/go-opc/colorutils"
	"bufio"
	"fmt"
	"github.com/davecheney/profile"
	"math"
	"net"
	"os"
	"strconv"
	"strings"
	"time"
)

const CONNECTION_TRIES = 1

// times in ms
const WAIT_TO_RETRY = 1000
const WAIT_BETWEEN_RETRIES = 1
const PIXEL_SLEEP_PER_FRAME = 1

// Read locations from JSON file into a slice of floats
func readLocations(fn string) []float64 {
	locations := make([]float64, 0)
	var file *os.File
	var err error
	if file, err = os.Open(fn); err != nil {
		panic(fmt.Sprintf("could not open layout file: %s", fn))
	}
	defer file.Close()
	scanner := bufio.NewScanner(file)
	for scanner.Scan() {
		line := scanner.Text()
		if len(line) == 0 || line[0] == '[' || line[0] == ']' {
			continue
		}
		line = strings.Split(line, "[")[1]
		line = strings.Split(line, "]")[0]
		coordStrings := strings.Split(line, ", ")
		var x, y, z float64
		x, err = strconv.ParseFloat(coordStrings[0], 64)
		y, err = strconv.ParseFloat(coordStrings[1], 64)
		z, err = strconv.ParseFloat(coordStrings[2], 64)
		locations = append(locations, x, y, z)
	}
	fmt.Printf("[readLocations] Read %v pixel locations from %s\n", len(locations), fn)
	return locations
}

// Try to connect a couple of times
// If we fail after several tries, return nil
func getConnection(ipPort string) net.Conn {
	fmt.Printf("[getConnection] connecting to %v...\n", ipPort)
	triesLeft := CONNECTION_TRIES
	var conn net.Conn
	var err error
	for {
		conn, err = net.Dial("tcp", ipPort)
		if err == nil {
			break
		}
		fmt.Println("[getConnection", triesLeft, err)
		time.Sleep(WAIT_BETWEEN_RETRIES * time.Millisecond)
		triesLeft -= 1
		if triesLeft == 0 {
			return nil
		}
	}
	fmt.Println("[getConnection]    connected")
	return conn
}

// Connect to an ip:port and send the values array with an OPC header in front of it.
func networkThread(sendThisSlice chan []byte, sliceIsSent chan int, ipPort string) {
	var conn net.Conn
	var err error

	// loop forever, getting slices to send
	for {
		// wait to get a slice to send
		values := <-sendThisSlice

		// if the connection has gone bad, make a new one
		if conn == nil {
			conn = getConnection(ipPort)
		}
		// if that didn't work, wait a second and restart the loop
		if conn == nil {
			sliceIsSent <- 1
			time.Sleep(WAIT_TO_RETRY * time.Millisecond)
			continue
		}

		// ok, connection is good

		// make and send header
		channel := byte(0)
		command := byte(0)
		lenLowByte := byte(len(values) % 256)
		lenHighByte := byte(len(values) / 256)
		header := []byte{channel, command, lenHighByte, lenLowByte}
		_, err = conn.Write(header)
		if err != nil {
			// net error -- set conn to nil so we can try to make a new one
			fmt.Println("[net]", err)
			conn = nil
			sliceIsSent <- 1
			continue
		}

		// send actual pixel values
		_, err = conn.Write(values)
		if err != nil {
			// net error -- set conn to nil so we can try to make a new one
			fmt.Println("[net]", err)
			conn = nil
			sliceIsSent <- 1
			continue
		}
		sliceIsSent <- 1
	}
}

func pixelThread(fillThisSlice chan []byte, sliceIsFilled chan int) {
	var (
		// how many sine wave cycles are squeezed into our n_pixels
		// 24 happens to create nice diagonal stripes on the wall layout
		freq_r float64 = 24
		freq_g float64 = 24
		freq_b float64 = 24

		// how many seconds the color sine waves take to shift through a complete cycle
		speed_r float64 = 7
		speed_g float64 = -13
		speed_b float64 = 19
	)

	for {
		// wait for slice to fill
		values := <-fillThisSlice
		n_pixels := len(values) / 3

		t := float64(time.Now().UnixNano()) / 1.0e9

		// fill in values array
		for ii := 0; ii < n_pixels; ii++ {
			pct := float64(ii) / float64(n_pixels)

			// diagonal black stripes
			pct_jittered := math.Mod((pct*77)+37, 37)
			blackstripes := colorutils.Cos(pct_jittered, t*0.05, 1, -1.5, 1.5) // offset, period, minn, maxx
			blackstripes_offset := colorutils.Cos(t, 0.9, 60, -0.5, 3)
			blackstripes = colorutils.Clamp(blackstripes+blackstripes_offset, 0, 1)

			// 3 sine waves for r, g, b which are out of sync with each other
			r := blackstripes * colorutils.Remap(math.Cos((t/speed_r+pct*freq_r)*math.Pi*2), -1, 1, 0, 1)
			g := blackstripes * colorutils.Remap(math.Cos((t/speed_g+pct*freq_g)*math.Pi*2), -1, 1, 0, 1)
			b := blackstripes * colorutils.Remap(math.Cos((t/speed_b+pct*freq_b)*math.Pi*2), -1, 1, 0, 1)

			//values[ii*3+0] = colorutils.FloatToByte(r)
			//values[ii*3+1] = colorutils.FloatToByte(g)
			//values[ii*3+2] = colorutils.FloatToByte(b)
			saveToSlice(values, ii, r, g, b)
		}

		// done
		time.Sleep(PIXEL_SLEEP_PER_FRAME * time.Millisecond)
		sliceIsFilled <- 1
	}
}

func saveToSlice(slice []byte, ii int, r, g, b float64) {
	slice[ii*3+0] = colorutils.FloatToByte(r)
	slice[ii*3+1] = colorutils.FloatToByte(g)
	slice[ii*3+2] = colorutils.FloatToByte(b)
}

func main() {
	defer profile.Start(profile.CPUProfile).Stop()

	path := "layouts/freespace.json"
	ipPort := "127.0.0.1:7890"
	//ipPort := "192.168.11.11:7890"

	LOCATIONS := readLocations(path)
	N_PIXELS := len(LOCATIONS) / 3
	VALUES := make([][]byte, 2)
	VALUES[0] = make([]byte, N_PIXELS*3)
	VALUES[1] = make([]byte, N_PIXELS*3)

	filling, sending := 0, 1

	fillThisSlice := make(chan []byte, 0)
	sliceIsFilled := make(chan int, 0)
	sendThisSlice := make(chan []byte, 0)
	sliceIsSent := make(chan int, 0)

	// start threads
	go networkThread(sendThisSlice, sliceIsSent, ipPort)
	go pixelThread(fillThisSlice, sliceIsFilled)

	// main loop
	startTime := float64(time.Now().UnixNano()) / 1.0e9
	lastPrintTime := startTime
	framesSinceLastPrint := int(0)
	var t float64
	for {
		// fps bookkeeping
		t = float64(time.Now().UnixNano()) / 1.0e9
		framesSinceLastPrint += 1
		if t > lastPrintTime+1 {
			lastPrintTime = t
			fmt.Printf("[main] %f ms (%d fps)\n", 1000.0/float64(framesSinceLastPrint), framesSinceLastPrint)
			framesSinceLastPrint = 0
		}

        // quit after 10 seconds, for profiling purposes
		if t > startTime+10 {
			return
		}

		// start filling and sending
		fillThisSlice <- VALUES[filling]
		sendThisSlice <- VALUES[sending]

		// wait until both are ready
		<-sliceIsFilled
		<-sliceIsSent

		// swap
		filling, sending = sending, filling
	}
}
<|endoftext|>