<commit_before>/*
Copyright 2017 Matthew Lord (mattalord@gmail.com)

WARNING: This is experimental and for demonstration purposes only!

Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:

1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.

2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.

3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

package instances

import (
  "os"
  "log"
  "errors"
  "strings"
  "strconv"
  "database/sql"
  _ "github.com/go-sql-driver/mysql"
)

// member variables that start with capital letters are public/exported
type Instance struct {
  Mysql_host string
  Mysql_port string
  Mysql_user string
  mysql_pass string

  // The status related vars can serve as an effective cache
  Group_name string
  Server_uuid string
  Member_state string
  Online_participants uint8
  Has_quorum bool
  Read_only bool
  db *sql.DB
}

// enable debug logging for all instances
var Debug bool = false

// setup debug logging for all instances
var DebugLog = log.New(os.Stderr,
               "DEBUG: ",
               log.Ldate|log.Ltime|log.Lshortfile)

func New( myh string, myp string, myu string, mys string ) * Instance {
  return &Instance{ Mysql_host: myh, Mysql_port: myp, Mysql_user: myu, mysql_pass: mys }
}

func (me *Instance) Connect() error {
  var err error

  if( me.db == nil ){
    conn_string := me.Mysql_user + ":" + me.mysql_pass + "@tcp(" + me.Mysql_host + ":" + me.Mysql_port + ")/performance_schema"

    if( Debug ){
      DebugLog.Printf( "Making SQL connection using: %s\n", conn_string )
    }

    me.db, err = sql.Open( "mysql", conn_string )

    if( err != nil ){
      DebugLog.Printf( "Error during sql.Open: %v", err )
    }
  }

  err = me.db.Ping()

  if( err == nil ){
    query_str := "SELECT variable_value FROM global_variables WHERE variable_name='group_replication_group_name'"

    if( Debug ){
      DebugLog.Printf( "Checking group name on '%s:%s'. Query: %s\n", me.Mysql_host, me.Mysql_port, query_str )
    }

    err = me.db.QueryRow( query_str ).Scan( &me.Group_name )

    if( err != nil || me.Group_name == "" ){
      err = errors.New( "Specified MySQL Instance is not a member of any Group Replication cluster!" )
    }

    query_str = "SELECT variable_value, member_state FROM global_variables gv INNER JOIN replication_group_members rgm ON(gv.variable_value=rgm.member_id) WHERE gv.variable_name='server_uuid'"

    if( Debug ){
      DebugLog.Printf( "Checking status of '%s:%s'. Query: %s\n", me.Mysql_host, me.Mysql_port, query_str )
    }

    err = me.db.QueryRow( query_str ).Scan( &me.Server_uuid, &me.Member_state )
  }

  return err
}

func (me *Instance) HasQuorum() (bool, error) {
  quorum_query := "SELECT IF( MEMBER_STATE='ONLINE' AND ((SELECT COUNT(*) FROM replication_group_members WHERE MEMBER_STATE != 'ONLINE') >= ((SELECT COUNT(*) FROM replication_group_members)/2) = 0), 'true', 'false' ) FROM replication_group_members JOIN replication_group_member_stats USING(member_id)"

  if( Debug ){
    DebugLog.Printf( "Checking if '%s:%s' has a quorum. Query: %s\n", me.Mysql_host, me.Mysql_port, quorum_query )
  }

  err := me.db.Ping()

  if( err == nil ){
    err = me.db.QueryRow( quorum_query ).Scan( &me.Has_quorum )
  }

  return me.Has_quorum, err
}

func (me *Instance) IsReadOnly() (bool, error) {
  ro_query := "SELECT variable_value FROM global_variables WHERE variable_name='super_read_only'"

  if( Debug ){
    DebugLog.Printf( "Checking if '%s:%s' is read only. Query: %s\n", me.Mysql_host, me.Mysql_port, ro_query )
  }

  err := me.db.Ping()

  if( err == nil ){
    err = me.db.QueryRow( ro_query ).Scan( &me.Read_only )
  }

  return me.Read_only, err
}

func (me *Instance) GetMembers() (*[]Instance, error) {
  membership_query := "SELECT member_id, member_host, member_port, member_state FROM replication_group_members"
  member_slice := []Instance{}
  me.Online_participants = 0

  if( Debug ){
    DebugLog.Printf( "Getting group members from '%s:%s'. Query: %s\n", me.Mysql_host, me.Mysql_port, membership_query )
  }

  err := me.db.Ping()

  if( err == nil ){
    rows, err := me.db.Query( membership_query )

    if( err == nil ){
      defer rows.Close()

      for( rows.Next() ){
        member := New( "", "", me.Mysql_user, me.mysql_pass )
        err = rows.Scan( &member.Server_uuid, &member.Mysql_host, &member.Mysql_port, &member.Member_state )
        if( member.Member_state == "ONLINE" ){
          me.Online_participants++
        }
        member_slice = append( member_slice, *member )
      }

      if( Debug ){
        DebugLog.Printf( "Group member info found for '%s:%s' -- ONLINE member count: %d, Members: %+v\n", me.Mysql_host, me.Mysql_port, me.Online_participants, member_slice )
      }
    }
  }

  return &member_slice, err
}

func (me *Instance) Shutdown() error {
  shutdown_query := "SHUTDOWN"

  if( Debug ){
    DebugLog.Printf( "Shutting down node '%s:%s'\n", me.Mysql_host, me.Mysql_port )
  }

  err := me.db.Ping()

  if( err == nil ){
    _, err = me.db.Exec( shutdown_query )
  }

  return err
}

func (me *Instance) TransactionsExecuted() (string, error) {
  // since this is such a fast changing metric, I won't cache the value in the struct
  var gtids string
  gtid_query := "SELECT @@global.GTID_EXECUTED"

  if( Debug ){
    DebugLog.Printf( "Getting the transactions executed on '%s:%s'\n", me.Mysql_host, me.Mysql_port )
  }

  err := me.db.Ping()

  if( err == nil ){
    err = me.db.QueryRow( gtid_query ).Scan( &gtids )
  }

  return gtids, err
}

func (me *Instance) TransactionsExecutedCount() (uint64, error) {
  var err error
  var gtid_set string
  var cnt uint64

  gtid_set, err = me.TransactionsExecuted()

  if( err != nil ){
    cnt, err = TransactionCount( gtid_set )
  }

  return cnt, err
}

func (me *Instance) ApplierQueueLength() (uint64, error) {
  // since this is such a fast changing metric, I won't cache the value in the struct
  var qlen uint64
  var gtid_subset string
  gtid_subset_query := "SELECT GTID_SUBTRACT( (SELECT Received_transaction_set FROM performance_schema.replication_connection_status WHERE Channel_name = 'group_replication_applier' ), (SELECT @@global.GTID_EXECUTED) )"

  if( Debug ){
    DebugLog.Printf( "Getting the applier queue length on '%s:%s'\n", me.Mysql_host, me.Mysql_port )
  }

  err := me.db.Ping()

  if( err == nil ){
    err = me.db.QueryRow( gtid_subset_query ).Scan( &gtid_subset )
  }

  qlen, err = TransactionCount( gtid_subset )

  return qlen, err
}

/*
 This is a global function to count a total of all the GTIDs in a set
 An example set being:
"39a07a39-4b82-44d2-a3cd-978511564a57:1-37,
49311a3a-e058-46ba-8e7b-857b5db7d33f:1,
550fa9ee-a1f8-4b6d-9bfe-c03c12cd1c72:1-550757:1001496-1749225:2001496-2835762,
de6858e8-0669-4b82-a188-d2906daa6d91:1-119927"
With the total transaction count for that set being: 2252719
*/
func TransactionCount( gtid_set string ) (uint64, error) {
  var err error
  var gtid_count uint64 = 0
  next_dash_pos := 0
  next_colon_pos := 0
  next_comma_pos := 0
  colon_pos := strings.IndexRune( gtid_set, ':' )
  var firstval uint64 = 0
  var secondval uint64 = 0
  var nextval uint64 = 0

  if( Debug ){
    DebugLog.Printf( "Calculating total number of GTIDs from a set of: %s\n", gtid_set )
  }

  for colon_pos != -1 {
    // lets get rid of everything before the current colon, and the colon itself, as it's UUID info that we don't care about
    gtid_set = gtid_set[colon_pos+1:]

    next_dash_pos = strings.IndexRune( gtid_set, '-' )
    next_colon_pos = strings.IndexRune( gtid_set, ':' )
    next_comma_pos = strings.IndexRune( gtid_set, ',' )

    firstval = 0
    secondval = 0
    nextval = 0

    if( next_dash_pos < next_colon_pos && next_dash_pos < next_comma_pos ){
      if( next_colon_pos < next_comma_pos ){
        firstval, err = strconv.ParseUint( gtid_set[:next_dash_pos], 10, 64 )
        secondval, err = strconv.ParseUint( gtid_set[next_dash_pos+1 : next_colon_pos], 10, 64 )

        // the first GTID counts too
        firstval = firstval-1

        nextval = secondval - firstval
      } else {
        firstval, err = strconv.ParseUint( gtid_set[:next_dash_pos], 10, 64 )
        secondval, err = strconv.ParseUint( gtid_set[next_dash_pos+1 : next_comma_pos], 10, 64 )

        // the first GTID counts too
        firstval = firstval-1

        nextval = secondval - firstval
      }
    } else if( next_colon_pos == -1 && next_dash_pos != -1 ){
      firstval, err = strconv.ParseUint( gtid_set[:next_dash_pos], 10, 64 )
      secondval, err = strconv.ParseUint( gtid_set[next_dash_pos+1:], 10, 64 )

      // the first GTID counts too
      firstval = firstval-1

      nextval = secondval - firstval
    } else {
      nextval = 1
    }

    if( err != nil ){
      break
    }

    if( Debug ){
      DebugLog.Printf( "The current calculation is: (%d - %d)\n", secondval, firstval )
      DebugLog.Printf( "Current total: %d, adding %d\n", gtid_count, nextval )
    }

    gtid_count = gtid_count + nextval

    colon_pos = strings.IndexRune( gtid_set, ':' )

    if( Debug ){
      DebugLog.Printf( "Remaining unprocessed GTID string: %s\n", gtid_set )
    }
  }

  return gtid_count, err
}

func (me *Instance) ForceMembers( fms string ) error {
  force_membership_query := "SET GLOBAL group_replication_force_members='" + fms + "'"

  if( Debug ){
    DebugLog.Printf( "Forcing group membership on '%s:%s'. Query: %s\n", me.Mysql_host, me.Mysql_port, force_membership_query )
  }

  err := me.db.Ping()

  if( err == nil ){
    _, err = me.db.Exec( force_membership_query )
  }

  return err
}

func (me *Instance) SetReadOnly( ro bool ) error {
  ro_query := "SET GLOBAL super_read_only="

  if( ro ){
    ro_query = ro_query + "ON"
  } else {
    ro_query = ro_query + "OFF"
  }

  if( Debug ){
    DebugLog.Printf( "Setting read_only mode to %b on '%s:%s'\n", ro, me.Mysql_host, me.Mysql_port )
  }

  err := me.db.Ping()

  if( err == nil ){
    _, err = me.db.Exec( ro_query )
  }

  return err
}

func (me *Instance) SetOfflineMode( om bool ) error {
  om_query := "SET GLOBAL offline_mode="

  if( om ){
    om_query = om_query + "ON"
  } else {
    om_query = om_query + "OFF"
  }

  if( Debug ){
    DebugLog.Printf( "Setting offline mode to %b on '%s:%s'\n", om, me.Mysql_host, me.Mysql_port )
  }

  err := me.db.Ping()

  if( err == nil ){
    _, err = me.db.Exec( om_query )
  }

  return err
}

