<commit_before>package main

import (
	"fmt"
	r "github.com/scascketta/capmetro-data/Godeps/_workspace/src/github.com/dancannon/gorethink"
	"time"
)

var (
	lastUpdated map[string]time.Time = map[string]time.Time{}

	firstNewVehicleCheck bool          = true
	nextNewVehicleCheck  time.Time     = time.Now()
	vehicleCheckInterval time.Duration = (4 * 60 * 60) * (1000 * time.Millisecond)

	normalDuration   time.Duration = (30) * (1000 * time.Millisecond)
	extendedDuration time.Duration = (10 * 60) * (1000 * time.Millisecond)

	emptyResponses      map[string]int  = map[string]int{}
	recentEmptyResponse map[string]bool = map[string]bool{}
)

type VehicleStopTime struct {
	VehicleID string    `gorethink:"vehicle_id"`
	Route     string    `gorethink:"route"`
	TripID    string    `gorethink:"trip_id"`
	StopID    string    `gorethink:"stop_id"`
	Time      time.Time `gorethink:"timestamp"`
}

func FilterUpdatedVehicles(vehicles []VehiclePosition) []VehiclePosition {
	updated := []VehiclePosition{}
	for _, v := range vehicles {
		updateTime, _ := lastUpdated[v.VehicleID]
		lastUpdated[v.VehicleID] = v.Time
		if !updateTime.Equal(v.Time) {
			updated = append(updated, v)
		}
	}
	return updated
}

func LogVehiclePositions(session *r.Session, route string) error {
	vehicles, err := FetchVehicles(route)
	if err != nil {
		return err
	}
	if vehicles == nil {
		// increment retry count if fetch just before was also empty
		// only subsequent empty responses matter when determining how long to sleep
		if recentEmptyResponse[route] {
			emptyResponses[route] += 1
		}
		recentEmptyResponse[route] = true
		return fmt.Errorf("No vehicles in response for route: %s.", route)
	} else {
		recentEmptyResponse[route] = false
	}

	updated := FilterUpdatedVehicles(vehicles)

	for _, v := range updated {
		dbglogger.Printf("Vehicle %s updated at %s\n", v.VehicleID, v.Time.Format("2006-01-02T15:04:05-07:00"))
	}

	if len(updated) > 0 {
		_, err = r.Table("vehicle_position").Insert(r.Expr(updated)).Run(session)
		if err != nil {
			return err
		}
		dbglogger.Printf("Log %d vehicles, route %s.\n", len(updated), route)
	} else {
		dbglogger.Printf("No new vehicle positions to record for route %s.\n", route)
	}
	return nil
}

// Check if the the routes are inactive
// There must have been MAX_RETRIES previous attempts to fetch data,
// and all attempts must have failed
func routesAreSleeping() bool {
	dbglogger.Println("emptyResponses:", emptyResponses)
	dbglogger.Println("recentEmptyResponse:", recentEmptyResponse)
	for _, retries := range emptyResponses {
		if retries < MAX_RETRIES {
			return false
		}
	}
	return true
}

// Check if any new vehicles appear in recorded vehicle positions, add them to vehicles table
func checkNewVehicles(session *r.Session) error {
	new_vehicles := 0
	dbglogger.Println("Check for new vehicles.")
	vehicles := []map[string]string{}
	cur, err := r.Table("vehicle_position").Pluck("vehicle_id", "route", "route_id", "trip_id").Distinct().Run(session)
	if err != nil {
		return err
	}
	cur.All(&vehicles)

	for _, data := range vehicles {
		id := data["vehicle_id"]
		stream := r.Table("vehicles").Pluck("vehicle_id")
		query_expr := r.Expr(map[string]string{"vehicle_id": data["vehicle_id"]})
		cur, err = stream.Contains(query_expr).Run(session)
		if err != nil {
			return err
		}
		var res bool

		cur.Next(&res)
		if !res {
			new_vehicles += 1
			dbglogger.Printf("Adding new vehicle %s to vehicles table.\n", id)
			vehicle := Vehicle{
				VehicleID:    data["vehicle_id"],
				Route:        data["route"],
				RouteID:      data["route_id"],
				TripID:       data["trip_id"],
				LastAnalyzed: time.Now(),
			}
			_, err := r.Table("vehicles").Insert(r.Expr(vehicle)).Run(session)
			if err != nil {
				return err
			}
		}
	}

	dbglogger.Printf("Inserted %d new vehicles.\n", new_vehicles)
	return nil
}

// Find closest stop at a given time for each recorded vehicle position
// A vehicle stop time (or just stop time) is a struct that relates a vehicle to a stop at a specific time
func MakeVehicleStopTimes(session *r.Session) error {

	// Get all vehicles
	vehicles := []Vehicle{}
	cur, err := r.Db("capmetro").Table("vehicles").Run(session)
	if err != nil {
		return err
	}
	cur.All(&vehicles)
	if vehicles == nil {
		return fmt.Errorf("No vehicles available for making stop times.")
	}

	// get all recorded positions for each vehicle
	for _, vehicle := range vehicles {

		stop_times := []VehicleStopTime{}
		// Not using []VehiclePosition because gorethink has trouble unmarshaling the Location field
		positions := []map[string]interface{}{}

		// get all vehicle_positions for vehicle_id after the vehicle was last analyzed
		between_opts := r.BetweenOpts{Index: "vehicle_timestamp"} // a compound index on a vehicle's id and timestamp
		lower_key := r.Expr([]interface{}{vehicle.VehicleID, vehicle.LastAnalyzed})
		upper_key := r.Expr([]interface{}{vehicle.VehicleID, r.EpochTime(2000005200)})
		query := r.Db("capmetro").Table("vehicle_position")
		query = query.Between(lower_key, upper_key, between_opts)

		cur, err := query.Run(session)
		if err != nil {
			errlogger.Println(err)
			continue
		}
		cur.All(&positions)
		if positions == nil {
			dbglogger.Printf("No positions available for vehicle %s after %s.\n", vehicle.VehicleID, vehicle.LastAnalyzed.Format("2006-01-02T15:04:05-07:00"))
			continue
		}

		dbglogger.Printf("Processing %d positions for vehicle %s after %s.\n", len(positions), vehicle.VehicleID, vehicle.LastAnalyzed.Format("2006-01-02T15:04:05-07:00"))
		for _, position := range positions {

			// find the closest stop within 100m for each position (if any)
			stops := []map[string]interface{}{}
			gn_opts := r.GetNearestOpts{Index: "location", MaxDist: 100, MaxResults: 1}
			query := r.Db("capmetro").Table("stops").GetNearest(position["location"], gn_opts)

			cur, err := query.Run(session)
			if err != nil {
				errlogger.Println(err)
				continue
			}
			cur.All(&stops)
			if len(stops) == 0 {
				continue
			}

			// the result of a GetNearest geospatial query contains the distance from the point specified (indexed by "dist")
			// and the document for the closest stop (indexed by "doc")
			stop := stops[0]["doc"].(map[string]interface{})
			stop_time := VehicleStopTime{
				VehicleID: vehicle.VehicleID,
				Route:     position["route"].(string),
				TripID:    position["trip_id"].(string),
				StopID:    stop["stop_id"].(string),
				Time:      position["timestamp"].(time.Time),
			}

			if len(stop_times) > 0 && stop_times[len(stop_times)-1].StopID == stop_time.StopID {
				// Replace most recent stop_time if current stop_time has earlier timestamp
				// We want to avoid duplicate stop_times and in the case of duplicate, use the earlier stop_time
				if stop_time.Time.Before(stop_times[len(stop_times)-1].Time) {
					stop_times[len(stop_times)-1] = stop_time
				}
				continue
			} else {
				stop_times = append(stop_times, stop_time)
			}
			dbglogger.Printf("Added stop_time: stop=%s, time=%s.\n", stop_time.StopID, stop_time.Time.Format("2006-01-02T15:04:05-07:00"))
		}

		_, err = r.Db("capmetro").Table("vehicle_stop_times").Insert(r.Expr(stop_times)).Run(session)
		if err != nil {
			errlogger.Println(err)
		}
		dbglogger.Printf("Added %d stop times for vehicle %s.\n", len(stop_times), vehicle.VehicleID)
		vehicle.LastAnalyzed = time.Now()
		_, err = r.Db("capmetro").Table("vehicles").Get(vehicle.ID).Update(r.Expr(vehicle)).RunWrite(session)
		if err != nil {
			return err
		}
	}
	return nil
}
<commit_msg>More efficient query for vehicle positions by vehicle and timestamp<commit_after>package main

import (
	"fmt"
	r "github.com/scascketta/capmetro-data/Godeps/_workspace/src/github.com/dancannon/gorethink"
	"time"
)

var (
	lastUpdated map[string]time.Time = map[string]time.Time{}

	firstNewVehicleCheck bool          = true
	nextNewVehicleCheck  time.Time     = time.Now()
	vehicleCheckInterval time.Duration = (4 * 60 * 60) * (1000 * time.Millisecond)

	normalDuration   time.Duration = (30) * (1000 * time.Millisecond)
	extendedDuration time.Duration = (10 * 60) * (1000 * time.Millisecond)

	emptyResponses      map[string]int  = map[string]int{}
	recentEmptyResponse map[string]bool = map[string]bool{}
)

type VehicleStopTime struct {
	VehicleID string    `gorethink:"vehicle_id"`
	Route     string    `gorethink:"route"`
	TripID    string    `gorethink:"trip_id"`
	StopID    string    `gorethink:"stop_id"`
	Direction string    `gorethink:"direction"`
	Time      time.Time `gorethink:"timestamp"`
}

func FilterUpdatedVehicles(vehicles []VehiclePosition) []VehiclePosition {
	updated := []VehiclePosition{}
	for _, v := range vehicles {
		updateTime, _ := lastUpdated[v.VehicleID]
		lastUpdated[v.VehicleID] = v.Time
		if !updateTime.Equal(v.Time) {
			updated = append(updated, v)
		}
	}
	return updated
}

func LogVehiclePositions(session *r.Session, route string) error {
	vehicles, err := FetchVehicles(route)
	if err != nil {
		return err
	}
	if vehicles == nil {
		// increment retry count if fetch just before was also empty
		// only subsequent empty responses matter when determining how long to sleep
		if recentEmptyResponse[route] {
			emptyResponses[route] += 1
		}
		recentEmptyResponse[route] = true
		dbglogger.Printf("No vehicles in response for route: %s.", route)
		return nil
	} else {
		recentEmptyResponse[route] = false
	}

	updated := FilterUpdatedVehicles(vehicles)

	for _, v := range updated {
		dbglogger.Printf("Vehicle %s updated at %s\n", v.VehicleID, v.Time.Format("2006-01-02T15:04:05-07:00"))
	}

	if len(updated) > 0 {
		_, err = r.Table("vehicle_position").Insert(r.Expr(updated)).Run(session)
		if err != nil {
			return err
		}
		dbglogger.Printf("Log %d vehicles, route %s.\n", len(updated), route)
	} else {
		dbglogger.Printf("No new vehicle positions to record for route %s.\n", route)
	}
	return nil
}

// Check if the the routes are inactive
// There must have been MAX_RETRIES previous attempts to fetch data,
// and all attempts must have failed
func routesAreSleeping() bool {
	dbglogger.Println("emptyResponses:", emptyResponses)
	dbglogger.Println("recentEmptyResponse:", recentEmptyResponse)
	for _, retries := range emptyResponses {
		if retries < MAX_RETRIES {
			return false
		}
	}
	return true
}

// Check if any new vehicles appear in recorded vehicle positions, add them to vehicles table
func checkNewVehicles(session *r.Session) error {
	new_vehicles := 0
	dbglogger.Println("Check for new vehicles.")
	vehicles := []map[string]string{}
	cur, err := r.Table("vehicle_position").Pluck("vehicle_id", "route", "route_id", "trip_id").Distinct().Run(session)
	if err != nil {
		return err
	}
	cur.All(&vehicles)

	for _, data := range vehicles {
		id := data["vehicle_id"]
		stream := r.Table("vehicles").Pluck("vehicle_id")
		query_expr := r.Expr(map[string]string{"vehicle_id": data["vehicle_id"]})
		cur, err = stream.Contains(query_expr).Run(session)
		if err != nil {
			return err
		}
		var res bool

		cur.Next(&res)
		if !res {
			new_vehicles += 1
			dbglogger.Printf("Adding new vehicle %s to vehicles table.\n", id)
			vehicle := Vehicle{
				VehicleID:    data["vehicle_id"],
				Route:        data["route"],
				RouteID:      data["route_id"],
				TripID:       data["trip_id"],
				LastAnalyzed: time.Now(),
			}
			_, err := r.Table("vehicles").Insert(r.Expr(vehicle)).Run(session)
			if err != nil {
				return err
			}
		}
	}

	dbglogger.Printf("Inserted %d new vehicles.\n", new_vehicles)
	return nil
}

// Find closest stop at a given time for each recorded vehicle position
// A vehicle stop time (or just stop time) is a struct that relates a vehicle to a stop at a specific time
func MakeVehicleStopTimes(session *r.Session) error {

	// Get all vehicles
	vehicles := []Vehicle{}
	cur, err := r.Db("capmetro").Table("vehicles").Run(session)
	if err != nil {
		return err
	}
	cur.All(&vehicles)
	if vehicles == nil {
		return fmt.Errorf("No vehicles available for making stop times.")
	}

	// get all recorded positions for each vehicle
	for _, vehicle := range vehicles {

		stop_times := []VehicleStopTime{}
		// Not using []VehiclePosition because gorethink has trouble unmarshaling the Location field
		positions := []map[string]interface{}{}

		// get all vehicle_positions for vehicle_id after the vehicle was last analyzed
		between_opts := r.BetweenOpts{Index: "vehicle_timestamp"} // a compound index on a vehicle's id and timestamp
		orderby_opts := r.OrderByOpts{Index: "vehicle_timestamp"} // must use same index to chain the orderBy with secondary index
		lower_key := r.Expr([]interface{}{vehicle.VehicleID, vehicle.LastAnalyzed})
		upper_key := r.Expr([]interface{}{vehicle.VehicleID, r.EpochTime(2000005200)})
		query := r.Db("capmetro").Table("vehicle_position")
		query = query.Between(lower_key, upper_key, between_opts).OrderBy(r.Desc("vehicle_timestamp"), orderby_opts)

		cur, err := query.Run(session)
		if err != nil {
			errlogger.Println(err)
			continue
		}
		cur.All(&positions)
		if positions == nil {
			dbglogger.Printf("No positions available for vehicle %s after %s.\n", vehicle.VehicleID, vehicle.LastAnalyzed.Format("2006-01-02T15:04:05-07:00"))
			continue
		}

		dbglogger.Printf("Processing %d positions for vehicle %s after %s.\n", len(positions), vehicle.VehicleID, vehicle.LastAnalyzed.Format("2006-01-02T15:04:05-07:00"))
		for _, position := range positions {

			// find the closest stop within 100m for each position (if any)
			stops := []map[string]interface{}{}
			gn_opts := r.GetNearestOpts{Index: "location", MaxDist: 100, MaxResults: 1}
			query := r.Db("capmetro").Table("stops").GetNearest(position["location"], gn_opts)

			cur, err := query.Run(session)
			if err != nil {
				errlogger.Println(err)
				continue
			}
			cur.All(&stops)
			if len(stops) == 0 {
				continue
			}

			// the result of a GetNearest geospatial query contains the distance from the point specified (indexed by "dist")
			// and the document for the closest stop (indexed by "doc")
			stop := stops[0]["doc"].(map[string]interface{})
			stop_time := VehicleStopTime{
				VehicleID: vehicle.VehicleID,
				Route:     position["route"].(string),
				TripID:    position["trip_id"].(string),
				StopID:    stop["stop_id"].(string),
				Time:      position["timestamp"].(time.Time),
				Direction: position["direction"].(string),
			}

			if len(stop_times) > 0 && stop_times[len(stop_times)-1].StopID == stop_time.StopID {
				// Replace most recent stop_time if current stop_time has earlier timestamp
				// We want to avoid duplicate stop_times and in the case of duplicate, use the earlier stop_time
				if stop_time.Time.Before(stop_times[len(stop_times)-1].Time) {
					stop_times[len(stop_times)-1] = stop_time
				}
				continue
			} else {
				stop_times = append(stop_times, stop_time)
			}
			dbglogger.Printf("Added stop_time: stop=%s, time=%s.\n", stop_time.StopID, stop_time.Time.Format("2006-01-02T15:04:05-07:00"))
		}

		_, err = r.Db("capmetro").Table("vehicle_stop_times").Insert(r.Expr(stop_times)).Run(session)
		if err != nil {
			errlogger.Println(err)
		}
		dbglogger.Printf("Added %d stop times for vehicle %s.\n", len(stop_times), vehicle.VehicleID)
		vehicle.LastAnalyzed = time.Now()
		_, err = r.Db("capmetro").Table("vehicles").Get(vehicle.ID).Update(r.Expr(vehicle)).RunWrite(session)
		if err != nil {
			return err
		}
	}
	return nil
}
<|endoftext|>


<commit_before>package protocol

import (
	"crypto/ecdsa"
	"encoding/json"
	"log"
	"reflect"

	"github.com/golang/protobuf/proto"
	"github.com/jinzhu/copier"
	"github.com/pkg/errors"

	"github.com/status-im/status-go/eth-node/crypto"
	"github.com/status-im/status-go/eth-node/types"
	"github.com/status-im/status-go/protocol/datasync"
	"github.com/status-im/status-go/protocol/encryption"
	"github.com/status-im/status-go/protocol/encryption/multidevice"
	"github.com/status-im/status-go/protocol/encryption/sharedsecret"
	"github.com/status-im/status-go/protocol/protobuf"
)

type StatusMessageT int

// StatusMessage is any Status Protocol message.
type StatusMessage struct {
	// TransportMessage is the parsed message received from the transport layer, i.e the input
	TransportMessage *types.Message `json:"transportMessage"`
	// Type is the type of application message contained
	Type protobuf.ApplicationMetadataMessage_Type `json:"-"`
	// ParsedMessage is the parsed message by the application layer, i.e the output
	ParsedMessage *reflect.Value `json:"-"`

	// TransportPayload is the payload as received from the transport layer
	TransportPayload []byte `json:"-"`
	// DecryptedPayload is the payload after having been processed by the encryption layer
	DecryptedPayload []byte `json:"decryptedPayload"`

	// ID is the canonical ID of the message
	ID types.HexBytes `json:"id"`
	// Hash is the transport layer hash
	Hash []byte `json:"-"`

	// Dst is the targeted public key
	Dst *ecdsa.PublicKey

	// TransportLayerSigPubKey contains the public key provided by the transport layer
	TransportLayerSigPubKey *ecdsa.PublicKey `json:"-"`
	// ApplicationMetadataLayerPubKey contains the public key provided by the application metadata layer
	ApplicationMetadataLayerSigPubKey *ecdsa.PublicKey `json:"-"`

	// Installations is the new installations returned by the encryption layer
	Installations []*multidevice.Installation
	// SharedSecret is the shared secret returned by the encryption layer
	SharedSecrets []*sharedsecret.Secret
}

// Temporary JSON marshaling for those messages that are not yet processed
// by the go code
func (m *StatusMessage) MarshalJSON() ([]byte, error) {
	item := struct {
		ID        types.HexBytes `json:"id"`
		Payload   string         `json:"payload"`
		From      types.HexBytes `json:"from"`
		Timestamp uint32         `json:"timestamp"`
	}{
		ID:        m.ID,
		Payload:   string(m.DecryptedPayload),
		Timestamp: m.TransportMessage.Timestamp,
		From:      m.TransportMessage.Sig,
	}
	return json.Marshal(item)
}

// SigPubKey returns the most important signature, from the application layer to transport
func (m *StatusMessage) SigPubKey() *ecdsa.PublicKey {
	if m.ApplicationMetadataLayerSigPubKey != nil {
		return m.ApplicationMetadataLayerSigPubKey
	}

	return m.TransportLayerSigPubKey
}

func (m *StatusMessage) Clone() (*StatusMessage, error) {
	copy := &StatusMessage{}

	err := copier.Copy(&copy, m)
	return copy, err
}

func (m *StatusMessage) HandleTransport(shhMessage *types.Message) error {
	publicKey, err := crypto.UnmarshalPubkey(shhMessage.Sig)
	if err != nil {
		return errors.Wrap(err, "failed to get signature")
	}

	m.TransportMessage = shhMessage
	m.Hash = shhMessage.Hash
	m.TransportLayerSigPubKey = publicKey
	m.TransportPayload = shhMessage.Payload

	if shhMessage.Dst != nil {
		publicKey, err := crypto.UnmarshalPubkey(shhMessage.Dst)
		if err != nil {
			return err
		}
		m.Dst = publicKey
	}

	return nil
}

func (m *StatusMessage) HandleEncryption(myKey *ecdsa.PrivateKey, senderKey *ecdsa.PublicKey, enc *encryption.Protocol, skipNegotiation bool) error {
	// As we handle non-encrypted messages, we make sure that DecryptPayload
	// is set regardless of whether this step is successful
	m.DecryptedPayload = m.TransportPayload
	// Nothing to do
	if skipNegotiation {
		return nil
	}

	var protocolMessage encryption.ProtocolMessage
	err := proto.Unmarshal(m.TransportPayload, &protocolMessage)
	if err != nil {
		return errors.Wrap(err, "failed to unmarshal ProtocolMessage")
	}

	response, err := enc.HandleMessage(
		myKey,
		senderKey,
		&protocolMessage,
		m.Hash,
	)

	if err != nil {
		return errors.Wrap(err, "failed to handle Encryption message")
	}

	m.DecryptedPayload = response.DecryptedMessage
	m.Installations = response.Installations
	m.SharedSecrets = response.SharedSecrets
	return nil
}

// HandleDatasync processes StatusMessage through data sync layer.
// This is optional and DataSync might be nil. In such a case,
// only one payload will be returned equal to DecryptedPayload.
func (m *StatusMessage) HandleDatasync(datasync *datasync.DataSync) ([]*StatusMessage, error) {
	var statusMessages []*StatusMessage

	payloads := datasync.Handle(
		m.SigPubKey(),
		m.DecryptedPayload,
	)

	for _, payload := range payloads {
		message, err := m.Clone()
		if err != nil {
			return nil, err
		}
		message.DecryptedPayload = payload
		statusMessages = append(statusMessages, message)
	}
	return statusMessages, nil
}

func (m *StatusMessage) HandleApplicationMetadata() error {
	message, err := protobuf.Unmarshal(m.DecryptedPayload)
	if err != nil {
		return err
	}

	recoveredKey, err := message.RecoverKey()
	if err != nil {
		return err
	}
	m.ApplicationMetadataLayerSigPubKey = recoveredKey
	// Calculate ID using the wrapped record
	m.ID = MessageID(recoveredKey, m.DecryptedPayload)
	m.DecryptedPayload = message.Payload
	m.Type = message.Type
	return nil

}

func (m *StatusMessage) HandleApplication() error {
	switch m.Type {
	case protobuf.ApplicationMetadataMessage_CHAT_MESSAGE:
		return m.unmarshalProtobufData(new(protobuf.ChatMessage))

	case protobuf.ApplicationMetadataMessage_MEMBERSHIP_UPDATE_MESSAGE:
		return m.unmarshalProtobufData(new(protobuf.MembershipUpdateMessage))

	case protobuf.ApplicationMetadataMessage_ACCEPT_REQUEST_ADDRESS_FOR_TRANSACTION:
		return m.unmarshalProtobufData(new(protobuf.AcceptRequestAddressForTransaction))

	case protobuf.ApplicationMetadataMessage_SEND_TRANSACTION:
		return m.unmarshalProtobufData(new(protobuf.SendTransaction))

	case protobuf.ApplicationMetadataMessage_REQUEST_TRANSACTION:
		return m.unmarshalProtobufData(new(protobuf.RequestTransaction))

	case protobuf.ApplicationMetadataMessage_DECLINE_REQUEST_ADDRESS_FOR_TRANSACTION:
		return m.unmarshalProtobufData(new(protobuf.DeclineRequestAddressForTransaction))

	case protobuf.ApplicationMetadataMessage_DECLINE_REQUEST_TRANSACTION:
		return m.unmarshalProtobufData(new(protobuf.DeclineRequestTransaction))

	case protobuf.ApplicationMetadataMessage_REQUEST_ADDRESS_FOR_TRANSACTION:
		return m.unmarshalProtobufData(new(protobuf.RequestAddressForTransaction))

	case protobuf.ApplicationMetadataMessage_CONTACT_UPDATE:
		return m.unmarshalProtobufData(new(protobuf.ContactUpdate))

	case protobuf.ApplicationMetadataMessage_SYNC_INSTALLATION:
		return m.unmarshalProtobufData(new(protobuf.SyncInstallation))

	case protobuf.ApplicationMetadataMessage_SYNC_INSTALLATION_CONTACT:
		log.Printf("Sync installation contact")
		return m.unmarshalProtobufData(new(protobuf.SyncInstallationContact))

	case protobuf.ApplicationMetadataMessage_SYNC_INSTALLATION_PUBLIC_CHAT:
		return m.unmarshalProtobufData(new(protobuf.SyncInstallationPublicChat))

	case protobuf.ApplicationMetadataMessage_SYNC_INSTALLATION_ACCOUNT:
		return m.unmarshalProtobufData(new(protobuf.SyncInstallationAccount))

	case protobuf.ApplicationMetadataMessage_PAIR_INSTALLATION:
		return m.unmarshalProtobufData(new(protobuf.PairInstallation))
	case protobuf.ApplicationMetadataMessage_CONTACT_CODE_ADVERTISEMENT:
		return m.unmarshalProtobufData(new(protobuf.ContactCodeAdvertisement))
	case protobuf.ApplicationMetadataMessage_PUSH_NOTIFICATION_REQUEST:
		return m.unmarshalProtobufData(new(protobuf.PushNotificationRequest))
	case protobuf.ApplicationMetadataMessage_PUSH_NOTIFICATION_REGISTRATION_RESPONSE:
		return m.unmarshalProtobufData(new(protobuf.PushNotificationRegistrationResponse))
	case protobuf.ApplicationMetadataMessage_PUSH_NOTIFICATION_QUERY:
		return m.unmarshalProtobufData(new(protobuf.PushNotificationQuery))
	case protobuf.ApplicationMetadataMessage_PUSH_NOTIFICATION_QUERY_RESPONSE:
		return m.unmarshalProtobufData(new(protobuf.PushNotificationQueryResponse))
	case protobuf.ApplicationMetadataMessage_PUSH_NOTIFICATION_RESPONSE:
		return m.unmarshalProtobufData(new(protobuf.PushNotificationResponse))
	case protobuf.ApplicationMetadataMessage_EMOJI_REACTION:
		return m.unmarshalProtobufData(new(protobuf.EmojiReaction))
	case protobuf.ApplicationMetadataMessage_GROUP_CHAT_INVITATION:
		return m.unmarshalProtobufData(new(protobuf.GroupChatInvitation))
	case protobuf.ApplicationMetadataMessage_PUSH_NOTIFICATION_REGISTRATION:
		// This message is a bit different as it's encrypted, so we pass it straight through
		v := reflect.ValueOf(m.DecryptedPayload)
		m.ParsedMessage = &v
		return nil
	}
	return nil
}