func (me *Instance) Cleanup() error {
  var err error = nil

  if( me.db != nil ){
    err = me.db.Close()
  }

  return err
}
<commit_msg>Setting the "cached" read-only state<commit_after>/*
Copyright 2017 Matthew Lord (mattalord@gmail.com)

WARNING: This is experimental and for demonstration purposes only!

Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:

1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.

2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.

3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

package instances

import (
  "os"
  "log"
  "errors"
  "strings"
  "strconv"
  "database/sql"
  _ "github.com/go-sql-driver/mysql"
)

// member variables that start with capital letters are public/exported
type Instance struct {
  Mysql_host string
  Mysql_port string
  Mysql_user string
  mysql_pass string

  // The status related vars can serve as an effective cache
  Group_name string
  Server_uuid string
  Member_state string
  Online_participants uint8
  Has_quorum bool
  Read_only bool
  db *sql.DB
}

// enable debug logging for all instances
var Debug bool = false

// setup debug logging for all instances
var DebugLog = log.New(os.Stderr,
               "DEBUG: ",
               log.Ldate|log.Ltime|log.Lshortfile)

func New( myh string, myp string, myu string, mys string ) * Instance {
  return &Instance{ Mysql_host: myh, Mysql_port: myp, Mysql_user: myu, mysql_pass: mys }
}

func (me *Instance) Connect() error {
  var err error

  if( me.db == nil ){
    conn_string := me.Mysql_user + ":" + me.mysql_pass + "@tcp(" + me.Mysql_host + ":" + me.Mysql_port + ")/performance_schema"

    if( Debug ){
      DebugLog.Printf( "Making SQL connection using: %s\n", conn_string )
    }

    me.db, err = sql.Open( "mysql", conn_string )

    if( err != nil ){
      DebugLog.Printf( "Error during sql.Open: %v", err )
    }
  }

  err = me.db.Ping()

  if( err == nil ){
    query_str := "SELECT variable_value FROM global_variables WHERE variable_name='group_replication_group_name'"

    if( Debug ){
      DebugLog.Printf( "Checking group name on '%s:%s'. Query: %s\n", me.Mysql_host, me.Mysql_port, query_str )
    }

    err = me.db.QueryRow( query_str ).Scan( &me.Group_name )

    if( err != nil || me.Group_name == "" ){
      err = errors.New( "Specified MySQL Instance is not a member of any Group Replication cluster!" )
    }

    query_str = "SELECT variable_value, member_state FROM global_variables gv INNER JOIN replication_group_members rgm ON(gv.variable_value=rgm.member_id) WHERE gv.variable_name='server_uuid'"

    if( Debug ){
      DebugLog.Printf( "Checking status of '%s:%s'. Query: %s\n", me.Mysql_host, me.Mysql_port, query_str )
    }

    err = me.db.QueryRow( query_str ).Scan( &me.Server_uuid, &me.Member_state )
  }

  return err
}

func (me *Instance) HasQuorum() (bool, error) {
  quorum_query := "SELECT IF( MEMBER_STATE='ONLINE' AND ((SELECT COUNT(*) FROM replication_group_members WHERE MEMBER_STATE != 'ONLINE') >= ((SELECT COUNT(*) FROM replication_group_members)/2) = 0), 'true', 'false' ) FROM replication_group_members JOIN replication_group_member_stats USING(member_id)"

  if( Debug ){
    DebugLog.Printf( "Checking if '%s:%s' has a quorum. Query: %s\n", me.Mysql_host, me.Mysql_port, quorum_query )
  }

  err := me.db.Ping()

  if( err == nil ){
    err = me.db.QueryRow( quorum_query ).Scan( &me.Has_quorum )
  }

  return me.Has_quorum, err
}

func (me *Instance) IsReadOnly() (bool, error) {
  ro_query := "SELECT variable_value FROM global_variables WHERE variable_name='super_read_only'"

  if( Debug ){
    DebugLog.Printf( "Checking if '%s:%s' is read only. Query: %s\n", me.Mysql_host, me.Mysql_port, ro_query )
  }

  err := me.db.Ping()

  if( err == nil ){
    err = me.db.QueryRow( ro_query ).Scan( &me.Read_only )
  }

  return me.Read_only, err
}

func (me *Instance) GetMembers() (*[]Instance, error) {
  membership_query := "SELECT member_id, member_host, member_port, member_state FROM replication_group_members"
  member_slice := []Instance{}
  me.Online_participants = 0

  if( Debug ){
    DebugLog.Printf( "Getting group members from '%s:%s'. Query: %s\n", me.Mysql_host, me.Mysql_port, membership_query )
  }

  err := me.db.Ping()

  if( err == nil ){
    rows, err := me.db.Query( membership_query )

    if( err == nil ){
      defer rows.Close()

      for( rows.Next() ){
        member := New( "", "", me.Mysql_user, me.mysql_pass )
        err = rows.Scan( &member.Server_uuid, &member.Mysql_host, &member.Mysql_port, &member.Member_state )
        if( member.Member_state == "ONLINE" ){
          me.Online_participants++
        }
        member_slice = append( member_slice, *member )
      }

      if( Debug ){
        DebugLog.Printf( "Group member info found for '%s:%s' -- ONLINE member count: %d, Members: %+v\n", me.Mysql_host, me.Mysql_port, me.Online_participants, member_slice )
      }
    }
  }

  return &member_slice, err
}

func (me *Instance) Shutdown() error {
  shutdown_query := "SHUTDOWN"

  if( Debug ){
    DebugLog.Printf( "Shutting down node '%s:%s'\n", me.Mysql_host, me.Mysql_port )
  }

  err := me.db.Ping()

  if( err == nil ){
    _, err = me.db.Exec( shutdown_query )
  }

  return err
}

func (me *Instance) TransactionsExecuted() (string, error) {
  // since this is such a fast changing metric, I won't cache the value in the struct
  var gtids string
  gtid_query := "SELECT @@global.GTID_EXECUTED"

  if( Debug ){
    DebugLog.Printf( "Getting the transactions executed on '%s:%s'\n", me.Mysql_host, me.Mysql_port )
  }

  err := me.db.Ping()

  if( err == nil ){
    err = me.db.QueryRow( gtid_query ).Scan( &gtids )
  }

  return gtids, err
}

func (me *Instance) TransactionsExecutedCount() (uint64, error) {
  var err error
  var gtid_set string
  var cnt uint64

  gtid_set, err = me.TransactionsExecuted()

  if( err != nil ){
    cnt, err = TransactionCount( gtid_set )
  }

  return cnt, err
}

func (me *Instance) ApplierQueueLength() (uint64, error) {
  // since this is such a fast changing metric, I won't cache the value in the struct
  var qlen uint64
  var gtid_subset string
  gtid_subset_query := "SELECT GTID_SUBTRACT( (SELECT Received_transaction_set FROM performance_schema.replication_connection_status WHERE Channel_name = 'group_replication_applier' ), (SELECT @@global.GTID_EXECUTED) )"

  if( Debug ){
    DebugLog.Printf( "Getting the applier queue length on '%s:%s'\n", me.Mysql_host, me.Mysql_port )
  }

  err := me.db.Ping()

  if( err == nil ){
    err = me.db.QueryRow( gtid_subset_query ).Scan( &gtid_subset )
  }

  qlen, err = TransactionCount( gtid_subset )

  return qlen, err
}

/*
 This is a global function to count a total of all the GTIDs in a set
 An example set being:
"39a07a39-4b82-44d2-a3cd-978511564a57:1-37,
49311a3a-e058-46ba-8e7b-857b5db7d33f:1,
550fa9ee-a1f8-4b6d-9bfe-c03c12cd1c72:1-550757:1001496-1749225:2001496-2835762,
de6858e8-0669-4b82-a188-d2906daa6d91:1-119927"
With the total transaction count for that set being: 2252719
*/
func TransactionCount( gtid_set string ) (uint64, error) {
  var err error
  var gtid_count uint64 = 0
  next_dash_pos := 0
  next_colon_pos := 0
  next_comma_pos := 0
  colon_pos := strings.IndexRune( gtid_set, ':' )
  var firstval uint64 = 0
  var secondval uint64 = 0
  var nextval uint64 = 0

  if( Debug ){
    DebugLog.Printf( "Calculating total number of GTIDs from a set of: %s\n", gtid_set )
  }

  for colon_pos != -1 {
    // lets get rid of everything before the current colon, and the colon itself, as it's UUID info that we don't care about
    gtid_set = gtid_set[colon_pos+1:]

    next_dash_pos = strings.IndexRune( gtid_set, '-' )
    next_colon_pos = strings.IndexRune( gtid_set, ':' )
    next_comma_pos = strings.IndexRune( gtid_set, ',' )

    firstval = 0
    secondval = 0
    nextval = 0

    if( next_dash_pos < next_colon_pos && next_dash_pos < next_comma_pos ){
      if( next_colon_pos < next_comma_pos ){
        firstval, err = strconv.ParseUint( gtid_set[:next_dash_pos], 10, 64 )
        secondval, err = strconv.ParseUint( gtid_set[next_dash_pos+1 : next_colon_pos], 10, 64 )

        // the first GTID counts too
        firstval = firstval-1

        nextval = secondval - firstval
      } else {
        firstval, err = strconv.ParseUint( gtid_set[:next_dash_pos], 10, 64 )
        secondval, err = strconv.ParseUint( gtid_set[next_dash_pos+1 : next_comma_pos], 10, 64 )

        // the first GTID counts too
        firstval = firstval-1

        nextval = secondval - firstval
      }
    } else if( next_colon_pos == -1 && next_dash_pos != -1 ){
      firstval, err = strconv.ParseUint( gtid_set[:next_dash_pos], 10, 64 )
      secondval, err = strconv.ParseUint( gtid_set[next_dash_pos+1:], 10, 64 )

      // the first GTID counts too
      firstval = firstval-1

      nextval = secondval - firstval
    } else {
      nextval = 1
    }

    if( err != nil ){
      break
    }

    if( Debug ){
      DebugLog.Printf( "The current calculation is: (%d - %d)\n", secondval, firstval )
      DebugLog.Printf( "Current total: %d, adding %d\n", gtid_count, nextval )
    }

    gtid_count = gtid_count + nextval

    colon_pos = strings.IndexRune( gtid_set, ':' )

    if( Debug ){
      DebugLog.Printf( "Remaining unprocessed GTID string: %s\n", gtid_set )
    }
  }

  return gtid_count, err
}

func (me *Instance) ForceMembers( fms string ) error {
  force_membership_query := "SET GLOBAL group_replication_force_members='" + fms + "'"

  if( Debug ){
    DebugLog.Printf( "Forcing group membership on '%s:%s'. Query: %s\n", me.Mysql_host, me.Mysql_port, force_membership_query )
  }

  err := me.db.Ping()

  if( err == nil ){
    _, err = me.db.Exec( force_membership_query )
  }

  return err
}

func (me *Instance) SetReadOnly( ro bool ) error {
  ro_query := "SET GLOBAL super_read_only="

  if( ro ){
    ro_query = ro_query + "ON"
  } else {
    ro_query = ro_query + "OFF"
  }

  if( Debug ){
    DebugLog.Printf( "Setting read_only mode to %b on '%s:%s'\n", ro, me.Mysql_host, me.Mysql_port )
  }

  err := me.db.Ping()

  if( err == nil ){
    _, err = me.db.Exec( ro_query )
    me.Read_only = ro
  }

  return err
}

func (me *Instance) SetOfflineMode( om bool ) error {
  om_query := "SET GLOBAL offline_mode="

  if( om ){
    om_query = om_query + "ON"
  } else {
    om_query = om_query + "OFF"
  }

  if( Debug ){
    DebugLog.Printf( "Setting offline mode to %b on '%s:%s'\n", om, me.Mysql_host, me.Mysql_port )
  }

  err := me.db.Ping()

  if( err == nil ){
    _, err = me.db.Exec( om_query )
  }

  return err
}

func (me *Instance) Cleanup() error {
  var err error = nil

  if( me.db != nil ){
    err = me.db.Close()
  }

  return err
}
<|endoftext|>


<commit_before>package whoson

import (
	"context"
	"fmt"
	"net"
	"time"

	"google.golang.org/grpc"

	"github.com/orcaman/concurrent-map"
	"github.com/pkg/errors"
)

var syncChan chan *WSRequest

// Store is hold Store API.
type Store interface {
	Set(k string, w *StoreData)
	Get(k string) (*StoreData, error)
	Del(k string) bool
	Items() map[string]interface{}
	Count() int
	SyncSet(k string, w *StoreData)
	SyncDel(k string) bool
}

// MemStore hold information for cmap.
type MemStore struct {
	cmap       cmap.ConcurrentMap
	SyncRemote bool
	Store
}

// NewMemStore return new MemStore.
func NewMemStore() Store {
	return MemStore{
		cmap:       cmap.New(),
		SyncRemote: false,
	}
}

// NewMainStore set MemStore to MainStore.
func NewMainStore() {
	if MainStore == nil {
		MainStore = NewMemStore()
	}
}

// NewMainStoreEnableSyncRemote set MemStore to MainStore, enable sync remote.
func NewMainStoreEnableSyncRemote() {
	if MainStore == nil {
		MainStore = MemStore{
			cmap:       cmap.New(),
			SyncRemote: true,
		}
	}
}

// Set data to cmap store.
func (ms MemStore) Set(k string, w *StoreData) {
	ms.cmap.Set(k, w)

	if ms.SyncRemote {
		r := &WSRequest{
			Expire: w.Expire.Unix(),
			IP:     w.IP.String(),
			Data:   w.Data,
			Method: "Set",
		}
		syncChan <- r
	}
}

// SyncSet data to remote host store.
func (ms MemStore) SyncSet(k string, w *StoreData) {
	//pp.Println(k)
	//pp.Println(w)
	ms.cmap.Set(k, w)
}

// Get data from cmap store.
func (ms MemStore) Get(k string) (*StoreData, error) {
	if v, ok := ms.cmap.Get(k); ok {
		if w, ok := v.(*StoreData); ok {
			if w.Expire.After(time.Now()) {
				return w, nil
			}
			ms.SyncDel(k)
			return nil, errors.New("data not found")
		}
		return nil, errors.New("type assertion error")
	}
	return nil, errors.New("data not found")
}

// Del delete data from cmap store.
func (ms MemStore) Del(k string) bool {
	if ms.SyncRemote {
		r := &WSRequest{
			IP:     k,
			Method: "Del",
		}
		syncChan <- r
	}

	if ms.cmap.Has(k) {
		ms.cmap.Remove(k)
		return true
	}
	return false
}

// SyncDel data from remote host store.
func (ms MemStore) SyncDel(k string) bool {
	//pp.Println(k)
	if ms.cmap.Has(k) {
		ms.cmap.Remove(k)
		return true
	}
	return false
}

// Items return all data from cmap store.
func (ms MemStore) Items() map[string]interface{} {
	return ms.cmap.Items()
}

// Count return all data size.
func (ms MemStore) Count() int {
	return ms.cmap.Count()
}

// StoreData hold information for whoson data.
type StoreData struct {
	Expire time.Time
	IP     net.IP
	Data   string
}

// UpdateExpire Update stored data of expire time.
func (sd *StoreData) UpdateExpire() {
	sd.Expire = time.Now().Add(StoreDataExpire)
}

// Key return key string.
func (sd *StoreData) Key() string {
	return sd.IP.String()
}

func deleteExpireData(store Store) {
	for k, v := range store.Items() {
		if w, ok := v.(*StoreData); ok {
			if w.Expire.Before(time.Now()) {
				msg := fmt.Sprintf("ExpireData:%s", k)
				Log("info", msg, nil, nil)
				store.Del(k)
			}
		}
	}
}

// RunExpireChecker Check expire for all cmap store data.
func RunExpireChecker(ctx context.Context) {
	t := time.NewTicker(ExpireCheckInterval)
	Log("info", "runExpireCheckerStart", nil, nil)
	for {
		select {
		case <-ctx.Done():
			Log("info", "runExpireCheckerStop", nil, nil)
			return
		case <-t.C:
			if MainStore != nil {
				deleteExpireData(MainStore)
			}
		}
	}
}

// RunSyncRemote is sync data to remote grpc servers.
func RunSyncRemote(ctx context.Context, hosts []string) {
	syncChan = make(chan *WSRequest, 32)
	defer close(syncChan)

	Log("info", "RunSyncRemoteStart", nil, nil)
	for {
		select {
		case <-ctx.Done():
			Log("info", "RunSyncRemoteStop", nil, nil)
			return
		case req, ok := <-syncChan:
			if !ok {
				return
			}
			for _, h := range hosts {
				if h != "" {
					go execSyncRemote(req, h)
				}
			}
		}
	}
}

func execSyncRemote(req *WSRequest, remotehost string) {
	l, err := grpc.Dial(remotehost,
		grpc.WithInsecure(),
		grpc.WithBlock(),
		grpc.WithTimeout(time.Duration(15*time.Second)))
	if err != nil {
		Log("error", "execSyncRemote:Error", nil, err)
		return
	}
	client := NewSyncClient(l)

	switch req.Method {
	case "Set":
		_, err = client.Set(context.Background(), req)
		Log("debug", "execSyncRemote:Set", nil, nil)
	case "Del":
		_, err = client.Del(context.Background(), req)
		Log("debug", "execSyncRemote:Del", nil, nil)
	}
	if err != nil {
		Log("error", "execSyncRemote:Error", nil, err)
	}
	l.Close()
}
<commit_msg>Fix initialization position of channel<commit_after>package whoson

import (
	"context"
	"fmt"
	"net"
	"time"

	"google.golang.org/grpc"

	"github.com/orcaman/concurrent-map"
	"github.com/pkg/errors"
)

var syncChan chan *WSRequest

// Store is hold Store API.
type Store interface {
	Set(k string, w *StoreData)
	Get(k string) (*StoreData, error)
	Del(k string) bool
	Items() map[string]interface{}
	Count() int
	SyncSet(k string, w *StoreData)
	SyncDel(k string) bool
}

// MemStore hold information for cmap.
type MemStore struct {
	cmap       cmap.ConcurrentMap
	SyncRemote bool
	Store
}

// NewMemStore return new MemStore.
func NewMemStore() Store {
	return MemStore{
		cmap:       cmap.New(),
		SyncRemote: false,
	}
}

// NewMainStore set MemStore to MainStore.
func NewMainStore() {
	if MainStore == nil {
		MainStore = NewMemStore()
	}
}

// NewMainStoreEnableSyncRemote set MemStore to MainStore, enable sync remote.
func NewMainStoreEnableSyncRemote() {
	if MainStore == nil {
		MainStore = MemStore{
			cmap:       cmap.New(),
			SyncRemote: true,
		}
	}
	if syncChan == nil {
		syncChan = make(chan *WSRequest, 32)
	}
}

// Set data to cmap store.
func (ms MemStore) Set(k string, w *StoreData) {
	ms.cmap.Set(k, w)

	if ms.SyncRemote {
		r := &WSRequest{
			Expire: w.Expire.Unix(),
			IP:     w.IP.String(),
			Data:   w.Data,
			Method: "Set",
		}
		syncChan <- r
	}
}

// SyncSet data to remote host store.
func (ms MemStore) SyncSet(k string, w *StoreData) {
	//pp.Println(k)
	//pp.Println(w)
	ms.cmap.Set(k, w)
}

// Get data from cmap store.
func (ms MemStore) Get(k string) (*StoreData, error) {
	if v, ok := ms.cmap.Get(k); ok {
		if w, ok := v.(*StoreData); ok {
			if w.Expire.After(time.Now()) {
				return w, nil
			}
			ms.SyncDel(k)
			return nil, errors.New("data not found")
		}
		return nil, errors.New("type assertion error")
	}
	return nil, errors.New("data not found")
}

// Del delete data from cmap store.
func (ms MemStore) Del(k string) bool {
	if ms.SyncRemote {
		r := &WSRequest{
			IP:     k,
			Method: "Del",
		}
		syncChan <- r
	}

	if ms.cmap.Has(k) {
		ms.cmap.Remove(k)
		return true
	}
	return false
}

// SyncDel data from remote host store.
func (ms MemStore) SyncDel(k string) bool {
	//pp.Println(k)
	if ms.cmap.Has(k) {
		ms.cmap.Remove(k)
		return true
	}
	return false
}

// Items return all data from cmap store.
func (ms MemStore) Items() map[string]interface{} {
	return ms.cmap.Items()
}

// Count return all data size.
func (ms MemStore) Count() int {
	return ms.cmap.Count()
}

// StoreData hold information for whoson data.
type StoreData struct {
	Expire time.Time
	IP     net.IP
	Data   string
}

// UpdateExpire Update stored data of expire time.
func (sd *StoreData) UpdateExpire() {
	sd.Expire = time.Now().Add(StoreDataExpire)
}

// Key return key string.
func (sd *StoreData) Key() string {
	return sd.IP.String()
}

func deleteExpireData(store Store) {
	for k, v := range store.Items() {
		if w, ok := v.(*StoreData); ok {
			if w.Expire.Before(time.Now()) {
				msg := fmt.Sprintf("ExpireData:%s", k)
				Log("info", msg, nil, nil)
				store.Del(k)
			}
		}
	}
}

// RunExpireChecker Check expire for all cmap store data.
func RunExpireChecker(ctx context.Context) {
	t := time.NewTicker(ExpireCheckInterval)
	Log("info", "runExpireCheckerStart", nil, nil)
	for {
		select {
		case <-ctx.Done():
			Log("info", "runExpireCheckerStop", nil, nil)
			return
		case <-t.C:
			if MainStore != nil {
				deleteExpireData(MainStore)
			}
		}
	}
}

// RunSyncRemote is sync data to remote grpc servers.
func RunSyncRemote(ctx context.Context, hosts []string) {
	defer close(syncChan)

	Log("info", "RunSyncRemoteStart", nil, nil)
	for {
		select {
		case <-ctx.Done():
			Log("info", "RunSyncRemoteStop", nil, nil)
			return
		case req, ok := <-syncChan:
			if !ok {
				return
			}
			for _, h := range hosts {
				if h != "" {
					go execSyncRemote(req, h)
				}
			}
		}
	}
}

func execSyncRemote(req *WSRequest, remotehost string) {
	l, err := grpc.Dial(remotehost,
		grpc.WithInsecure(),
		grpc.WithBlock(),
		grpc.WithTimeout(time.Duration(15*time.Second)))
	if err != nil {
		Log("error", "execSyncRemote:Error", nil, err)
		return
	}
	client := NewSyncClient(l)

	switch req.Method {
	case "Set":
		_, err = client.Set(context.Background(), req)
		Log("debug", "execSyncRemote:Set", nil, nil)
	case "Del":
		_, err = client.Del(context.Background(), req)
		Log("debug", "execSyncRemote:Del", nil, nil)
	}
	if err != nil {
		Log("error", "execSyncRemote:Error", nil, err)
	}
	l.Close()
}
<|endoftext|>


<commit_before>package airbrakehandler

import "github.com/airbrake/gobrake"

func ExampleNew() {
	projectID := int64(1)
	projectKey := "key"

	_ = New(projectID, projectKey)

	// Output:
}

func ExampleNewFromNotifier() {
	projectID := int64(1)
	projectKey := "key"

	notifier := gobrake.NewNotifier(projectID, projectKey)
	_ = NewFromNotifier(notifier)

	// Output:
}

func ExampleNewAsync() {
	projectID := int64(1)
	projectKey := "key"

	handler := NewAsync(projectID, projectKey)
	defer handler.Close() // Make sure to close the handler to flush all error reporting in progress

	// Output:
}

func ExampleNewAsyncFromNotifier() {
	projectID := int64(1)
	projectKey := "key"

	notifier := gobrake.NewNotifier(projectID, projectKey)
	handler := NewAsyncFromNotifier(notifier)
	defer handler.Close() // Make sure to close the handler to flush all error reporting in progress

	// Output:
}
<commit_msg>Fix CS<commit_after>// nolint: goconst
package airbrakehandler

import "github.com/airbrake/gobrake"

func ExampleNew() {
	projectID := int64(1)
	projectKey := "key"

	_ = New(projectID, projectKey)

	// Output:
}

func ExampleNewFromNotifier() {
	projectID := int64(1)
	projectKey := "key"

	notifier := gobrake.NewNotifier(projectID, projectKey)
	_ = NewFromNotifier(notifier)

	// Output:
}

func ExampleNewAsync() {
	projectID := int64(1)
	projectKey := "key"

	handler := NewAsync(projectID, projectKey)
	defer handler.Close() // Make sure to close the handler to flush all error reporting in progress

	// Output:
}

func ExampleNewAsyncFromNotifier() {
	projectID := int64(1)
	projectKey := "key"

	notifier := gobrake.NewNotifier(projectID, projectKey)
	handler := NewAsyncFromNotifier(notifier)
	defer handler.Close() // Make sure to close the handler to flush all error reporting in progress

	// Output:
}
<|endoftext|>


<commit_before>package main

import "C"
import (
	"bytes"
	"errors"
	"fmt"
	"time"
	"unsafe"

	"encoding/binary"
)

const (
	IRSDK_MEMMAPFILENAME     = "Local\\IRSDKMemMapFileName"
	IRSDK_DATAVALIDEVENTNAME = "Local\\IRSDKDataValidEvent"
	INT_MAX                  = 2147483647
	MEMMAPFILESIZE           = 780 * 1024

	IRSDK_MAX_BUFS   = 4
	IRSDK_MAX_STRING = 32
	// descriptions can be longer than max_string!
	IRSDK_MAX_DESC = 64

	irsdk_stConnected = 1
	TIMEOUT           = time.Duration(30) // timeout after 30 seconds with no communication
)

const (
	// 1 byte
	irsdk_char = iota
	irsdk_bool = iota

	// 4 bytes
	irsdk_int      = iota
	irsdk_bitField = iota
	irsdk_float    = iota

	// 8 bytes
	irsdk_double = iota
)

type irsdk_varBuf struct {
	TickCount C.int    // used to detect changes in data
	BufOffset C.int    // offset from header
	Pad       [2]C.int // (16 byte align)
}

type irsdk_header struct {
	Ver      C.int // api version 1 for now
	Status   C.int // bitfield using irsdk_StatusField
	TickRate C.int // ticks per second (60 or 360 etc)

	// session information, updated periodicaly
	SessionInfoUpdate C.int // Incremented when session info changes
	SessionInfoLen    C.int // Length in bytes of session info string
	SessionInfoOffset C.int // Session info, encoded in YAML format

	// State data, output at tickRate
	NumVars         C.int // length of array pointed to by varHeaderOffset
	VarHeaderOffset C.int // offset to irsdk_varHeader[numVars] array, Describes the variables recieved in varBuf

	NumBuf C.int    // <= IRSDK_MAX_BUFS (3 for now)
	BufLen C.int    // length in bytes for one line
	Pad1   [2]C.int // (16 byte align)
	VarBuf [IRSDK_MAX_BUFS]irsdk_varBuf
}

type irsdk_varHeader struct {
	Type   C.int // irsdk_VarType
	Offset C.int // offset fron start of buffer row
	Count  C.int // number of entrys (array)
	// so length in bytes would be irsdk_VarTypeBytes[type] * count

	Pad [1]C.int // (16 byte align)

	Name [IRSDK_MAX_STRING]byte
	Desc [IRSDK_MAX_DESC]byte
	Unit [IRSDK_MAX_STRING]byte // something like "kg/m^2"
}

// Local memory

var hDataValidEvent uintptr
var hMemMapFile uintptr

var pHeader *irsdk_header
var isInitialized bool
var lastValidTime time.Time
var timeout time.Duration
var pSharedMem []byte

// var sharedMemPtr uintptr
var lastTickCount = INT_MAX

func irsdk_startup() error {
	var err error

	if hMemMapFile == 0 {
		hMemMapFile, err = openFileMapping(IRSDK_MEMMAPFILENAME)
		if err != nil {
			fmt.Println(err)
			return err
		}
		lastTickCount = INT_MAX
	}

	if hMemMapFile != 0 {
		if len(pSharedMem) == 0 {
			sharedMemPtr, err := mapViewOfFile(hMemMapFile, MEMMAPFILESIZE)
			if err != nil {
				return err
			}

			pHeader = (*irsdk_header)(unsafe.Pointer(sharedMemPtr))
			pSharedMem = (*[1 << 30]byte)(unsafe.Pointer(sharedMemPtr))[:]
			lastTickCount = INT_MAX
		}

		if len(pSharedMem) != 0 {
			if hDataValidEvent == 0 {
				// hDataValidEvent = try.N("OpenEvent", SYNCHRONIZE, false, syscall.StringToUTF16Ptr(IRSDK_DATAVALIDEVENTNAME))
				hDataValidEvent, err = openEvent(IRSDK_DATAVALIDEVENTNAME)
				if err != nil {
					return err
				}

				lastTickCount = INT_MAX
			}

			if hDataValidEvent != 0 {
				isInitialized = true
				return nil
			}
			//else printf("Error opening event: %d\n", GetLastError());
		}
		//else printf("Error mapping file: %d\n", GetLastError());
	}
	//else printf("Error opening file: %d\n", GetLastError()); `

	isInitialized = false
	return errors.New("Failed to initialize")
}