func (m *StatusMessage) unmarshalProtobufData(pb proto.Message) error {
	var ptr proto.Message
	rv := reflect.ValueOf(pb)
	if rv.Kind() == reflect.Ptr {
		ptr = pb
	} else {
		ptr = rv.Addr().Interface().(proto.Message)
	}

	err := proto.Unmarshal(m.DecryptedPayload, ptr)
	if err != nil {
		m.ParsedMessage = nil
		log.Printf("[message::DecodeMessage] could not decode %T: %#x, err: %v", pb, m.Hash, err.Error())
	} else {
		rv = reflect.ValueOf(ptr)
		elem := rv.Elem()
		m.ParsedMessage = &elem
		return nil
	}

	return nil
}
<commit_msg>Added ChatIdentity to HandleApplication()<commit_after>package protocol

import (
	"crypto/ecdsa"
	"encoding/json"
	"log"
	"reflect"

	"github.com/golang/protobuf/proto"
	"github.com/jinzhu/copier"
	"github.com/pkg/errors"

	"github.com/status-im/status-go/eth-node/crypto"
	"github.com/status-im/status-go/eth-node/types"
	"github.com/status-im/status-go/protocol/datasync"
	"github.com/status-im/status-go/protocol/encryption"
	"github.com/status-im/status-go/protocol/encryption/multidevice"
	"github.com/status-im/status-go/protocol/encryption/sharedsecret"
	"github.com/status-im/status-go/protocol/protobuf"
)

type StatusMessageT int

// StatusMessage is any Status Protocol message.
type StatusMessage struct {
	// TransportMessage is the parsed message received from the transport layer, i.e the input
	TransportMessage *types.Message `json:"transportMessage"`
	// Type is the type of application message contained
	Type protobuf.ApplicationMetadataMessage_Type `json:"-"`
	// ParsedMessage is the parsed message by the application layer, i.e the output
	ParsedMessage *reflect.Value `json:"-"`

	// TransportPayload is the payload as received from the transport layer
	TransportPayload []byte `json:"-"`
	// DecryptedPayload is the payload after having been processed by the encryption layer
	DecryptedPayload []byte `json:"decryptedPayload"`

	// ID is the canonical ID of the message
	ID types.HexBytes `json:"id"`
	// Hash is the transport layer hash
	Hash []byte `json:"-"`

	// Dst is the targeted public key
	Dst *ecdsa.PublicKey

	// TransportLayerSigPubKey contains the public key provided by the transport layer
	TransportLayerSigPubKey *ecdsa.PublicKey `json:"-"`
	// ApplicationMetadataLayerPubKey contains the public key provided by the application metadata layer
	ApplicationMetadataLayerSigPubKey *ecdsa.PublicKey `json:"-"`

	// Installations is the new installations returned by the encryption layer
	Installations []*multidevice.Installation
	// SharedSecret is the shared secret returned by the encryption layer
	SharedSecrets []*sharedsecret.Secret
}

// Temporary JSON marshaling for those messages that are not yet processed
// by the go code
func (m *StatusMessage) MarshalJSON() ([]byte, error) {
	item := struct {
		ID        types.HexBytes `json:"id"`
		Payload   string         `json:"payload"`
		From      types.HexBytes `json:"from"`
		Timestamp uint32         `json:"timestamp"`
	}{
		ID:        m.ID,
		Payload:   string(m.DecryptedPayload),
		Timestamp: m.TransportMessage.Timestamp,
		From:      m.TransportMessage.Sig,
	}
	return json.Marshal(item)
}

// SigPubKey returns the most important signature, from the application layer to transport
func (m *StatusMessage) SigPubKey() *ecdsa.PublicKey {
	if m.ApplicationMetadataLayerSigPubKey != nil {
		return m.ApplicationMetadataLayerSigPubKey
	}

	return m.TransportLayerSigPubKey
}

func (m *StatusMessage) Clone() (*StatusMessage, error) {
	copy := &StatusMessage{}

	err := copier.Copy(&copy, m)
	return copy, err
}

func (m *StatusMessage) HandleTransport(shhMessage *types.Message) error {
	publicKey, err := crypto.UnmarshalPubkey(shhMessage.Sig)
	if err != nil {
		return errors.Wrap(err, "failed to get signature")
	}

	m.TransportMessage = shhMessage
	m.Hash = shhMessage.Hash
	m.TransportLayerSigPubKey = publicKey
	m.TransportPayload = shhMessage.Payload

	if shhMessage.Dst != nil {
		publicKey, err := crypto.UnmarshalPubkey(shhMessage.Dst)
		if err != nil {
			return err
		}
		m.Dst = publicKey
	}

	return nil
}

func (m *StatusMessage) HandleEncryption(myKey *ecdsa.PrivateKey, senderKey *ecdsa.PublicKey, enc *encryption.Protocol, skipNegotiation bool) error {
	// As we handle non-encrypted messages, we make sure that DecryptPayload
	// is set regardless of whether this step is successful
	m.DecryptedPayload = m.TransportPayload
	// Nothing to do
	if skipNegotiation {
		return nil
	}

	var protocolMessage encryption.ProtocolMessage
	err := proto.Unmarshal(m.TransportPayload, &protocolMessage)
	if err != nil {
		return errors.Wrap(err, "failed to unmarshal ProtocolMessage")
	}

	response, err := enc.HandleMessage(
		myKey,
		senderKey,
		&protocolMessage,
		m.Hash,
	)

	if err != nil {
		return errors.Wrap(err, "failed to handle Encryption message")
	}

	m.DecryptedPayload = response.DecryptedMessage
	m.Installations = response.Installations
	m.SharedSecrets = response.SharedSecrets
	return nil
}

// HandleDatasync processes StatusMessage through data sync layer.
// This is optional and DataSync might be nil. In such a case,
// only one payload will be returned equal to DecryptedPayload.
func (m *StatusMessage) HandleDatasync(datasync *datasync.DataSync) ([]*StatusMessage, error) {
	var statusMessages []*StatusMessage

	payloads := datasync.Handle(
		m.SigPubKey(),
		m.DecryptedPayload,
	)

	for _, payload := range payloads {
		message, err := m.Clone()
		if err != nil {
			return nil, err
		}
		message.DecryptedPayload = payload
		statusMessages = append(statusMessages, message)
	}
	return statusMessages, nil
}

func (m *StatusMessage) HandleApplicationMetadata() error {
	message, err := protobuf.Unmarshal(m.DecryptedPayload)
	if err != nil {
		return err
	}

	recoveredKey, err := message.RecoverKey()
	if err != nil {
		return err
	}
	m.ApplicationMetadataLayerSigPubKey = recoveredKey
	// Calculate ID using the wrapped record
	m.ID = MessageID(recoveredKey, m.DecryptedPayload)
	m.DecryptedPayload = message.Payload
	m.Type = message.Type
	return nil

}

func (m *StatusMessage) HandleApplication() error {
	switch m.Type {
	case protobuf.ApplicationMetadataMessage_CHAT_MESSAGE:
		return m.unmarshalProtobufData(new(protobuf.ChatMessage))

	case protobuf.ApplicationMetadataMessage_MEMBERSHIP_UPDATE_MESSAGE:
		return m.unmarshalProtobufData(new(protobuf.MembershipUpdateMessage))

	case protobuf.ApplicationMetadataMessage_ACCEPT_REQUEST_ADDRESS_FOR_TRANSACTION:
		return m.unmarshalProtobufData(new(protobuf.AcceptRequestAddressForTransaction))

	case protobuf.ApplicationMetadataMessage_SEND_TRANSACTION:
		return m.unmarshalProtobufData(new(protobuf.SendTransaction))

	case protobuf.ApplicationMetadataMessage_REQUEST_TRANSACTION:
		return m.unmarshalProtobufData(new(protobuf.RequestTransaction))

	case protobuf.ApplicationMetadataMessage_DECLINE_REQUEST_ADDRESS_FOR_TRANSACTION:
		return m.unmarshalProtobufData(new(protobuf.DeclineRequestAddressForTransaction))

	case protobuf.ApplicationMetadataMessage_DECLINE_REQUEST_TRANSACTION:
		return m.unmarshalProtobufData(new(protobuf.DeclineRequestTransaction))

	case protobuf.ApplicationMetadataMessage_REQUEST_ADDRESS_FOR_TRANSACTION:
		return m.unmarshalProtobufData(new(protobuf.RequestAddressForTransaction))

	case protobuf.ApplicationMetadataMessage_CONTACT_UPDATE:
		return m.unmarshalProtobufData(new(protobuf.ContactUpdate))

	case protobuf.ApplicationMetadataMessage_SYNC_INSTALLATION:
		return m.unmarshalProtobufData(new(protobuf.SyncInstallation))

	case protobuf.ApplicationMetadataMessage_SYNC_INSTALLATION_CONTACT:
		log.Printf("Sync installation contact")
		return m.unmarshalProtobufData(new(protobuf.SyncInstallationContact))

	case protobuf.ApplicationMetadataMessage_SYNC_INSTALLATION_PUBLIC_CHAT:
		return m.unmarshalProtobufData(new(protobuf.SyncInstallationPublicChat))

	case protobuf.ApplicationMetadataMessage_SYNC_INSTALLATION_ACCOUNT:
		return m.unmarshalProtobufData(new(protobuf.SyncInstallationAccount))

	case protobuf.ApplicationMetadataMessage_PAIR_INSTALLATION:
		return m.unmarshalProtobufData(new(protobuf.PairInstallation))
	case protobuf.ApplicationMetadataMessage_CONTACT_CODE_ADVERTISEMENT:
		return m.unmarshalProtobufData(new(protobuf.ContactCodeAdvertisement))
	case protobuf.ApplicationMetadataMessage_PUSH_NOTIFICATION_REQUEST:
		return m.unmarshalProtobufData(new(protobuf.PushNotificationRequest))
	case protobuf.ApplicationMetadataMessage_PUSH_NOTIFICATION_REGISTRATION_RESPONSE:
		return m.unmarshalProtobufData(new(protobuf.PushNotificationRegistrationResponse))
	case protobuf.ApplicationMetadataMessage_PUSH_NOTIFICATION_QUERY:
		return m.unmarshalProtobufData(new(protobuf.PushNotificationQuery))
	case protobuf.ApplicationMetadataMessage_PUSH_NOTIFICATION_QUERY_RESPONSE:
		return m.unmarshalProtobufData(new(protobuf.PushNotificationQueryResponse))
	case protobuf.ApplicationMetadataMessage_PUSH_NOTIFICATION_RESPONSE:
		return m.unmarshalProtobufData(new(protobuf.PushNotificationResponse))
	case protobuf.ApplicationMetadataMessage_EMOJI_REACTION:
		return m.unmarshalProtobufData(new(protobuf.EmojiReaction))
	case protobuf.ApplicationMetadataMessage_GROUP_CHAT_INVITATION:
		return m.unmarshalProtobufData(new(protobuf.GroupChatInvitation))
	case protobuf.ApplicationMetadataMessage_PUSH_NOTIFICATION_REGISTRATION:
		// This message is a bit different as it's encrypted, so we pass it straight through
		v := reflect.ValueOf(m.DecryptedPayload)
		m.ParsedMessage = &v
		return nil
	case protobuf.ApplicationMetadataMessage_CHAT_IDENTITY:
		return m.unmarshalProtobufData(new(protobuf.ChatIdentity))
	}
	return nil
}

func (m *StatusMessage) unmarshalProtobufData(pb proto.Message) error {
	var ptr proto.Message
	rv := reflect.ValueOf(pb)
	if rv.Kind() == reflect.Ptr {
		ptr = pb
	} else {
		ptr = rv.Addr().Interface().(proto.Message)
	}

	err := proto.Unmarshal(m.DecryptedPayload, ptr)
	if err != nil {
		m.ParsedMessage = nil
		log.Printf("[message::DecodeMessage] could not decode %T: %#x, err: %v", pb, m.Hash, err.Error())
	} else {
		rv = reflect.ValueOf(ptr)
		elem := rv.Elem()
		m.ParsedMessage = &elem
		return nil
	}

	return nil
}
<|endoftext|>


<commit_before>package couchbase

import (
	"encoding/json"
)

func (b *Bucket) do(k string, f func(mc *memcachedClient, vb uint16) error) error {
	vb := b.VBHash(k)
	masterId := b.VBucketServerMap.VBucketMap[vb][0]
	if b.connections[masterId] == nil {
		b.connections[masterId] = connect("tcp", b.VBucketServerMap.ServerList[masterId])
	}
	return f(b.connections[masterId], uint16(vb))
}

// Set a value in this bucket.
// The value will be serialized into a JSON document.
func (b *Bucket) Set(k string, v interface{}) error {
	return b.do(k, func(mc *memcachedClient, vb uint16) error {
		data, err := json.Marshal(v)
		if err != nil {
			return err
		}
		res := mc.Set(vb, k, 0, 0, data)
		if res.Status != mcSUCCESS {
			return res
		}
		return nil
	})
}

// Get a value from this bucket.
// The value is expected to be a JSON stream and will be deserialized
// into rv.
func (b *Bucket) Get(k string, rv interface{}) error {
	return b.do(k, func(mc *memcachedClient, vb uint16) error {
		res := mc.Get(vb, k)
		if res.Status != mcSUCCESS {
			return res
		}
		return json.Unmarshal(res.Body, rv)
	})
}

// Delete a key from this bucket.
func (b *Bucket) Delete(k string) error {
	return b.do(k, func(mc *memcachedClient, vb uint16) error {
		res := mc.Del(vb, k)
		if res.Status != mcSUCCESS {
			return res
		}
		return nil
	})
}
<commit_msg>Simplify the client a bit.<commit_after>package couchbase

import (
	"encoding/json"
)

func (b *Bucket) getVBInfo(k string) (*memcachedClient, uint16) {
	vb := b.VBHash(k)
	masterId := b.VBucketServerMap.VBucketMap[vb][0]
	if b.connections[masterId] == nil {
		b.connections[masterId] = connect("tcp", b.VBucketServerMap.ServerList[masterId])
	}
	return b.connections[masterId], uint16(vb)
}

// Set a value in this bucket.
// The value will be serialized into a JSON document.
func (b *Bucket) Set(k string, v interface{}) error {
	mc, vb := b.getVBInfo(k)
	data, err := json.Marshal(v)
	if err != nil {
		return err
	}
	res := mc.Set(vb, k, 0, 0, data)
	if res.Status != mcSUCCESS {
		return res
	}
	return nil
}

// Get a value from this bucket.
// The value is expected to be a JSON stream and will be deserialized
// into rv.
func (b *Bucket) Get(k string, rv interface{}) error {
	mc, vb := b.getVBInfo(k)
	res := mc.Get(vb, k)
	if res.Status != mcSUCCESS {
		return res
	}
	return json.Unmarshal(res.Body, rv)
}

// Delete a key from this bucket.
func (b *Bucket) Delete(k string) error {
	mc, vb := b.getVBInfo(k)
	res := mc.Del(vb, k)
	if res.Status != mcSUCCESS {
		return res
	}
	return nil
}
<|endoftext|>


<commit_before>/*
A smart client for go.

Usage:

 client, err := couchbase.Connect("http://myserver:8091/")
 handleError(err)
 pool, err := client.GetPool("default")
 handleError(err)
 bucket, err := pool.getBucket("MyAwesomeBucket")
 handleError(err)
 ...
*/
package couchbase

import (
	"encoding/json"
	"errors"
	"fmt"
	"math/rand"
	"net/http"
	"net/url"
	"sync/atomic"

	"github.com/dustin/gomemcached"
	"github.com/dustin/gomemcached/client"
)

func (b *Bucket) ensureConnection(which int) error {
	if b.connections == nil {
		b.connections = []*memcached.Client{}
	}
	if b.connections[which] == nil {
		var err error
		b.connections[which], err = memcached.Connect("tcp",
			b.VBucketServerMap.ServerList[which])
		if err != nil {
			return err
		}
	}
	return nil
}

func (b *Bucket) do(k string, f func(mc *memcached.Client, vb uint16) error) error {
	vb := b.VBHash(k)
	for {
		masterId := b.VBucketServerMap.VBucketMap[vb][0]
		if err := b.ensureConnection(masterId); err != nil {
			return err
		}
		err := f(b.connections[masterId], uint16(vb))
		switch err.(type) {
		default:
			return err
		case gomemcached.MCResponse:
			st := err.(gomemcached.MCResponse).Status
			atomic.AddUint64(&b.pool.client.Statuses[st], 1)
			if st == gomemcached.NOT_MY_VBUCKET {
				b.refresh()
			} else {
				return err
			}
		}
	}
	panic("Unreachable.")
}

type gathered_stats struct {
	sn   string
	vals map[string]string
}

func getStatsParallel(b *Bucket, offset int, which string, ch chan<- gathered_stats) {
	sn := b.VBucketServerMap.ServerList[offset]

	results := map[string]string{}
	err := b.ensureConnection(offset)
	if err != nil {
		ch <- gathered_stats{sn, results}
	} else {
		for _, statval := range b.connections[offset].Stats(which) {
			results[statval.Key] = statval.Val
		}
		ch <- gathered_stats{sn, results}
	}
}

func (b *Bucket) GetStats(which string) map[string]map[string]string {
	// Go grab all the things at once.
	todo := len(b.VBucketServerMap.ServerList)
	ch := make(chan gathered_stats, todo)

	for offset, _ := range b.VBucketServerMap.ServerList {
		go getStatsParallel(b, offset, which, ch)
	}

	// Gather the results
	rv := map[string]map[string]string{}
	for i := 0; i < len(b.VBucketServerMap.ServerList); i++ {
		g := <-ch
		if len(g.vals) > 0 {
			rv[g.sn] = g.vals
		}
	}

	return rv
}

// Set a value in this bucket.
// The value will be serialized into a JSON document.
func (b *Bucket) Set(k string, v interface{}) error {
	return b.do(k, func(mc *memcached.Client, vb uint16) error {
		data, err := json.Marshal(v)
		if err != nil {
			return err
		}
		res := mc.Set(vb, k, 0, 0, data)
		if res.Status != gomemcached.SUCCESS {
			return res
		}
		return nil
	})
}

// Get a value from this bucket.
// The value is expected to be a JSON stream and will be deserialized
// into rv.
func (b *Bucket) Get(k string, rv interface{}) error {
	return b.do(k, func(mc *memcached.Client, vb uint16) error {
		res := mc.Get(vb, k)
		if res.Status != gomemcached.SUCCESS {
			return res
		}
		return json.Unmarshal(res.Body, rv)
	})
}

// Delete a key from this bucket.
func (b *Bucket) Delete(k string) error {
	return b.do(k, func(mc *memcached.Client, vb uint16) error {
		res := mc.Del(vb, k)
		if res.Status != gomemcached.SUCCESS {
			return res
		}
		return nil
	})
}

type ViewRow struct {
	ID    string
	Key   interface{}
	Value interface{}
	Doc   *interface{}
}

type ViewResult struct {
	TotalRows int `json:"total_rows"`
	Rows      []ViewRow
	Errors    []struct {
		From   string
		Reason string
	}
}

// Execute a view
func (b *Bucket) View(ddoc, name string, params map[string]interface{}) (vres ViewResult, err error) {
	// Pick a random node to service our request.
	node := b.Nodes[rand.Intn(len(b.Nodes))]
	u, err := url.Parse(node.CouchAPIBase)
	if err != nil {
		return ViewResult{}, err
	}

	values := url.Values{}
	for k, v := range params {
		values[k] = []string{fmt.Sprintf("%v", v)}
	}

	u.Path = fmt.Sprintf("/%s/_design/%s/_view/%s", b.Name, ddoc, name)
	u.RawQuery = values.Encode()

	res, err := http.Get(u.String())
	if err != nil {
		return ViewResult{}, err
	}
	defer res.Body.Close()

	if res.StatusCode != 200 {
		return ViewResult{}, errors.New(res.Status)
	}

	d := json.NewDecoder(res.Body)
	if err = d.Decode(&vres); err != nil {
		return ViewResult{}, err
	}
	return vres, nil

}
<commit_msg>Use new memcached StatsMap convenience.<commit_after>/*
A smart client for go.

Usage:

 client, err := couchbase.Connect("http://myserver:8091/")
 handleError(err)
 pool, err := client.GetPool("default")
 handleError(err)
 bucket, err := pool.getBucket("MyAwesomeBucket")
 handleError(err)
 ...
*/
package couchbase

import (
	"encoding/json"
	"errors"
	"fmt"
	"math/rand"
	"net/http"
	"net/url"
	"sync/atomic"

	"github.com/dustin/gomemcached"
	"github.com/dustin/gomemcached/client"
)

func (b *Bucket) ensureConnection(which int) error {
	if b.connections == nil {
		b.connections = []*memcached.Client{}
	}
	if b.connections[which] == nil {
		var err error
		b.connections[which], err = memcached.Connect("tcp",
			b.VBucketServerMap.ServerList[which])
		if err != nil {
			return err
		}
	}
	return nil
}

func (b *Bucket) do(k string, f func(mc *memcached.Client, vb uint16) error) error {
	vb := b.VBHash(k)
	for {
		masterId := b.VBucketServerMap.VBucketMap[vb][0]
		if err := b.ensureConnection(masterId); err != nil {
			return err
		}
		err := f(b.connections[masterId], uint16(vb))
		switch err.(type) {
		default:
			return err
		case gomemcached.MCResponse:
			st := err.(gomemcached.MCResponse).Status
			atomic.AddUint64(&b.pool.client.Statuses[st], 1)
			if st == gomemcached.NOT_MY_VBUCKET {
				b.refresh()
			} else {
				return err
			}
		}
	}
	panic("Unreachable.")
}

type gathered_stats struct {
	sn   string
	vals map[string]string
}

func getStatsParallel(b *Bucket, offset int, which string, ch chan<- gathered_stats) {
	sn := b.VBucketServerMap.ServerList[offset]

	results := map[string]string{}
	err := b.ensureConnection(offset)
	if err != nil {
		ch <- gathered_stats{sn, results}
	} else {
		st := b.connections[offset].StatsMap(which)
		ch <- gathered_stats{sn, st}
	}
}

func (b *Bucket) GetStats(which string) map[string]map[string]string {
	// Go grab all the things at once.
	todo := len(b.VBucketServerMap.ServerList)
	ch := make(chan gathered_stats, todo)

	for offset, _ := range b.VBucketServerMap.ServerList {
		go getStatsParallel(b, offset, which, ch)
	}

	// Gather the results
	rv := map[string]map[string]string{}
	for i := 0; i < len(b.VBucketServerMap.ServerList); i++ {
		g := <-ch
		if len(g.vals) > 0 {
			rv[g.sn] = g.vals
		}
	}

	return rv
}

// Set a value in this bucket.
// The value will be serialized into a JSON document.
func (b *Bucket) Set(k string, v interface{}) error {
	return b.do(k, func(mc *memcached.Client, vb uint16) error {
		data, err := json.Marshal(v)
		if err != nil {
			return err
		}
		res := mc.Set(vb, k, 0, 0, data)
		if res.Status != gomemcached.SUCCESS {
			return res
		}
		return nil
	})
}