func irsdk_shutdown() {
	if hDataValidEvent != 0 {
		closeHandle(hDataValidEvent)

		if len(pSharedMem) != 0 {
			sharedMemPtr := uintptr(unsafe.Pointer(&pSharedMem))
			unmapViewOfFile(sharedMemPtr)

			if hMemMapFile != 0 {
				closeHandle(hMemMapFile)

				hDataValidEvent = 0
				pSharedMem = nil
				pHeader = nil
				hMemMapFile = 0

				isInitialized = false
				lastTickCount = INT_MAX
			}
		}
	}
}

func irsdk_getNewData() ([]byte, error) {
	if !isInitialized {
		err := irsdk_startup()
		if err != nil {
			return nil, err
		}
	}

	// if sim is not active, then no new data
	if (int(pHeader.Status) & irsdk_stConnected) == 0 {
		lastTickCount = INT_MAX
		return nil, nil
	}

	latest := 0
	for i := 0; i < int(pHeader.NumBuf); i++ {
		if pHeader.VarBuf[latest].TickCount < pHeader.VarBuf[i].TickCount {
			latest = i
		}
	}

	// if newer than last recieved, than report new data
	if lastTickCount < int(pHeader.VarBuf[latest].TickCount) {

		for count := 0; count < 2; count++ {
			curTickCount := int(pHeader.VarBuf[latest].TickCount)
			bufLen := int(pHeader.BufLen)
			startByte := int(pHeader.VarBuf[latest].BufOffset)
			endByte := startByte + bufLen

			// Copy data
			data := make([]byte, bufLen)
			copy(data, pSharedMem[startByte:endByte])
			// data := pSharedMem[startByte:endByte]

			if curTickCount == int(pHeader.VarBuf[latest].TickCount) {
				lastTickCount = curTickCount
				lastValidTime = time.Now()
				return data, nil
			}
		}
		// if here, the data changed out from under us.
		return nil, errors.New("Data changed out from under us")
	} else if lastTickCount > int(pHeader.VarBuf[latest].TickCount) {
		// if older than last recieved, than reset, we probably disconnected
		lastTickCount = int(pHeader.VarBuf[latest].TickCount)
		return nil, errors.New("We probably disconnected")
	}

	// else the same, and nothing changed this tick
	return nil, errors.New("Nothing changed this tick")
}

func irsdk_waitForDataReady(timeOut int) ([]byte, error) {
	var data []byte
	var err error

	if !isInitialized {
		err = irsdk_startup()

		if err != nil {
			// sleep if error
			// @TODO: fix this
			if timeOut > 0 {
				sleep(timeOut)
			}

			return nil, nil
		}
	}

	// just to be sure, check before we sleep
	data, err = irsdk_getNewData()
	if err == nil {
		return data, err
	}

	// sleep till signaled
	waitForSingleObject(hDataValidEvent, timeOut)

	// we woke up, so check for data
	data, err = irsdk_getNewData()
	if err == nil {
		return data, err
	} else {
		return nil, err
	}

	// sleep if error
	if timeOut > 0 {
		sleep(timeOut)
	}

	return nil, nil
}
func irsdk_isConnected() bool {
	if isInitialized {
		elapsed := time.Now().Sub(lastValidTime)
		if (pHeader.Status&irsdk_stConnected) > 0 && (elapsed < timeout) {
			return true
		}
	}

	return false
}

// direct access to the data buffer
// // Warnign! This buffer is volitile so read it out fast!
// // Use the cached copy from irsdk_waitForDataReady() or irsdk_getNewData()
// instead
func irsdk_getData(index int) []byte {
	if isInitialized {
		endByte := int(pHeader.VarBuf[index].BufOffset)
		return pSharedMem[:endByte]
	}

	return nil
}

func irsdk_getSessionInfoStr() []byte {
	if isInitialized {
		return pSharedMem[pHeader.SessionInfoOffset:pHeader.SessionInfoLen]
	}
	return nil
}

func irsdk_getVarHeaderPtr() *irsdk_varHeader {
	if isInitialized {
		varHeaderOffset := int(pHeader.VarHeaderOffset)
		varHeader := &irsdk_varHeader{}
		varHeaderSize := int(unsafe.Sizeof(*varHeader))

		startByte := varHeaderOffset
		endByte := startByte + varHeaderSize

		// create a io.Reader
		b := bytes.NewBuffer(pSharedMem[startByte:endByte])
		// read []byte and convert it into irsdk_varHeader
		binary.Read(b, binary.LittleEndian, varHeader)

		return varHeader
	}
	return nil
}

func irsdk_getVarHeaderEntry(index int) *irsdk_varHeader {
	if isInitialized {
		if index >= 0 && index < (int)(pHeader.NumVars) {
			varHeaderOffset := int(pHeader.VarHeaderOffset)
			varHeader := &irsdk_varHeader{}
			varHeaderSize := int(unsafe.Sizeof(*varHeader))

			startByte := varHeaderOffset + (index * varHeaderSize)
			endByte := startByte + varHeaderSize

			// create a io.Reader
			b := bytes.NewBuffer(pSharedMem[startByte:endByte])
			// read []byte and convert it into irsdk_varHeader
			binary.Read(b, binary.LittleEndian, varHeader)

			return varHeader
		}
	}
	return nil
}

// Note: this is a linear search, so cache the results
func irsdk_varNameToIndex(name string) int {
	var pVar *irsdk_varHeader

	if name != "" {
		numVars := int(pHeader.NumVars)
		for index := 0; index <= numVars; index++ {
			pVar = irsdk_getVarHeaderEntry(index)
			pVarName := CToGoString(pVar.Name[:])
			if pVar != nil && pVarName == name {
				return index
			}
		}
	}

	return -1
}

func irsdk_varNameToOffset(name string) C.int {
	var pVar *irsdk_varHeader

	if name != "" {
		numVars := int(pHeader.NumVars)
		for index := 0; index <= numVars; index++ {
			pVar = irsdk_getVarHeaderEntry(index)
			pVarName := CToGoString(pVar.Name[:])
			if pVar != nil && pVarName == name {
				return pVar.Offset
			}
		}
	}

	return -1
}

func CToGoString(c []byte) string {
	n := -1
	for i, b := range c {
		if b == 0 {
			break
		}
		n = i
	}
	return string(c[:n+1])
}

// unsigned int irsdk_getBroadcastMsgID()
// {
// 	static unsigned int msgId = RegisterWindowMessageA(IRSDK_BROADCASTMSGNAME);

// 	return msgId;
// }

// void irsdk_broadcastMsg(irsdk_BroadcastMsg msg, int var1, int var2, int var3)
// {
// 	irsdk_broadcastMsg(msg, var1, MAKELONG(var2, var3));
// }

// void irsdk_broadcastMsg(irsdk_BroadcastMsg msg, int var1, int var2)
// {
// 	static unsigned int msgId = irsdk_getBroadcastMsgID();

// 	if(msgId && msg >= 0 && msg < irsdk_BroadcastLast)
// 	{
// 		SendNotifyMessage(HWND_BROADCAST, msgId, MAKELONG(msg, var1), var2);
// 	}
// }

// int irsdk_padCarNum(int num, int zero)
// {
// 	int retVal = num;
// 	int numPlace = 1;
// 	if(num > 99)
// 		numPlace = 3;
// 	else if(num > 9)
// 		numPlace = 2;
// 	if(zero)
// 	{
// 		numPlace += zero;
// 		retVal = num + 1000*numPlace;
// 	}

// 	return retVal;
// }
<commit_msg>go vet issue fixed<commit_after>package main

import "C"
import (
	"bytes"
	"errors"
	"fmt"
	"time"
	"unsafe"

	"encoding/binary"
)

const (
	IRSDK_MEMMAPFILENAME     = "Local\\IRSDKMemMapFileName"
	IRSDK_DATAVALIDEVENTNAME = "Local\\IRSDKDataValidEvent"
	INT_MAX                  = 2147483647
	MEMMAPFILESIZE           = 780 * 1024

	IRSDK_MAX_BUFS   = 4
	IRSDK_MAX_STRING = 32
	// descriptions can be longer than max_string!
	IRSDK_MAX_DESC = 64

	irsdk_stConnected = 1
	TIMEOUT           = time.Duration(30) // timeout after 30 seconds with no communication
)

const (
	// 1 byte
	irsdk_char = iota
	irsdk_bool = iota

	// 4 bytes
	irsdk_int      = iota
	irsdk_bitField = iota
	irsdk_float    = iota

	// 8 bytes
	irsdk_double = iota
)

type irsdk_varBuf struct {
	TickCount C.int    // used to detect changes in data
	BufOffset C.int    // offset from header
	Pad       [2]C.int // (16 byte align)
}

type irsdk_header struct {
	Ver      C.int // api version 1 for now
	Status   C.int // bitfield using irsdk_StatusField
	TickRate C.int // ticks per second (60 or 360 etc)

	// session information, updated periodicaly
	SessionInfoUpdate C.int // Incremented when session info changes
	SessionInfoLen    C.int // Length in bytes of session info string
	SessionInfoOffset C.int // Session info, encoded in YAML format

	// State data, output at tickRate
	NumVars         C.int // length of array pointed to by varHeaderOffset
	VarHeaderOffset C.int // offset to irsdk_varHeader[numVars] array, Describes the variables recieved in varBuf

	NumBuf C.int    // <= IRSDK_MAX_BUFS (3 for now)
	BufLen C.int    // length in bytes for one line
	Pad1   [2]C.int // (16 byte align)
	VarBuf [IRSDK_MAX_BUFS]irsdk_varBuf
}

type irsdk_varHeader struct {
	Type   C.int // irsdk_VarType
	Offset C.int // offset fron start of buffer row
	Count  C.int // number of entrys (array)
	// so length in bytes would be irsdk_VarTypeBytes[type] * count

	Pad [1]C.int // (16 byte align)

	Name [IRSDK_MAX_STRING]byte
	Desc [IRSDK_MAX_DESC]byte
	Unit [IRSDK_MAX_STRING]byte // something like "kg/m^2"
}

// Local memory

var hDataValidEvent uintptr
var hMemMapFile uintptr

var pHeader *irsdk_header
var isInitialized bool
var lastValidTime time.Time
var timeout time.Duration
var pSharedMem []byte

// var sharedMemPtr uintptr
var lastTickCount = INT_MAX

func irsdk_startup() error {
	var err error

	if hMemMapFile == 0 {
		hMemMapFile, err = openFileMapping(IRSDK_MEMMAPFILENAME)
		if err != nil {
			fmt.Println(err)
			return err
		}
		lastTickCount = INT_MAX
	}

	if hMemMapFile != 0 {
		if len(pSharedMem) == 0 {
			sharedMemPtr, err := mapViewOfFile(hMemMapFile, MEMMAPFILESIZE)
			if err != nil {
				return err
			}

			pHeader = (*irsdk_header)(unsafe.Pointer(sharedMemPtr))
			pSharedMem = (*[1 << 30]byte)(unsafe.Pointer(sharedMemPtr))[:]
			lastTickCount = INT_MAX
		}

		if len(pSharedMem) != 0 {
			if hDataValidEvent == 0 {
				// hDataValidEvent = try.N("OpenEvent", SYNCHRONIZE, false, syscall.StringToUTF16Ptr(IRSDK_DATAVALIDEVENTNAME))
				hDataValidEvent, err = openEvent(IRSDK_DATAVALIDEVENTNAME)
				if err != nil {
					return err
				}

				lastTickCount = INT_MAX
			}

			if hDataValidEvent != 0 {
				isInitialized = true
				return nil
			}
			//else printf("Error opening event: %d\n", GetLastError());
		}
		//else printf("Error mapping file: %d\n", GetLastError());
	}
	//else printf("Error opening file: %d\n", GetLastError()); `

	isInitialized = false
	return errors.New("Failed to initialize")
}

func irsdk_shutdown() {
	if hDataValidEvent != 0 {
		closeHandle(hDataValidEvent)

		if len(pSharedMem) != 0 {
			sharedMemPtr := uintptr(unsafe.Pointer(&pSharedMem))
			unmapViewOfFile(sharedMemPtr)

			if hMemMapFile != 0 {
				closeHandle(hMemMapFile)

				hDataValidEvent = 0
				pSharedMem = nil
				pHeader = nil
				hMemMapFile = 0

				isInitialized = false
				lastTickCount = INT_MAX
			}
		}
	}
}