// Get a value from this bucket.
// The value is expected to be a JSON stream and will be deserialized
// into rv.
func (b *Bucket) Get(k string, rv interface{}) error {
	return b.do(k, func(mc *memcached.Client, vb uint16) error {
		res := mc.Get(vb, k)
		if res.Status != gomemcached.SUCCESS {
			return res
		}
		return json.Unmarshal(res.Body, rv)
	})
}

// Delete a key from this bucket.
func (b *Bucket) Delete(k string) error {
	return b.do(k, func(mc *memcached.Client, vb uint16) error {
		res := mc.Del(vb, k)
		if res.Status != gomemcached.SUCCESS {
			return res
		}
		return nil
	})
}

type ViewRow struct {
	ID    string
	Key   interface{}
	Value interface{}
	Doc   *interface{}
}

type ViewResult struct {
	TotalRows int `json:"total_rows"`
	Rows      []ViewRow
	Errors    []struct {
		From   string
		Reason string
	}
}

// Execute a view
func (b *Bucket) View(ddoc, name string, params map[string]interface{}) (vres ViewResult, err error) {
	// Pick a random node to service our request.
	node := b.Nodes[rand.Intn(len(b.Nodes))]
	u, err := url.Parse(node.CouchAPIBase)
	if err != nil {
		return ViewResult{}, err
	}

	values := url.Values{}
	for k, v := range params {
		values[k] = []string{fmt.Sprintf("%v", v)}
	}

	u.Path = fmt.Sprintf("/%s/_design/%s/_view/%s", b.Name, ddoc, name)
	u.RawQuery = values.Encode()

	res, err := http.Get(u.String())
	if err != nil {
		return ViewResult{}, err
	}
	defer res.Body.Close()

	if res.StatusCode != 200 {
		return ViewResult{}, errors.New(res.Status)
	}

	d := json.NewDecoder(res.Body)
	if err = d.Decode(&vres); err != nil {
		return ViewResult{}, err
	}
	return vres, nil

}
<|endoftext|>


<commit_before>package gode

import (
	"path/filepath"
	"runtime"
)

// The Node version to install.
// Override this by setting client.Version.
const DefaultNodeVersion = "0.10.34"
const DefaultNpmVersion = "2.1.14"

// Client is the interface between Node and Go.
// It also setups up the Node environment if needed.
type Client struct {
	RootPath    string
	NodeVersion string
	NpmVersion  string
	Registry    string
}

// NewClient creates a new Client at the specified rootPath
// The Node installation can then be setup here with client.Setup()
func NewClient(rootPath string) *Client {
	client := &Client{
		RootPath:    rootPath,
		NodeVersion: DefaultNodeVersion,
		NpmVersion:  DefaultNpmVersion,
	}

	return client
}

func (c *Client) nodeBase() string {
	switch {
	case runtime.GOARCH == "386":
		return "node-v" + c.NodeVersion + "-" + runtime.GOOS + "-x86"
	default:
		return "node-v" + c.NodeVersion + "-" + runtime.GOOS + "-x64"
	}
}

func (c *Client) nodeURL() string {
	switch {
	case runtime.GOOS == "windows" && runtime.GOARCH == "386":
		return "http://d1gvo455cekpjp.cloudfront.net/node/v" + c.NodeVersion + "/node.exe"
	case runtime.GOOS == "windows" && runtime.GOARCH == "amd64":
		return "http://d1gvo455cekpjp.cloudfront.net/node/v" + c.NodeVersion + "/x64/node.exe"
	case runtime.GOARCH == "386":
		return "http://d1gvo455cekpjp.cloudfront.net/node/v" + c.NodeVersion + "/" + c.nodeBase() + ".tar.gz"
	default:
		return "http://d1gvo455cekpjp.cloudfront.net/node/v" + c.NodeVersion + "/" + c.nodeBase() + ".tar.gz"
	}
}

func (c *Client) nodePath() string {
	switch {
	case runtime.GOOS == "windows":
		return filepath.Join(c.RootPath, c.nodeBase(), "bin", "node.exe")
	default:
		return filepath.Join(c.RootPath, c.nodeBase(), "bin", "node")
	}
}

func (c *Client) npmURL() string {
	return "http://d1gvo455cekpjp.cloudfront.net/npm/npm-" + c.NpmVersion + ".zip"
}

func (c *Client) npmPath() string {
	return filepath.Join(c.RootPath, c.nodeBase(), "lib", "node_modules", "npm", "cli.js")
}
<commit_msg>use cloudfront for downloading node/npm<commit_after>package gode

import (
	"path/filepath"
	"runtime"
)

// The Node version to install.
// Override this by setting client.Version.
const DefaultNodeVersion = "0.10.34"
const DefaultNpmVersion = "2.1.14"

const NodeCDN = "http://d1nhjzpj45o0rc.cloudfront.net"
const GithubCDN = "http://d2v1cis2kqjysd.cloudfront.net"

// Client is the interface between Node and Go.
// It also setups up the Node environment if needed.
type Client struct {
	RootPath    string
	NodeVersion string
	NpmVersion  string
	Registry    string
}

// NewClient creates a new Client at the specified rootPath
// The Node installation can then be setup here with client.Setup()
func NewClient(rootPath string) *Client {
	client := &Client{
		RootPath:    rootPath,
		NodeVersion: DefaultNodeVersion,
		NpmVersion:  DefaultNpmVersion,
	}

	return client
}

func (c *Client) nodeBase() string {
	switch {
	case runtime.GOARCH == "386":
		return "node-v" + c.NodeVersion + "-" + runtime.GOOS + "-x86"
	default:
		return "node-v" + c.NodeVersion + "-" + runtime.GOOS + "-x64"
	}
}

func (c *Client) nodeURL() string {
	switch {
	case runtime.GOOS == "windows" && runtime.GOARCH == "386":
		return NodeCDN + "/v" + c.NodeVersion + "/node.exe"
	case runtime.GOOS == "windows" && runtime.GOARCH == "amd64":
		return NodeCDN + "/v" + c.NodeVersion + "/x64/node.exe"
	case runtime.GOARCH == "386":
		return NodeCDN + "/v" + c.NodeVersion + "/" + c.nodeBase() + ".tar.gz"
	default:
		return NodeCDN + "/v" + c.NodeVersion + "/" + c.nodeBase() + ".tar.gz"
	}
}

func (c *Client) nodePath() string {
	switch {
	case runtime.GOOS == "windows":
		return filepath.Join(c.RootPath, c.nodeBase(), "bin", "node.exe")
	default:
		return filepath.Join(c.RootPath, c.nodeBase(), "bin", "node")
	}
}

func (c *Client) npmURL() string {
	return GithubCDN + "/npm/npm/archive/v" + c.NpmVersion + ".zip"
}

func (c *Client) npmPath() string {
	return filepath.Join(c.RootPath, c.nodeBase(), "lib", "node_modules", "npm", "cli.js")
}
<|endoftext|>


<commit_before>package main

import (
	"bufio"
	"fmt"
	"log"
	"net"
	"time"
)

func main() {
	conn, err := net.Dial("tcp", "127.0.0.1:1337")
	if err != nil {
		log.Fatalf("[ERROR] Can't create connection!: %v", err)
	}
	defer conn.Close()

	//Escrever na socket
	fmt.Fprintf(conn, "Testing connection")

	time.Sleep(1 * time.Second)

	response, _ := bufio.NewReader(conn).ReadString('\n')
	fmt.Println("[RESP] - ", response)
}
<commit_msg>Removed a sleep func<commit_after>package main

import (
	"bufio"
	"fmt"
	"log"
	"net"
)

func main() {
	conn, err := net.Dial("tcp", "127.0.0.1:1337")
	if err != nil {
		log.Fatalf("[ERROR] Can't create connection!: %v", err)
	}
	defer conn.Close()

	//Escrever na socket
	fmt.Fprintf(conn, "Testing connection")

	response, _ := bufio.NewReader(conn).ReadString('\n')
	fmt.Println("[RESP] - ", response)
}
<|endoftext|>


<commit_before>package redtable

import (
	"errors"
	"fmt"
	"strconv"
	"sync"

	"github.com/garyburd/redigo/redis"
)

const (
	opMulti   = "MULTI"
	opHGet    = "HGET"
	opHMGet   = "HMGET"
	opHDel    = "HDEL"
	opHSet    = "HSET"
	opHKeys   = "HKEYS"
	opHExists = "HEXISTS"
	opExec    = "EXEC"
	opDel     = "DEL"

	EnvRedisServerURL = "REDIS_SERVER_URL"
)

var (
	ErrConnectionAlreadyClosed = errors.New("connection already closed")
)

type Client struct {
	shutdownMu    sync.Mutex
	alreadyClosed bool
	// TODO: (@odeke-em) decide if we should have a pool of connections
	// instead of using one connection or different dials per operation.
	conn redis.Conn
}

var errExpectingAURL = fmt.Errorf("expecting at least one URL")

func New(redisServerURLs ...string) (*Client, error) {
	var conn redis.Conn
	var err error = errExpectingAURL
	for _, srvURL := range redisServerURLs {
		conn, err = redis.DialURL(srvURL)

		if err != nil {
			continue
		}

		client := &Client{conn: conn}
		return client, nil
	}

	return nil, err
}

func (c *Client) Close() error {
	c.shutdownMu.Lock()
	defer c.shutdownMu.Unlock()
	if c.alreadyClosed {
		return ErrConnectionAlreadyClosed
	}

	if err := c.conn.Close(); err != nil {
		return err
	}

	c.alreadyClosed = true
	return nil
}

func (c *Client) doHashOp(opName, hashTableName string, args ...interface{}) ([]interface{}, error) {
	if c.alreadyClosed {
		return nil, ErrConnectionAlreadyClosed
	}

	if err := c.conn.Send(opMulti); err != nil {
		return nil, err
	}

	allArgs := append([]interface{}{hashTableName}, args...)
	if sendErr := c.conn.Send(opName, allArgs...); sendErr != nil {
		return nil, sendErr
	}

	return redis.Values(c.conn.Do(opExec))
}

func (c *Client) HSet(hashTableName string, key, value interface{}) (interface{}, error) {
	replies, err := c.doHashOp(opHSet, hashTableName, key, value)
	if err != nil {
		return nil, err
	}
	return replies[0], nil
}

func multiKeysOp(c *Client, opName, hashTableName string, keys ...interface{}) ([]interface{}, error) {
	return c.doHashOp(opName, hashTableName, keys...)
}

func byKeyOp(c *Client, opName, hashTableName string, keys ...interface{}) (interface{}, error) {
	replies, err := multiKeysOp(c, opName, hashTableName, keys)
	if err != nil {
		return nil, err
	}
	return replies[0], nil
}

func (c *Client) HGet(hashTableName string, key interface{}) (interface{}, error) {
	return byKeyOp(c, opHGet, hashTableName, key)
}

func (c *Client) HMGet(hashTableName string, keys ...interface{}) ([]interface{}, error) {
	return c.doHashOp(opHMGet, hashTableName, keys...)
}

func (c *Client) HDel(hashTableName string, key interface{}) (interface{}, error) {
	return byKeyOp(c, opHDel, hashTableName, key)
}

// HPop performs a pop which is a combination of `HGet` and `HDel` from a HashTable
func (c *Client) HPop(hashTableName string, key interface{}) (interface{}, error) {
	// HPop is a combination of HGet+HDel, in that order
	retrieved, err := c.HGet(hashTableName, key)
	if err != nil {
		return nil, err
	}
	if _, err := c.HDel(hashTableName, key); err != nil {
		return nil, err
	}
	return retrieved, nil
}

// HMove moves the contents keyed by a key from hashTableName1 to hashTableName2
func (c *Client) HMove(hashTableName1, hashTableName2 string, key interface{}) (interface{}, error) {
	table1Entry, err := c.HPop(hashTableName1, key)
	if err != nil {
		return nil, err
	}

	if _, err := c.HSet(hashTableName2, key, table1Entry); err != nil {
		return nil, err
	}
	return table1Entry, nil
}

func (c *Client) HKeys(hashTableName string) ([]interface{}, error) {
	return c.doHashOp(opHKeys, hashTableName)
}

func (c *Client) HLen(hashTableName string) (int64, error) {
	replies, err := c.doHashOp(opHDel, hashTableName)
	if err != nil {
		return 0, err
	}

	first := replies[0]
	if count, ok := first.(int64); ok {
		return count, nil
	}

	vStr := fmt.Sprintf("%v", first)
	return strconv.ParseInt(vStr, 10, 64)
}

// Del deletes one or more collections by the
// collection name, where any of the types are:
// hash, set, sorted set, list.
func (c *Client) Del(firstTable string, otherTables ...interface{}) (interface{}, error) {
	return byKeyOp(c, opDel, firstTable, otherTables...)
}

func (c *Client) HExists(hashTableName string, key interface{}) (bool, error) {
	replies, err := c.doHashOp(opHExists, hashTableName, key)
	if err != nil {
		return false, err
	}

	first := replies[0]
	if existance, ok := first.(bool); ok {
		return existance, nil
	}

	return strconv.ParseBool(fmt.Sprintf("%v", first))
}
<commit_msg>multiKeyOp: fix unintended conversion from []interface{} to interface{}<commit_after>package redtable

import (
	"errors"
	"fmt"
	"strconv"
	"sync"

	"github.com/garyburd/redigo/redis"
)

const (
	opMulti   = "MULTI"
	opHGet    = "HGET"
	opHMGet   = "HMGET"
	opHDel    = "HDEL"
	opHSet    = "HSET"
	opHKeys   = "HKEYS"
	opHExists = "HEXISTS"
	opExec    = "EXEC"
	opDel     = "DEL"

	EnvRedisServerURL = "REDIS_SERVER_URL"
)

var (
	ErrConnectionAlreadyClosed = errors.New("connection already closed")
)

type Client struct {
	shutdownMu    sync.Mutex
	alreadyClosed bool
	// TODO: (@odeke-em) decide if we should have a pool of connections
	// instead of using one connection or different dials per operation.
	conn redis.Conn
}

var errExpectingAURL = fmt.Errorf("expecting at least one URL")

func New(redisServerURLs ...string) (*Client, error) {
	var conn redis.Conn
	var err error = errExpectingAURL
	for _, srvURL := range redisServerURLs {
		conn, err = redis.DialURL(srvURL)

		if err != nil {
			continue
		}

		client := &Client{conn: conn}
		return client, nil
	}

	return nil, err
}

func (c *Client) Close() error {
	c.shutdownMu.Lock()
	defer c.shutdownMu.Unlock()
	if c.alreadyClosed {
		return ErrConnectionAlreadyClosed
	}

	if err := c.conn.Close(); err != nil {
		return err
	}

	c.alreadyClosed = true
	return nil
}

func (c *Client) doHashOp(opName, hashTableName string, args ...interface{}) ([]interface{}, error) {
	if c.alreadyClosed {
		return nil, ErrConnectionAlreadyClosed
	}

	if err := c.conn.Send(opMulti); err != nil {
		return nil, err
	}

	allArgs := append([]interface{}{hashTableName}, args...)
	if sendErr := c.conn.Send(opName, allArgs...); sendErr != nil {
		return nil, sendErr
	}
	return redis.Values(c.conn.Do(opExec))
}

func (c *Client) HSet(hashTableName string, key, value interface{}) (interface{}, error) {
	replies, err := c.doHashOp(opHSet, hashTableName, key, value)
	if err != nil {
		return nil, err
	}
	return replies[0], nil
}

func multiKeysOp(c *Client, opName, hashTableName string, keys ...interface{}) ([]interface{}, error) {
	return c.doHashOp(opName, hashTableName, keys...)
}

func byKeyOp(c *Client, opName, hashTableName string, keys ...interface{}) (interface{}, error) {
	replies, err := multiKeysOp(c, opName, hashTableName, keys...)
	if err != nil {
		return nil, err
	}
	return replies[0], nil
}

func (c *Client) HGet(hashTableName string, key interface{}) (interface{}, error) {
	return byKeyOp(c, opHGet, hashTableName, key)
}

func (c *Client) HMGet(hashTableName string, keys ...interface{}) ([]interface{}, error) {
	return c.doHashOp(opHMGet, hashTableName, keys...)
}

func (c *Client) HDel(hashTableName string, key interface{}) (interface{}, error) {
	return byKeyOp(c, opHDel, hashTableName, key)
}

// HPop performs a pop which is a combination of `HGet` and `HDel` from a HashTable
func (c *Client) HPop(hashTableName string, key interface{}) (interface{}, error) {
	// HPop is a combination of HGet+HDel, in that order
	retrieved, err := c.HGet(hashTableName, key)
	if err != nil {
		return nil, err
	}
	if _, err := c.HDel(hashTableName, key); err != nil {
		return nil, err
	}
	return retrieved, nil
}

// HMove moves the contents keyed by a key from hashTableName1 to hashTableName2
func (c *Client) HMove(hashTableName1, hashTableName2 string, key interface{}) (interface{}, error) {
	table1Entry, err := c.HPop(hashTableName1, key)
	if err != nil {
		return nil, err
	}

	if _, err := c.HSet(hashTableName2, key, table1Entry); err != nil {
		return nil, err
	}
	return table1Entry, nil
}

func (c *Client) HKeys(hashTableName string) ([]interface{}, error) {
	return c.doHashOp(opHKeys, hashTableName)
}

func (c *Client) HLen(hashTableName string) (int64, error) {
	replies, err := c.doHashOp(opHDel, hashTableName)
	if err != nil {
		return 0, err
	}

	first := replies[0]
	if count, ok := first.(int64); ok {
		return count, nil
	}

	vStr := fmt.Sprintf("%v", first)
	return strconv.ParseInt(vStr, 10, 64)
}

// Del deletes one or more collections by the
// collection name, where any of the types are:
// hash, set, sorted set, list.
func (c *Client) Del(firstTable string, otherTables ...interface{}) (interface{}, error) {
	return byKeyOp(c, opDel, firstTable, otherTables...)
}

func (c *Client) HExists(hashTableName string, key interface{}) (bool, error) {
	replies, err := c.doHashOp(opHExists, hashTableName, key)
	if err != nil {
		return false, err
	}

	first := replies[0]
	if existance, ok := first.(bool); ok {
		return existance, nil
	}

	return strconv.ParseBool(fmt.Sprintf("%v", first))
}
<|endoftext|>


<commit_before>package main

import (
	"crypto/x509"
	"fmt"
	"io/ioutil"
	"os"
	"path/filepath"
	"regexp"
	"time"

	"github.com/ogier/pflag"
	"github.com/papertrail/remote_syslog2/papertrail"
	"github.com/papertrail/remote_syslog2/syslog"
	"github.com/papertrail/remote_syslog2/utils"
	"launchpad.net/goyaml"
)

const (
	MinimumRefreshInterval = (time.Duration(10) * time.Second)
	DefaultConfigFile      = "/etc/log_files.yml"
)

type ConfigFile struct {
	Files       []string
	Destination struct {
		Host     string `yaml:"host"`
		Port     int    `yaml:"port"`
		Protocol string `yaml:"protocol"`
	}
	Hostname       string `yaml:"hostname"`
	ConnectTimeout int    `yaml:"connect_timeout"`
	WriteTimeout   int    `yaml:"write_timeout"`
	//SetYAML is only called on pointers
	RefreshInterval  *RefreshInterval `yaml:"new_file_check_interval"`
	ExcludeFiles     *RegexCollection `yaml:"exclude_files"`
	ExcludePatterns  *RegexCollection `yaml:"exclude_patterns"`
	TcpMaxLineLength int              `yaml:"tcp_max_line_length"`
}

type ConfigManager struct {
	Config    ConfigFile
	FlagFiles []string
	Flags     struct {
		Hostname         string
		DestHost         string
		DestPort         int
		ConfigFile       string
		LogLevels        string
		DebugLogFile     string
		PidFile          string
		RefreshInterval  RefreshInterval
		UseTCP           bool
		UseTLS           bool
		NoDaemonize      bool
		Severity         string
		Facility         string
		Poll             bool
		TcpMaxLineLength int
	}
}

type RefreshInterval struct {
	Duration time.Duration
}

func (r *RefreshInterval) String() string {
	return fmt.Sprint(*r)
}

func (r *RefreshInterval) Set(value string) error {
	d, err := time.ParseDuration(value)

	if err != nil {
		return err
	}

	if d < MinimumRefreshInterval {
		return fmt.Errorf("refresh interval must be greater than %s", MinimumRefreshInterval)
	}
	r.Duration = d
	return nil
}

func (r *RefreshInterval) SetYAML(tag string, value interface{}) bool {
	err := r.Set(value.(string))
	if err != nil {
		return false
	}
	return true
}

type RegexCollection []*regexp.Regexp

func (r *RegexCollection) Set(value string) error {
	exp, err := regexp.Compile(value)
	if err != nil {
		return err
	}
	*r = append(*r, exp)
	return nil
}

func (r *RegexCollection) String() string {
	return fmt.Sprint(*r)
}

func (r *RegexCollection) SetYAML(tag string, value interface{}) bool {
	items, ok := value.([]interface{})

	if !ok {
		return false
	}

	for _, item := range items {
		s, ok := item.(string)

		if !ok {
			return false
		}

		err := r.Set(s)
		if err != nil {
			panic(fmt.Sprintf("Failed to compile regex expression \"%s\"", s))
		}
	}

	return true
}

func NewConfigManager() ConfigManager {
	cm := ConfigManager{}
	err := cm.Initialize()

	if err != nil {
		log.Criticalf("Failed to configure the application: %s", err)
		os.Exit(1)
	}

	return cm
}

func (cm *ConfigManager) Initialize() error {
	cm.Config.ExcludeFiles = &RegexCollection{}
	cm.Config.ExcludePatterns = &RegexCollection{}
	cm.parseFlags()

	err := cm.readConfig()
	if err != nil {
		return err
	}
	return nil
}

func (cm *ConfigManager) parseFlags() {
	pflag.StringVarP(&cm.Flags.ConfigFile, "configfile", "c", DefaultConfigFile, "Path to config")
	pflag.StringVarP(&cm.Flags.DestHost, "dest-host", "d", "", "Destination syslog hostname or IP")
	pflag.IntVarP(&cm.Flags.DestPort, "dest-port", "p", 0, "Destination syslog port")
	if utils.CanDaemonize {
		pflag.BoolVarP(&cm.Flags.NoDaemonize, "no-detach", "D", false, "Don't daemonize and detach from the terminal")
	} else {
		cm.Flags.NoDaemonize = true
	}
	pflag.StringVarP(&cm.Flags.Facility, "facility", "f", "user", "Facility")
	pflag.StringVar(&cm.Flags.Hostname, "hostname", "", "Local hostname to send from")
	pflag.StringVar(&cm.Flags.PidFile, "pid-file", "", "Location of the PID file")
	// --parse-syslog
	pflag.StringVarP(&cm.Flags.Severity, "severity", "s", "notice", "Severity")
	// --strip-color
	pflag.BoolVar(&cm.Flags.UseTCP, "tcp", false, "Connect via TCP (no TLS)")
	pflag.BoolVar(&cm.Flags.UseTLS, "tls", false, "Connect via TCP with TLS")
	pflag.BoolVar(&cm.Flags.Poll, "poll", false, "Detect changes by polling instead of inotify")
	pflag.Var(&cm.Flags.RefreshInterval, "new-file-check-interval", "How often to check for new files")
	_ = pflag.Bool("no-eventmachine-tail", false, "No action, provided for backwards compatibility")
	_ = pflag.Bool("eventmachine-tail", false, "No action, provided for backwards compatibility")
	pflag.StringVar(&cm.Flags.DebugLogFile, "debug-log-cfg", "", "the debug log file")
	pflag.StringVar(&cm.Flags.LogLevels, "log", "<root>=INFO", "set loggo config, like: --log=\"<root>=DEBUG\"")
	pflag.IntVar(&cm.Flags.TcpMaxLineLength, "tcp-max-line-length", 0, "Maximum TCP line length")
	pflag.Parse()
	cm.FlagFiles = pflag.Args()
}

func (cm *ConfigManager) readConfig() error {
	log.Infof("Reading configuration file %s", cm.Flags.ConfigFile)
	err := cm.loadConfigFile()
	if err != nil {
		log.Errorf("%s", err)
		return err
	}
	return nil
}

func (cm *ConfigManager) loadConfigFile() error {
	file, err := ioutil.ReadFile(cm.Flags.ConfigFile)
	// don't error if the default config file isn't found
	if os.IsNotExist(err) && cm.Flags.ConfigFile == DefaultConfigFile {
		return nil
	}
	if err != nil {
		return fmt.Errorf("Could not read the config file: %s", err)
	}

	err = goyaml.Unmarshal(file, &cm.Config)
	if err != nil {
		return fmt.Errorf("Could not parse the config file: %s", err)
	}
	return nil
}

func (cm *ConfigManager) Daemonize() bool {
	return !cm.Flags.NoDaemonize
}

func (cm *ConfigManager) Hostname() string {
	switch {
	case cm.Flags.Hostname != "":
		return cm.Flags.Hostname
	case cm.Config.Hostname != "":
		return cm.Config.Hostname
	default:
		hostname, _ := os.Hostname()
		return hostname
	}
}