func irsdk_getNewData() ([]byte, error) {
	if !isInitialized {
		err := irsdk_startup()
		if err != nil {
			return nil, err
		}
	}

	// if sim is not active, then no new data
	if (int(pHeader.Status) & irsdk_stConnected) == 0 {
		lastTickCount = INT_MAX
		return nil, nil
	}

	latest := 0
	for i := 0; i < int(pHeader.NumBuf); i++ {
		if pHeader.VarBuf[latest].TickCount < pHeader.VarBuf[i].TickCount {
			latest = i
		}
	}

	// if newer than last recieved, than report new data
	if lastTickCount < int(pHeader.VarBuf[latest].TickCount) {

		for count := 0; count < 2; count++ {
			curTickCount := int(pHeader.VarBuf[latest].TickCount)
			bufLen := int(pHeader.BufLen)
			startByte := int(pHeader.VarBuf[latest].BufOffset)
			endByte := startByte + bufLen

			// Copy data
			data := make([]byte, bufLen)
			copy(data, pSharedMem[startByte:endByte])
			// data := pSharedMem[startByte:endByte]

			if curTickCount == int(pHeader.VarBuf[latest].TickCount) {
				lastTickCount = curTickCount
				lastValidTime = time.Now()
				return data, nil
			}
		}
		// if here, the data changed out from under us.
		return nil, errors.New("Data changed out from under us")
	} else if lastTickCount > int(pHeader.VarBuf[latest].TickCount) {
		// if older than last recieved, than reset, we probably disconnected
		lastTickCount = int(pHeader.VarBuf[latest].TickCount)
		return nil, errors.New("We probably disconnected")
	}

	// else the same, and nothing changed this tick
	return nil, errors.New("Nothing changed this tick")
}

func irsdk_waitForDataReady(timeOut int) ([]byte, error) {
	var data []byte
	var err error

	if !isInitialized {
		err = irsdk_startup()

		if err != nil {
			// sleep if error
			// @TODO: fix this
			if timeOut > 0 {
				sleep(timeOut)
			}

			return nil, nil
		}
	}

	// just to be sure, check before we sleep
	data, err = irsdk_getNewData()
	if err == nil {
		return data, err
	}

	// sleep till signaled
	waitForSingleObject(hDataValidEvent, timeOut)

	// we woke up, so check for data
	data, err = irsdk_getNewData()
	if err != nil {
		return nil, err
	}

	return data, err
}
func irsdk_isConnected() bool {
	if isInitialized {
		elapsed := time.Now().Sub(lastValidTime)
		if (pHeader.Status&irsdk_stConnected) > 0 && (elapsed < timeout) {
			return true
		}
	}

	return false
}

// direct access to the data buffer
// // Warnign! This buffer is volitile so read it out fast!
// // Use the cached copy from irsdk_waitForDataReady() or irsdk_getNewData()
// instead
func irsdk_getData(index int) []byte {
	if isInitialized {
		endByte := int(pHeader.VarBuf[index].BufOffset)
		return pSharedMem[:endByte]
	}

	return nil
}

func irsdk_getSessionInfoStr() []byte {
	if isInitialized {
		return pSharedMem[pHeader.SessionInfoOffset:pHeader.SessionInfoLen]
	}
	return nil
}

func irsdk_getVarHeaderPtr() *irsdk_varHeader {
	if isInitialized {
		varHeaderOffset := int(pHeader.VarHeaderOffset)
		varHeader := &irsdk_varHeader{}
		varHeaderSize := int(unsafe.Sizeof(*varHeader))

		startByte := varHeaderOffset
		endByte := startByte + varHeaderSize

		// create a io.Reader
		b := bytes.NewBuffer(pSharedMem[startByte:endByte])
		// read []byte and convert it into irsdk_varHeader
		binary.Read(b, binary.LittleEndian, varHeader)

		return varHeader
	}
	return nil
}

func irsdk_getVarHeaderEntry(index int) *irsdk_varHeader {
	if isInitialized {
		if index >= 0 && index < (int)(pHeader.NumVars) {
			varHeaderOffset := int(pHeader.VarHeaderOffset)
			varHeader := &irsdk_varHeader{}
			varHeaderSize := int(unsafe.Sizeof(*varHeader))

			startByte := varHeaderOffset + (index * varHeaderSize)
			endByte := startByte + varHeaderSize

			// create a io.Reader
			b := bytes.NewBuffer(pSharedMem[startByte:endByte])
			// read []byte and convert it into irsdk_varHeader
			binary.Read(b, binary.LittleEndian, varHeader)

			return varHeader
		}
	}
	return nil
}

// Note: this is a linear search, so cache the results
func irsdk_varNameToIndex(name string) int {
	var pVar *irsdk_varHeader

	if name != "" {
		numVars := int(pHeader.NumVars)
		for index := 0; index <= numVars; index++ {
			pVar = irsdk_getVarHeaderEntry(index)
			pVarName := CToGoString(pVar.Name[:])
			if pVar != nil && pVarName == name {
				return index
			}
		}
	}

	return -1
}

func irsdk_varNameToOffset(name string) C.int {
	var pVar *irsdk_varHeader

	if name != "" {
		numVars := int(pHeader.NumVars)
		for index := 0; index <= numVars; index++ {
			pVar = irsdk_getVarHeaderEntry(index)
			pVarName := CToGoString(pVar.Name[:])
			if pVar != nil && pVarName == name {
				return pVar.Offset
			}
		}
	}

	return -1
}

func CToGoString(c []byte) string {
	n := -1
	for i, b := range c {
		if b == 0 {
			break
		}
		n = i
	}
	return string(c[:n+1])
}

// unsigned int irsdk_getBroadcastMsgID()
// {
// 	static unsigned int msgId = RegisterWindowMessageA(IRSDK_BROADCASTMSGNAME);

// 	return msgId;
// }

// void irsdk_broadcastMsg(irsdk_BroadcastMsg msg, int var1, int var2, int var3)
// {
// 	irsdk_broadcastMsg(msg, var1, MAKELONG(var2, var3));
// }

// void irsdk_broadcastMsg(irsdk_BroadcastMsg msg, int var1, int var2)
// {
// 	static unsigned int msgId = irsdk_getBroadcastMsgID();

// 	if(msgId && msg >= 0 && msg < irsdk_BroadcastLast)
// 	{
// 		SendNotifyMessage(HWND_BROADCAST, msgId, MAKELONG(msg, var1), var2);
// 	}
// }

// int irsdk_padCarNum(int num, int zero)
// {
// 	int retVal = num;
// 	int numPlace = 1;
// 	if(num > 99)
// 		numPlace = 3;
// 	else if(num > 9)
// 		numPlace = 2;
// 	if(zero)
// 	{
// 		numPlace += zero;
// 		retVal = num + 1000*numPlace;
// 	}

// 	return retVal;
// }
<|endoftext|>


<commit_before>// Copyright 2009 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// +build ignore

// Generate a self-signed X.509 certificate for a TLS server. Outputs to
// 'cert.pem' and 'key.pem' and will overwrite existing files.

package main

import (
	"crypto/ecdsa"
	"crypto/elliptic"
	"crypto/rand"
	"crypto/rsa"
	"crypto/x509"
	"crypto/x509/pkix"
	"encoding/pem"
	"flag"
	"fmt"
	"log"
	"math/big"
	"net"
	"os"
	"strings"
	"time"
)

var (
	host       = flag.String("host", "", "Comma-separated hostnames and IPs to generate a certificate for")
	validFrom  = flag.String("start-date", "", "Creation date formatted as Jan 1 15:04:05 2011")
	validFor   = flag.Duration("duration", 365*24*time.Hour, "Duration that certificate is valid for")
	isCA       = flag.Bool("ca", false, "whether this cert should be its own Certificate Authority")
	rsaBits    = flag.Int("rsa-bits", 2048, "Size of RSA key to generate. Ignored if --ecdsa-curve is set")
	ecdsaCurve = flag.String("ecdsa-curve", "", "ECDSA curve to use to generate a key. Valid values are P224, P256, P384, P521")
)

func publicKey(priv interface{}) interface{} {
	switch k := priv.(type) {
	case *rsa.PrivateKey:
		return &k.PublicKey
	case *ecdsa.PrivateKey:
		return &k.PublicKey
	default:
		return nil
	}
}

func pemBlockForKey(priv interface{}) *pem.Block {
	switch k := priv.(type) {
	case *rsa.PrivateKey:
		return &pem.Block{Type: "RSA PRIVATE KEY", Bytes: x509.MarshalPKCS1PrivateKey(k)}
	case *ecdsa.PrivateKey:
		b, err := x509.MarshalECPrivateKey(k)
		if err != nil {
			fmt.Fprintf(os.Stderr, "Unable to marshal ECDSA private key: %v", err)
			os.Exit(2)
		}
		return &pem.Block{Type: "EC PRIVATE KEY", Bytes: b}
	default:
		return nil
	}
}

func main() {
	flag.Parse()

	if len(*host) == 0 {
		log.Fatalf("Missing required --host parameter")
	}

	var priv interface{}
	var err error
	switch *ecdsaCurve {
	case "":
		priv, err = rsa.GenerateKey(rand.Reader, *rsaBits)
	case "P224":
		priv, err = ecdsa.GenerateKey(elliptic.P224(), rand.Reader)
	case "P256":
		priv, err = ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
	case "P384":
		priv, err = ecdsa.GenerateKey(elliptic.P384(), rand.Reader)
	case "P521":
		priv, err = ecdsa.GenerateKey(elliptic.P521(), rand.Reader)
	default:
		fmt.Fprintf(os.Stderr, "Unrecognized elliptic curve: %q", *ecdsaCurve)
		os.Exit(1)
	}
	if err != nil {
		log.Fatalf("failed to generate private key: %s", err)
	}

	var notBefore time.Time
	if len(*validFrom) == 0 {
		notBefore = time.Now()
	} else {
		notBefore, err = time.Parse("Jan 2 15:04:05 2006", *validFrom)
		if err != nil {
			fmt.Fprintf(os.Stderr, "Failed to parse creation date: %s\n", err)
			os.Exit(1)
		}
	}

	notAfter := notBefore.Add(*validFor)

	serialNumberLimit := new(big.Int).Lsh(big.NewInt(1), 128)
	serialNumber, err := rand.Int(rand.Reader, serialNumberLimit)
	if err != nil {
		log.Fatalf("failed to generate serial number: %s", err)
	}

	template := x509.Certificate{
		SerialNumber: serialNumber,
		Subject: pkix.Name{
			Organization: []string{"Acme Co"},
		},
		NotBefore: notBefore,
		NotAfter:  notAfter,

		KeyUsage:              x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature,
		ExtKeyUsage:           []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth},
		BasicConstraintsValid: true,
	}

	hosts := strings.Split(*host, ",")
	for _, h := range hosts {
		if ip := net.ParseIP(h); ip != nil {
			template.IPAddresses = append(template.IPAddresses, ip)
		} else {
			template.DNSNames = append(template.DNSNames, h)
		}
	}

	if *isCA {
		template.IsCA = true
		template.KeyUsage |= x509.KeyUsageCertSign
	}

	derBytes, err := x509.CreateCertificate(rand.Reader, &template, &template, publicKey(priv), priv)
	if err != nil {
		log.Fatalf("Failed to create certificate: %s", err)
	}

	certOut, err := os.Create("cert.pem")
	if err != nil {
		log.Fatalf("failed to open cert.pem for writing: %s", err)
	}
	pem.Encode(certOut, &pem.Block{Type: "CERTIFICATE", Bytes: derBytes})
	certOut.Close()
	log.Print("written cert.pem\n")

	keyOut, err := os.OpenFile("key.pem", os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0600)
	if err != nil {
		log.Print("failed to open key.pem for writing:", err)
		return
	}
	pem.Encode(keyOut, pemBlockForKey(priv))
	keyOut.Close()
	log.Print("written key.pem\n")
}
<commit_msg>crypto/tls: recommend P256 elliptic curve<commit_after>// Copyright 2009 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// +build ignore

// Generate a self-signed X.509 certificate for a TLS server. Outputs to
// 'cert.pem' and 'key.pem' and will overwrite existing files.

package main

import (
	"crypto/ecdsa"
	"crypto/elliptic"
	"crypto/rand"
	"crypto/rsa"
	"crypto/x509"
	"crypto/x509/pkix"
	"encoding/pem"
	"flag"
	"fmt"
	"log"
	"math/big"
	"net"
	"os"
	"strings"
	"time"
)

var (
	host       = flag.String("host", "", "Comma-separated hostnames and IPs to generate a certificate for")
	validFrom  = flag.String("start-date", "", "Creation date formatted as Jan 1 15:04:05 2011")
	validFor   = flag.Duration("duration", 365*24*time.Hour, "Duration that certificate is valid for")
	isCA       = flag.Bool("ca", false, "whether this cert should be its own Certificate Authority")
	rsaBits    = flag.Int("rsa-bits", 2048, "Size of RSA key to generate. Ignored if --ecdsa-curve is set")
	ecdsaCurve = flag.String("ecdsa-curve", "", "ECDSA curve to use to generate a key. Valid values are P224, P256 (recommended), P384, P521")
)

func publicKey(priv interface{}) interface{} {
	switch k := priv.(type) {
	case *rsa.PrivateKey:
		return &k.PublicKey
	case *ecdsa.PrivateKey:
		return &k.PublicKey
	default:
		return nil
	}
}

func pemBlockForKey(priv interface{}) *pem.Block {
	switch k := priv.(type) {
	case *rsa.PrivateKey:
		return &pem.Block{Type: "RSA PRIVATE KEY", Bytes: x509.MarshalPKCS1PrivateKey(k)}
	case *ecdsa.PrivateKey:
		b, err := x509.MarshalECPrivateKey(k)
		if err != nil {
			fmt.Fprintf(os.Stderr, "Unable to marshal ECDSA private key: %v", err)
			os.Exit(2)
		}
		return &pem.Block{Type: "EC PRIVATE KEY", Bytes: b}
	default:
		return nil
	}
}

func main() {
	flag.Parse()

	if len(*host) == 0 {
		log.Fatalf("Missing required --host parameter")
	}

	var priv interface{}
	var err error
	switch *ecdsaCurve {
	case "":
		priv, err = rsa.GenerateKey(rand.Reader, *rsaBits)
	case "P224":
		priv, err = ecdsa.GenerateKey(elliptic.P224(), rand.Reader)
	case "P256":
		priv, err = ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
	case "P384":
		priv, err = ecdsa.GenerateKey(elliptic.P384(), rand.Reader)
	case "P521":
		priv, err = ecdsa.GenerateKey(elliptic.P521(), rand.Reader)
	default:
		fmt.Fprintf(os.Stderr, "Unrecognized elliptic curve: %q", *ecdsaCurve)
		os.Exit(1)
	}
	if err != nil {
		log.Fatalf("failed to generate private key: %s", err)
	}

	var notBefore time.Time
	if len(*validFrom) == 0 {
		notBefore = time.Now()
	} else {
		notBefore, err = time.Parse("Jan 2 15:04:05 2006", *validFrom)
		if err != nil {
			fmt.Fprintf(os.Stderr, "Failed to parse creation date: %s\n", err)
			os.Exit(1)
		}
	}

	notAfter := notBefore.Add(*validFor)

	serialNumberLimit := new(big.Int).Lsh(big.NewInt(1), 128)
	serialNumber, err := rand.Int(rand.Reader, serialNumberLimit)
	if err != nil {
		log.Fatalf("failed to generate serial number: %s", err)
	}

	template := x509.Certificate{
		SerialNumber: serialNumber,
		Subject: pkix.Name{
			Organization: []string{"Acme Co"},
		},
		NotBefore: notBefore,
		NotAfter:  notAfter,

		KeyUsage:              x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature,
		ExtKeyUsage:           []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth},
		BasicConstraintsValid: true,
	}

	hosts := strings.Split(*host, ",")
	for _, h := range hosts {
		if ip := net.ParseIP(h); ip != nil {
			template.IPAddresses = append(template.IPAddresses, ip)
		} else {
			template.DNSNames = append(template.DNSNames, h)
		}
	}

	if *isCA {
		template.IsCA = true
		template.KeyUsage |= x509.KeyUsageCertSign
	}

	derBytes, err := x509.CreateCertificate(rand.Reader, &template, &template, publicKey(priv), priv)
	if err != nil {
		log.Fatalf("Failed to create certificate: %s", err)
	}

	certOut, err := os.Create("cert.pem")
	if err != nil {
		log.Fatalf("failed to open cert.pem for writing: %s", err)
	}
	pem.Encode(certOut, &pem.Block{Type: "CERTIFICATE", Bytes: derBytes})
	certOut.Close()
	log.Print("written cert.pem\n")

	keyOut, err := os.OpenFile("key.pem", os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0600)
	if err != nil {
		log.Print("failed to open key.pem for writing:", err)
		return
	}
	pem.Encode(keyOut, pemBlockForKey(priv))
	keyOut.Close()
	log.Print("written key.pem\n")
}
<|endoftext|>


<commit_before>package policy

import (
	"bytes"
	"net"
	"sync"
	"testing"
	"time"

	"github.com/simonz05/util/assert"
	"github.com/simonz05/util/log"
)

func init() {
	log.Severity = log.LevelError
	Timeout = time.Second
}

var policyTests = []struct {
	send      []byte
	exp       []byte
	failWrite bool
	failRead  bool
}{
	{
		send: protocolPing,
		exp:  protocolPingResponse,
	},
	{
		send:     []byte(""),
		exp:      []byte(""),
		failRead: true,
	},
	{
		send: protocolPolicy,
		exp:  protocolPolicyResponse,
	},
	{
		send:     protocolPolicy[:len(protocolPolicy)-1],
		exp:      protocolPolicyResponse,
		failRead: true,
	},
}

const testBufSize = 8 << 4

func TestPolicy(t *testing.T) {
	ast := assert.NewAssert(t)

	var wg sync.WaitGroup
	l, err := net.Listen("tcp", ":9001")
	defer l.Close()

	ast.Nil(err)
	log.Printf("Listen on %v", l.Addr())

	wg.Add(1)
	go func() {
		serve(l)
		wg.Done()
	}()

	for _, p := range policyTests {
		conn, err := net.Dial("tcp", ":9001")
		ast.Nil(err)

		n, err := conn.Write(p.send)

		if !p.failWrite {
			ast.Nil(err)
			ast.Equal(len(p.send), n)
		} else {
			ast.NotNil(err)
			continue
		}

		buf := make([]byte, testBufSize)
		n, err = conn.Read(buf)

		if !p.failRead {
			ast.Nil(err)
			ast.Equal(len(p.exp), n)
			ast.True(bytes.Equal(p.exp, buf[:n]))
		} else {
			ast.NotNil(err)
			continue
		}
	}
	l.Close()
	wg.Wait()
}

func BenchmarkPolicy(b *testing.B) {
	b.StopTimer()
	var wg sync.WaitGroup
	l, err := net.Listen("tcp", ":9001")
	defer l.Close()

	if err != nil {
		b.Fatal(err)
	}

	log.Printf("Listen on %v", l.Addr())

	wg.Add(1)
	go func() {
		serve(l)
		wg.Done()
	}()

	b.StartTimer()
	k := 8 << 2
	for i := 0; i < b.N; i += k {
		var wg2 sync.WaitGroup
		for j := 0; j < k; j++ {
			wg2.Add(1)
			go func() {
				conn, err := net.Dial("tcp", ":9001")

				if err != nil {
					b.Errorf("error dialing: %v", err)
				}

				n, err := conn.Write(protocolPolicy)

				if err != nil || n != len(protocolPolicy) {
					b.Errorf("error sending: %v, %d == %d", err, n, len(protocolPolicy))
				}

				buf := make([]byte, testBufSize)
				n, err = conn.Read(buf)

				if err != nil || n != len(protocolPolicyResponse) {
					b.Errorf("error reading: %v, %d == %d", err, n, len(protocolPolicyResponse))
				}

				if !bytes.Equal(protocolPolicyResponse, buf[:n]) {
					b.Errorf("unexpected response value, exp: %+q, got %+q", protocolPolicyResponse, buf[:n])
				}
				wg2.Done()
			}()
		}
		wg2.Wait()
	}
	b.StopTimer()
	l.Close()
	wg.Wait()
	b.StartTimer()
}
<commit_msg>reorder tests<commit_after>package policy

import (
	"bytes"
	"net"
	"sync"
	"testing"
	"time"

	"github.com/simonz05/util/assert"
	"github.com/simonz05/util/log"
)

func init() {
	log.Severity = log.LevelError
	Timeout = time.Second
}

var policyTests = []struct {
	send      []byte
	exp       []byte
	failWrite bool
	failRead  bool
}{
	{
		send: protocolPing,
		exp:  protocolPingResponse,
	},
	{
		send: protocolPolicy,
		exp:  protocolPolicyResponse,
	},
	{
		send:     []byte(""),
		exp:      []byte(""),
		failRead: true,
	},
	{
		send:     protocolPolicy[:len(protocolPolicy)-1],
		exp:      protocolPolicyResponse,
		failRead: true,
	},
}

const testBufSize = 8 << 4

func TestPolicy(t *testing.T) {
	ast := assert.NewAssert(t)

	var wg sync.WaitGroup
	l, err := net.Listen("tcp", ":9001")
	defer l.Close()

	ast.Nil(err)
	log.Printf("Listen on %v", l.Addr())

	wg.Add(1)
	go func() {
		serve(l)
		wg.Done()
	}()

	for _, p := range policyTests {
		conn, err := net.Dial("tcp", ":9001")
		ast.Nil(err)

		n, err := conn.Write(p.send)

		if !p.failWrite {
			ast.Nil(err)
			ast.Equal(len(p.send), n)
		} else {
			ast.NotNil(err)
			continue
		}

		buf := make([]byte, testBufSize)
		n, err = conn.Read(buf)

		if !p.failRead {
			ast.Nil(err)
			ast.Equal(len(p.exp), n)
			ast.True(bytes.Equal(p.exp, buf[:n]))
		} else {
			ast.NotNil(err)
			continue
		}
	}
	l.Close()
	wg.Wait()
}

func BenchmarkPolicy(b *testing.B) {
	b.StopTimer()
	var wg sync.WaitGroup
	l, err := net.Listen("tcp", ":9001")
	defer l.Close()

	if err != nil {
		b.Fatal(err)
	}

	log.Printf("Listen on %v", l.Addr())

	wg.Add(1)
	go func() {
		serve(l)
		wg.Done()
	}()

	b.StartTimer()
	k := 8 << 2
	for i := 0; i < b.N; i += k {
		var wg2 sync.WaitGroup
		for j := 0; j < k; j++ {
			wg2.Add(1)
			go func() {
				conn, err := net.Dial("tcp", ":9001")

				if err != nil {
					b.Errorf("error dialing: %v", err)
				}

				n, err := conn.Write(protocolPolicy)

				if err != nil || n != len(protocolPolicy) {
					b.Errorf("error sending: %v, %d == %d", err, n, len(protocolPolicy))
				}

				buf := make([]byte, testBufSize)
				n, err = conn.Read(buf)

				if err != nil || n != len(protocolPolicyResponse) {
					b.Errorf("error reading: %v, %d == %d", err, n, len(protocolPolicyResponse))
				}

				if !bytes.Equal(protocolPolicyResponse, buf[:n]) {
					b.Errorf("unexpected response value, exp: %+q, got %+q", protocolPolicyResponse, buf[:n])
				}
				wg2.Done()
			}()
		}
		wg2.Wait()
	}
	b.StopTimer()
	l.Close()
	wg.Wait()
	b.StartTimer()
}
<|endoftext|>


<commit_before>// (C) Copyright 2019 Hewlett Packard Enterprise Development LP
//
// 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 oneview

import (
	"github.com/HewlettPackard/oneview-golang/ov"
)

func dataSourceScope() *schema.Resource {
	return &schema.Resource{
		Read: resourceScopeRead,

		Schema: map[string]*schema.Schema{
			"description": {
				Type:     schema.TypeString,
				Optional: true,
			},
			"modified": {
				Type:     schema.TypeString,
				Computed: true,
			},
			"name": {
				Type:     schema.TypeString,
				Required: true,
			},
			"state": {
				Type:     schema.TypeString,
				Computed: true,
			},
			"type": {
				Type:     schema.TypeString,
				Required: true,
			},
			"uri": {
				Type:     schema.TypeString,
				Computed: true,
			},
			"ext_attributes": {
				Optional: true,
				Type:     schema.TypeList,
				Elem: &schema.Resource{
					Schema: map[string]*schema.Schema{
						"type": {
							Type:     schema.TypeString,
							Optional: true,
						},
					},
				},
			},
			"appliance_id": {
				Type:     schema.TypeString,
				Computed: true,
			},
			"category": {
				Type:     schema.TypeString,
				Computed: true,
			},
			"created": {
				Type:     schema.TypeString,
				Computed: true,
			},
			"etag": {
				Type:     schema.TypeString,
				Computed: true,
			},
			"old_uri": {
				Type:     schema.TypeString,
				Computed: true,
			},
			"scopes_uri": {
				Type:     schema.TypeString,
				Computed: true,
			},
			"initial_scope_uris": {
				Optional: true,
				Type:     schema.TypeSet,
				Elem: &schema.Schema{
					Type: schema.TypeString,
				},
				Set: schema.HashString,
			},
			"added_resource_uris": {
				Optional: true,
				Type:     schema.TypeSet,
				Elem: &schema.Schema{
					Type: schema.TypeString,
				},
				Set: schema.HashString,
			},
			"removed_resource_uris": {
				Optional: true,
				Type:     schema.TypeSet,
				Elem: &schema.Schema{
					Type: schema.TypeString,
				},
				Set: schema.HashString,
			},
		},
	}
}

func dataSourceScopeRead(d *schema.ResourceData, meta interface{}) error {

	config := meta.(*Config)
	name := d.Get("name").(string)

	scope, err := config.ovClient.GetScopeByName(name)
	if err != nil || scope.URI.IsNil() {
		d.SetId("")
		return nil
	}
	d.Set("name", scope.Name)
	d.Set("description", scope.Description)
	d.Set("modified", scope.Modified)
	d.Set("state", scope.State)
	d.Set("status", scope.Status)
	d.Set("type", scope.Type)
	d.Set("uri", scope.URI.String())
	d.Set("appliance_id", scope.ApplianceId)
	d.Set("category", scope.Category)
	d.Set("etag", scope.Etag)
	d.Set("created", scope.Created)
	d.Set("old_uri", scope.OldUri.String())
	d.Set("scopes_uri", scope.ScopesUri.String())
	d.Set("initial_scope_uris", scope.InitialScopeUris)
	d.Set("added_resource_uris", scope.AddedResourceUris)
	d.Set("removed_resource_uris", scope.RemovedResourceUris)

	return nil
}
<commit_msg>Build issue fix<commit_after>// (C) Copyright 2019 Hewlett Packard Enterprise Development LP
//
// 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 oneview

import (
	"github.com/hashicorp/terraform/helper/schema"
)