func (cm *ConfigManager) RootCAs() *x509.CertPool {
	if cm.DestProtocol() == "tls" && cm.DestHost() == "logs.papertrailapp.com" {
		return papertrail.RootCA()
	} else {
		return nil
	}
}

func (cm *ConfigManager) DestHost() string {
	switch {
	case cm.Flags.DestHost != "":
		return cm.Flags.DestHost
	case cm.Config.Destination.Host == "":
		log.Criticalf("No destination hostname specified")
		os.Exit(1)
	}
	return cm.Config.Destination.Host
}

func (cm ConfigManager) DestPort() int {
	switch {
	case cm.Flags.DestPort != 0:
		return cm.Flags.DestPort
	case cm.Config.Destination.Port != 0:
		return cm.Config.Destination.Port
	default:
		return 514
	}
}

func (cm *ConfigManager) DestProtocol() string {
	switch {
	case cm.Flags.UseTLS:
		return "tls"
	case cm.Flags.UseTCP:
		return "tcp"
	case cm.Config.Destination.Protocol != "":
		return cm.Config.Destination.Protocol
	default:
		return "udp"
	}
}

func (cm *ConfigManager) TcpMaxLineLength() int {
	switch {
	case cm.Flags.TcpMaxLineLength != 0:
		return cm.Flags.TcpMaxLineLength
	case cm.Config.TcpMaxLineLength != 0:
		return cm.Config.TcpMaxLineLength
	default:
		return 100000
	}
}

func (cm *ConfigManager) Severity() syslog.Priority {
	s, err := syslog.Severity(cm.Flags.Severity)
	if err != nil {
		log.Criticalf("%s is not a designated facility", cm.Flags.Severity)
		os.Exit(1)
	}
	return s
}

func (cm *ConfigManager) Facility() syslog.Priority {
	f, err := syslog.Facility(cm.Flags.Facility)
	if err != nil {
		log.Criticalf("%s is not a designated facility", cm.Flags.Facility)
		os.Exit(1)
	}
	return f
}

func (cm *ConfigManager) Poll() bool {
	return cm.Flags.Poll
}

func (cm *ConfigManager) Files() []string {
	return append(cm.FlagFiles, cm.Config.Files...)
}

func (cm *ConfigManager) DebugLogFile() string {
	switch {
	case cm.Flags.DebugLogFile != "":
		return cm.Flags.DebugLogFile
	default:
		return "/dev/null"
	}
}

func (cm *ConfigManager) defaultPidFile() string {
	pidFiles := []string{
		"/var/run/remote_syslog.pid",
		os.Getenv("HOME") + "/run/remote_syslog.pid",
		os.Getenv("HOME") + "/tmp/remote_syslog.pid",
		os.Getenv("HOME") + "/remote_syslog.pid",
		os.TempDir() + "/remote_syslog.pid",
		os.Getenv("TMPDIR") + "/remote_syslog.pid",
	}
	for _, f := range pidFiles {
		dir := filepath.Dir(f)
		dirStat, err := os.Stat(dir)
		if err != nil || dirStat == nil || !dirStat.IsDir() {
			continue
		}
		fd, err := os.OpenFile(f, os.O_WRONLY|os.O_CREATE, 0644)
		if err != nil {
			continue
		}
		fd.Close()
		return f
	}
	return "/tmp/remote_syslog.pid"
}

func (cm *ConfigManager) PidFile() string {
	switch {
	case cm.Flags.PidFile != "":
		return cm.Flags.PidFile
	default:
		return cm.defaultPidFile()
	}
}

func (cm *ConfigManager) LogLevels() string {
	return cm.Flags.LogLevels
}

func (cm *ConfigManager) RefreshInterval() RefreshInterval {
	switch {
	case cm.Config.RefreshInterval != nil && cm.Flags.RefreshInterval.Duration != 0:
		return cm.Flags.RefreshInterval
	case cm.Config.RefreshInterval != nil:
		return *cm.Config.RefreshInterval
	case cm.Flags.RefreshInterval.Duration != 0:
		return cm.Flags.RefreshInterval
	}
	return RefreshInterval{Duration: MinimumRefreshInterval}
}

func (cm *ConfigManager) ExcludeFiles() []*regexp.Regexp {
	return *cm.Config.ExcludeFiles
}

func (cm *ConfigManager) ExcludePatterns() []*regexp.Regexp {
	return *cm.Config.ExcludePatterns
}

func (cm *ConfigManager) ConnectTimeout() time.Duration {
	connectTimeout := cm.Config.ConnectTimeout
	if connectTimeout != 0 {
		return time.Duration(connectTimeout) * time.Second
	}
	return time.Duration(30) * time.Second
}

func (cmd *ConfigManager) WriteTimeout() time.Duration {
	writeTimeout := cmd.Config.WriteTimeout
	if writeTimeout != 0 {
		return time.Duration(writeTimeout) * time.Second
	}
	return time.Duration(30) * time.Second
}
<commit_msg>Stay under the 100000 byte limit<commit_after>package main

import (
	"crypto/x509"
	"fmt"
	"io/ioutil"
	"os"
	"path/filepath"
	"regexp"
	"time"

	"github.com/ogier/pflag"
	"github.com/papertrail/remote_syslog2/papertrail"
	"github.com/papertrail/remote_syslog2/syslog"
	"github.com/papertrail/remote_syslog2/utils"
	"launchpad.net/goyaml"
)

const (
	MinimumRefreshInterval = (time.Duration(10) * time.Second)
	DefaultConfigFile      = "/etc/log_files.yml"
)

type ConfigFile struct {
	Files       []string
	Destination struct {
		Host     string `yaml:"host"`
		Port     int    `yaml:"port"`
		Protocol string `yaml:"protocol"`
	}
	Hostname       string `yaml:"hostname"`
	ConnectTimeout int    `yaml:"connect_timeout"`
	WriteTimeout   int    `yaml:"write_timeout"`
	//SetYAML is only called on pointers
	RefreshInterval  *RefreshInterval `yaml:"new_file_check_interval"`
	ExcludeFiles     *RegexCollection `yaml:"exclude_files"`
	ExcludePatterns  *RegexCollection `yaml:"exclude_patterns"`
	TcpMaxLineLength int              `yaml:"tcp_max_line_length"`
}

type ConfigManager struct {
	Config    ConfigFile
	FlagFiles []string
	Flags     struct {
		Hostname         string
		DestHost         string
		DestPort         int
		ConfigFile       string
		LogLevels        string
		DebugLogFile     string
		PidFile          string
		RefreshInterval  RefreshInterval
		UseTCP           bool
		UseTLS           bool
		NoDaemonize      bool
		Severity         string
		Facility         string
		Poll             bool
		TcpMaxLineLength int
	}
}

type RefreshInterval struct {
	Duration time.Duration
}

func (r *RefreshInterval) String() string {
	return fmt.Sprint(*r)
}

func (r *RefreshInterval) Set(value string) error {
	d, err := time.ParseDuration(value)

	if err != nil {
		return err
	}

	if d < MinimumRefreshInterval {
		return fmt.Errorf("refresh interval must be greater than %s", MinimumRefreshInterval)
	}
	r.Duration = d
	return nil
}

func (r *RefreshInterval) SetYAML(tag string, value interface{}) bool {
	err := r.Set(value.(string))
	if err != nil {
		return false
	}
	return true
}

type RegexCollection []*regexp.Regexp

func (r *RegexCollection) Set(value string) error {
	exp, err := regexp.Compile(value)
	if err != nil {
		return err
	}
	*r = append(*r, exp)
	return nil
}

func (r *RegexCollection) String() string {
	return fmt.Sprint(*r)
}

func (r *RegexCollection) SetYAML(tag string, value interface{}) bool {
	items, ok := value.([]interface{})

	if !ok {
		return false
	}

	for _, item := range items {
		s, ok := item.(string)

		if !ok {
			return false
		}

		err := r.Set(s)
		if err != nil {
			panic(fmt.Sprintf("Failed to compile regex expression \"%s\"", s))
		}
	}

	return true
}

func NewConfigManager() ConfigManager {
	cm := ConfigManager{}
	err := cm.Initialize()

	if err != nil {
		log.Criticalf("Failed to configure the application: %s", err)
		os.Exit(1)
	}

	return cm
}

func (cm *ConfigManager) Initialize() error {
	cm.Config.ExcludeFiles = &RegexCollection{}
	cm.Config.ExcludePatterns = &RegexCollection{}
	cm.parseFlags()

	err := cm.readConfig()
	if err != nil {
		return err
	}
	return nil
}

func (cm *ConfigManager) parseFlags() {
	pflag.StringVarP(&cm.Flags.ConfigFile, "configfile", "c", DefaultConfigFile, "Path to config")
	pflag.StringVarP(&cm.Flags.DestHost, "dest-host", "d", "", "Destination syslog hostname or IP")
	pflag.IntVarP(&cm.Flags.DestPort, "dest-port", "p", 0, "Destination syslog port")
	if utils.CanDaemonize {
		pflag.BoolVarP(&cm.Flags.NoDaemonize, "no-detach", "D", false, "Don't daemonize and detach from the terminal")
	} else {
		cm.Flags.NoDaemonize = true
	}
	pflag.StringVarP(&cm.Flags.Facility, "facility", "f", "user", "Facility")
	pflag.StringVar(&cm.Flags.Hostname, "hostname", "", "Local hostname to send from")
	pflag.StringVar(&cm.Flags.PidFile, "pid-file", "", "Location of the PID file")
	// --parse-syslog
	pflag.StringVarP(&cm.Flags.Severity, "severity", "s", "notice", "Severity")
	// --strip-color
	pflag.BoolVar(&cm.Flags.UseTCP, "tcp", false, "Connect via TCP (no TLS)")
	pflag.BoolVar(&cm.Flags.UseTLS, "tls", false, "Connect via TCP with TLS")
	pflag.BoolVar(&cm.Flags.Poll, "poll", false, "Detect changes by polling instead of inotify")
	pflag.Var(&cm.Flags.RefreshInterval, "new-file-check-interval", "How often to check for new files")
	_ = pflag.Bool("no-eventmachine-tail", false, "No action, provided for backwards compatibility")
	_ = pflag.Bool("eventmachine-tail", false, "No action, provided for backwards compatibility")
	pflag.StringVar(&cm.Flags.DebugLogFile, "debug-log-cfg", "", "the debug log file")
	pflag.StringVar(&cm.Flags.LogLevels, "log", "<root>=INFO", "set loggo config, like: --log=\"<root>=DEBUG\"")
	pflag.IntVar(&cm.Flags.TcpMaxLineLength, "tcp-max-line-length", 0, "Maximum TCP line length")
	pflag.Parse()
	cm.FlagFiles = pflag.Args()
}

func (cm *ConfigManager) readConfig() error {
	log.Infof("Reading configuration file %s", cm.Flags.ConfigFile)
	err := cm.loadConfigFile()
	if err != nil {
		log.Errorf("%s", err)
		return err
	}
	return nil
}

func (cm *ConfigManager) loadConfigFile() error {
	file, err := ioutil.ReadFile(cm.Flags.ConfigFile)
	// don't error if the default config file isn't found
	if os.IsNotExist(err) && cm.Flags.ConfigFile == DefaultConfigFile {
		return nil
	}
	if err != nil {
		return fmt.Errorf("Could not read the config file: %s", err)
	}

	err = goyaml.Unmarshal(file, &cm.Config)
	if err != nil {
		return fmt.Errorf("Could not parse the config file: %s", err)
	}
	return nil
}

func (cm *ConfigManager) Daemonize() bool {
	return !cm.Flags.NoDaemonize
}

func (cm *ConfigManager) Hostname() string {
	switch {
	case cm.Flags.Hostname != "":
		return cm.Flags.Hostname
	case cm.Config.Hostname != "":
		return cm.Config.Hostname
	default:
		hostname, _ := os.Hostname()
		return hostname
	}
}

func (cm *ConfigManager) RootCAs() *x509.CertPool {
	if cm.DestProtocol() == "tls" && cm.DestHost() == "logs.papertrailapp.com" {
		return papertrail.RootCA()
	} else {
		return nil
	}
}

func (cm *ConfigManager) DestHost() string {
	switch {
	case cm.Flags.DestHost != "":
		return cm.Flags.DestHost
	case cm.Config.Destination.Host == "":
		log.Criticalf("No destination hostname specified")
		os.Exit(1)
	}
	return cm.Config.Destination.Host
}

func (cm ConfigManager) DestPort() int {
	switch {
	case cm.Flags.DestPort != 0:
		return cm.Flags.DestPort
	case cm.Config.Destination.Port != 0:
		return cm.Config.Destination.Port
	default:
		return 514
	}
}

func (cm *ConfigManager) DestProtocol() string {
	switch {
	case cm.Flags.UseTLS:
		return "tls"
	case cm.Flags.UseTCP:
		return "tcp"
	case cm.Config.Destination.Protocol != "":
		return cm.Config.Destination.Protocol
	default:
		return "udp"
	}
}

func (cm *ConfigManager) TcpMaxLineLength() int {
	switch {
	case cm.Flags.TcpMaxLineLength != 0:
		return cm.Flags.TcpMaxLineLength
	case cm.Config.TcpMaxLineLength != 0:
		return cm.Config.TcpMaxLineLength
	default:
		return 99990
	}
}

func (cm *ConfigManager) Severity() syslog.Priority {
	s, err := syslog.Severity(cm.Flags.Severity)
	if err != nil {
		log.Criticalf("%s is not a designated facility", cm.Flags.Severity)
		os.Exit(1)
	}
	return s
}

func (cm *ConfigManager) Facility() syslog.Priority {
	f, err := syslog.Facility(cm.Flags.Facility)
	if err != nil {
		log.Criticalf("%s is not a designated facility", cm.Flags.Facility)
		os.Exit(1)
	}
	return f
}

func (cm *ConfigManager) Poll() bool {
	return cm.Flags.Poll
}

func (cm *ConfigManager) Files() []string {
	return append(cm.FlagFiles, cm.Config.Files...)
}

func (cm *ConfigManager) DebugLogFile() string {
	switch {
	case cm.Flags.DebugLogFile != "":
		return cm.Flags.DebugLogFile
	default:
		return "/dev/null"
	}
}

func (cm *ConfigManager) defaultPidFile() string {
	pidFiles := []string{
		"/var/run/remote_syslog.pid",
		os.Getenv("HOME") + "/run/remote_syslog.pid",
		os.Getenv("HOME") + "/tmp/remote_syslog.pid",
		os.Getenv("HOME") + "/remote_syslog.pid",
		os.TempDir() + "/remote_syslog.pid",
		os.Getenv("TMPDIR") + "/remote_syslog.pid",
	}
	for _, f := range pidFiles {
		dir := filepath.Dir(f)
		dirStat, err := os.Stat(dir)
		if err != nil || dirStat == nil || !dirStat.IsDir() {
			continue
		}
		fd, err := os.OpenFile(f, os.O_WRONLY|os.O_CREATE, 0644)
		if err != nil {
			continue
		}
		fd.Close()
		return f
	}
	return "/tmp/remote_syslog.pid"
}

func (cm *ConfigManager) PidFile() string {
	switch {
	case cm.Flags.PidFile != "":
		return cm.Flags.PidFile
	default:
		return cm.defaultPidFile()
	}
}

func (cm *ConfigManager) LogLevels() string {
	return cm.Flags.LogLevels
}

func (cm *ConfigManager) RefreshInterval() RefreshInterval {
	switch {
	case cm.Config.RefreshInterval != nil && cm.Flags.RefreshInterval.Duration != 0:
		return cm.Flags.RefreshInterval
	case cm.Config.RefreshInterval != nil:
		return *cm.Config.RefreshInterval
	case cm.Flags.RefreshInterval.Duration != 0:
		return cm.Flags.RefreshInterval
	}
	return RefreshInterval{Duration: MinimumRefreshInterval}
}

func (cm *ConfigManager) ExcludeFiles() []*regexp.Regexp {
	return *cm.Config.ExcludeFiles
}

func (cm *ConfigManager) ExcludePatterns() []*regexp.Regexp {
	return *cm.Config.ExcludePatterns
}

func (cm *ConfigManager) ConnectTimeout() time.Duration {
	connectTimeout := cm.Config.ConnectTimeout
	if connectTimeout != 0 {
		return time.Duration(connectTimeout) * time.Second
	}
	return time.Duration(30) * time.Second
}

func (cmd *ConfigManager) WriteTimeout() time.Duration {
	writeTimeout := cmd.Config.WriteTimeout
	if writeTimeout != 0 {
		return time.Duration(writeTimeout) * time.Second
	}
	return time.Duration(30) * time.Second
}
<|endoftext|>


<commit_before>/*
 * chronos-client
 * Copyright (c) 2015 Yieldbot, Inc. (http://github.com/yieldbot/chronos-client)
 * For the full copyright and license information, please view the LICENSE.txt file.
 */

// Package client provides Chronos operations
package client

import (
	"bytes"
	"encoding/json"
	"errors"
	"fmt"
	"io/ioutil"
	"net/http"
	"strings"
)

// Client represents the Chronos client interface
type Client struct {
	URL string
}

// Jobs returns Chronos jobs
func (cl Client) Jobs() ([]Job, error) {

	// Get jobs
	req, err := http.NewRequest("GET", cl.URL+"/scheduler/jobs", nil)
	res, err := cl.doRequest(req)
	if err != nil {
		return nil, errors.New("failed to fetch jobs due to " + err.Error())
	}

	// Parse jobs
	var jobs []Job
	if err = json.Unmarshal(res, &jobs); err != nil {
		return nil, errors.New("failed to unmarshal JSON data due to " + err.Error())
	}

	return jobs, nil
}

// PrintJobs prints jobs
func (cl Client) PrintJobs(pretty bool) error {

	// Get jobs
	jobs, err := cl.Jobs()
	if err != nil {
		return err
	}

	// Parse jobs
	var jsonb []byte

	// If pretty is true then
	if pretty {
		jsonb, err = json.MarshalIndent(jobs, "", "  ")
	} else {
		// Otherwise just parse it
		jsonb, err = json.Marshal(jobs)
	}

	if err != nil {
		return err
	}

	fmt.Printf("%s", jsonb)

	return nil
}

// RunJob runs a Chronos job by the given job name
func (cl Client) RunJob(name, args string) (bool, error) {

	// Check job
	if name == "" {
		return false, errors.New("invalid job name")
	}

	query := name
	if args != "" {
		query += fmt.Sprintf("?arguments=%s", args)
	}

	// Run job
	req, err := http.NewRequest("PUT", cl.URL+"/scheduler/job/"+query, nil)
	res, err := cl.doRequest(req)
	if bytes.Index(res, []byte("not found")) != -1 {
		return true, errors.New(name + " job couldn't be found")
	} else if err != nil {
		return false, errors.New("failed to run job due to " + err.Error())
	}

	return true, nil
}

// DeleteJob deletes a Chronos job by the given job name
func (cl Client) DeleteJob(name string) (bool, error) {

	// Check job
	if name == "" {
		return false, errors.New("invalid job name")
	}

	// Delete job
	req, err := http.NewRequest("DELETE", cl.URL+"/scheduler/job/"+name, nil)
	res, err := cl.doRequest(req)
	if err != nil {
		return false, errors.New("failed to delete job due to " + err.Error())
	} else if bytes.Index(res, []byte("not found")) != -1 {
		//if strings.Index(string(res), "not found") != -1 {
		return true, errors.New(name + " job couldn't be found")
	}

	return true, nil
}

// KillTasks kills Chronos job tasks by the given job name
func (cl Client) KillTasks(name string) (bool, error) {

	// Check job
	if name == "" {
		return false, errors.New("invalid job name")
	}

	// Kill job tasks
	req, err := http.NewRequest("DELETE", cl.URL+"/scheduler/task/kill/"+name, nil)
	_, err = cl.doRequest(req)
	if err != nil && strings.Index(err.Error(), "bad response") != -1 {
		return true, errors.New(name + " job couldn't be found")
	} else if err != nil {
		return false, errors.New("failed to kill tasks due to " + err.Error())
	}

	return true, nil
}

// doRequest makes a request to Chronos by the given request
func (cl Client) doRequest(req *http.Request) ([]byte, error) {

	// Init a client
	client := &http.Client{}

	// Do request
	resp, err := client.Do(req)
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()

	// Read data
	data, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return nil, err
	}

	if resp.StatusCode < 200 || resp.StatusCode > 299 {
		return data, errors.New("bad response: " + fmt.Sprintf("%d", resp.StatusCode))
	}

	return data, nil
}
<commit_msg>Add AddJob func<commit_after>/*
 * chronos-client
 * Copyright (c) 2015 Yieldbot, Inc. (http://github.com/yieldbot/chronos-client)
 * For the full copyright and license information, please view the LICENSE.txt file.
 */

// Package client provides Chronos operations
package client

import (
	"bytes"
	"encoding/json"
	"errors"
	"fmt"
	"io/ioutil"
	"net/http"
	"strings"
)

// Client represents the Chronos client interface
type Client struct {
	URL string
}

// Jobs returns Chronos jobs
func (cl Client) Jobs() ([]Job, error) {

	// Get jobs
	req, err := http.NewRequest("GET", cl.URL+"/scheduler/jobs", nil)
	res, err := cl.doRequest(req)
	if err != nil {
		return nil, errors.New("failed to fetch jobs due to " + err.Error())
	}

	// Parse jobs
	var jobs []Job
	if err = json.Unmarshal(res, &jobs); err != nil {
		return nil, errors.New("failed to unmarshal JSON data due to " + err.Error())
	}

	return jobs, nil
}

// PrintJobs prints jobs
func (cl Client) PrintJobs(pretty bool) error {

	// Get jobs
	jobs, err := cl.Jobs()
	if err != nil {
		return err
	}

	// Parse jobs
	var jsonb []byte

	// If pretty is true then
	if pretty {
		jsonb, err = json.MarshalIndent(jobs, "", "  ")
	} else {
		// Otherwise just parse it
		jsonb, err = json.Marshal(jobs)
	}

	if err != nil {
		return err
	}

	fmt.Printf("%s", jsonb)

	return nil
}

// AddJob adds a Chronos job by the given json content
func (cl Client) AddJob(jsonc string) (bool, error) {

	// Check job
	jsonb := []byte(jsonc)
	var job Job
	if err := json.Unmarshal(jsonb, &job); err != nil {
		return false, errors.New("failed to unmarshal JSON data due to " + err.Error())
	}

	// Add job
	req, err := http.NewRequest("POST", cl.URL+"/scheduler/iso8601", bytes.NewBuffer(jsonb))
	req.Header.Set("Content-Type", "application/json")
	_, err = cl.doRequest(req)
	if err != nil {
		return false, errors.New("failed to add job due to " + err.Error())
	}

	return true, nil
}

// RunJob runs a Chronos job by the given job name
func (cl Client) RunJob(name, args string) (bool, error) {

	// Check job
	if name == "" {
		return false, errors.New("invalid job name")
	}

	query := name
	if args != "" {
		query += fmt.Sprintf("?arguments=%s", args)
	}

	// Run job
	req, err := http.NewRequest("PUT", cl.URL+"/scheduler/job/"+query, nil)
	res, err := cl.doRequest(req)
	if bytes.Index(res, []byte("not found")) != -1 {
		return true, errors.New(name + " job couldn't be found")
	} else if err != nil {
		return false, errors.New("failed to run job due to " + err.Error())
	}

	return true, nil
}

// DeleteJob deletes a Chronos job by the given job name
func (cl Client) DeleteJob(name string) (bool, error) {

	// Check job
	if name == "" {
		return false, errors.New("invalid job name")
	}

	// Delete job
	req, err := http.NewRequest("DELETE", cl.URL+"/scheduler/job/"+name, nil)
	res, err := cl.doRequest(req)
	if err != nil {
		return false, errors.New("failed to delete job due to " + err.Error())
	} else if bytes.Index(res, []byte("not found")) != -1 {
		//if strings.Index(string(res), "not found") != -1 {
		return true, errors.New(name + " job couldn't be found")
	}

	return true, nil
}

// KillTasks kills Chronos job tasks by the given job name
func (cl Client) KillTasks(name string) (bool, error) {

	// Check job
	if name == "" {
		return false, errors.New("invalid job name")
	}

	// Kill job tasks
	req, err := http.NewRequest("DELETE", cl.URL+"/scheduler/task/kill/"+name, nil)
	_, err = cl.doRequest(req)
	if err != nil && strings.Index(err.Error(), "bad response") != -1 {
		return true, errors.New(name + " job couldn't be found")
	} else if err != nil {
		return false, errors.New("failed to kill tasks due to " + err.Error())
	}

	return true, nil
}

// doRequest makes a request to Chronos by the given request
func (cl Client) doRequest(req *http.Request) ([]byte, error) {

	// Init a client
	client := &http.Client{}

	// Do request
	resp, err := client.Do(req)
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()

	// Read data
	data, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return nil, err
	}

	if resp.StatusCode < 200 || resp.StatusCode > 299 {
		return data, errors.New("bad response: " + fmt.Sprintf("%d", resp.StatusCode))
	}

	return data, nil
}
<|endoftext|>