func dataSourceScope() *schema.Resource {
	return &schema.Resource{
		Read: resourceScopeRead,

		Schema: map[string]*schema.Schema{
			"description": {
				Type:     schema.TypeString,
				Optional: true,
			},
			"modified": {
				Type:     schema.TypeString,
				Computed: true,
			},
			"name": {
				Type:     schema.TypeString,
				Required: true,
			},
			"state": {
				Type:     schema.TypeString,
				Computed: true,
			},
			"type": {
				Type:     schema.TypeString,
				Required: true,
			},
			"uri": {
				Type:     schema.TypeString,
				Computed: true,
			},
			"ext_attributes": {
				Optional: true,
				Type:     schema.TypeList,
				Elem: &schema.Resource{
					Schema: map[string]*schema.Schema{
						"type": {
							Type:     schema.TypeString,
							Optional: true,
						},
					},
				},
			},
			"appliance_id": {
				Type:     schema.TypeString,
				Computed: true,
			},
			"category": {
				Type:     schema.TypeString,
				Computed: true,
			},
			"created": {
				Type:     schema.TypeString,
				Computed: true,
			},
			"etag": {
				Type:     schema.TypeString,
				Computed: true,
			},
			"old_uri": {
				Type:     schema.TypeString,
				Computed: true,
			},
			"scopes_uri": {
				Type:     schema.TypeString,
				Computed: true,
			},
			"initial_scope_uris": {
				Optional: true,
				Type:     schema.TypeSet,
				Elem: &schema.Schema{
					Type: schema.TypeString,
				},
				Set: schema.HashString,
			},
			"added_resource_uris": {
				Optional: true,
				Type:     schema.TypeSet,
				Elem: &schema.Schema{
					Type: schema.TypeString,
				},
				Set: schema.HashString,
			},
			"removed_resource_uris": {
				Optional: true,
				Type:     schema.TypeSet,
				Elem: &schema.Schema{
					Type: schema.TypeString,
				},
				Set: schema.HashString,
			},
		},
	}
}

func dataSourceScopeRead(d *schema.ResourceData, meta interface{}) error {

	config := meta.(*Config)
	name := d.Get("name").(string)

	scope, err := config.ovClient.GetScopeByName(name)
	if err != nil || scope.URI.IsNil() {
		d.SetId("")
		return nil
	}
	d.Set("name", scope.Name)
	d.Set("description", scope.Description)
	d.Set("modified", scope.Modified)
	d.Set("state", scope.State)
	d.Set("status", scope.Status)
	d.Set("type", scope.Type)
	d.Set("uri", scope.URI.String())
	d.Set("appliance_id", scope.ApplianceId)
	d.Set("category", scope.Category)
	d.Set("etag", scope.Etag)
	d.Set("created", scope.Created)
	d.Set("old_uri", scope.OldUri.String())
	d.Set("scopes_uri", scope.ScopesUri.String())
	d.Set("initial_scope_uris", scope.InitialScopeUris)
	d.Set("added_resource_uris", scope.AddedResourceUris)
	d.Set("removed_resource_uris", scope.RemovedResourceUris)

	return nil
}
<|endoftext|>


<commit_before>// Copyright 2015 Google Inc. All Rights Reserved.
//
// 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 kubelet

import (
	"fmt"
	"net/url"
	"strings"
	"time"

	. "k8s.io/heapster/metrics/core"

	"github.com/golang/glog"
	cadvisor "github.com/google/cadvisor/info/v1"
	"github.com/prometheus/client_golang/prometheus"
	kube_api "k8s.io/kubernetes/pkg/api"
	"k8s.io/kubernetes/pkg/client/cache"
	kube_client "k8s.io/kubernetes/pkg/client/unversioned"
	"k8s.io/kubernetes/pkg/fields"
)

const (
	infraContainerName = "POD"
	// TODO: following constants are copied from k8s, change to use them directly
	kubernetesPodNameLabel      = "io.kubernetes.pod.name"
	kubernetesPodNamespaceLabel = "io.kubernetes.pod.namespace"
	kubernetesPodUID            = "io.kubernetes.pod.uid"
	kubernetesContainerLabel    = "io.kubernetes.container.name"
)

var (
	// The Kubelet request latencies in microseconds.
	kubeletRequestLatency = prometheus.NewSummaryVec(
		prometheus.SummaryOpts{
			Namespace: "heapster",
			Subsystem: "kubelet",
			Name:      "request_duration_microseconds",
			Help:      "The Kubelet request latencies in microseconds.",
		},
		[]string{"node"},
	)
)

func init() {
	prometheus.MustRegister(kubeletRequestLatency)
}

// Kubelet-provided metrics for pod and system container.
type kubeletMetricsSource struct {
	host          Host
	kubeletClient *KubeletClient
	nodename      string
	hostname      string
	hostId        string
}

func NewKubeletMetricsSource(host Host, client *KubeletClient, nodeName string, hostName string, hostId string) MetricsSource {
	return &kubeletMetricsSource{
		host:          host,
		kubeletClient: client,
		nodename:      nodeName,
		hostname:      hostName,
		hostId:        hostId,
	}
}

func (this *kubeletMetricsSource) Name() string {
	return this.String()
}

func (this *kubeletMetricsSource) String() string {
	return fmt.Sprintf("kubelet:%s:%d", this.host.IP, this.host.Port)
}

func (this *kubeletMetricsSource) handleSystemContainer(c *cadvisor.ContainerInfo, cMetrics *MetricSet) string {
	glog.V(8).Infof("Found system container %v with labels: %+v", c.Name, c.Spec.Labels)
	cName := c.Name
	if strings.HasPrefix(cName, "/") {
		cName = cName[1:]
	}
	cMetrics.Labels[LabelMetricSetType.Key] = MetricSetTypeSystemContainer
	cMetrics.Labels[LabelContainerName.Key] = cName
	return NodeContainerKey(this.nodename, cName)
}

func (this *kubeletMetricsSource) handleKubernetesContainer(cName, ns, podName string, c *cadvisor.ContainerInfo, cMetrics *MetricSet) string {
	var metricSetKey string
	if cName == infraContainerName {
		metricSetKey = PodKey(ns, podName)
		cMetrics.Labels[LabelMetricSetType.Key] = MetricSetTypePod
	} else {
		metricSetKey = PodContainerKey(ns, podName, cName)
		cMetrics.Labels[LabelMetricSetType.Key] = MetricSetTypePodContainer
		cMetrics.Labels[LabelContainerName.Key] = cName
		cMetrics.Labels[LabelContainerBaseImage.Key] = c.Spec.Image
	}
	cMetrics.Labels[LabelPodId.Key] = c.Spec.Labels[kubernetesPodUID]
	cMetrics.Labels[LabelPodName.Key] = podName
	cMetrics.Labels[LabelNamespaceName.Key] = ns
	// Needed for backward compatibility
	cMetrics.Labels[LabelPodNamespace.Key] = ns
	return metricSetKey
}

func (this *kubeletMetricsSource) decodeMetrics(c *cadvisor.ContainerInfo) (string, *MetricSet) {
	if len(c.Stats) == 0 {
		return "", nil
	}

	var metricSetKey string
	cMetrics := &MetricSet{
		CreateTime:   c.Spec.CreationTime,
		ScrapeTime:   c.Stats[0].Timestamp,
		MetricValues: map[string]MetricValue{},
		Labels: map[string]string{
			LabelNodename.Key: this.nodename,
			LabelHostname.Key: this.hostname,
			LabelHostID.Key:   this.hostId,
		},
		LabeledMetrics: []LabeledMetric{},
	}

	if isNode(c) {
		metricSetKey = NodeKey(this.nodename)
		cMetrics.Labels[LabelMetricSetType.Key] = MetricSetTypeNode
	} else {
		cName := c.Spec.Labels[kubernetesContainerLabel]
		ns := c.Spec.Labels[kubernetesPodNamespaceLabel]
		podName := c.Spec.Labels[kubernetesPodNameLabel]

		// Support for kubernetes 1.0.*
		if ns == "" && strings.Contains(podName, "/") {
			tokens := strings.SplitN(podName, "/", 2)
			if len(tokens) == 2 {
				ns = tokens[0]
				podName = tokens[1]
			}
		}
		if cName == "" {
			// Better this than nothing. This is a temporary hack for new heapster to work
			// with Kubernetes 1.0.*.
			// TODO: fix this with POD list.
			// Parsing name like:
			// k8s_kube-ui.7f9b83f6_kube-ui-v1-bxj1w_kube-system_9abfb0bd-811f-11e5-b548-42010af00002_e6841e8d
			pos := strings.Index(c.Name, ".")
			if pos >= 0 {
				// remove first 4 chars.
				cName = c.Name[len("k8s_"):pos]
			}
		}

		// No Kubernetes metadata so treat this as a system container.
		if cName == "" || ns == "" || podName == "" {
			metricSetKey = this.handleSystemContainer(c, cMetrics)
		} else {
			metricSetKey = this.handleKubernetesContainer(cName, ns, podName, c, cMetrics)
		}
	}

	for _, metric := range StandardMetrics {
		if metric.HasValue != nil && metric.HasValue(&c.Spec) {
			cMetrics.MetricValues[metric.Name] = metric.GetValue(&c.Spec, c.Stats[0])
		}
	}

	for _, metric := range LabeledMetrics {
		if metric.HasLabeledMetric != nil && metric.HasLabeledMetric(&c.Spec) {
			labeledMetrics := metric.GetLabeledMetric(&c.Spec, c.Stats[0])
			cMetrics.LabeledMetrics = append(cMetrics.LabeledMetrics, labeledMetrics...)
		}
	}

	if c.Spec.HasCustomMetrics {
	metricloop:
		for _, spec := range c.Spec.CustomMetrics {
			if cmValue, ok := c.Stats[0].CustomMetrics[spec.Name]; ok && cmValue != nil && len(cmValue) >= 1 {
				newest := cmValue[0]
				for _, metricVal := range cmValue {
					if newest.Timestamp.Before(metricVal.Timestamp) {
						newest = metricVal
					}
				}
				mv := MetricValue{}
				switch spec.Type {
				case cadvisor.MetricGauge:
					mv.MetricType = MetricGauge
				case cadvisor.MetricCumulative:
					mv.MetricType = MetricCumulative
				default:
					glog.V(4).Infof("Skipping %s: unknown custom metric type: %v", spec.Name, spec.Type)
					continue metricloop
				}

				switch spec.Format {
				case cadvisor.IntType:
					mv.ValueType = ValueInt64
					mv.IntValue = newest.IntValue
				case cadvisor.FloatType:
					mv.ValueType = ValueFloat
					mv.FloatValue = float32(newest.FloatValue)
				default:
					glog.V(4).Infof("Skipping %s: unknown custom metric format", spec.Name, spec.Format)
					continue metricloop
				}

				cMetrics.MetricValues[CustomMetricPrefix+spec.Name] = mv
			}
		}
	}

	return metricSetKey, cMetrics
}

func (this *kubeletMetricsSource) ScrapeMetrics(start, end time.Time) *DataBatch {
	containers, err := this.scrapeKubelet(this.kubeletClient, this.host, start, end)
	if err != nil {
		glog.Errorf("error while getting containers from Kubelet: %v", err)
	}
	glog.V(2).Infof("successfully obtained stats for %v containers", len(containers))

	result := &DataBatch{
		Timestamp:  end,
		MetricSets: map[string]*MetricSet{},
	}
	keys := make(map[string]bool)
	for _, c := range containers {
		name, metrics := this.decodeMetrics(&c)
		if name == "" || metrics == nil {
			continue
		}
		result.MetricSets[name] = metrics
		keys[name] = true
	}
	return result
}

func (this *kubeletMetricsSource) scrapeKubelet(client *KubeletClient, host Host, start, end time.Time) ([]cadvisor.ContainerInfo, error) {
	startTime := time.Now()
	defer kubeletRequestLatency.WithLabelValues(this.hostname).Observe(float64(time.Since(startTime)))
	return client.GetAllRawContainers(host, start, end)
}

type kubeletProvider struct {
	nodeLister    *cache.StoreToNodeLister
	reflector     *cache.Reflector
	kubeletClient *KubeletClient
}

func (this *kubeletProvider) GetMetricsSources() []MetricsSource {
	sources := []MetricsSource{}
	nodes, err := this.nodeLister.List()
	if err != nil {
		glog.Errorf("error while listing nodes: %v", err)
		return sources
	}

	nodeNames := make(map[string]bool)
	for _, node := range nodes.Items {
		nodeNames[node.Name] = true
		hostname, ip, err := getNodeHostnameAndIP(&node)
		if err != nil {
			glog.Errorf("%v", err)
			continue
		}
		sources = append(sources, NewKubeletMetricsSource(
			Host{IP: ip, Port: this.kubeletClient.GetPort()},
			this.kubeletClient,
			node.Name,
			hostname,
			node.Spec.ExternalID,
		))
	}
	return sources
}

func getNodeHostnameAndIP(node *kube_api.Node) (string, string, error) {
	for _, c := range node.Status.Conditions {
		if c.Type == kube_api.NodeReady && c.Status != kube_api.ConditionTrue {
			return "", "", fmt.Errorf("Node %v is not ready", node.Name)
		}
	}
	hostname, ip := node.Name, ""
	for _, addr := range node.Status.Addresses {
		if addr.Type == kube_api.NodeHostName && addr.Address != "" {
			hostname = addr.Address
		}
		if addr.Type == kube_api.NodeInternalIP && addr.Address != "" {
			ip = addr.Address
		}
		if addr.Type == kube_api.NodeLegacyHostIP && addr.Address != "" && ip == "" {
			ip = addr.Address
		}
	}
	if ip != "" {
		return hostname, ip, nil
	}
	return "", "", fmt.Errorf("Node %v has no valid hostname and/or IP address: %v %v", node.Name, hostname, ip)
}

func NewKubeletProvider(uri *url.URL) (MetricsSourceProvider, error) {
	// create clients
	kubeConfig, kubeletConfig, err := GetKubeConfigs(uri)
	if err != nil {
		return nil, err
	}
	kubeClient := kube_client.NewOrDie(kubeConfig)
	kubeletClient, err := NewKubeletClient(kubeletConfig)
	if err != nil {
		return nil, err
	}
	// watch nodes
	lw := cache.NewListWatchFromClient(kubeClient, "nodes", kube_api.NamespaceAll, fields.Everything())
	nodeLister := &cache.StoreToNodeLister{Store: cache.NewStore(cache.MetaNamespaceKeyFunc)}
	reflector := cache.NewReflector(lw, &kube_api.Node{}, nodeLister.Store, time.Hour)
	reflector.Run()

	return &kubeletProvider{
		nodeLister:    nodeLister,
		reflector:     reflector,
		kubeletClient: kubeletClient,
	}, nil
}
<commit_msg>Be more verbose on apiserver connection failures<commit_after>// Copyright 2015 Google Inc. All Rights Reserved.
//
// 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 kubelet

import (
	"fmt"
	"net/url"
	"strings"
	"time"

	. "k8s.io/heapster/metrics/core"

	"github.com/golang/glog"
	cadvisor "github.com/google/cadvisor/info/v1"
	"github.com/prometheus/client_golang/prometheus"
	kube_api "k8s.io/kubernetes/pkg/api"
	"k8s.io/kubernetes/pkg/client/cache"
	kube_client "k8s.io/kubernetes/pkg/client/unversioned"
	"k8s.io/kubernetes/pkg/fields"
	"k8s.io/kubernetes/pkg/labels"
)

const (
	infraContainerName = "POD"
	// TODO: following constants are copied from k8s, change to use them directly
	kubernetesPodNameLabel      = "io.kubernetes.pod.name"
	kubernetesPodNamespaceLabel = "io.kubernetes.pod.namespace"
	kubernetesPodUID            = "io.kubernetes.pod.uid"
	kubernetesContainerLabel    = "io.kubernetes.container.name"
)

var (
	// The Kubelet request latencies in microseconds.
	kubeletRequestLatency = prometheus.NewSummaryVec(
		prometheus.SummaryOpts{
			Namespace: "heapster",
			Subsystem: "kubelet",
			Name:      "request_duration_microseconds",
			Help:      "The Kubelet request latencies in microseconds.",
		},
		[]string{"node"},
	)
)

func init() {
	prometheus.MustRegister(kubeletRequestLatency)
}

// Kubelet-provided metrics for pod and system container.
type kubeletMetricsSource struct {
	host          Host
	kubeletClient *KubeletClient
	nodename      string
	hostname      string
	hostId        string
}

func NewKubeletMetricsSource(host Host, client *KubeletClient, nodeName string, hostName string, hostId string) MetricsSource {
	return &kubeletMetricsSource{
		host:          host,
		kubeletClient: client,
		nodename:      nodeName,
		hostname:      hostName,
		hostId:        hostId,
	}
}

func (this *kubeletMetricsSource) Name() string {
	return this.String()
}

func (this *kubeletMetricsSource) String() string {
	return fmt.Sprintf("kubelet:%s:%d", this.host.IP, this.host.Port)
}

func (this *kubeletMetricsSource) handleSystemContainer(c *cadvisor.ContainerInfo, cMetrics *MetricSet) string {
	glog.V(8).Infof("Found system container %v with labels: %+v", c.Name, c.Spec.Labels)
	cName := c.Name
	if strings.HasPrefix(cName, "/") {
		cName = cName[1:]
	}
	cMetrics.Labels[LabelMetricSetType.Key] = MetricSetTypeSystemContainer
	cMetrics.Labels[LabelContainerName.Key] = cName
	return NodeContainerKey(this.nodename, cName)
}

func (this *kubeletMetricsSource) handleKubernetesContainer(cName, ns, podName string, c *cadvisor.ContainerInfo, cMetrics *MetricSet) string {
	var metricSetKey string
	if cName == infraContainerName {
		metricSetKey = PodKey(ns, podName)
		cMetrics.Labels[LabelMetricSetType.Key] = MetricSetTypePod
	} else {
		metricSetKey = PodContainerKey(ns, podName, cName)
		cMetrics.Labels[LabelMetricSetType.Key] = MetricSetTypePodContainer
		cMetrics.Labels[LabelContainerName.Key] = cName
		cMetrics.Labels[LabelContainerBaseImage.Key] = c.Spec.Image
	}
	cMetrics.Labels[LabelPodId.Key] = c.Spec.Labels[kubernetesPodUID]
	cMetrics.Labels[LabelPodName.Key] = podName
	cMetrics.Labels[LabelNamespaceName.Key] = ns
	// Needed for backward compatibility
	cMetrics.Labels[LabelPodNamespace.Key] = ns
	return metricSetKey
}

func (this *kubeletMetricsSource) decodeMetrics(c *cadvisor.ContainerInfo) (string, *MetricSet) {
	if len(c.Stats) == 0 {
		return "", nil
	}

	var metricSetKey string
	cMetrics := &MetricSet{
		CreateTime:   c.Spec.CreationTime,
		ScrapeTime:   c.Stats[0].Timestamp,
		MetricValues: map[string]MetricValue{},
		Labels: map[string]string{
			LabelNodename.Key: this.nodename,
			LabelHostname.Key: this.hostname,
			LabelHostID.Key:   this.hostId,
		},
		LabeledMetrics: []LabeledMetric{},
	}

	if isNode(c) {
		metricSetKey = NodeKey(this.nodename)
		cMetrics.Labels[LabelMetricSetType.Key] = MetricSetTypeNode
	} else {
		cName := c.Spec.Labels[kubernetesContainerLabel]
		ns := c.Spec.Labels[kubernetesPodNamespaceLabel]
		podName := c.Spec.Labels[kubernetesPodNameLabel]

		// Support for kubernetes 1.0.*
		if ns == "" && strings.Contains(podName, "/") {
			tokens := strings.SplitN(podName, "/", 2)
			if len(tokens) == 2 {
				ns = tokens[0]
				podName = tokens[1]
			}
		}
		if cName == "" {
			// Better this than nothing. This is a temporary hack for new heapster to work
			// with Kubernetes 1.0.*.
			// TODO: fix this with POD list.
			// Parsing name like:
			// k8s_kube-ui.7f9b83f6_kube-ui-v1-bxj1w_kube-system_9abfb0bd-811f-11e5-b548-42010af00002_e6841e8d
			pos := strings.Index(c.Name, ".")
			if pos >= 0 {
				// remove first 4 chars.
				cName = c.Name[len("k8s_"):pos]
			}
		}

		// No Kubernetes metadata so treat this as a system container.
		if cName == "" || ns == "" || podName == "" {
			metricSetKey = this.handleSystemContainer(c, cMetrics)
		} else {
			metricSetKey = this.handleKubernetesContainer(cName, ns, podName, c, cMetrics)
		}
	}

	for _, metric := range StandardMetrics {
		if metric.HasValue != nil && metric.HasValue(&c.Spec) {
			cMetrics.MetricValues[metric.Name] = metric.GetValue(&c.Spec, c.Stats[0])
		}
	}

	for _, metric := range LabeledMetrics {
		if metric.HasLabeledMetric != nil && metric.HasLabeledMetric(&c.Spec) {
			labeledMetrics := metric.GetLabeledMetric(&c.Spec, c.Stats[0])
			cMetrics.LabeledMetrics = append(cMetrics.LabeledMetrics, labeledMetrics...)
		}
	}

	if c.Spec.HasCustomMetrics {
	metricloop:
		for _, spec := range c.Spec.CustomMetrics {
			if cmValue, ok := c.Stats[0].CustomMetrics[spec.Name]; ok && cmValue != nil && len(cmValue) >= 1 {
				newest := cmValue[0]
				for _, metricVal := range cmValue {
					if newest.Timestamp.Before(metricVal.Timestamp) {
						newest = metricVal
					}
				}
				mv := MetricValue{}
				switch spec.Type {
				case cadvisor.MetricGauge:
					mv.MetricType = MetricGauge
				case cadvisor.MetricCumulative:
					mv.MetricType = MetricCumulative
				default:
					glog.V(4).Infof("Skipping %s: unknown custom metric type: %v", spec.Name, spec.Type)
					continue metricloop
				}

				switch spec.Format {
				case cadvisor.IntType:
					mv.ValueType = ValueInt64
					mv.IntValue = newest.IntValue
				case cadvisor.FloatType:
					mv.ValueType = ValueFloat
					mv.FloatValue = float32(newest.FloatValue)
				default:
					glog.V(4).Infof("Skipping %s: unknown custom metric format", spec.Name, spec.Format)
					continue metricloop
				}

				cMetrics.MetricValues[CustomMetricPrefix+spec.Name] = mv
			}
		}
	}

	return metricSetKey, cMetrics
}

func (this *kubeletMetricsSource) ScrapeMetrics(start, end time.Time) *DataBatch {
	containers, err := this.scrapeKubelet(this.kubeletClient, this.host, start, end)
	if err != nil {
		glog.Errorf("error while getting containers from Kubelet: %v", err)
	}
	glog.V(2).Infof("successfully obtained stats for %v containers", len(containers))

	result := &DataBatch{
		Timestamp:  end,
		MetricSets: map[string]*MetricSet{},
	}
	keys := make(map[string]bool)
	for _, c := range containers {
		name, metrics := this.decodeMetrics(&c)
		if name == "" || metrics == nil {
			continue
		}
		result.MetricSets[name] = metrics
		keys[name] = true
	}
	return result
}

func (this *kubeletMetricsSource) scrapeKubelet(client *KubeletClient, host Host, start, end time.Time) ([]cadvisor.ContainerInfo, error) {
	startTime := time.Now()
	defer kubeletRequestLatency.WithLabelValues(this.hostname).Observe(float64(time.Since(startTime)))
	return client.GetAllRawContainers(host, start, end)
}

type kubeletProvider struct {
	nodeLister    *cache.StoreToNodeLister
	reflector     *cache.Reflector
	kubeletClient *KubeletClient
}

func (this *kubeletProvider) GetMetricsSources() []MetricsSource {
	sources := []MetricsSource{}
	nodes, err := this.nodeLister.List()
	if err != nil {
		glog.Errorf("error while listing nodes: %v", err)
		return sources
	}
	if len(nodes.Items) == 0 {
		glog.Error("No nodes received from APIserver.")
		return sources
	}

	nodeNames := make(map[string]bool)
	for _, node := range nodes.Items {
		nodeNames[node.Name] = true
		hostname, ip, err := getNodeHostnameAndIP(&node)
		if err != nil {
			glog.Errorf("%v", err)
			continue
		}
		sources = append(sources, NewKubeletMetricsSource(
			Host{IP: ip, Port: this.kubeletClient.GetPort()},
			this.kubeletClient,
			node.Name,
			hostname,
			node.Spec.ExternalID,
		))
	}
	return sources
}

func getNodeHostnameAndIP(node *kube_api.Node) (string, string, error) {
	for _, c := range node.Status.Conditions {
		if c.Type == kube_api.NodeReady && c.Status != kube_api.ConditionTrue {
			return "", "", fmt.Errorf("Node %v is not ready", node.Name)
		}
	}
	hostname, ip := node.Name, ""
	for _, addr := range node.Status.Addresses {
		if addr.Type == kube_api.NodeHostName && addr.Address != "" {
			hostname = addr.Address
		}
		if addr.Type == kube_api.NodeInternalIP && addr.Address != "" {
			ip = addr.Address
		}
		if addr.Type == kube_api.NodeLegacyHostIP && addr.Address != "" && ip == "" {
			ip = addr.Address
		}
	}
	if ip != "" {
		return hostname, ip, nil
	}
	return "", "", fmt.Errorf("Node %v has no valid hostname and/or IP address: %v %v", node.Name, hostname, ip)
}

func NewKubeletProvider(uri *url.URL) (MetricsSourceProvider, error) {
	// create clients
	kubeConfig, kubeletConfig, err := GetKubeConfigs(uri)
	if err != nil {
		return nil, err
	}
	kubeClient := kube_client.NewOrDie(kubeConfig)
	kubeletClient, err := NewKubeletClient(kubeletConfig)
	if err != nil {
		return nil, err
	}

	// Get nodes to test if the client is configured well. Watch gives less error information.
	if _, err := kubeClient.Nodes().List(kube_api.ListOptions{
		LabelSelector: labels.Everything(),
		FieldSelector: fields.Everything()}); err != nil {
		glog.Errorf("Failed to load nodes: %v", err)
	}

	// watch nodes
	lw := cache.NewListWatchFromClient(kubeClient, "nodes", kube_api.NamespaceAll, fields.Everything())
	nodeLister := &cache.StoreToNodeLister{Store: cache.NewStore(cache.MetaNamespaceKeyFunc)}
	reflector := cache.NewReflector(lw, &kube_api.Node{}, nodeLister.Store, time.Hour)
	reflector.Run()

	return &kubeletProvider{
		nodeLister:    nodeLister,
		reflector:     reflector,
		kubeletClient: kubeletClient,
	}, nil
}
<|endoftext|>